CMake 中的全局自定义目标

Global custom target in CMake

我想用 CMake 编写一个小的单元测试系统,我可以轻松地将其复制并粘贴到我的所有项目中,所以我想到了这个:

include(CMakeParseArguments)
set(UNIT_TEST "unit_tests")
add_custom_target(${UNIT_TEST} ALL VERBATIM)

function(add_unit_test dependency)
    cmake_parse_arguments(UT_ "" "NAME" "" ${ARGN})
    if(NOT ${UT_NAME})
        set(${UT_NAME} ${ARG0})
    endif()

    add_test(${ARGN})
    add_dependencies(${UNIT_TEST} ${dependency})
    add_custom_command(TARGET ${UNIT_TEST}
                   COMMENT "Run tests"
                   POST_BUILD COMMAND ctest
                   ARGS -R ${UT_NAME} --output-on-failures
                   WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
                   VERBATIM)
endfunction(add_unit_test)

我希望它能像那样正常工作,并且 运行 我将通过调用 add_unit_test(dep ...) 添加到我的项目中的所有单元测试,并在之前进行编译,然后使用与 add_test(...)。实际上这个错误出现了:

CMake Warning (dev) at cmake/testing.cmake:13 (add_custom_command):
  Policy CMP0040 is not set: The target in the TARGET signature of
  add_custom_command() must exist.  Run "cmake --help-policy CMP0040" for
  policy details.  Use the cmake_policy command to set the policy and
  suppress this warning.

  The target name "unit_tests" is unknown in this context.
Call Stack (most recent call first):
  source/test/CMakeLists.txt:10 (add_unit_test)
This warning is for project developers.  Use -Wno-dev to suppress it.

为什么此时具体目标未知? include(cmake/testing.cmake) 是我在项目构建脚本中 cmake_minimum_required 之后调用的第一个东西,所以不可能是因为 add_custom_target(${UNIT_TEST} ALL VERBATIM) 还没有被调用。

有什么方法可以将自定义命令添加到 UNIT_TEST 目标?

刚刚遇到您的问题并尝试重现您的问题。

我的第一个建议是:如果你调用 enable_testing() 你直接得到一个由 CMake 生成的 "Run Tests" 目标。见 CMake: Testing with CTest:

Once you have built the project, you can execute all tests via

make test

with Makefile generators, or by rebuilding the RUN_TESTS target in your IDE. Internally this runs CTest to actually perform the testing; you could just as well execute

ctest

in the binary directory of your build.

正在调试您给定的代码

如果我将有效目标命名为您的 add_unit_test() 的第一个参数,我不会收到 CMP0040 错误。因此,请再次检查您提供的目标是否有效。

但是你的脚本有一些错误。这是一个工作版本:

include(CMakeParseArguments)

enable_testing()

set(UNIT_TEST "unit_tests")
add_custom_target(${UNIT_TEST} ALL)

function(add_unit_test dependency)
    cmake_parse_arguments(UT "" "NAME" "COMMAND" ${ARGN} )
    if("${UT_NAME}" STREQUAL "")
        set(${UT_NAME} "${ARGV1}")
    endif()
    add_test(${ARGN})
    add_dependencies(${UNIT_TEST} ${dependency})
    add_custom_command(TARGET ${UNIT_TEST}
                   COMMENT "Run tests"
                   POST_BUILD COMMAND ctest
                   ARGS -C $<CONFIGURATION> -R "^${UT_NAME}$" --output-on-failures)
endfunction(add_unit_test)
  • 已添加enable_testing()
  • 删除了 cmake_parse_arguments(UT ...)
  • UT 的尾随 _
  • 比较空字符串与 STREQUAL ""
  • ARGV1 添加了一个 V 并用于第二个参数(索引 0 是 dependency
  • 删除不需要VERBATIM
  • 添加了所需的 -C $<CONFIGURATION> 参数(参见 How to run ctest after building my project with cmake
  • 将 RegEx 更改为 "^${UT_NAME}$" 以实现精确名称匹配

将上面的代码移动到一个名为 UnitTest.cmake 的文件中 我已经成功地测试了以下主要 CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)
project(Example CXX)

include(${CMAKE_CURRENT_LIST_DIR}/UnitTest.cmake)

file(WRITE foo.cc "#include <windows.h>\nint main() {\nreturn 0;\n}")

add_executable(MyTest foo.cc)
add_unit_test(MyTest NAME SomeTest COMMAND $<TARGET_FILE:MyTest>)

add_executable(MyTest2 foo.cc)
add_unit_test(MyTest2 NAME SomeTest2 COMMAND $<TARGET_FILE:MyTest2>)