Per cominciare, ci sono alcuni nomi convenzionali per le directory che non puoi ignorare, questi sono basati sulla lunga tradizione con il file system Unix. Questi sono:
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
Probabilmente è una buona idea attenersi a questo layout di base, almeno al livello più alto.
Per quanto riguarda la divisione dei file di intestazione e dei file sorgente (cpp), entrambi gli schemi sono abbastanza comuni. Tuttavia, tendo a preferire tenerli insieme, è solo più pratico nelle attività quotidiane avere i file insieme. Inoltre, quando tutto il codice si trova in una cartella di primo livello, ovvero il filetrunk/src/
cartella, puoi notare che tutte le altre cartelle (bin, lib, include, doc e forse qualche cartella di test) al livello superiore, oltre a la directory "build" per una build out-of-source, sono tutte le cartelle che non contengono altro che file generati nel processo di build. E quindi, solo la cartella src deve essere sottoposta a backup, o molto meglio, mantenuta sotto un sistema / server di controllo della versione (come Git o SVN).
E quando si tratta di installare i file di intestazione sul sistema di destinazione (se si desidera eventualmente distribuire la libreria), beh, CMake ha un comando per l'installazione dei file (crea implicitamente una destinazione di "installazione", per fare "make install") che puoi usare per mettere tutte le intestazioni nella /usr/include/
directory. Uso solo la seguente macro cmake per questo scopo:
# 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)
Dov'è SRCROOT
una variabile cmake che ho impostato nella cartella src, ed INCLUDEROOT
è una variabile cmake che configuro dove devono andare le intestazioni. Naturalmente, ci sono molti altri modi per farlo, e sono sicuro che il mio non sia il migliore. Il punto è che non c'è motivo di dividere le intestazioni e le fonti solo perché solo le intestazioni devono essere installate sul sistema di destinazione, perché è molto facile, specialmente con CMake (o CPack), scegliere e configurare le intestazioni per essere installato senza doverli avere in una directory separata. E questo è quello che ho visto nella maggior parte delle biblioteche.
Quote: In secondo luogo, vorrei utilizzare Google C ++ Testing Framework per testare il mio codice in quanto sembra abbastanza facile da usare. Suggerite di raggrupparlo con il mio codice, ad esempio in una cartella "inc / gtest" o "contrib / gtest"? Se in bundle, suggerisci di utilizzare lo script fuse_gtest_files.py per ridurre il numero di file o di lasciarlo così com'è? Se non in bundle come viene gestita questa dipendenza?
Non raggruppare le dipendenze con la tua libreria. Questa è generalmente un'idea piuttosto orribile, e odio sempre quando sono bloccato nel tentativo di costruire una libreria che lo ha fatto. Dovrebbe essere la tua ultima risorsa e fai attenzione alle insidie. Spesso, le persone raggruppano le dipendenze con la loro libreria o perché prendono di mira un terribile ambiente di sviluppo (ad esempio, Windows), o perché supportano solo una vecchia versione (deprecata) della libreria (dipendenza) in questione. La trappola principale è che la tua dipendenza in bundle potrebbe scontrarsi con le versioni già installate della stessa libreria / applicazione (ad esempio, hai raggruppato gtest, ma la persona che cerca di costruire la tua libreria ha già una versione più recente (o precedente) di gtest già installata, quindi i due potrebbero scontrarsi e dare a quella persona un brutto mal di testa). Quindi, come ho detto, fallo a tuo rischio, e direi solo come ultima risorsa. Chiedere alle persone di installare alcune dipendenze prima di essere in grado di compilare la tua libreria è un male minore rispetto al tentativo di risolvere i conflitti tra le dipendenze in bundle e le installazioni esistenti.
Quote: Quando si tratta di scrivere test, come sono generalmente organizzati? Stavo pensando di avere un file cpp per ogni classe (test_vector3.cpp per esempio) ma tutto compilato in un binario in modo che possano essere eseguiti tutti insieme facilmente?
Un file cpp per classe (o un piccolo gruppo coeso di classi e funzioni) è più usuale e pratico secondo me. Tuttavia, sicuramente, non compilarli tutti in un binario solo in modo che "possano essere eseguiti tutti insieme". Questa è davvero una pessima idea. In generale, quando si tratta di codifica, si desidera dividere le cose per quanto è ragionevole farlo. Nel caso degli unit-test, non vuoi che un binario esegua tutti i test, perché ciò significa che qualsiasi piccola modifica che fai a qualcosa nella tua libreria probabilmente causerà una ricompilazione quasi totale di quel programma di unit-test , e sono solo minuti / ore persi in attesa di ricompilazione. Attenersi a uno schema semplice: 1 unità = 1 programma di test unitario. Poi,
Quote: Dato che la libreria gtest è generalmente costruita usando cmake e make, stavo pensando che avrebbe senso che anche il mio progetto fosse costruito in questo modo? Se ho deciso di utilizzare il seguente layout di progetto:
Preferirei suggerire questo layout:
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
Alcune cose da notare qui. Primo, le sottodirectory della tua directory src dovrebbero rispecchiare le sottodirectory della tua directory include, questo è solo per mantenere le cose intuitive (inoltre, prova a mantenere la struttura della tua sottodirectory ragionevolmente piatta (superficiale), perché l'annidamento profondo delle cartelle è spesso più una seccatura che altro). In secondo luogo, la directory "include" è solo una directory di installazione, i suoi contenuti sono solo le intestazioni selezionate dalla directory src.
Terzo, il sistema CMake è pensato per essere distribuito nelle sottodirectory di origine, non come un file CMakeLists.txt al livello superiore. Ciò mantiene le cose locali, e questo è un bene (nello spirito di dividere le cose in pezzi indipendenti). Se aggiungi una nuova sorgente, una nuova intestazione o un nuovo programma di test, tutto ciò di cui hai bisogno è modificare un piccolo e semplice file CMakeLists.txt nella sottodirectory in questione, senza influire su nient'altro. Ciò consente anche di ristrutturare facilmente le directory (le CMakeList sono locali e sono contenute nelle sottodirectory che vengono spostate). Le CMakeList di primo livello dovrebbero contenere la maggior parte delle configurazioni di primo livello, come l'impostazione di directory di destinazione, comandi personalizzati (o macro) e la ricerca di pacchetti installati sul sistema. Le CMakeLists di livello inferiore dovrebbero contenere solo semplici elenchi di intestazioni, fonti,
Quote: Come dovrebbe apparire il CMakeLists.txt in modo che possa costruire solo la libreria o la libreria e i test?
La risposta di base è che CMake ti consente di escludere specificamente determinati target da "all" (che è ciò che viene creato quando digiti "make"), e puoi anche creare bundle specifici di target. Non posso fare un tutorial su CMake qui, ma è abbastanza semplice scoprirlo da solo. In questo caso specifico, tuttavia, la soluzione consigliata è, ovviamente, utilizzare CTest, che è solo un set aggiuntivo di comandi che è possibile utilizzare nei file CMakeLists per registrare una serie di target (programmi) contrassegnati come unit- test. Quindi, CMake metterà tutti i test in una categoria speciale di build, ed è esattamente quello che hai chiesto, quindi, problema risolto.
Quote: Inoltre ho visto alcuni progetti che hanno un annuncio di build una directory bin. La compilazione avviene nella directory build e quindi i binari vengono spostati nella directory bin? I binari per i test e la libreria risiederebbero nello stesso posto? Oppure avrebbe più senso strutturarlo come segue:
Avere una directory di compilazione al di fuori dei sorgenti (build "out-of-source") è davvero l'unica cosa sensata da fare, è lo standard di fatto in questi giorni. Quindi, sicuramente, avere una directory di "build" separata, al di fuori della directory dei sorgenti, proprio come consigliano le persone di CMake e come fa ogni programmatore che abbia mai incontrato. Per quanto riguarda la directory bin, beh, questa è una convenzione, ed è probabilmente una buona idea attenersi ad essa, come ho detto all'inizio di questo post.
Quote: Vorrei anche usare doxygen per documentare il mio codice. È possibile farlo funzionare automaticamente con cmake e make?
Sì. È più che possibile, è fantastico. A seconda di quanto desideri ottenere, ci sono diverse possibilità. CMake ha un modulo per Doxygen (cioè find_package(Doxygen)
) che ti permette di registrare obiettivi che eseguiranno Doxygen su alcuni file. Se vuoi fare cose più fantasiose, come aggiornare il numero di versione nel Doxyfile, o inserire automaticamente un timbro data / autore per i file sorgente e così via, tutto è possibile con un po 'di CMake kung-fu. Generalmente, fare questo comporterà la conservazione di un Doxyfile sorgente (ad esempio, il "Doxyfile.in" che ho inserito nel layout della cartella sopra) che deve trovare i token e sostituirli con i comandi di analisi di CMake. Nel mio file CMakeLists di primo livello , troverai uno di questi pezzi di CMake kung-fu che fa alcune cose fantasiose con cmake-doxygen insieme.