且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

CMake的:如何添加Boost.Test例相对路径?

更新时间:2023-12-01 08:20:52

可以指定相对标记和目录到文件(GLOB ...)命令。虽然没有文件(GLOB)的文档中直接提到,这个工程的文件(GLOB_RECURSE ...)太。请注意,我测试了这个在我的Windows安装程序。我不知道关于* nix中。


  1. 加上一些 get_filename_component 与 NAME_WE
    和/或 PATH 标志,现在有可能重建的名称和
    在cpp文件相对于文件名匹配目录的相对路径。

  2. 提取的路径和名称(不包括扩展名),大多是类似
    通过的Massimiliano 的答案。另外,我用他的
    建议生成具有串唯一的测试名(正则表达式REPLACE ...);
    更换着用下划线斜杠。

  3. 凭借独特的测试的名称,可以生成可执行文件,之后它的输出目录可以用的 set_target_properties

检查this和this问题关于修改输出目录的详细信息。

 文件(GLOB_RECURSE TEST_CPP_SOURCES相对$ {CMAKE_CURRENT_SOURCE_DIR} *的.cpp)的foreach(test_case $ {} TEST_CPP_SOURCES)
    #获取不带扩展名
    get_filename_component(TEST_NAME $ {} test_case NAME_WE)
    #获取路径测试用例,相对于$ {CMAKE_CURRENT_SOURCE_DIR}
    #感谢的相对标志文件(GLOB_RECURSE ...)
    get_filename_component(test_path $ {} test_case PATH)    消息(STATUSNAME =$ {} TEST_NAME)
    消息(STATUSPATH =$ {} test_path)
    #我想有可能建立一种独特的测试名
    字符串(替换/,_full_testcase$ {TEST_NAME} / $ {test_path})    #添加使用独一无二的测试名称的可执行
    消息(STATUS添加$ {} full_testcase中的$ {} test_path)
    add_executable($ {full_testcase} $ {} test_case)
    #并修改其输出路径。
    set_target_properties($ {} full_testcase性能RUNTIME_OUTPUT_DIRECTORY $ {} test_path)
endforeach(test_case $ {} TEST_CPP_SOURCES)

I have a working project with CMake and Boost.Test with a directory structure like this (pardon the ASCII art):

+-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

I currently compile all source files into one big executable that I can run with CTest. My CMakeLists.txt looks like this:

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)

I would like to compile each .cpp file into a separate executable, and add it separately as a test so that I can use the CTest regular expression machinery (especially the test exclusion which Boost.Test doesn't seem to have) to selectively run certain tests. However, I get a name conflict when CMake is generating build targets for foo/bar from dir1/dir2.

My question is: how can I mirror the entire directory tree under test to a similar tree under build so that there are no more name conflicts between the various executables and so that CTest can run them all?

Note: Renaming them in the source tree is not an option. I'd like to do a foreach() over the variable ${test_cases} (as explained in this answer), but I am having trouble to extract the relative directory and the file name, and port those to the build/ directory on a file-by-file basis.

UPDATE: In the end, I pieced together this script:

# 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()

It is possible to specify a RELATIVE flag and a directory to a file( GLOB ... ) command. Although not mentioned directly in the documentation of file( GLOB ), this works for file( GLOB_RECURSE ... ) too. Note, I tested this on my windows setup. I don't known about *nix.

  1. Together with some get_filename_component calls with NAME_WE and/or PATH flags, it is now possible to reconstruct the name and the relative path of the cpp-file with respect to the globbing dir.
  2. Extracting a path and a name (without extension) is mostly similar to the answer by Massimiliano. In addition, I have used his suggestion to generate a unique testname with string( REGEX REPLACE ... ); replacing forward slashes by underscores.
  3. With a unique test-name, the executable can be generated and afterwards its output directory can be modified with set_target_properties.

Check this and this question for more info on modifying the output directory.

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