在使用 CMake 构建 Windows DLL 期间运行单元测试

Posted

技术标签:

【中文标题】在使用 CMake 构建 Windows DLL 期间运行单元测试【英文标题】:Running unit tests during build of Windows DLL with CMake 【发布时间】:2013-09-13 21:25:08 【问题描述】:

我有一个用 cmake 构建的 C++ 库。该库通过单元测试进行测试,我希望将其作为构建的一部分运行。我希望在库或单元测试源更改时运行测试。

我在 cmake 邮件列表的过去帖子中找到了一些帮助:

http://www.cmake.org/pipermail/cmake/2010-January/034419.html

这种方法在单元测试程序上使用 add_custom_command(),当库是共享的或静态的时适用于 Linux,当库是静态的时适用于 Windows。问题是构建在 Windows 上共享的库。在这种情况下,测试会在初始构建中运行,但在库更改时不会运行。

这是一个说明问题的例子:

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project(xyz)

SET(BUILD_SHARED_LIBS ON)

#----------- build library
if($BUILD_SHARED_LIBS)
    add_definitions(-DXYZ_SHARED)
endif($BUILD_SHARED_LIBS)
add_library($PROJECT_NAME xyz.cpp xyz.h)

#----------- build unit tests program
add_executable($PROJECT_NAME_unit_tests unit_tests.cpp)
target_link_libraries($PROJECT_NAME_unit_tests $PROJECT_NAME)

#----------- run unit tests program after it is built
add_custom_command(TARGET $PROJECT_NAME_unit_tests
    POST_BUILD COMMAND $PROJECT_NAME_unit_tests)

xyz.h

#pragma once

#ifdef XYZ_SHARED
#ifdef _WIN32
#ifdef xyz_EXPORTS
#define XYZ_EXPORT __declspec(dllexport)
#else
#define XYZ_EXPORT __declspec(dllimport)
#endif
#else //_WIN32
#define XYZ_EXPORT
#endif //_WIN32
#else //XYZ_SHARED
#define XYZ_EXPORT
#endif //XYZ_SHARED

XYZ_EXPORT bool xyz_return_true();

xyz.cpp

#include "xyz.h"

XYZ_EXPORT bool xyz_return_true()

    return true;

unit_tests.cpp

#include <iostream>
#include "xyz.h"

int main(int argc, char *argv[])

    using namespace std;

    cout << "running unit tests: ";

    if (xyz_return_true()) 
        //no error
        cout << "pass" << endl;
        return 0;
    

    //error
    cout << "fail" << endl;
    return 1;

如果我在 Windows 上构建,然后将 xyz.cpp 的函数更改为返回 false 并再次构建,则不会运行测试。在这种情况下,我该怎么做才能使测试运行?

我认为测试没有运行,因为在构建时 xyz_unit_tests.exe 仅依赖于导入库 (xyz.lib)。如果库接口不改变,导入库也不会改变。

我正在使用 Visual Studio 10 和 cmake 2.8.11.2。

【问题讨论】:

你了解CTest吗? 我在解决这个问题时查看了 CTest。除非我错过了文档中的某些内容,否则它需要您运行一个单独的目标(“make test”)来运行测试。我没有看到在构建库时会自动运行测试的任何东西。 @mistapink 【参考方案1】:

您可以通过删除作为库的构建后事件的测试可执行文件来触发测试重建。所以,例如:

if(WIN32 AND BUILD_SHARED_LIBS)
  #----------- trigger rebuild of tests if lib has changed
  get_target_property(TestPath $PROJECT_NAME_unit_tests LOCATION)
  add_custom_command(TARGET $PROJECT_NAME
      POST_BUILD COMMAND $CMAKE_COMMAND -E remove $TestPath
      COMMENT "Removing $TestPath")
endif()

您必须在COMMAND 参数中使用LOCATION 目标属性而不是生成器表达式,因为这不会在CMake 的依赖关系图中引入循环依赖关系。理想情况下,我们将COMMAND 参数设置为:

COMMAND $CMAKE_COMMAND -E remove $<TARGET_FILE:$PROJECT_NAME_unit_tests>

但这会导致库依赖于可执行文件,显然target_link_libraries 调用会导致 exe 依赖于库。

可能有更好的方法使测试可执行文件过时,而不是仅仅删除它;这对我来说似乎有点蛮力。但它应该可以工作。

【讨论】:

以上是关于在使用 CMake 构建 Windows DLL 期间运行单元测试的主要内容,如果未能解决你的问题,请参考以下文章

使用 Cmake 为 Visual Studio 2015 构建 OpenCV 3.0.0 的 DLL

使用 CMake 将 Qt DLL 复制到 Windows 上的可执行目录

CMake - 构建没有首选 32 位标志的 C#

Cmake 问题。复制 dll 构建后

使用 cmake 和 msvc 2010 构建 gdcm 2.4.1 无法获取所有 dll

使用 mingw-w64 和 cmake 构建 32 位和 64 位应用程序