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

146

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

CMakeLists.txt:

cmake_minimum_required (VERSION 2.8) 
project (TEST) 

add_subdirectory (src) 
add_subdirectory (test) 

src / CMakeLists.txt:

add_executable (demo main.cpp sqr.cpp) 

src / sqr.h

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

src / sqr.cpp

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

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

test / 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 / 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));
}

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

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

Ответы:

129

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

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

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

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

И наконец, не следует называть исполняемые файлы «тестовыми» (с учетом регистра). О причинах см. Этот ответ .

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

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)


test / 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}
                       )
Fraser
источник
2
Я только что заметил, что вы также добавили файлы .h в файлы CMakeLists.txt соответственно. Это обязательно? А что будет, если я их оставлю?
Grzenio
3
@Grzenio Это просто удобная функция - они появляются в IDE, например MSVC, как часть цели, но в остальном это не имеет никакого эффекта.
Fraser
1
Где установлен TEST_SOURCE_DIR?
aggsol
6
Он автоматически устанавливается CMake при вызове project (TEST)- см. Cmake.org/cmake/help/v3.6/variable/PROJECT-NAME_SOURCE_DIR.html
Fraser
> они появляются в IDE, как MSVC, как часть цели --- MSVC не поддерживает пометку каталога с заголовками как «включаемый каталог»?
isnullxbh
48

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

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

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

CMakeLists.txt

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

enable_testing ()
add_subdirectory (test)

src / CMakeLists.txt

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

test / 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)
Матиас
источник
1
Спасибо, я не показывал никаких тестов, ctest -Nпока вы не дадите подсказку о включении тестирования перед добавлением подкаталога.
alaferg
@alaferg: в противном случае они попали бы в подкаталог test внутри каталога сборки.
gauteh
5
Я бы хотел, чтобы у CMake было что-то похожее на структуру.
ruipacheco