web-dev-qa-db-de.com

CMake: Projektstruktur mit Unit-Tests

Ich versuche, mein Projekt so zu strukturieren, dass es die Produktionsquellen (im Unterordner src) und Tests (im Unterordner test) enthält. Ich benutze CMake, um dies zu erstellen. Als minimales Beispiel habe ich folgende 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 beim Strukturieren dieses Codes? (Ich komme aus C # und Java, und dort ist es in gewissem Sinne einfacher)
  2. Es gefällt mir nicht, dass ich alle Dateien aus dem Ordner src in der Datei test/CMakeLists.txt Auflisten muss. Wenn dies ein Bibliotheksprojekt wäre, würde ich einfach die Bibliothek verlinken. Gibt es eine Möglichkeit zu vermeiden, alle CPP-Dateien aus dem anderen Projekt aufzulisten?
  3. Was machen die Zeilen enable_testing() und add_test(MyTest test)? Ich habe keine Wirkung gesehen. Wie kann ich die Tests von CMake (oder CTest) ausführen?
  4. Bisher habe ich nur cmake . Im Stammordner ausgeführt, dies hat jedoch überall zu einem Durcheinander mit temporären Dateien geführt. Wie kann ich die Zusammenstellungsergebnisse in einer vernünftigen Struktur erhalten?
123
Grzenio

Bei den Fragen 1 und 2 würde ich empfehlen, eine Bibliothek aus Ihren Nicht-Test-Dateien 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 mit dem Namen "MyTest" hinzu, der Ihren ausführbaren "Test" ohne Argumente aufruft. Da Sie diese Befehle jedoch zu test/CMakeLists.txt und nicht zu Ihrer CMakeLists.txt der obersten Ebene hinzugefügt haben, können Sie den Test nur im Unterverzeichnis "test" Ihres Build-Baums aufrufen (versuchen Sie es mit cd test && ctest -N ). Wenn Sie möchten, dass der Test in Ihrem Build-Verzeichnis der obersten Ebene ausgeführt werden kann, müssen Sie add_test In der CMakeLists.txt der obersten Ebene aufrufen. Dies bedeutet auch, dass Sie die ausführlichere Form von add_test verwenden müssen, da Ihre Test-Exe nicht in derselben CMakeLists.txt definiert ist

In Ihrem Fall sind Ihr Build-Baum und Ihr Quell-Baum identisch, 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 Out-of-Source-Build durchzuführen, d. H. Ein Verzeichnis außerhalb Ihres Source-Baums zu erstellen und cmake von dort auszuführen. Selbst wenn Sie ein "Build" -Verzeichnis im Stammverzeichnis Ihres Projekts erstellen und cmake .. Ausführen, erhalten Sie eine saubere Struktur, die Ihren Quellbaum nicht beeinträchtigt.

Ein letzter Punkt ist, zu vermeiden, dass ausführbare Dateien "test" (Groß- und Kleinschreibung beachten) genannt werden. Aus Gründen, warum, siehe diese 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}
                       )
116
Fraser

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 im 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 wieder verwendet):

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)
45
Mathias