【问题标题】:CMake: how to add Boost.Test cases with relative directories?CMake:如何使用相关目录添加 Boost.Test 用例?
【发布时间】:2023-03-24 23:20:01
【问题描述】:

我有一个使用 CMake 和 Boost.Test 的工作项目,其目录结构如下(请原谅 ASCII 艺术):

+-proj
|---CMakeLists.txt
|---build
|---test
|\----dir1
|   \----foo.cpp // contains one BOOST_AUTO_TEST_SUITE and several BOOST_AUTO_TEST_CASE
|    |---bar.cpp // contains one BOOST_AUTO_TEST_SUITE and several BOOST_AUTO_TEST_CASE
 \----dir2
    \----foo.cpp // contains one BOOST_AUTO_TEST_SUITE and several BOOST_AUTO_TEST_CASE
     |---bar.cpp // contains one BOOST_AUTO_TEST_SUITE and several BOOST_AUTO_TEST_CASE

我目前将所有源文件编译成一个可以使用 CTest 运行的大型可执行文件。我的 CMakeLists.txt 看起来像这样:

file(GLOB_RECURSE test_cases FOLLOW_SYMLINKS "test/*.[h,c]pp")
add_executable(test_suite ${test_cases})
include_directories(${PROJECT_SOURCE_DIR} ${Boost_INCLUDE_DIRS})
target_link_libraries(test_suite ${Boost_LIBRARIES})
include(CTest)
add_test(test_runner test_suite)

我想将每个 .cpp 文件编译成单独的可执行文件,并将其作为测试单独添加,以便我可以使用 CTest 正则表达式机制(尤其是 Boost.Test 似乎没有的测试排除)选择性地运行某些测试。但是,当 CMake 从 dir1/dir2 生成 foo/bar 的构建目标时,我遇到了名称冲突。

我的问题是:如何将test 下的整个目录树镜像到build 下的类似树,以便在各种可执行文件之间不再有名称冲突,以便 CTest可以全部运行吗?

注意:不能在源代码树中重命名它们。我想对变量${test_cases} 做一个foreach()(如this answer 中所述),但我无法提取相对目录和文件名,并将它们移植到build/ 目录逐个文件的基础。

更新:最后,我拼凑了这个脚本:

# get the test sources
file(GLOB_RECURSE test_sources RELATIVE ${PROJECT_SOURCE_DIR} *.cpp)

# except any CMake generated sources under build/
string(REGEX REPLACE "build/[^;]+;?" "" test_sources "${test_sources}")

# get the test headers
file(GLOB_RECURSE test_headers RELATIVE ${PROJECT_SOURCE_DIR} *.hpp)

# except any CMake generated headers under build/
string(REGEX REPLACE "build/[^;]+;?" "" test_headers "${test_headers}")

# compile against the test headers, the parent project, and the Boost libraries
include_directories(${PROJECT_SOURCE_DIR} ${ParentProject_include_dirs} ${Boost_INCLUDE_DIRS})

# calls enable_testing()
include(CTest)

foreach(t ${test_sources} )
  # get the relative path in the source tree
  get_filename_component(test_path ${t} PATH)

  # get the source name without extension
  get_filename_component(test_name ${t} NAME_WE)

  # concatenate the relative path and name in an underscore separated identifier
  string(REPLACE "/" "_" test_concat "${test_path}/${test_name}")

  # strip the leading "test_" part from the test ID
  string(REGEX REPLACE "^test_" "" test_id ${test_concat})

  # depend on the current source file, all the test headers, and the parent project headers
  add_executable(${test_id} ${t} ${test_headers} ${ParentProject_headers})

  # link against the Boost libraries
  target_link_libraries(${test_id} ${Boost_LIBRARIES})

  # match the relative path in the build tree with the corresponding one in the source tree 
  set_target_properties(${test_id} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${test_path})

  # add a test with executable in the relative path of the build tree
  add_test(${test_id} ${test_path}/${test_id})
endforeach()

【问题讨论】:

    标签: c++ boost cmake boost-test ctest


    【解决方案1】:

    在目录结构中消除名称歧义的一种可能的解决方案是:

    # Set Cmake version and policy
    CMAKE_MINIMUM_REQUIRED( VERSION 2.8.7 )
    CMAKE_POLICY( VERSION 2.8.7 )
    
    PROJECT( DUMMY CXX )
    
    FILE( GLOB_RECURSE test_cases FOLLOW_SYMLINKS "test/*.[h,c]pp" )
    
    FOREACH( case ${test_cases} )
      ## Get filename without extension
      GET_FILENAME_COMPONENT(case_name_we ${case} NAME_WE)
      ## Get innermost directory name
      GET_FILENAME_COMPONENT(case_directory ${case} PATH)
      GET_FILENAME_COMPONENT(case_innermost ${case_directory} NAME_WE)
      ## Construct executable name
      SET( exe_name "${case_innermost}_${case_name_we}")
      ## Construct test name
      SET( test_name "${exe_name}_test")
      ## Add executable and test
      ADD_EXECUTABLE( ${exe_name} ${case} )
      ADD_TEST( ${test_name} ${exe_name} )
    ENDFOREACH()
    

    如您所见,CMakeLists.txt 创建了 4 个不同的测试/可执行对。

    【讨论】:

    • 顺便说一句,是否也可以将dir/sub/file.cpp 转换为驼峰式格式的可执行文件:DirSubFile
    • 在连接字符串之前,您可以尝试使用 string 命令 (REGEX REPLACE) 和适当的 regular expression
    • 再次感谢您的回答,但我很可能会选择 @Andre 的解决方案,因为他使用完整的相对路径,这对我的项目来说更方便。
    【解决方案2】:

    可以为file( GLOB ... ) 命令指定RELATIVE 标志和目录。尽管documentation of file( GLOB ) 中没有直接提及,但这也适用于file( GLOB_RECURSE ... )。请注意,我在 Windows 设置中对此进行了测试。我不知道 *nix。

    1. 连同一些get_filename_componentNAME_WE 通话 和/或PATH 标志,现在可以重建名称和 cpp 文件相对于 globbing 目录的相对路径。
    2. 提取路径和名称(不带扩展名)大多相似 到the answer by Massimiliano。另外,我用过他的 建议使用string( REGEX REPLACE ... ) 生成唯一的测试名称; 用下划线替换正斜杠。
    3. 使用唯一的测试名称,可以生成可执行文件,然后可以使用set_target_properties 修改其输出目录。

    查看thisthis question 了解有关修改输出目录的更多信息。

    file( GLOB_RECURSE TEST_CPP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp )
    
    foreach( test_case ${TEST_CPP_SOURCES} )
        # Get the name without extension
        get_filename_component( test_name ${test_case} NAME_WE )
        # Get the path to the test-case, relative to the ${CMAKE_CURRENT_SOURCE_DIR} 
        # thanks to the RELATIVE flag in file( GLOB_RECURSE ... )
        get_filename_component( test_path ${test_case} PATH )
    
        message( STATUS "  name = " ${test_name} )
        message( STATUS "  path = " ${test_path} )
        # I would suggests constructing a 'unique' test-name
        string( REPLACE "/" "_" full_testcase "${test_name}/${test_path}" )
    
        # Add an executable using the 'unique' test-name
        message( STATUS "  added " ${full_testcase} " in " ${test_path} )
        add_executable( ${full_testcase} ${test_case} )
        # and modify its output paths. 
        set_target_properties( ${full_testcase} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${test_path} )
    endforeach( test_case ${TEST_CPP_SOURCES} )
    

    【讨论】:

    • 好的,tnx 今晚将在我的 (Linux) 开发机器上尝试这个。正如我在问题中所写,我希望能够使用 CTest 选择性地运行或排除某些测试。您是否建议将所有可执行文件放在同一目录中或保持嵌套目录结构?换句话说,我可以将目录路径参数传递给 CTest 还是只传递文件名正则表达式?
    • 抱歉,我没有使用 CTest 的经验,也不知道那里有什么可能。但这真的重要吗?即使 CTest 无法处理目录路径参数,也许您可​​以选择带有下划线的完整生成测试名称的输出名称。
    • 刚刚测试过,它主要是我想要的,谢谢!一个问题:当我调用cmake .. 时,它还会添加build/CMakeFiles/2.8.10.1/CompilerIdCXX 作为构建目标,这似乎是错误的。但是,当我尝试将 RELATIVE 路径修改为 `${CMAKE_CURRENT_SOURCE_DIR}/test(带或不带引号)时,这个问题并没有消失。有什么建议吗?
    • 没关系,它解释了here 如何过滤这些文件。当我完全满意时,我将继续进行更多微调并分配赏金:-)
    • 我用我的最终脚本更新了我的问题,并对我的项目进行了一些润色和调整(这是一个 C++ 模板库的单元测试项目)。效果很好,再次感谢您的回答!
    猜你喜欢
    • 2011-05-17
    • 2021-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-01
    • 1970-01-01
    相关资源
    最近更新 更多