CMake: Struktura projektu z testami jednostkowymi

139

Próbuję ustrukturyzować mój projekt, aby uwzględnić źródła produkcji (w srcpodfolderze) i testy (w testpodfolderze). Do zbudowania tego używam CMake. Jako minimalny przykład mam następujące pliki:

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 - używa sqr, tak naprawdę nie ma znaczenia

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

Kilka pytań:

  1. Czy ta struktura ma sens? Jakie są najlepsze praktyki podczas tworzenia struktury tego kodu? (Pochodzę z C # i javy, a tam jest w pewnym sensie łatwiej)
  2. Nie podoba mi się fakt, że muszę wymieniać wszystkie pliki z srcfolderu w test/CMakeLists.txtpliku. Gdyby to był projekt biblioteki, po prostu podlinkowałbym bibliotekę. Czy istnieje sposób, aby uniknąć wyświetlania listy wszystkich plików cpp z innego projektu?
  3. Jakie są linie enable_testing()i co add_test(MyTest test)robisz? Nie widziałem żadnego efektu. Jak mogę uruchomić testy z CMake (lub CTest)?
  4. Do tej pory po prostu biegałem cmake .w folderze głównym, ale spowodowało to bałagan z plikami tymczasowymi wszędzie. Jak uzyskać wyniki kompilacji w rozsądnej strukturze?
Grzenio
źródło
Uważam się za nowicjusza w CMake, więc nie wiem, jakie są akceptowane najlepsze praktyki, ale FWIW stworzyłbym bibliotekę "sqr" *, od której zależał zarówno main, jak i test. (* lub jego moralny odpowiednik)
user786653

Odpowiedzi:

125

W przypadku pytań 1 i 2 zalecałbym utworzenie biblioteki z plików niebędących testami, z wyłączeniem main.cpp (w tym przypadku po prostu src / sqr.cpp i src / sqr.h), a następnie możesz uniknąć wyświetlania listy (i co ważniejsze ponowna kompilacja) wszystkie źródła dwukrotnie.

W przypadku pytania 3 polecenia te dodają test o nazwie „MyTest”, który wywołuje plik wykonywalny „test” bez żadnych argumentów. Jednakże, ponieważ dodałeś te polecenia do test / CMakeLists.txt, a nie do pliku CMakeLists.txt najwyższego poziomu, możesz wywołać test tylko z podkatalogu "test" twojego drzewa kompilacji (try cd test && ctest -N). Jeśli chcesz, aby test można było uruchomić z katalogu kompilacji najwyższego poziomu, musisz wywołać add_testz pliku CMakeLists.txt najwyższego poziomu. Oznacza to również, że musisz użyć bardziej szczegółowej formy, add_testponieważ twój exe testowy nie jest zdefiniowany w tym samym CMakeLists.txt

W twoim przypadku, ponieważ używasz cmake w folderze głównym, drzewo kompilacji i drzewo źródłowe są jednym i tym samym. Jest to znane jako kompilacja w źródle i nie jest idealne, co prowadzi do pytania 4.

Preferowaną metodą generowania drzewa kompilacji jest wykonanie kompilacji poza źródłem, tj. Utworzenie katalogu gdzieś poza drzewem źródłowym i wykonanie polecenia cmake z tego miejsca. Nawet utworzenie katalogu „build” w katalogu głównym projektu i wykonanie cmake ..zapewni czystą strukturę, która nie będzie kolidować z drzewem źródłowym.

Ostatnim punktem jest uniknięcie wywoływania plików wykonywalnych „test” (z uwzględnieniem wielkości liter). Z powodów dlaczego, zobacz tę odpowiedź .

Aby osiągnąć te zmiany, zrobiłbym następujące rzeczy:

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
źródło
2
Właśnie zauważyłem, że odpowiednio dodałeś również pliki .h do plików CMakeLists.txt. Czy to jest wymagane? A co by się stało, gdybym je pominął?
Grzenio
3
@Grzenio To tylko wygoda - pojawiają się w IDE jak MSVC jako część celu, ale poza tym nie ma to żadnego efektu.
Fraser
1
Gdzie jest ustawiony TEST_SOURCE_DIR?
aggsol
6
Jest automatycznie ustawiana przez CMake podczas dzwonienia project (TEST)- patrz cmake.org/cmake/help/v3.6/variable/PROJECT-NAME_SOURCE_DIR.html
Fraser
> pojawiają się w IDE jak MSVC jako część celu --- MSVC nie obsługuje oznaczania katalogu z nagłówkami jako „włącz katalog”?
isnullxbh
46

Podoba mi się przykład @Fraser, ale użyłbym polecenia add_test w test / CMakeLists.txt i użyłbym enable_testing przed add_subdirectory (test).

W ten sposób możesz uruchamiać testy z katalogu kompilacji najwyższego poziomu, określając testy w pliku test / CMakeLists.txt.

Wynik wyglądałby tak (ponownie użyłem przykładu @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)
Mathias
źródło
1
Dzięki, nie pokazałem żadnych testów, ctest -Ndopóki nie otrzymałeś porady dotyczącej włączenia testowania przed dodaniem podkatalogu.
alaferg
@alaferg: w przeciwnym razie znalazłyby się w podkatalogu testowym wewnątrz katalogu kompilacji.
gauteh
4
Chciałbym, żeby CMake miał coś, co przypominałoby strukturę.
ruipacheco