C++ CMake:如何添加具有相对目录的Boost测试用例?

C++ CMake:如何添加具有相对目录的Boost测试用例?,c++,boost,cmake,boost-test,ctest,C++,Boost,Cmake,Boost Test,Ctest,我有一个使用CMake和Boost的工作项目。使用如下目录结构进行测试(请原谅ASCII艺术): 我目前将所有源文件编译成一个大的可执行文件,可以使用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}

我有一个使用CMake和Boost的工作项目。使用如下目录结构进行测试(请原谅ASCII艺术):

我目前将所有源文件编译成一个大的可执行文件,可以使用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\u cases}
执行
foreach()
(如中所述),但我很难提取相对目录和文件名,并逐个文件将它们移植到
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()

使用
${test\u cases}
上的
FOREACH()
消除目录结构中名称歧义的可能解决方案可能是:

# 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个不同的测试/可执行对。

可以为
文件(GLOB…
命令指定
相对
标志和目录。虽然在中没有直接提到,但这也适用于
文件(GLOB_RECURSE…
)。注意,我在windows安装程序中测试了这个。我不知道尼克斯

  • 连同使用
    NAME\u的一些调用,我们
    和/或
    PATH
    标志,现在可以重建名称和 cpp文件相对于全局目录的相对路径
  • 提取路径和名称(不带扩展名)基本相似 到此外,我还使用了他的 建议使用
    字符串(REGEX REPLACE…)生成唯一的testname
    ; 用下划线替换正斜杠
  • 使用唯一的测试名称,可以生成可执行文件,然后使用修改其输出目录 有关修改输出目录的更多信息,请查看和

    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} )
    

    顺便说一句,是否也可以将
    dir/sub/file.cpp
    转换为驼峰大小写形式的可执行文件:
    DirSubFile
    ?您可以尝试使用命令(
    REGEX REPLACE
    )和适当的,但是我很可能会使用@Andre的解决方案,因为他使用了完整的相对路径,这对我的项目更方便。好的,tnx今晚将在我的(Linux)开发机器上尝试这个。正如我在问题中所写的,我希望能够使用CTest选择性地运行或排除某些测试。您建议将所有可执行文件放在同一个目录中还是保留嵌套的目录结构?换句话说,我可以将目录路径参数传递给CTest,还是只传递文件名regex?对不起,我没有CTest的经验,也不知道那里可能有什么。但这真的重要吗?即使CTest不能处理目录路径参数,也许您可以选择一个带有下划线的完整生成测试名的输出名。刚刚测试过,它基本上满足了我的要求,谢谢!有一个问题:当我调用
    cmake..
    时,它还添加了
    build/CMakeFiles/2.8.10.1/CompilerIdCXX
    作为构建目标,这似乎是错误的。但是,当我试图修改`${CMAKE\u CURRENT\u SOURCE\u DIR}/test(带引号或不带引号)的相对路径时,这个问题并没有消失。有什么建议吗?没关系,它解释了如何过滤这些文件。当我完全满意的时候,我会继续做更多的事情并且分配赏金:-我用我的最后一个脚本更新我的问题,对我的项目进行了一些修改和调整(它是一个C++模板库的单元测试项目)。它的工作非常出色,再次感谢您的回答!
    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} )