Struktura projektu, cmake, git

2

Cześć,

Dotychczas w przypadku pisania czegoś z cpp prawie zawsze korzystałem z środowiska Visuala, potrafię jednak skompilować coś z linii komend czy napisać jakiegoś prostego makefile który pozałącza jakieś zewnętrzne biblioteki (tutaj już mowa o jakimś linuxie/Mingw/wsl). Chciałem teraz wrzucić jakiś projekt na githuba co wiąże się z tym, że wypada, żeby to wszystko (cały projekt) jakoś wyglądało. Wiąże się z tym struktura projektu a także utworzenie pliku cmake. Dotychczas w visualu wszystkie pliki .h (.hpp) oraz *.cpp trzymane są w jednym katalogu, linkowanie bibliotek to klikanie guzików itp., jest to mało wymagające i nie wymaga jakoś szczególnie dbania o strukturę projektu. Teraz mam parę pytań co do tego jak powinien dobrze prezentować się projekt w CPP umieszczony na githubie.

  1. Czy umieszczenie pliku makefile, albo pliku cmake jest standardem? Jaki jest cel umieszczania takiego pliku? Czy powinien on umożliwić zbudowanie projektu 'od tak', czy powinien on umożliwić zbudowanie projektu gdy spełnione są odpowiednie warunki np. odpowiedzialność za posiadanie odpowiednich bibliotek spada na użytkownika który chce zbudować mój projekt.

  2. Co z bibliotekami (zewnętrznymi)? Czy przykładowo w strukturze projektu powinienem utworzyć folder lib w którym będą skompilowane biblioteki? Rozumiem, że nie powinny być one trzymane w repo (zależność od systemu)? Przeglądając projekty na githubie widzę, że albo nie ma takiego katalogu, albo on jest i zawiera skompilowane biblioteki, albo jest i zawiera źródła tych bibliotek (czyli to by dawało największą elastyczność, każdy może zaciągnąć wszystko jako piliki źródłowe i skompilować u siebie wymagane biblioteki i pliki źródłowe mojego programu przy pomocy cmake? Ale przecież nie zawsze jest to możliwe, źródła chyba nie zawsze są udostępniane...)

  3. Co z dll'kami (.so)? Normalnie ręcznie kopiuje je tam gdzie plik wykonywalny, teraz jeśli ktoś chce zbudować mój projekt to na niego spada odpowiedzialność, żeby je tam umieścił? Czy może jeśli mam pliki źródłowe w katalogu np. lib to skrypt cmake powinien zbudować taką bibliotekę i skopiowiać tam dll'ki?

  4. Często w strukturach projektu znajduje się katalog include, czy powinny się tam znaleźć pliki nagłówkowe bibliotek zewnętrznych czy może mojego projektu?

  5. Przykładowo - używam CUDA, rozumiem, że skrypt budujący powinien sprawdzić czy dostępna jest ta technologia i w razie czego poinformować użytkownika o jej braku?

  6. Wreszcie jak powinna wyglądać struktura projektu, tak aby "nie było wstydu tego umieścić"? Czy to powinno mniej więcej tak wyglądać? Pomijam katalogi typu doc, test czy pliki typu .gitignore.

    --src <- moje pliki .cpp
    --include <- moje pliki .hpp
    --lib <- wszystko związanego z zewnętrznymi bibliotekami (folder nie przechowywany w repo)
      --include
        --sfml       <- headery bibliotek
        --glfw itp...<-
      --sfml         <- skompilowane biblioteki
      --glfw itp...  <- 
    --bin <- plik z zbudowanym plikiem wykonywalnym (folder nie przechowywany w repo)
      --release
      --debug
    --plik cmake <- zbuduje program o ile użytkownik sam skonfiguruje to co potrzebne (np. będzie miał wymagane biblioteki)
2

warto obejrzeć sobie

ja do struktury podchodziłem różnie. Kiedyś miałem osobno src i inc. ale teraz trzymam cpp + hpp w jednym katalogu ale mam projekt podzielny na kawałki(i też tak go buduję po kawałku). czyli mam np. w src foldery controller, model itd. W nich cpp i h a w kązdym z nich cmake.

Libki można dodać różnie. Wrzucić do folderu 3rd party po prostu, zrobić submoduł w git, jak to jakieś zastrzeżone to pewnie będziesz miał jakieś .so/.a. Ale gneeralnie trzyma się je nie w katalogu w którym jest twój kod czyli np. pod src. cmake też ci po wie czy czegoś ci nie brak np. qt. jak w nim będziesz miał np. find packegs.
oczywiście pozostaję takie rzeczy jak conan
https://conan.io/

edit:
Po robocie podeślę jeszcze linki z stacka, ktoś tam znalazł jakieś dobre omówienie cmake a nie jak często bywa w tutkach do niego jakieś źle napisane przykłady ale działa.
Możesz też wejść na github turnera on tam ma chyba przykładowy projekt na cmake.
edit2: coś tych zakładek nie moge znaleźć ale np. jak mówiłem od lefticusa można obejrzeć przykład
https://github.com/cpp-best-practices/cpp_starter_project

4
nssx napisał(a):
  1. Czy umieszczenie pliku makefile, albo pliku cmake jest standardem?

Tak.

Jaki jest cel umieszczania takiego pliku?

Celem jest zbudowanie projektu.
Owszem, mozna napisac readme z 37 krokami jak to zbudowac, ale wtedy to w praktyce uzytkownik sam sobie tego builda napisze, tyle ze z reguly w konsoli wielokrotnie sie mylac i powtarzajac te same kroki przy poprawie. A jesli mowa o projekcie ktory jest jedna z wielu zaleznosci, i zadna nie ma builda bo przeciez wystarczy kliknac na zielony guzik w jakims gui (i do tego kazda zaleznosc by wymagala zainstalowania innego gui), to wtedy zbudowanie zadnego wiekszego projektu pewnie nigdy nikomu by sie nie udalo.

Czy powinien on umożliwić zbudowanie projektu 'od tak', czy powinien on umożliwić zbudowanie projektu gdy spełnione są odpowiednie warunki np. odpowiedzialność za posiadanie odpowiednich bibliotek spada na użytkownika który chce zbudować mój projekt.

Roznie bywa. Czasami w cmake sie wpisze find_package() i jak czegos brakuje to make bedzie krzyczal. Kojarze jeden projekt w ktorym cmake ma wpisane dociaganie zaleznosci. Z kolei nie kojarze buila w bazelu ktory sam by nie dociagnal wszystkich zaleznosci.

  1. Co z bibliotekami (zewnętrznymi)? Czy przykładowo w strukturze projektu powinienem utworzyć folder lib w którym będą skompilowane biblioteki?

Idealem jest jak sa dostepne publicznie zrodla. Wtedy najlepiej dodac zaleznosc od zrodel (ktore skompiluje build ktory piszesz), np. jako submodul. Jesli zrodla sa dostepne, ale niekoniecznie w repo, i licencja pozwala, to ich skopiowanie nie jest bardzo straszne. Jesli zrodel nie ma, i licencja pozwala, to mozna skopiowac binarki. Czasami licencja nie pozwala (np. kiedys, i zapewne ciagle, JDK nie mozna bylo dostarczac z wlasna apka). Wtedy albo trzeba wymagac zeby uzytkownik sam sobie zainstalowal, albo napisac instalator, ktory pomoze uzytkownikowi sciagnac i zainstalowac zaleznosc.

  1. Co z dll'kami (.so)? Normalnie ręcznie kopiuje je tam gdzie plik wykonywalny, teraz jeśli ktoś chce zbudować mój projekt to na niego spada odpowiedzialność, żeby je tam umieścił? Czy może jeśli mam pliki źródłowe w katalogu np. lib to skrypt cmake powinien zbudować taką bibliotekę i skopiowiać tam dll'ki?

Przy linkowaniu statycznym takich problemow nie ma. Czasami sie nie da statycznie. Wtedy warto do cmake dodac opcje install (co moze zrobic dokladnie to samo co robiles recznie), albo napisac instalator.

  1. Często w strukturach projektu znajduje się katalog include, czy powinny się tam znaleźć pliki nagłówkowe bibliotek zewnętrznych czy może mojego projektu?

W zrodlach projektu nie spodziewam sie znalezc naglowkow z zaleznosci - w koncu one maja swoja osobna strukture i te pliki juz sa gdzie indziej.

  1. Przykładowo - używam CUDA, rozumiem, że skrypt budujący powinien sprawdzić czy dostępna jest ta technologia i w razie czego poinformować użytkownika o jej braku?

find_package() z cmake sam poszuka, i sam powie ze nie znalazl. CUDA nie jest typowa zaleznoscia. Tu masz opisane 2 opcje jak mozna dodac: https://cmake.org/cmake/help/latest/module/FindCUDA.html (sam nie korzystalem).

  1. Wreszcie jak powinna wyglądać struktura projektu, tak aby "nie było wstydu tego umieścić"? Czy to powinno mniej więcej tak wyglądać? Pomijam katalogi typu doc, test czy pliki typu .gitignore.
--src <- moje pliki .cpp
--include <- moje pliki .hpp
--lib <- wszystko związanego z zewnętrznymi bibliotekami (folder nie przechowywany w repo)
--include
--sfml <- headery bibliotek
--glfw itp...<-
--sfml <- skompilowane biblioteki
--glfw itp... <-
--bin <- plik z zbudowanym plikiem wykonywalnym (folder nie przechowywany w repo)
--release
--debug
--plik cmake <- zbuduje program o ile użytkownik sam skonfiguruje to co potrzebne (np. będzie miał wymagane biblioteki)

Nie sadze zeby [dla C++] bylo powszechne przekonanie ze jakos konkretnie to powinno wygladac. To co powyzej jest dla mnie w miare OK. Z dokladnoscia do wymagania od uzytkownika zeby sam sobie cos dociagal. Jesli sie da, submodul jest lepszy. Ewentualne dopisanie do readme zeby sobie zainstalowac pakiet costam-devel (dosyc czesto sie to zdaza dla projektow budowanych na linuxie. do innych systemow dawno sie nie dotykalem).

3
nssx napisał(a):
  1. Czy umieszczenie pliku makefile, albo pliku cmake jest standardem? Jaki jest cel umieszczania takiego pliku? Czy powinien on umożliwić zbudowanie projektu 'od tak', czy powinien on umożliwić zbudowanie projektu gdy spełnione są odpowiednie warunki np. odpowiedzialność za posiadanie odpowiednich bibliotek spada na użytkownika który chce zbudować mój projekt.

Wszystko zależy od wybranego procesu budowania. Ty pewnie używasz projektów solucji z Visual Studio, a to działa tylko na Windows. Nie jest to problem, jeśli sam projekt jest skazany na Windowsa.
Większość projektów jest wieloprofilowa, ergo VS odpada. Do wyboru jest wiele rozwiazań od starszuka make (makefile), prze obecny standard przemysłowy cmake przez bazel po inne rozwiązania.

nssx napisał(a):
  1. Co z bibliotekami (zewnętrznymi)? Czy przykładowo w strukturze projektu powinienem utworzyć folder lib w którym będą skompilowane biblioteki? Rozumiem, że nie powinny być one trzymane w repo (zależność od systemu)? Przeglądając projekty na githubie widzę, że albo nie ma takiego katalogu, albo on jest i zawiera skompilowane biblioteki, albo jest i zawiera źródła tych bibliotek (czyli to by dawało największą elastyczność, każdy może zaciągnąć wszystko jako piliki źródłowe i skompilować u siebie wymagane biblioteki i pliki źródłowe mojego programu przy pomocy cmake? Ale przecież nie zawsze jest to możliwe, źródła chyba nie zawsze są udostępniane...)

Zależy od procesu budowania i ogólnej filozofii. Można użyć menadżera paczek: vcpkg, Buckaroo, conan ... albo użyć submodule/subtree z git i budować źródła biblioteki samemu.
Najgorsze, co można zrobić to wpisywać ścieżki specyficzne dla komputera developera.
Dodawanie plików wykonalnych do repo też jet źle widziane.

conan jest kandydatem na standard przemysłowy taki jak cmake (z którym jest powiązany)

nssx napisał(a):
  1. Co z dll'kami (.so)? Normalnie ręcznie kopiuje je tam gdzie plik wykonywalny, teraz jeśli ktoś chce zbudować mój projekt to na niego spada odpowiedzialność, żeby je tam umieścił? Czy może jeśli mam pliki źródłowe w katalogu np. lib to skrypt cmake powinien zbudować taką bibliotekę i skopiowiać tam dll'ki?

Jak wyżej

nssx napisał(a):
  1. Często w strukturach projektu znajduje się katalog include, czy powinny się tam znaleźć pliki nagłówkowe bibliotek zewnętrznych czy może mojego projektu?

Celem jest odizolowanie publicznych nagłówków biblioteki od reszty, żeby łatwo weryfikować, że publikowane są wszystkie wymagane nagłówki biblioteki.
Jak wszystko jest w jednym katalogu, to można przegapić, że jakiś nagłówek powinien być dystrybuowany z biblioteką a uchował się jako prywatny

nssx napisał(a):
  1. Przykładowo - używam CUDA, rozumiem, że skrypt budujący powinien sprawdzić czy dostępna jest ta technologia i w razie czego poinformować użytkownika o jej braku?

Tak, w kroku konfiguracji projektu

nssx napisał(a):
  1. Wreszcie jak powinna wyglądać struktura projektu, tak aby "nie było wstydu tego umieścić"? Czy to powinno mniej więcej tak wyglądać? Pomijam katalogi typu doc, test czy pliki typu .gitignore.

Kwestia gustu, wymagań projektu, oraz dyscypliny członków zespołu.

4

jak powinna wyglądać struktura projektu, tak aby "nie było wstydu tego umieścić"?

Powinno się skompilować w maks dwóch krokach a reszta nie ma znaczenia, na prawdę. To nie strukturą powinieneś się przejmować a istotą projektu. Na githubie jest sporo gównianych projektów gdzie forma dominuje nad treścią, więc serio użyj takiej struktury by Tobie było wygodnie. Jeśli projekt zostanie zauważony, a statystycznie rzecz biorą najpewniej nie będzie, to wtedy ewentualnie możesz zacząć się przejmować jak ułatwić innym pracę z Twoim kodem.

"Treściwe" projekty, doceniane przez społeczność często mają prostą strukturę i nikt na to nie narzeka. Przykłady:

  1. cryptopp - płaska struktura z większością plików w folderze główny
  2. mongoose - prosta struktura z folderami test i src oraz plikami do deplojmentu w folderze głównym
  3. stb - źródła w folderze głównym i osobne foldery na poboczne rzeczy

Także radziłbym, by nad samą strukturą spędzić minimum potrzebnego czasu, tylko tyle by móc wygodnie prowadzić development i nie przejmować się czy człowiekowi z zewnątrz, któremu na projekcie nie zależy struktura się podoba czy nie.

0

podążam tropem autora wątku ;) koniec końców temat został ogarnięty ?

jak rozwiązałeś punkt "3" ?

1 użytkowników online, w tym zalogowanych: 0, gości: 1