Na początek istnieje kilka konwencjonalnych nazw katalogów, których nie można zignorować. Są one oparte na długiej tradycji z systemem plików Unix. To są:
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
Prawdopodobnie dobrze jest trzymać się tego podstawowego układu, przynajmniej na najwyższym poziomie.
Jeśli chodzi o dzielenie plików nagłówkowych i plików źródłowych (cpp), oba schematy są dość powszechne. Jednak wolę trzymać je razem, po prostu bardziej praktyczne jest trzymanie plików razem w codziennych zadaniach. Ponadto, gdy cały kod znajduje się w jednym folderze najwyższego poziomu, tj. trunk/src/
Folderze, można zauważyć, że wszystkie inne foldery (bin, lib, include, doc i może jakiś folder testowy) na najwyższym poziomie, oprócz katalog „build” dla kompilacji spoza źródła to wszystkie foldery, które zawierają tylko pliki generowane w procesie budowania. Dlatego tylko folder src musi być zarchiwizowany lub, o wiele lepiej, przechowywany w systemie kontroli wersji / serwerze (takim jak Git lub SVN).
A jeśli chodzi o instalowanie plików nagłówkowych w systemie docelowym (jeśli chcesz ostatecznie dystrybuować swoją bibliotekę), cóż, CMake ma polecenie do instalowania plików (niejawnie tworzy cel „instalacji”, aby wykonać „instalację”), które możesz użyć, aby umieścić wszystkie nagłówki w /usr/include/
katalogu. W tym celu używam tylko następującego makra cmake:
# 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)
Gdzie SRCROOT
jest zmienna cmake, którą ustawiłem na folder src, i INCLUDEROOT
jest to zmienna cmake, którą konfiguruję, aby wszędzie tam, gdzie mają iść nagłówki. Oczywiście jest na to wiele innych sposobów i jestem pewien, że mój sposób nie jest najlepszy. Chodzi o to, że nie ma powodu, aby dzielić nagłówki i źródła tylko dlatego, że tylko nagłówki muszą być zainstalowane w systemie docelowym, ponieważ jest bardzo łatwe, szczególnie z CMake (lub CPack), aby wybrać i skonfigurować nagłówki do być instalowane bez konieczności umieszczania ich w oddzielnym katalogu. I to właśnie widziałem w większości bibliotek.
Cytat: Po drugie, chciałbym użyć Google C ++ Testing Framework do testów jednostkowych mojego kodu, ponieważ wydaje się on dość łatwy w użyciu. Czy sugerujesz dołączenie tego do mojego kodu, na przykład w folderze „inc / gtest” lub „contrib / gtest”? Jeśli jest dołączony, czy sugerujesz użycie skryptu fuse_gtest_files.py w celu zmniejszenia liczby lub plików, czy też pozostawienie go tak, jak jest? Jeśli nie w pakiecie, jak jest obsługiwana ta zależność?
Nie łącz zależności ze swoją biblioteką. Generalnie jest to dość okropny pomysł i zawsze go nienawidzę, kiedy utknąłem, próbując zbudować bibliotekę, która to zrobiła. To powinna być twoja ostatnia deska ratunku i uważaj na pułapki. Często ludzie łączą zależności ze swoją biblioteką albo dlatego, że celują w okropne środowisko programistyczne (np. Windows), albo dlatego, że obsługują tylko starą (przestarzałą) wersję danej biblioteki (zależność). Główną pułapką jest to, że Twoja powiązana zależność może kolidować z już zainstalowanymi wersjami tej samej biblioteki / aplikacji (np. Spakowałeś gtest, ale osoba próbująca zbudować bibliotekę ma już zainstalowaną nowszą (lub starszą) wersję gtest, a następnie mogą się zderzyć i sprawić tej osobie bardzo okropny ból głowy). Więc, jak powiedziałem, zrób to na własne ryzyko, i powiedziałbym tylko w ostateczności. Poproszenie ludzi o zainstalowanie kilku zależności, zanim będzie można skompilować bibliotekę, jest znacznie mniejszym złem niż próba rozwiązania konfliktów między powiązanymi zależnościami a istniejącymi instalacjami.
Cytat: Jeśli chodzi o pisanie testów, jak są one ogólnie zorganizowane? Myślałem o jednym pliku cpp dla każdej klasy (na przykład test_vector3.cpp), ale wszystkie skompilowane do jednego pliku binarnego, aby można je było łatwo uruchomić razem?
Jeden plik cpp na klasę (lub małą spójną grupę klas i funkcji) jest moim zdaniem bardziej zwyczajny i praktyczny. Jednak zdecydowanie nie kompiluj ich wszystkich w jeden plik binarny tylko po to, aby „wszystkie mogły działać razem”. To naprawdę zły pomysł. Ogólnie rzecz biorąc, jeśli chodzi o kodowanie, chcesz podzielić rzeczy na tyle, na ile jest to uzasadnione. W przypadku testów jednostkowych nie chcesz, aby jeden plik binarny uruchamiał wszystkie testy, ponieważ oznacza to, że każda niewielka zmiana, którą wprowadzisz do czegokolwiek w swojej bibliotece, może spowodować prawie całkowitą rekompilację tego programu testów jednostkowych , a to tylko minuty / godziny stracone w oczekiwaniu na rekompilację. Po prostu trzymaj się prostego schematu: 1 jednostka = 1 program testów jednostkowych. Następnie,
Cytat: Ponieważ biblioteka gtest jest generalnie budowana przy użyciu cmake i make, pomyślałem, że miałoby sens, aby mój projekt był również budowany w ten sposób? Gdybym zdecydował się na następujący układ projektu:
Raczej zasugerowałbym taki układ:
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
Kilka rzeczy, na które należy zwrócić uwagę. Po pierwsze, podkatalogi twojego katalogu src powinny odzwierciedlać podkatalogi twojego katalogu include, to tylko po to, aby zachować intuicyjność (także staraj się utrzymać strukturę podkatalogów w miarę płaską (płytką), ponieważ głębokie zagnieżdżanie folderów jest często bardziej kłopotliwa niż cokolwiek innego). Po drugie, katalog „include” jest po prostu katalogiem instalacyjnym, a jego zawartością są po prostu wszystkie nagłówki, które są pobierane z katalogu src.
Po trzecie, system CMake ma być dystrybuowany w podkatalogach źródłowych, a nie jako jeden plik CMakeLists.txt na najwyższym poziomie. Dzięki temu rzeczy są lokalne i to jest dobre (w duchu dzielenia rzeczy na niezależne części). Jeśli dodasz nowe źródło, nowy nagłówek lub nowy program testowy, wszystko, czego potrzebujesz, to edytować jeden mały i prosty plik CMakeLists.txt w danym podkatalogu, bez wpływu na cokolwiek innego. Pozwala to również na łatwą restrukturyzację katalogów (listy CMakeLists są lokalne i zawarte w przenoszonych podkatalogach). CMakeLists najwyższego poziomu powinny zawierać większość konfiguracji najwyższego poziomu, takich jak konfigurowanie katalogów docelowych, niestandardowe polecenia (lub makra) i znajdowanie pakietów zainstalowanych w systemie. Niższy poziom CMakeLists powinien zawierać tylko proste listy nagłówków, źródeł,
Cytat: Jak musiałby wyglądać plik CMakeLists.txt, aby mógł zbudować tylko bibliotekę lub bibliotekę i testy?
Podstawowa odpowiedź jest taka, że CMake pozwala konkretnie wykluczyć określone cele ze „wszystkich” (co jest budowane po wpisaniu „make”), a także można tworzyć określone zestawy celów. Nie mogę tutaj zrobić samouczka dotyczącego CMake, ale dość łatwo jest się przekonać samemu. Jednak w tym konkretnym przypadku zalecanym rozwiązaniem jest oczywiście użycie CTest, który jest tylko dodatkowym zestawem poleceń, których można użyć w plikach CMakeLists do zarejestrowania wielu celów (programów) oznaczonych jako jednostka- testy. Tak więc CMake umieści wszystkie testy w specjalnej kategorii kompilacji i właśnie o to prosiłeś, więc problem został rozwiązany.
Cytat: Widziałem też sporo projektów, które miały budowanie reklamy w katalogu bin. Czy kompilacja odbywa się w katalogu kompilacji, a następnie pliki binarne są przenoszone do katalogu bin? Czy pliki binarne dla testów i biblioteki będą znajdować się w tym samym miejscu? A może bardziej sensowne byłoby sformułowanie tego w następujący sposób:
Posiadanie katalogu kompilacji poza źródłem (kompilacja „poza kodem”) jest naprawdę jedyną rozsądną rzeczą do zrobienia, jest to obecnie de facto standard. Tak więc na pewno mam oddzielny katalog "build", poza katalogiem źródłowym, tak jak zalecają ludzie z CMake i jak każdy programista, którego kiedykolwiek spotkałem. Jeśli chodzi o katalog bin to cóż, jest to konwencja i chyba warto się jej trzymać, o czym wspomniałem na początku tego wpisu.
Cytat: Chciałbym również użyć doxygen do udokumentowania mojego kodu. Czy możliwe jest automatyczne uruchamianie tego z cmake i make?
Tak. To więcej niż możliwe, jest niesamowite. W zależności od tego, jak fantazyjny chcesz uzyskać, istnieje kilka możliwości. CMake ma moduł dla Doxygen (tj. find_package(Doxygen)
), Który pozwala na rejestrowanie celów, które będą uruchamiać Doxygen na niektórych plikach. Jeśli chcesz robić bardziej wymyślne rzeczy, takie jak aktualizowanie numeru wersji w Doxyfile lub automatyczne wprowadzanie znaczników daty / autora dla plików źródłowych i tak dalej, to wszystko jest możliwe dzięki odrobinie kung-fu CMake. Ogólnie rzecz biorąc, zrobienie tego będzie wymagało zachowania źródła Doxyfile (np. „Doxyfile.in”, który umieściłem w układzie folderów powyżej), które zawiera tokeny do znalezienia i zastąpienia przez polecenia parsujące CMake. W moim pliku CMakeLists najwyższego poziomu , znajdziesz jeden taki kawałek kung-fu CMake, który robi kilka wymyślnych rzeczy razem z cmake-doxygen.