Jak zrobić SIMPLE C ++ Makefile

302

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?

Zdarzać się
źródło
9
.EXE to zdecydowanie Windows. Z drugiej strony ... ścieżka jest w stylu uniksowym. Prawdopodobnie używa Mingw-32.
Nathan Osman
2
Westchnienie. Przypuszczam, że musisz nauczyć się podstaw każdej transakcji, nawet jeśli nigdy ich nie użyjesz. Musisz tylko zrozumieć, jak działają rzeczy. Są jednak duże szanse, że zawsze rozwiniesz się w środowisku IDE, takim jak Eclipse. Tutaj znajdziesz odpowiedź na swój prosty, jednoliniowy przypadek i jest wiele samouczków internetowych, ale jeśli chcesz dokładnej wiedzy, nie możesz pobić książki O'reilly (to samo w przypadku większości tematów s / w). amazon.com/Managing-Projects-Make-Nutshell-Handbooks/dp/... Wybierz kopię drugiej ręki z amazon, half.com, Betterworldbooks eBay
Mawg mówi, aby przywrócić Monikę
2
Link opublikowany przez @Dennis jest już martwy, ale ten sam materiał można znaleźć na tej stronie archive.org .
Guilherme Salomé
Wolę pomysły tej osoby. ( hiltmon.com/blog/2013/07/03/… ) Strukturę projektu można łatwo zmodyfikować, aby dopasować. Zgadzam się również, że czas programisty powinien zostać poświęcony na inne rzeczy niż automake / autoconf. Narzędzia te mają swoje miejsce, ale być może nie w przypadku wewnętrznych projektów. Buduję skrypt, który stworzy taką strukturę projektu.
Daisuke Aramaki
@ GuilhermeSalomé Dzięki, uważam, że jest to najlepszy prosty i kompletny samouczek.
Hareen Laks

Odpowiedzi:

559

Ponieważ dotyczy to Uniksa, pliki wykonywalne nie mają żadnych rozszerzeń.

Należy zauważyć, że root-configjest 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.hhi, support.októry zależy rooti 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 tooljest już nieaktualny) i chcesz skompiluj program.

Aby to zrobić samemu, możesz

  1. Sprawdź, czy któryś support.cclub support.hhjest nowszy support.o, a jeśli tak, uruchom polecenie podobne

    g++ -g -c -pthread -I/sw/include/root support.cc
  2. Sprawdź, czy któryś z nich jest nowszy support.hhlub , a jeśli tak, uruchom polecenie podobnetool.cctool.o

    g++ -g  -c -pthread -I/sw/include/root tool.cc
  3. Sprawdź, czy tool.ojest 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 makefiletak:

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.cczmiany oznaczają, że support.onależy go odbudować, ale tool.omożna je zostawić w spokoju. Kiedy support.ozmiany toolmuszą 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 opatz npattekstem.

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

  1. Nadal wyraźnie stwierdzamy zależności dla każdego pliku obiektowego i końcowego pliku wykonywalnego
  2. 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:

  1. reguły sufiksów (uważane za przestarzałe w GNU make, ale zachowane dla kompatybilności wstecznej)
  2. zasady niejawne
  3. 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

  1. Nie ma już żadnych linii zależności dla plików źródłowych!?!
  2. Istnieje pewna dziwna magia związana z .depend i depend
  3. Jeśli tak makewtedy ls -Azobaczysz 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.)

dmckee --- były kot moderator
źródło
9
Ta metoda generowania zależności jest przestarzała i faktycznie szkodliwa. Zobacz Zaawansowane generowanie automatycznej zależności .
Maxim Egorushkin 30.09.11
5
-pthreadflaga powoduje gcczdefiniowanie niezbędnych makr, -D_REENTRANTjest niepotrzebna.
Maxim Egorushkin 30.09.11
8
@ jcoe Robi niepotrzebne dodatkowe przejście preprocesora w celu wygenerowania zależności. Wykonując niepotrzebną pracę, po prostu rozprasza ciepło topiące bieguny lodu i, na większą skalę, zbliża się do śmierci cieplnej naszego wszechświata.
Maxim Egorushkin,
2
Prawdopodobnie „szkodliwy” to odrobinę za dużo, ale biorąc pod uwagę, że wyraźne fazy generowania zależności lub cele są nieaktualne, ponieważ przynajmniej GCC 3, naprawdę uważam, że wszyscy powinniśmy przejść obok nich. bruno.defraine.net/techtips/makefile-auto-dependencies-with-gcc/…
hmijail opłakuje powrót
2
Naprawdę, zaakceptowana odpowiedź nie powinna zależeć od bardzo konkretnego oprogramowania ( 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.
zielona dioda
56

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):

a3driver: a3driver.o
    g++ -o a3driver a3driver.o

a3driver.o: a3driver.cpp
    g++ -c a3driver.cpp

Podczas pisania makewybierzesz 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:

a3driver: a3driver.cpp
    g++ -o a3driver a3driver.cpp

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):

a3driver: a3driver.o secondFile.o
    g++ -o a3driver a3driver.o secondFile.o

a3driver.o: a3driver.cpp
    g++ -c a3driver.cpp

secondFile.o: secondFile.cpp secondFile.h
    g++ -c secondFile.cpp

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:

all: a3driver ;

clean:
    # -f so this will succeed even if the files don't exist
    rm -f a3driver a3driver.o

EDYCJA: Nie zauważyłem, że jesteś na Windowsie. Myślę, że jedyną różnicą jest zmiana -o a3driverdo -o a3driver.exe.

Brendan Long
źródło
Kod absolutny, którego próbuję użyć to: p4a.exe: p4driver.cpp g ++ -o p4a p4driver.cpp ALE, mówi mi „brak separatora”. Używam TAB, ale wciąż mi to mówi. Dowolny pomysł?
Befall
2
O ile wiem, ten komunikat o błędzie pojawia się tylko wtedy, gdy masz spacje. Upewnij się, że nie masz żadnych linii zaczynających się od spacji (spacja + tabulator da ten błąd). To jedyna rzecz, o której mogę myśleć ...
Brendan Long
Uwaga dla przyszłych redaktorów: StackOverflow nie może renderować kart, nawet jeśli edytujesz je w odpowiedzi, więc nie próbuj „naprawiać” mojej notatki na ten temat.
Brendan Long,
35

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 .Cpliki, a następnie wpisz make...

appname := myapp

CXX := clang++
CXXFLAGS := -std=c++11

srcfiles := $(shell find . -name "*.C")
objects  := $(patsubst %.C, %.o, $(srcfiles))

all: $(appname)

$(appname): $(objects)
    $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(appname) $(objects) $(LDLIBS)

depend: .depend

.depend: $(srcfiles)
    rm -f ./.depend
    $(CXX) $(CXXFLAGS) -MM $^>>./.depend;

clean:
    rm -f $(objects)

dist-clean: clean
    rm -f *~ .depend

include .depend
Friedmud
źródło
2
Powodem, dla którego nie można automatycznie znaleźć plików źródłowych, jest to, że można mieć różne cele kompilacji wymagające różnych plików.
Hmijail opłakuje powrót
Uzgodniony @hmijail, a także podmoduły zawierające mnóstwo źródeł / nagłówków, których nie chcesz skompilować / połączony ... i niewątpliwie wiele innych okoliczności, w których wyczerpujące wyszukiwanie / użycie jest nieodpowiednie.
Inżynier
Dlaczego zamiast tego należy używać opcji „znajdź powłokę”, a nie „symbol wieloznaczny”?
Nolan
1
@Nolan, aby znaleźć pliki źródłowe w drzewie katalogów źródłowych
AlejandroVD,
13

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:

nmake a3a.exe

I to wszystko. Jeśli używasz GNU Make, użyj „make”, „gmake” lub cokolwiek innego.

Opcja 2: 2-liniowy plik makefile.

a3a.exe: a3driver.obj
    link /out:a3a.exe a3driver.obj
Nikt
źródło
3
Byłaby to doskonała odpowiedź, gdyby nie zakładała tak wielu rzeczy na temat szczegółów środowiska PO. Tak, są w systemie Windows, ale to nie znaczy, że używają nmake. linkLinii poleceń wygląda również bardzo specyficzne dla konkretnego kompilatora i powinien przynajmniej jednego dokumentu.
tripleee
6

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):

main_target : source1 source2 etc
    command to build main_target from sources

source1 : dependents for source1
    command to build source1

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.

a3a.exe : a3driver.obj 
    link /out:a3a.exe a3driver.obj

a3driver.obj : a3driver.cpp
    cc a3driver.cpp
John Knoeller
źródło
6

Sugeruję (zauważ, że wcięcie to TAB):

tool: tool.o file1.o file2.o
    $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@

lub

LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
tool: tool.o file1.o file2.o

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.ci tool).

Zauważ, że nie jest konieczne deklarowanie źródeł. Pliki obiektów pośrednich są generowane przy użyciu reguły niejawnej. W konsekwencji Makefiledział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 ++. Modyfikujemy LINK.otylko z tego powodu. Jeśli chcesz skompilować kod C, nie musisz wymuszać LINK.owartości.

Jasne, możesz również dodać swoje flagi kompilacji ze zmienną CFLAGS i dodać biblioteki w LDLIBS. Na przykład:

CFLAGS = -Wall
LDLIBS = -lm

Jedna uwaga: jeśli musisz korzystać z zewnętrznych bibliotek, sugeruję użycie pkg-config , aby poprawnie ustawić CFLAGSi LDLIBS:

CFLAGS += $(shell pkg-config --cflags libssl)
LDLIBS += $(shell pkg-config --libs libssl)

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:

override CPPFLAGS += -MMD
include $(wildcard *.d)

-MMDpozwala 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ć cleanidistclean regulować:

clean:
    $(RM) *.o *.d

distclean: clean
    $(RM) tool

Ogłoszenie, $(RM) jest odpowiednikiem rm -f, ale dobrą praktyką jest, aby nie dzwonić rmbezpośrednio.

The allReguła jest również mile widziane. Aby działać, powinna być pierwszą regułą twojego pliku:

all: tool

Możesz także dodać install regułę:

PREFIX = /usr/local
install:
    install -m 755 tool $(DESTDIR)$(PREFIX)/bin

DESTDIRjest 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 toMakefile 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:

LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
PREFIX = /usr/local
override CPPFLAGS += -MMD
include $(wildcard *.d)

all: tool
tool: tool.o file1.o file2.o
clean:
    $(RM) *.o *.d
distclean: clean
    $(RM) tool
install:
    install -m 755 tool $(DESTDIR)$(PREFIX)/bin
Jérôme Pouiller
źródło
Pod koniec: czy między regułami nie powinny być puste linie? Twierdził, że odpowiedź Johna Knoellera .
Peter Mortensen
Żadna z implementacji tego make, co wiem (GNU Make i BSD Make) nie potrzebuje pustych linii między regułami. Istnieje jednak mnóstwo makewdrożeń z ich własnymi błędami ^ Wspólności.
Jérôme Pouiller
5

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.

appname := myapp

CXX := g++
CXXFLAGS := -Wall -g

srcfiles := $(shell find . -maxdepth 1 -name "*.cpp")
objects  := $(patsubst %.cpp, %.o, $(srcfiles))

all: $(appname)

$(appname): $(objects)
    $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(appname) $(objects) $(LDLIBS)

depend: .depend

.depend: $(srcfiles)
    rm -f ./.depend
    $(CXX) $(CXXFLAGS) -MM $^>>./.depend;

clean:
    rm -f $(objects)

dist-clean: clean
    rm -f *~ .depend

include .depend

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.

VectorVortec
źródło