Musimy użyć pliku Makefile, aby zebrać wszystko razem w naszym projekcie, ale nasz profesor nigdy nie pokazał nam, jak to zrobić.
Mam tylko jeden plik a3driver.cpp
. Sterownik importuje klasę z lokalizacji,"/user/cse232/Examples/example32.sequence.cpp"
.
Otóż to. Wszystko inne jest zawarte w.cpp
.
Jak miałbym zrobić prosty plik Makefile, który tworzy plik wykonywalny o nazwie a3a.exe
?
Odpowiedzi:
Ponieważ dotyczy to Uniksa, pliki wykonywalne nie mają żadnych rozszerzeń.
Należy zauważyć, że
root-config
jest to narzędzie, które zapewnia odpowiednią kompilację i łączenie flag; i odpowiednie biblioteki do budowania aplikacji przeciwko rootowi. To tylko szczegół związany z oryginalnymi odbiorcami tego dokumentu.Make Me Baby
lub nigdy nie zapomnisz o swoim pierwszym wykonaniu
Wstępna dyskusja o marce i jak napisać prosty makefile
Co to jest marka? I dlaczego powinienem się przejmować?
Narzędzie o nazwie Make jest menedżerem zależności kompilacji. Oznacza to, że dba o to, jakie polecenia należy wykonać w jakiej kolejności, aby pobrać projekt z kolekcji plików źródłowych, plików obiektowych, bibliotek, nagłówków itp. Itp. - niektóre z nich mogły ulec zmianie ostatnio --- i przekształcenie ich w poprawną, aktualną wersję programu.
Właściwie możesz użyć Make również do innych rzeczy, ale nie zamierzam o tym mówić.
Trywialny plik makefile
Załóżmy, że masz katalog zawierający:
tool
tool.cc
tool.o
support.cc
support.hh
i,support.o
który zależyroot
i powinien zostać skompilowany w program o nazwietool
, i załóżmy, że włamałeś się do plików źródłowych (co oznacza, że istniejącytool
jest już nieaktualny) i chcesz skompiluj program.Aby to zrobić samemu, możesz
Sprawdź, czy któryś
support.cc
lubsupport.hh
jest nowszysupport.o
, a jeśli tak, uruchom polecenie podobneSprawdź, czy któryś z nich jest nowszy
support.hh
lub , a jeśli tak, uruchom polecenie podobnetool.cc
tool.o
Sprawdź, czy
tool.o
jest nowszy niżtool
, a jeśli tak, uruchom polecenie takie jakUff! Co za kłopot! Jest wiele do zapamiętania i kilka okazji do popełnienia błędów. (BTW - dane pokazane tutaj wierszy poleceń zależą od naszego środowiska oprogramowania. Te działają na moim komputerze).
Oczywiście, możesz po prostu uruchomić wszystkie trzy polecenia za każdym razem. To by działało, ale nie skaluje się dobrze do znacznego oprogramowania (takiego jak DOGS, którego kompilacja od podstaw na moim MacBooku zajmuje ponad 15 minut).
Zamiast tego możesz napisać plik o nazwie
makefile
tak:i po prostu pisz
make
w wierszu poleceń. Który wykona trzy powyższe kroki automatycznie.Nieindentowane linie tutaj mają postać „cel: zależności” i mówią Make, że powiązane polecenia (wcięte linie) powinny zostać uruchomione, jeśli którakolwiek z zależności jest nowsza niż cel. Oznacza to, że linie zależności opisują logikę tego, co należy przebudować, aby uwzględnić zmiany w różnych plikach. Jeśli
support.cc
zmiany oznaczają, żesupport.o
należy go odbudować, aletool.o
można je zostawić w spokoju. Kiedysupport.o
zmianytool
muszą zostać przebudowane.Polecenia związane z każdą linią zależności są uruchamiane za pomocą tabulatora (patrz poniżej) powinien zmodyfikować cel (lub przynajmniej go dotknąć, aby zaktualizować czas modyfikacji).
Zmienne, wbudowane reguły i inne gadżety
W tym momencie nasz plik makefile po prostu pamięta pracę, którą należy wykonać, ale wciąż musieliśmy wymyślić i wpisać każde potrzebne polecenie w całości. Nie musi tak być: Make to potężny język ze zmiennymi, funkcjami manipulacji tekstem i całą masą wbudowanych reguł, które mogą nam to znacznie ułatwić.
Twórz zmienne
Składnia dostępu do zmiennej make jest następująca
$(VAR)
.Składnia przypisywania do zmiennej Make to:
VAR = A text value of some kind
(lubVAR := A different text value but ignore this for the moment
).Możesz używać zmiennych w regułach takich jak ta ulepszona wersja naszego makefile:
co jest nieco bardziej czytelne, ale wciąż wymaga dużo pisania
Wykonuj funkcje
GNU make obsługuje różne funkcje dostępu do informacji z systemu plików lub innych poleceń w systemie. W tym przypadku jesteśmy zainteresowani tym,
$(shell ...)
które rozwinięcie do wyniku argumentu (argumentów), i$(subst opat,npat,text)
które zastępuje wszystkie wystąpienia ciąguopat
znpat
tekstem.Skorzystanie z tego daje nam:
który jest łatwiejszy do pisania i znacznie bardziej czytelny.
Zauważ, że
Zasady niejawne i wzorce
Generalnie spodziewalibyśmy się, że wszystkie pliki źródłowe C ++ powinny być traktowane w ten sam sposób, a Make udostępnia trzy sposoby na stwierdzenie tego:
Domniemane reguły są wbudowane, a kilka zostanie omówionych poniżej. Reguły wzorców są określone w formie podobnej do
co oznacza, że pliki obiektowe są generowane z plików źródłowych C, uruchamiając pokazane polecenie, gdzie zmienna „automatyczna”
$<
rozwija się do nazwy pierwszej zależności.Wbudowane reguły
Make ma cały szereg wbudowanych reguł, co oznacza, że bardzo często projekt można skompilować za pomocą bardzo prostego makefile.
Wbudowana reguła GNU make dla plików źródłowych w języku C jest pokazana powyżej. Podobnie tworzymy pliki obiektowe z plików źródłowych C ++ z regułą podobną do
$(CXX) -c $(CPPFLAGS) $(CFLAGS)
.Pliki pojedynczych obiektów są łączone za pomocą
$(LD) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
, ale w naszym przypadku nie będzie to działać, ponieważ chcemy połączyć pliki wielu obiektów.Zmienne używane przez wbudowane reguły
Wbudowane reguły używają zestawu standardowych zmiennych, które pozwalają określić lokalne informacje o środowisku (np. Gdzie znaleźć pliki ROOT zawierające pliki) bez ponownego zapisywania wszystkich reguł. Najbardziej interesujące dla nas są:
CC
- używany kompilator C.CXX
- kompilator C ++ do użyciaLD
- linker do użyciaCFLAGS
- flaga kompilacji plików źródłowych C.CXXFLAGS
- flagi kompilacji plików źródłowych C ++CPPFLAGS
- flagi dla c-preprocesora (zazwyczaj zawierają ścieżki plików i symbole zdefiniowane w wierszu poleceń), używane przez C i C ++LDFLAGS
- flagi linkeraLDLIBS
- biblioteki do połączeniaPodstawowy plik makefile
Korzystając z wbudowanych reguł, możemy uprościć nasz plik makefile, aby:
Dodaliśmy również kilka standardowych celów, które wykonują specjalne akcje (takie jak czyszczenie katalogu źródłowego).
Zauważ, że gdy make jest wywoływane bez argumentu, używa pierwszego celu znalezionego w pliku (w tym przypadku wszystkich), ale możesz także nazwać cel, aby uzyskać to, co czyni
make clean
usunięcie plików obiektowych w tym przypadku.Nadal mamy na stałe wszystkie zależności.
Niektóre tajemnicze ulepszenia
Zauważ, że
make
wtedyls -A
zobaczysz plik o nazwie.depend
, która zawiera rzeczy, które wyglądają jak tworzyć linie zależnośćInne czytanie
Poznaj błędy i uwagi historyczne
Język wprowadzania dla Make jest wrażliwy na białe znaki. W szczególności wiersze akcji następujące po zależnościach muszą zaczynać się od tabulatora . Ale seria spacji może wyglądać tak samo (i rzeczywiście istnieją edytory, które po cichu konwertują tabulacje na spacje lub odwrotnie), co powoduje, że plik Make wygląda poprawnie i nadal nie działa. Zostało to wcześnie zidentyfikowane jako błąd, ale ( historia mówi ) nie zostało naprawione, ponieważ było już 10 użytkowników.
(Zostało to skopiowane z posta na wiki, który napisałem dla doktorantów fizyki.)
źródło
-pthread
flaga powodujegcc
zdefiniowanie niezbędnych makr,-D_REENTRANT
jest niepotrzebna.root-config
). Należy zaproponować bardziej ogólną alternatywę o takich samych możliwościach, jeśli taka istnieje lub należy ją po prostu pominąć. Nie oddałem głosu z powodu listy i wyjaśnienia najczęściej używanych makr make.Zawsze uważałem, że łatwiej jest się tego nauczyć na szczegółowym przykładzie, więc oto, co myślę o plikach makefile. Dla każdej sekcji masz jeden wiersz, który nie jest wcięty i pokazuje nazwę sekcji, a następnie zależności. Zależności mogą być albo innymi sekcjami (które zostaną uruchomione przed bieżącą sekcją) lub plikami (które, jeśli zostaną zaktualizowane, spowodują ponowne uruchomienie bieżącej sekcji przy następnym uruchomieniu
make
).Oto szybki przykład (pamiętaj, że używam 4 spacji, w których powinienem używać tabulacji, przepełnienie stosu nie pozwoli mi używać tabulatorów):
Podczas pisania
make
wybierzesz pierwszą sekcję (a3driver). a3driver zależy od a3driver.o, więc przejdzie do tej sekcji. a3driver.o zależy od a3driver.cpp, więc będzie działać tylko wtedy, gdy a3driver.cpp zmienił się od czasu ostatniego uruchomienia. Zakładając, że został (lub nigdy nie został uruchomiony), skompiluje a3driver.cpp do pliku .o, a następnie wróci do a3driver i skompiluje końcowy plik wykonywalny.Ponieważ jest tylko jeden plik, można go nawet zredukować do:
Powodem, dla którego pokazałem pierwszy przykład, jest to, że pokazuje on moc plików makefile. Jeśli musisz skompilować inny plik, możesz po prostu dodać kolejną sekcję. Oto przykład z plikiem secondFile.cpp (który ładuje się w nagłówku o nazwie secondFile.h):
W ten sposób, jeśli zmienisz coś w secondFile.cpp lub secondFile.h i przekompilujesz, to tylko przekompiluje secondFile.cpp (nie a3driver.cpp). Lub naprzemiennie, jeśli zmienisz coś w a3driver.cpp, nie dokona ponownej kompilacji secondFile.cpp.
Daj mi znać, jeśli masz jakieś pytania na ten temat.
Tradycyjne jest również dołączanie sekcji o nazwie „wszystkie” i sekcji o nazwie „czyste”. „all” zwykle buduje wszystkie pliki wykonywalne, a „clean” usuwa „artefakty kompilacji”, takie jak pliki .o i pliki wykonywalne:
EDYCJA: Nie zauważyłem, że jesteś na Windowsie. Myślę, że jedyną różnicą jest zmiana
-o a3driver
do-o a3driver.exe
.źródło
Dlaczego wszyscy lubią wymieniać pliki źródłowe? Proste polecenie znalezienia może to załatwić z łatwością.
Oto przykład prostego makefile C ++. Po prostu upuść go w katalogu zawierającym
.C
pliki, a następnie wpiszmake
...źródło
Miałeś dwie opcje.
Opcja 1: najprostszy plik makefile = NO MAKEFILE.
Zmień nazwę „a3driver.cpp” na „a3a.cpp”, a następnie w wierszu polecenia wpisz:
I to wszystko. Jeśli używasz GNU Make, użyj „make”, „gmake” lub cokolwiek innego.
Opcja 2: 2-liniowy plik makefile.
źródło
nmake
.link
Linii poleceń wygląda również bardzo specyficzne dla konkretnego kompilatora i powinien przynajmniej jednego dokumentu.Twój plik Make będzie miał jedną lub dwie reguły zależności w zależności od tego, czy kompilujesz i łączysz za pomocą pojedynczego polecenia, czy też jednego polecenia dla kompilacji i jednego dla łącza.
Zależność to drzewo reguł, które wyglądają tak (zwróć uwagę, że wcięcie musi być TAB):
Tam musi być pusty wiersz po poleceniach na cel i musi nie być pusty wiersz przed poleceniami. Pierwszy cel w makefile jest celem ogólnym, a inne cele są budowane tylko wtedy, gdy pierwszy cel zależy od nich.
Twój plik makefile będzie wyglądał mniej więcej tak.
źródło
Sugeruję (zauważ, że wcięcie to TAB):
lub
Ta ostatnia sugestia jest nieco lepsza, ponieważ ponownie wykorzystuje GNU Make implicite rules. Jednak aby działać, plik źródłowy musi mieć taką samą nazwę jak końcowy plik wykonywalny (tj .:
tool.c
itool
).Zauważ, że nie jest konieczne deklarowanie źródeł. Pliki obiektów pośrednich są generowane przy użyciu reguły niejawnej. W konsekwencji
Makefile
działa to dla C i C ++ (a także dla Fortran itp.).Zauważ też, domyślnie, użycie Makefile
$(CC)
jako linkera.$(CC)
nie działa w przypadku łączenia plików obiektów C ++. ModyfikujemyLINK.o
tylko z tego powodu. Jeśli chcesz skompilować kod C, nie musisz wymuszaćLINK.o
wartości.Jasne, możesz również dodać swoje flagi kompilacji ze zmienną
CFLAGS
i dodać biblioteki wLDLIBS
. Na przykład:Jedna uwaga: jeśli musisz korzystać z zewnętrznych bibliotek, sugeruję użycie pkg-config , aby poprawnie ustawić
CFLAGS
iLDLIBS
:Uważny czytelnik to zauważy
Makefile
nie poprawnie, jeśli zmieni się jeden nagłówek. Dodaj następujące wiersze, aby rozwiązać problem:-MMD
pozwala budować pliki .d zawierające fragmenty Makefile dotyczące zależności nagłówków. Druga linia po prostu ich używa.Na pewno dobrze napisany plik Makefile powinien również zawierać
clean
idistclean
regulować:Ogłoszenie,
$(RM)
jest odpowiednikiemrm -f
, ale dobrą praktyką jest, aby nie dzwonićrm
bezpośrednio.The
all
Reguła jest również mile widziane. Aby działać, powinna być pierwszą regułą twojego pliku:Możesz także dodać
install
regułę:DESTDIR
jest domyślnie pusty. Użytkownik może ustawić go tak, aby instalował program w alternatywnym systemie (obowiązkowe w procesie kompilacji krzyżowej). Opiekunowie pakietów dla wielu dystrybucji mogą również ulec zmianiePREFIX
w celu zainstalowania pakietu/usr
.Ostatnie słowo: nie umieszczaj plików źródłowych w podkatalogach. Jeśli naprawdę chcesz to zrobić, zachowaj to
Makefile
w katalogu głównym i użyj pełnych ścieżek do identyfikacji plików (tjsubdir/file.o
.).Podsumowując, twój pełny Makefile powinien wyglądać następująco:
źródło
make
, co wiem (GNU Make i BSD Make) nie potrzebuje pustych linii między regułami. Istnieje jednak mnóstwomake
wdrożeń z ich własnymi błędami ^ Wspólności.Kiedyś odpowiedź friedmud męska . Przyglądałem się temu przez chwilę i wydaje się, że to dobry sposób na rozpoczęcie. To rozwiązanie ma również dobrze zdefiniowaną metodę dodawania flag kompilatora. Odpowiedziałem ponownie, ponieważ wprowadziłem zmiany, aby działało w moim środowisku, Ubuntu i g ++. Czasami więcej przykładów roboczych jest najlepszym nauczycielem.
Pliki makefile wydają się być bardzo złożone. Używałem jednego, ale generował błąd związany z brakiem łączenia w bibliotekach g ++. Ta konfiguracja rozwiązała ten problem.
źródło