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

person TemplateRex    schedule 31.05.2013    source источник


Ответы (2)


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

  1. Вместе с некоторыми вызовами get_filename_component с NAME_WE и/или PATH, теперь можно восстановить имя и относительный путь cpp-файла по отношению к подстановочному каталогу.
  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} )
person André    schedule 03.06.2013
comment
хорошо, tnx попробует это сегодня вечером на моей (Linux) машине разработки. Как я писал в вопросе, я хотел бы иметь возможность выборочно запускать или исключать определенные тесты с помощью CTest. Вы рекомендуете размещать все исполняемые файлы в одном каталоге или сохранять вложенную структуру каталогов? Другими словами, могу ли я передавать аргументы пути к каталогу в CTest или только регулярные выражения имен файлов? - person TemplateRex; 03.06.2013
comment
Извините, но у меня нет опыта работы с CTest и я не знаю, что там возможно. Но действительно ли это имеет значение? Даже если CTest не может обрабатывать аргументы пути к каталогу, возможно, вы могли бы выбрать выходное имя с полным сгенерированным тестовым именем с символами подчеркивания. - person André; 03.06.2013
comment
Только что протестировал его, он делает в основном то, что я хочу, спасибо! Одна проблема: когда я вызываю cmake .., он также добавляет build/CMakeFiles/2.8.10.1/CompilerIdCXX в качестве цели сборки, и это кажется неправильным. Однако, когда я пытаюсь изменить ОТНОСИТЕЛЬНЫЙ путь к `${CMAKE_CURRENT_SOURCE_DIR}/test (с кавычками или без них), эта проблема не исчезает. Какие-либо предложения? - person TemplateRex; 04.06.2013
comment
Неважно, здесь объясняется, как фильтровать эти файлы. . Я продолжу еще немного тонкой настройки и назначу награду, когда буду полностью удовлетворен :-) - person TemplateRex; 04.06.2013
comment
Я обновил свой вопрос своим окончательным сценарием с некоторой полировкой и настройкой для моего проекта (это проект модульного тестирования для библиотеки шаблонов C++). Работает отлично, еще раз спасибо за ответ! - person TemplateRex; 04.06.2013

Возможным решением для устранения неоднозначности имен в структуре каталогов, подобной той, которую вы используете, используя 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 отдельные пары тестов/исполняемых файлов.

person Massimiliano    schedule 03.06.2013
comment
Кстати, возможно ли также перевести dir/sub/file.cpp в исполняемый файл в форме верблюжьего регистра: DirSubFile? - person TemplateRex; 03.06.2013
comment
Вы можете попробовать использовать команду string ( REGEX REPLACE) с соответствующим регулярным выражением перед объединением строк - person Massimiliano; 03.06.2013
comment
еще раз спасибо за ваш ответ, но я, скорее всего, выберу решение @Andre, поскольку он использует полный относительный путь, который более удобен для моего проекта. - person TemplateRex; 04.06.2013