CMake: структура проекта с модульными испытаниями

Я пытаюсь структурировать мой проект, чтобы включить источники производства (в подпапку src) и тесты (в подпапку test). Я использую CMake для его создания. В качестве минимального примера у меня есть следующие файлы:

CMakeLists.txt:

cmake_minimum_required (VERSION 2.8) 
project (TEST) 

add_subdirectory (src) 
add_subdirectory (test) 

ЦСИ/CMakeLists.txt:

add_executable (demo main.cpp sqr.cpp) 

ЦСИ/sqr.h

#ifndef SQR_H
#define SQR_H
double sqr(double);    
#endif // SQR_H

ЦСИ/sqr.cpp

#include "sqr.h"
double sqr(double x) { return x*x; }

src/main.cpp - использует sqr, на самом деле не имеет значения

Тест/CMakeLists.txt:

find_package(Boost COMPONENTS system filesystem unit_test_framework REQUIRED)

include_directories (${TEST_SOURCE_DIR}/src) 

ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK) 

add_executable (test test.cpp ${TEST_SOURCE_DIR}/src/sqr.cpp) 

target_link_libraries(test
                      ${Boost_FILESYSTEM_LIBRARY}
                      ${Boost_SYSTEM_LIBRARY}
                      ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
                      )

enable_testing()
add_test(MyTest test)

Тест/test.cpp:

#define BOOST_TEST_MODULE SqrTests
#include <boost/test/unit_test.hpp>

#include "sqr.h"

BOOST_AUTO_TEST_CASE(FailTest)
{
    BOOST_CHECK_EQUAL(5, sqr(2));
}

BOOST_AUTO_TEST_CASE(PassTest)
{
    BOOST_CHECK_EQUAL(4, sqr(2));
}

Несколько вопросов:

  • Имеет ли смысл эта структура? Каковы наилучшие методы структурирования этого кода? (Я исхожу из С# и java, и там в каком-то смысле это проще)
  • Мне не нравится тот факт, что я должен перечислить все файлы из папки src в файле test/CMakeLists.txt. Если это был проект библиотеки, я бы просто связал библиотеку. Есть ли способ избежать перечисления всех файлов cpp из другого проекта?
  • Что делают линии enable_testing() и add_test(MyTest test)? Я не видел никакого эффекта. Как запустить тесты из CMake (или CTest)?
  • До сих пор я просто запускал cmake . в корневой папке, но это создавало беспорядок с временными файлами повсюду. Как я могу получить результаты компиляции в разумной структуре?

Ответ 1

Для вопросов 1 и 2 я бы рекомендовал создать библиотеку из ваших не-тестовых файлов, исключая main.cpp(в данном случае только src/sqr.cpp и src/sqr.h), а затем вы можете избежать листинга ( и, что более важно, перекомпилировать) все источники дважды.

Для вопроса 3 эти команды добавляют тест под названием "MyTest", который вызывает ваш исполняемый "тест" без каких-либо аргументов. Однако, поскольку вы добавили эти команды в test/CMakeLists.txt, а не на свой CMakeLists.txt верхнего уровня, вы можете только вызывать тест из подкаталога test в дереве сборки (try cd test && ctest -N). Если вы хотите, чтобы тест был запущен из вашего каталога создания верхнего уровня, вам нужно вызвать add_test из CMakeLists.txt верхнего уровня. Это также означает, что вам нужно использовать более подробную форму add_test, поскольку ваш тестовый exe не определен в том же CMakeLists.txt

В вашем случае, поскольку вы запускаете cmake в корневой папке, ваше дерево сборки и исходное дерево одно и то же. Это называется сборкой в ​​источнике и не является идеальным, что приводит к вопросу 4.

Предпочтительным способом генерации дерева сборки является создание сборки вне источника, т.е. создание каталога где-то вне вашего исходного дерева и выполнение cmake оттуда. Даже создание каталога "build" в корне вашего проекта и выполнение cmake .. обеспечит чистую структуру, которая не будет мешать вашему исходному дереву.

Конечным моментом является отказ от вызова исполняемых файлов "test" (с учетом регистра). По причинам, см. этот ответ.

Чтобы добиться этих изменений, я бы сделал следующее:

CMakeLists.txt:

cmake_minimum_required (VERSION 2.8)
project (TEST)
add_subdirectory (src) 
add_subdirectory (test)
enable_testing ()
add_test (NAME MyTest COMMAND Test)


SRC/CMakeLists.txt:

add_library (Sqr sqr.cpp sqr.h)
add_executable (demo main.cpp)
target_link_libraries (demo Sqr)


тест /CMakeLists.txt:

find_package (Boost COMPONENTS system filesystem unit_test_framework REQUIRED)
include_directories (${TEST_SOURCE_DIR}/src
                     ${Boost_INCLUDE_DIRS}
                     )
add_definitions (-DBOOST_TEST_DYN_LINK)
add_executable (Test test.cpp)
target_link_libraries (Test
                       Sqr
                       ${Boost_FILESYSTEM_LIBRARY}
                       ${Boost_SYSTEM_LIBRARY}
                       ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
                       )

Ответ 2

Мне нравится пример @Fraser, но он должен использовать команду add_test в test/CMakeLists.txt и использовать enable_testing перед add_subdirectory (test).

Таким образом, вы можете запустить свои тесты из каталога сборки верхнего уровня, указав свои тесты в тесте /CMakeLists.txt.

Результат будет выглядеть следующим образом (я снова использовал пример @Fraser):

CMakeLists.txt

cmake_minimum_required (VERSION 2.8)
project (TEST)
add_subdirectory (src)

enable_testing ()
add_subdirectory (test)

ЦСИ/CMakeLists.txt

add_library (Sqr sqr.cpp sqr.h)
add_executable (demo main.cpp)
target_link_libraries (demo Sqr)

Тест/CMakeLists.txt

find_package (Boost COMPONENTS system filesystem unit_test_framework REQUIRED)
include_directories (${TEST_SOURCE_DIR}/src
                     ${Boost_INCLUDE_DIRS}
                     )
add_definitions (-DBOOST_TEST_DYN_LINK)
add_executable (Test test.cpp)
target_link_libraries (Test
                       Sqr
                       ${Boost_FILESYSTEM_LIBRARY}
                       ${Boost_SYSTEM_LIBRARY}
                       ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
                       )
add_test (NAME MyTest COMMAND Test)