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ży root
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ący tool
jest już nieaktualny) i chcesz skompiluj program.
Aby to zrobić samemu, możesz
Sprawdź, czy któryś support.cc
lub support.hh
jest nowszy support.o
, a jeśli tak, uruchom polecenie podobne
g++ -g -c -pthread -I/sw/include/root support.cc
Sprawdź, czy któryś z nich jest nowszy support.hh
lub , a jeśli tak, uruchom polecenie podobnetool.cc
tool.o
g++ -g -c -pthread -I/sw/include/root tool.cc
Sprawdź, czy tool.o
jest nowszy niż tool
, a jeśli tak, uruchom polecenie takie jak
g++ -g tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
-lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
-Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl
Uff! 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:
tool: tool.o support.o
g++ -g -o tool tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
-lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
-Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl
tool.o: tool.cc support.hh
g++ -g -c -pthread -I/sw/include/root tool.cc
support.o: support.hh support.cc
g++ -g -c -pthread -I/sw/include/root support.cc
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ą, że support.o
należy go odbudować, ale tool.o
można je zostawić w spokoju. Kiedy support.o
zmiany tool
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
(lub VAR := 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:
CPPFLAGS=-g -pthread -I/sw/include/root
LDFLAGS=-g
LDLIBS=-L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
-lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz \
-Wl,-framework,CoreServices -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root \
-lm -ldl
tool: tool.o support.o
g++ $(LDFLAGS) -o tool tool.o support.o $(LDLIBS)
tool.o: tool.cc support.hh
g++ $(CPPFLAGS) -c tool.cc
support.o: support.hh support.cc
g++ $(CPPFLAGS) -c support.cc
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ągu opat
z npat
tekstem.
Skorzystanie z tego daje nam:
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)
SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))
tool: $(OBJS)
g++ $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)
tool.o: tool.cc support.hh
g++ $(CPPFLAGS) -c tool.cc
support.o: support.hh support.cc
g++ $(CPPFLAGS) -c support.cc
który jest łatwiejszy do pisania i znacznie bardziej czytelny.
Zauważ, że
- Nadal wyraźnie stwierdzamy zależności dla każdego pliku obiektowego i końcowego pliku wykonywalnego
- Musieliśmy wyraźnie wpisać regułę kompilacji dla obu plików źródłowych
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:
- reguły sufiksów (uważane za przestarzałe w GNU make, ale zachowane dla kompatybilności wstecznej)
- zasady niejawne
- reguły wzorców
Domniemane reguły są wbudowane, a kilka zostanie omówionych poniżej. Reguły wzorców są określone w formie podobnej do
%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $<
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życia
LD
- linker do użycia
CFLAGS
- 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 linkera
LDLIBS
- biblioteki do połączenia
Podstawowy plik makefile
Korzystając z wbudowanych reguł, możemy uprościć nasz plik makefile, aby:
CC=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)
SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))
all: tool
tool: $(OBJS)
$(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)
tool.o: tool.cc support.hh
support.o: support.hh support.cc
clean:
$(RM) $(OBJS)
distclean: clean
$(RM) tool
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
CC=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)
SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))
all: tool
tool: $(OBJS)
$(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)
depend: .depend
.depend: $(SRCS)
$(RM) ./.depend
$(CXX) $(CPPFLAGS) -MM $^>>./.depend;
clean:
$(RM) $(OBJS)
distclean: clean
$(RM) *~ .depend
include .depend
Zauważ, że
- Nie ma już żadnych linii zależności dla plików źródłowych!?!
- Istnieje pewna dziwna magia związana z .depend i depend
- Jeśli tak
make
wtedy ls -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.)