Als Starter gibt es einige herkömmliche Namen für Verzeichnisse, die Sie nicht ignorieren können. Diese basieren auf der langen Tradition des Unix-Dateisystems. Diese sind:
trunk
├── bin : for all executables (applications)
├── lib : for all other binaries (static and shared libraries (.so or .dll))
├── include : for all header files
├── src : for source files
└── doc : for documentation
Es ist wahrscheinlich eine gute Idee, sich an dieses Grundlayout zu halten, zumindest auf oberster Ebene.
Beim Aufteilen der Header- und Quelldateien (cpp) sind beide Schemata ziemlich häufig. Ich ziehe es jedoch vor, sie zusammen zu halten. Bei alltäglichen Aufgaben ist es nur praktischer, die Dateien zusammen zu haben. Wenn sich der gesamte Code unter einem Ordner der obersten Ebene befindet, dh unter dem trunk/src/
Ordner, können Sie feststellen, dass sich alle anderen Ordner (bin, lib, include, doc und möglicherweise ein Testordner) zusätzlich zu Das "Build" -Verzeichnis für einen Out-of-Source-Build sind alle Ordner, die nur Dateien enthalten, die im Build-Prozess generiert werden. Daher muss nur der src-Ordner gesichert oder viel besser unter einem Versionskontrollsystem / -server (wie Git oder SVN) aufbewahrt werden.
Und wenn es darum geht, Ihre Header-Dateien auf dem Zielsystem zu installieren (wenn Sie Ihre Bibliothek eventuell verteilen möchten), verfügt CMake über einen Befehl zum Installieren von Dateien (erstellt implizit ein "install" -Ziel, um "make install" auszuführen) Sie können damit alle Header in das /usr/include/
Verzeichnis einfügen . Ich benutze nur das folgende cmake-Makro für diesen Zweck:
# custom macro to register some headers as target for installation:
# setup_headers("/path/to/header/something.h" "/relative/install/path")
macro(setup_headers HEADER_FILES HEADER_PATH)
foreach(CURRENT_HEADER_FILE ${HEADER_FILES})
install(FILES "${SRCROOT}${CURRENT_HEADER_FILE}" DESTINATION "${INCLUDEROOT}${HEADER_PATH}")
endforeach(CURRENT_HEADER_FILE)
endmacro(setup_headers)
Wo SRCROOT
ist eine cmake-Variable, die ich auf den Ordner src gesetzt habe, und wo ist eine cmake-Variable, die ich INCLUDEROOT
überall dort konfiguriere, wo Header benötigt werden. Natürlich gibt es viele andere Möglichkeiten, dies zu tun, und ich bin sicher, dass mein Weg nicht der beste ist. Der Punkt ist, dass es keinen Grund gibt, die Header und Quellen zu teilen, nur weil nur die Header auf dem Zielsystem installiert werden müssen, da es insbesondere mit CMake (oder CPack) sehr einfach ist, die Header auszuwählen und zu konfigurieren installiert werden, ohne sie in einem separaten Verzeichnis haben zu müssen. Und das habe ich in den meisten Bibliotheken gesehen.
Quote: Zweitens möchte ich das Google C ++ Testing Framework zum Unit-Testen meines Codes verwenden, da es ziemlich einfach zu verwenden scheint. Schlagen Sie vor, dies mit meinem Code zu bündeln, beispielsweise in einem Ordner "inc / gtest" oder "contrib / gtest"? Wenn Sie gebündelt sind, schlagen Sie vor, das Skript fuse_gtest_files.py zu verwenden, um die Anzahl oder die Dateien zu reduzieren, oder es unverändert zu lassen? Wenn nicht gebündelt, wie wird diese Abhängigkeit behandelt?
Bündeln Sie keine Abhängigkeiten mit Ihrer Bibliothek. Dies ist im Allgemeinen eine ziemlich schreckliche Idee, und ich hasse es immer, wenn ich beim Versuch, eine Bibliothek aufzubauen, die das tut, nicht weiterkomme. Es sollte Ihr letzter Ausweg sein und sich vor den Fallstricken hüten. Häufig bündeln Benutzer Abhängigkeiten mit ihrer Bibliothek, entweder weil sie auf eine schreckliche Entwicklungsumgebung (z. B. Windows) abzielen oder weil sie nur eine alte (veraltete) Version der betreffenden Bibliothek (Abhängigkeit) unterstützen. Die größte Gefahr besteht darin, dass Ihre gebündelte Abhängigkeit möglicherweise mit bereits installierten Versionen derselben Bibliothek / Anwendung kollidiert (z. B. Sie haben gtest gebündelt, aber die Person, die versucht, Ihre Bibliothek zu erstellen, hat bereits eine neuere (oder ältere) Version von gtest installiert Die beiden könnten zusammenstoßen und dieser Person sehr schlimme Kopfschmerzen bereiten. Also, wie gesagt, tun Sie es auf eigenes Risiko, und ich würde nur als letzten Ausweg sagen. Das Auffordern der Benutzer, einige Abhängigkeiten zu installieren, bevor Sie Ihre Bibliothek kompilieren können, ist weitaus weniger schlimm als der Versuch, Konflikte zwischen Ihren gebündelten Abhängigkeiten und vorhandenen Installationen zu lösen.
Quote: Wie sind diese beim Schreiben von Tests allgemein organisiert? Ich dachte daran, eine CPP-Datei für jede Klasse zu haben (z. B. test_vector3.cpp), aber alle in einer Binärdatei kompiliert, damit sie alle problemlos zusammen ausgeführt werden können.
Eine CPP-Datei pro Klasse (oder eine kleine zusammenhängende Gruppe von Klassen und Funktionen) ist meiner Meinung nach üblicher und praktischer. Kompilieren Sie sie jedoch auf keinen Fall alle zu einer Binärdatei, nur damit "sie alle zusammen ausgeführt werden können". Das ist eine wirklich schlechte Idee. Wenn es um das Codieren geht, möchten Sie die Dinge im Allgemeinen so weit aufteilen, wie es vernünftig ist. Bei Komponententests möchten Sie nicht, dass eine Binärdatei alle Tests ausführt, da dies bedeutet, dass jede kleine Änderung, die Sie an irgendetwas in Ihrer Bibliothek vornehmen, wahrscheinlich zu einer nahezu vollständigen Neukompilierung dieses Komponententestprogramms führt und das sind nur Minuten / Stunden, die auf das erneute Kompilieren warten. Halten Sie sich einfach an ein einfaches Schema: 1 Einheit = 1 Einheitentestprogramm. Dann,
Quote: Da die gtest-Bibliothek in der Regel mit cmake und make erstellt wird, dachte ich, dass es sinnvoll wäre, mein Projekt auch so zu erstellen? Wenn ich mich für das folgende Projektlayout entschieden habe:
Ich würde eher dieses Layout vorschlagen:
trunk
├── bin
├── lib
│ └── project
│ └── libvector3.so
│ └── libvector3.a products of installation / building
├── docs
│ └── Doxyfile
├── include
│ └── project
│ └── vector3.hpp
│_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
│
├── src
│ └── CMakeLists.txt
│ └── Doxyfile.in
│ └── project part of version-control / source-distribution
│ └── CMakeLists.txt
│ └── vector3.hpp
│ └── vector3.cpp
│ └── test
│ └── test_vector3.cpp
│_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
│
├── build
└── test working directories for building / testing
└── test_vector3
Ein paar Dinge, die hier zu beachten sind. Erstens sollten die Unterverzeichnisse Ihres src-Verzeichnisses die Unterverzeichnisse Ihres include-Verzeichnisses widerspiegeln. Dies dient nur dazu, die Dinge intuitiv zu halten (versuchen Sie auch, Ihre Unterverzeichnisstruktur relativ flach (flach) zu halten, da Ordner tief verschachtelt sind ist oft mehr ein Ärger als alles andere). Zweitens ist das "include" -Verzeichnis nur ein Installationsverzeichnis. Sein Inhalt entspricht genau den Headern, die aus dem src-Verzeichnis ausgewählt wurden.
Drittens soll das CMake-System über die Quell-Unterverzeichnisse verteilt werden, nicht als eine CMakeLists.txt-Datei auf der obersten Ebene. Dies hält die Dinge lokal, und das ist gut so (im Geiste, die Dinge in unabhängige Teile aufzuteilen). Wenn Sie eine neue Quelle, einen neuen Header oder ein neues Testprogramm hinzufügen, müssen Sie lediglich eine kleine und einfache CMakeLists.txt-Datei in dem betreffenden Unterverzeichnis bearbeiten, ohne dass dies Auswirkungen hat. Auf diese Weise können Sie die Verzeichnisse auch problemlos umstrukturieren (CMakeLists sind lokal und in den zu verschiebenden Unterverzeichnissen enthalten). Die CMakeLists der obersten Ebene sollten die meisten Konfigurationen der obersten Ebene enthalten, z. B. das Einrichten von Zielverzeichnissen, benutzerdefinierten Befehlen (oder Makros) und das Auffinden von auf dem System installierten Paketen. Die untergeordneten CMakeLists sollten nur einfache Listen von Headern, Quellen,
Quote: Wie müsste die CMakeLists.txt aussehen, damit sie entweder nur die Bibliothek oder die Bibliothek und die Tests erstellen kann?
Die grundlegende Antwort lautet, dass Sie mit CMake bestimmte Ziele gezielt von "all" ausschließen können (was bei der Eingabe von "make" erstellt wird) und bestimmte Zielbündel erstellen können. Ich kann hier kein CMake-Tutorial machen, aber es ist ziemlich einfach, es selbst herauszufinden. In diesem speziellen Fall besteht die empfohlene Lösung natürlich darin, CTest zu verwenden. Dies ist nur ein zusätzlicher Befehlssatz, den Sie in den CMakeLists-Dateien verwenden können, um eine Reihe von Zielen (Programmen) zu registrieren, die als unit- gekennzeichnet sind. Tests. CMake wird also alle Tests in eine spezielle Kategorie von Builds einordnen, und genau darum haben Sie gebeten, um das Problem zu lösen.
Quote: Ich habe auch einige Projekte gesehen, die eine Build-Anzeige und ein Bin-Verzeichnis haben. Findet der Build im Build-Verzeichnis statt und werden die Binärdateien in das bin-Verzeichnis verschoben? Würden sich die Binärdateien für die Tests und die Bibliothek am selben Ort befinden? Oder wäre es sinnvoller, es wie folgt zu strukturieren:
Ein Build-Verzeichnis außerhalb der Quelle zu haben ("Out-of-Source" Build) ist wirklich das einzig Vernünftige, es ist heutzutage der De-facto-Standard. Haben Sie also definitiv ein separates "Build" -Verzeichnis außerhalb des Quellverzeichnisses, so wie es die CMake-Leute empfehlen und wie es jeder Programmierer tut, den ich jemals getroffen habe. Was das bin-Verzeichnis betrifft, so ist dies eine Konvention, und es ist wahrscheinlich eine gute Idee, sich daran zu halten, wie ich am Anfang dieses Beitrags sagte.
Quote: Ich möchte auch doxygen verwenden, um meinen Code zu dokumentieren. Ist es möglich, dass dies automatisch mit cmake and make ausgeführt wird?
Ja. Es ist mehr als möglich, es ist großartig. Je nachdem, wie ausgefallen Sie werden möchten, gibt es mehrere Möglichkeiten. CMake verfügt über ein Modul für Doxygen (dh find_package(Doxygen)
), mit dem Sie Ziele registrieren können, auf denen Doxygen für einige Dateien ausgeführt wird. Wenn Sie ausgefallenere Dinge tun möchten, z. B. die Versionsnummer in der Doxy-Datei aktualisieren oder automatisch Datums- / Autorenstempel für Quelldateien eingeben usw., ist dies alles mit etwas CMake-Kung-Fu möglich. Im Allgemeinen bedeutet dies, dass Sie eine Quell-Doxy-Datei (z. B. die "Doxy-Datei.in", die ich in das obige Ordnerlayout eingefügt habe) behalten, deren Token gefunden und durch die Analysebefehle von CMake ersetzt werden müssen. In meiner CMakeLists-Datei der obersten Ebene finden Sie ein solches Stück CMake-Kung-Fu, das ein paar ausgefallene Dinge mit cmake-doxygen zusammen macht.