Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
148 views
in Technique[技术] by (71.8m points)

c++ - CMake: how to add Boost.Test cases with relative directories?

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()
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

A possible solution to disambiguate names in a directory structure like the one you have using a FOREACH() over ${test_cases} may be:

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

As you can see this CMakeLists.txt creates 4 distinct test/executable couples.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...