CMake: Projektstruktur mit Unit-Tests

139

Ich versuche, mein Projekt so zu strukturieren, dass es die Produktionsquellen (im srcUnterordner) und Tests (im testUnterordner) enthält. Ich benutze CMake, um dies zu erstellen. Als minimales Beispiel habe ich die folgenden Dateien:

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 - verwendet sqr, spielt keine Rolle

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

Ein paar Fragen:

  1. Ist diese Struktur sinnvoll? Was sind die Best Practices bei der Strukturierung dieses Codes? (Ich komme aus C # und Java, und dort ist es in gewissem Sinne einfacher)
  2. Mir gefällt nicht, dass ich alle Dateien aus dem srcOrdner in der test/CMakeLists.txtDatei auflisten muss. Wenn dies ein Bibliotheksprojekt wäre, würde ich einfach die Bibliothek verknüpfen. Gibt es eine Möglichkeit zu vermeiden, dass alle CPP-Dateien aus dem anderen Projekt aufgelistet werden?
  3. Was sind die Zeilen enable_testing()und add_test(MyTest test)tun? Ich habe keinen Effekt gesehen. Wie kann ich die Tests von CMake (oder CTest) ausführen?
  4. Bisher bin ich nur cmake .im Stammordner gelaufen , aber dies hat überall zu einem Durcheinander mit temporären Dateien geführt. Wie kann ich die Kompilierungsergebnisse in einer vernünftigen Struktur erhalten?
Grzenio
quelle
Ich betrachte mich als CMake-Neuling, daher weiß ich nicht, welche Best Practices akzeptiert werden, aber FWIW würde ich eine "sqr" -Bibliothek * erstellen, von der sowohl Haupt- als auch Test abhängig sind. (* oder das moralische Äquivalent davon)
user786653

Antworten:

125

Bei den Fragen 1 und 2 würde ich empfehlen, eine Bibliothek aus Ihren Nicht-Testdateien ohne main.cpp (in diesem Fall nur src / sqr.cpp und src / sqr.h) zu erstellen. Dann können Sie eine Auflistung vermeiden (und was noch wichtiger ist) alle Quellen zweimal neu kompilieren.

Für Frage 3 fügen diese Befehle einen Test namens "MyTest" hinzu, der Ihren ausführbaren "Test" ohne Argumente aufruft. Da Sie diese Befehle jedoch zu test / CMakeLists.txt und nicht zu CMakeLists.txt der obersten Ebene hinzugefügt haben, können Sie den Test nur aus dem Unterverzeichnis "test" Ihres Build-Baums aufrufen (try cd test && ctest -N). Wenn Sie möchten, dass der Test in Ihrem Build-Verzeichnis add_testder obersten Ebene ausgeführt werden kann, müssen Sie ihn über die CMakeLists.txt der obersten Ebene aufrufen. Dies bedeutet auch, dass Sie die ausführlichere Form von verwenden müssen, add_testda Ihre Test-Exe nicht in derselben CMakeLists.txt definiert ist

In Ihrem Fall sind Ihr Build-Baum und Ihr Quellbaum ein und dasselbe, da Sie cmake im Stammordner ausführen. Dies ist als In-Source-Build bekannt und nicht ideal, was zu Frage 4 führt.

Die bevorzugte Methode zum Generieren des Build-Baums besteht darin, einen Build außerhalb des Quellbaums durchzuführen, dh ein Verzeichnis außerhalb Ihres Quellbaums zu erstellen und cmake von dort aus auszuführen. Selbst wenn Sie ein "Build" -Verzeichnis im Stammverzeichnis Ihres Projekts erstellen und ausführen cmake .., erhalten Sie eine saubere Struktur, die Ihren Quellbaum nicht beeinträchtigt.

Ein letzter Punkt ist, zu vermeiden, dass ausführbare Dateien "test" genannt werden (Groß- und Kleinschreibung beachten). Gründe dafür finden Sie in dieser Antwort .

Um diese Änderungen zu erreichen, würde ich Folgendes tun:

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
quelle
2
Ich habe gerade bemerkt, dass Sie die .h-Dateien auch entsprechend zu den CMakeLists.txt-Dateien hinzugefügt haben. Ist es erforderlich? Und was würde passieren, wenn ich sie weglasse?
Grzenio
3
@Grzenio Dies ist nur eine praktische Funktion - sie werden in IDEs wie MSVC als Teil des Ziels angezeigt, ansonsten hat dies jedoch keine Auswirkungen.
Fraser
1
Wo ist TEST_SOURCE_DIR gesetzt?
Aggsol
6
Es wird automatisch von CMake beim Aufruf festgelegt project (TEST)- siehe cmake.org/cmake/help/v3.6/variable/PROJECT-NAME_SOURCE_DIR.html
Fraser
> Sie werden in IDEs wie MSVC als Teil des Ziels angezeigt. --- MSVC unterstützt es nicht, ein Verzeichnis mit Headern als "Include-Verzeichnis" zu markieren.
isnullxbh
46

Ich mag das Beispiel von @Fraser, würde aber den Befehl add_test in der Datei test / CMakeLists.txt verwenden und enable_testing vor add_subdirectory (test) verwenden.

Auf diese Weise können Sie Ihre Tests aus dem Build-Verzeichnis der obersten Ebene ausführen, während Sie Ihre Tests in der Datei test / CMakeLists.txt angeben.

Das Ergebnis würde so aussehen (ich habe das Beispiel von @Fraser wiederverwendet):

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
quelle
1
Vielen Dank, ich habe keine Tests über ctest -Nangezeigt, bis Sie den Tipp zum Aktivieren von Tests vor dem Hinzufügen des Unterverzeichnisses erhalten haben.
Alaferg
@alaferg: Sie würden sonst im Test-Unterverzeichnis im Build-Verzeichnis landen.
Gauteh
4
Ich wünschte, CMake hätte etwas, das der Struktur ähnelt.
Ruipacheco