CMake: как добавить случаи Boost.Test с относительными каталогами?

У меня есть рабочий проект с 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 генерирует цели сборки для foo / bar из dir1 / dir2.

Мой вопрос: как мне отразить все дерево каталогов в test к похожему дереву под build чтобы больше не возникало конфликтов имен между различными исполняемыми файлами и чтобы CTest мог их всех запустить?

Заметка: Переименовать их в исходном дереве не вариант. Я хотел бы сделать foreach() над переменной ${test_cases} (как объяснено в этот ответ), но у меня возникают проблемы с извлечением относительного каталога и имени файла, и портирование их на 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()

5

Решение

Можно указать RELATIVE флаг и каталог к file( GLOB ... ) команда. Хотя не упоминается прямо в документация файла (GLOB), это работает для file( GLOB_RECURSE ... ) тоже. Обратите внимание, я проверял это на моей установке Windows. Я не знаю о * nix.

  1. Вместе с некоторыми get_filename_component звонки с NAME_WE
    и / или PATH флаги, теперь можно восстановить имя и
    относительный путь к cpp-файлу по отношению к директории globbing.
  2. Извлечение пути и имени (без расширения) в основном похоже
    в ответ Массимилиано. Кроме того, я использовал его
    предложение создать уникальное имя теста с string( REGEX REPLACE ... );
    замена косой черты подчеркиванием.
  3. С уникальным именем теста исполняемый файл может быть сгенерирован, а затем его выходной каталог может быть изменен с set_target_properties.

Проверьте этот а также этот вопрос для получения дополнительной информации об изменении выходного каталога.

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

Другие решения

Возможное решение для устранения неоднозначности имен в структуре каталогов, как у вас, используя FOREACH() над ${test_cases} может быть:

# 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 различных тестовых / исполняемых пары.

4

По вопросам рекламы [email protected]