EngelsFransSpaans

Ad


OnWorks-favicon

makepp_cookbook - Online in de cloud

Voer makepp_cookbook uit in de gratis hostingprovider van OnWorks via Ubuntu Online, Fedora Online, Windows online emulator of MAC OS online emulator

Dit is de opdracht makepp_cookbook die kan worden uitgevoerd in de gratis hostingprovider van OnWorks met behulp van een van onze meerdere gratis online werkstations zoals Ubuntu Online, Fedora Online, Windows online emulator of MAC OS online emulator

PROGRAMMA:

NAAM


makepp_cookbook -- De beste manier om makefiles in te stellen voor verschillende situaties

PRODUCTBESCHRIJVING


Ik ontdekte dat vrijwel niemand ooit een handleiding voor een maakgereedschap leest, want eerlijk gezegd
niemand is echt geïnteresseerd in het maakproces zelf - we zijn alleen geïnteresseerd in resultaten.
Dus dit kookboek is samengesteld in de hoop dat mensen kunnen krijgen wat ze nodig hebben
snel uit de voorbeelden zonder door de handleiding te hoeven waden. Dit laat zien hoe je moet typen
vragen, terwijl installatie-instructies en struikelblokken te vinden zijn in de
Veel Gestelde Vragen.

Gebouw bibliotheken
Do u werkelijk genoodzaakt bent a bibliotheek?

Ik heb een aantal grote programma's gezien die bestaan ​​uit een groot aantal modules, elk van:
die in zijn eigen directory leeft. Gewoonlijk wordt elke map in zijn eigen bibliotheek geplaatst,
en dan linkt het uiteindelijke programma met alle bibliotheken.

In veel gevallen denk ik dat in plaats van een bibliotheek te gebruiken, er een betere aanpak is. bibliotheken
zijn niet echt de juiste oplossing als elke module niet in een andere kan of zal worden hergebruikt
programma, want dan krijg je alle nadelen van bibliotheken en geen van de
voordelen. Bibliotheken zijn handig in de volgende gevallen:

1. Als je een aantal subroutines hebt die aan verschillende
programma's, en geen enkel programma gebruikt daadwerkelijk 100% van de subroutines - elk programma gebruikt een
verschillende deelverzameling. In dit geval is het waarschijnlijk een goed idee om een ​​statische bibliotheek te gebruiken (a
.a bestand of een archiefbestand).

2. Als je een module hebt die aan verschillende programma's moet worden gekoppeld, en je
wil het dynamisch laden, zodat elk programma geen aparte kopie hoeft te hebben van
de bibliotheek. Dynamische bibliotheken kunnen uitvoerbare bestandsruimte besparen en soms verbeteren
systeemprestaties omdat er slechts één exemplaar van de bibliotheek is geladen voor alle
verschillende programma's die het gebruiken.

3. Wanneer uw linktijd onbetaalbaar lang is, gebruik dan gedeelde bibliotheken voor grote stukken van
het programma kan de koppeling aanzienlijk versnellen.

Het gebruik van statische bibliotheken heeft één groot nadeel: op sommige systemen (bijv. Linux) is de volgorde
waarin u de bibliotheken koppelt, is van cruciaal belang. De linker verwerkt bibliotheken
in de volgorde die is opgegeven op de opdrachtregel. Het grijpt alles wat het denkt nodig te hebben uit
elke bibliotheek en gaat vervolgens naar de volgende bibliotheek. Als een volgende bibliotheek verwijst naar a
symbool dat nog niet is opgenomen uit een eerdere bibliotheek, de linker niet
weet dat je terug moet gaan en het uit de vorige bibliotheek moet pakken. Als gevolg hiervan kan het nodig zijn
om de bibliotheek meerdere keren op de linker-opdrachtregel weer te geven. (Ik werkte aan een project
waar we de hele lijst met bibliotheken drie keer moesten herhalen. Dit project is wat gemaakt
ik geef de voorkeur aan de alternatieve benadering die hieronder wordt voorgesteld, die van incrementeel koppelen.)

Het gebruik van dynamische bibliotheken heeft verschillende nadelen. Ten eerste kan uw programma enigszins zijn
langzamer opstarten als de bibliotheek nog niet door een ander programma wordt gebruikt, omdat
het moet worden gevonden en geladen. Ten tweede kan het een heel gedoe zijn om alle dynamiek te krijgen
bibliotheken geïnstalleerd op de juiste locaties; je kunt het uitvoerbare programma niet zomaar kopiëren,
je moet er ook voor zorgen dat je alle bibliotheken kopieert. Ten derde, op sommige systemen is het
is moeilijk om code te debuggen in gedeelde bibliotheken omdat de debuggers dit niet ondersteunen
ze goed.

Als uw module nooit in een ander programma zal worden gebruikt, is er weinig reden om deze te gebruiken
een bibliotheek: u krijgt alle nadelen van het gebruik van bibliotheken en geen van de voordelen.
De techniek waar ik de voorkeur aan geef, is om incrementele koppeling te gebruiken, waar dit beschikbaar is.

Hier is hoe je dit kunt doen op Linux:

my_module.o : $(filter_out my_module.o, $(jokerteken *.o))
ld -r -o $(uitvoer) $(invoer)

Wat dit zal doen, is een andere creëren .o bestand genaamd mijn_module.o, die zal bestaan ​​uit
alle .o bestanden in deze submap. De linker zal zoveel mogelijk van de . oplossen
referenties als het kan, en laat de resterende referenties in a
volgende fase van koppelen. Op het hoogste niveau, wanneer u eindelijk uw programma bouwt,
in plaats van te linken met libmy_module.a or libmy_module.so, je zou gewoon linken met
mijn_module.o. Wanneer je linkt .o bestanden, hebt u geen problemen met de afhankelijkheid van bestellingen in de
linker-opdrachtregel.

Verhuur makepp figuur uit welke bibliotheek modules zijn nodig

Zelfs als je een echte bibliotheek hebt, waar een bepaald programma maar een paar bestanden nodig heeft
(in plaats van elke afzonderlijke module), kan makepp misschien achterhalen welke modules dat zijn
nodig uit de bibliotheek en neem alleen die op in de build. Dit kan de compilatie redden
tijd als u de bibliotheek samen met een programma ontwikkelt, omdat u niet de moeite neemt om
compileer bibliotheekmodules die niet nodig zijn voor het specifieke programma waaraan u werkt.

Als uw bibliotheek zich strikt houdt aan de conventie dat alle functies of klassen gedeclareerd in
een bestand xyz.h zijn volledig geïmplementeerd in een bronbestand dat wordt gecompileerd naar xyz.o (dat wil zeggen, jij
splits de implementatie niet op in: xyz1.o en xyz2.o), dan kunt u de
"$(infer_objects)" functie om makepp te vertellen om alleen de relevante modules uit de . te halen
bibliotheek. Dit kan verrassend goed werken voor bibliotheken met zelfs tientallen include-bestanden.
Kortom, "$(infer_objects)" onderzoekt de lijst met .h bestanden die zijn opgenomen, en looks
voor corresponderende .o bestanden. Als je snel een bibliotheek en een programma ontwikkelt
samen kan dit compilatietijd besparen, omdat u nooit de moeite neemt om modules van . te compileren
de bibliotheek die het programma niet gebruikt.

Hier is een voorbeeld van hoe ik het gebruik:

mijn_programma: $(infer_objects *.o, $(LIB1)/*.o $(LIB2)/*.o)
$(CXX) $(invoer) -o $(uitvoer) $(SYSTEM_LIBRARIES)

De functie "$(infer_objects )" retourneert zijn eerste argument (na het uitvoeren van een jokerteken
uitbreiding erop), en kijkt ook door de lijst met bestanden in het tweede argument, for
bestanden waarvan de naam hetzelfde is als de naam van een .h bestanden opgenomen door elk bestand in zijn eerste
argument. Als dergelijke bestanden worden gevonden, worden deze aan de lijst toegevoegd.

Gebouw a statisch bibliotheek

Als u zeker weet dat u een bibliotheek nodig heeft en incrementele koppeling niet beschikbaar is of
is niet wat je wilt doen, er zijn een aantal manieren om het te doen. Ten eerste, hier is een voorbeeld:
waar alle bestanden expliciet worden vermeld:

LIBRARY_FILES = abcde

libmine.a: $(LIBRARY_FILES).o
&rm -f $(uitvoer)
$(AR) cr $(uitvoer) $(invoer)
ranlib $(output) # Mogelijk niet nodig, afhankelijk van uw besturingssysteem.

De &rm is het ingebouwde "rm"-commando van makepp. Als je gewend bent om makefiles te schrijven, ben je misschien:
een beetje verrast door dit commando; je bent misschien meer gewend aan iets als dit:

libmine.a: $(LIBRARY_FILES).o
$(AR) ru $@ $? # Niet aangeraden!!!!!!!
ranlib $(uitvoer)

waar $? (ook bekend als "$(changed_inputs)") is een automatische variabele die alle bestanden betekent
die zijn veranderd sinds de laatste keer dat de bibliotheek werd gebouwd, en $@ is ongeveer hetzelfde
als "$(uitvoer)".

Deze aanpak wordt om verschillende redenen niet aanbevolen:

· Stel dat u een bronbestand uit de huidige directory verwijdert. Het is nog in de
bibliotheek, omdat u de bibliotheek niet helemaal opnieuw hebt opgebouwd. Als gevolg hiervan kan alles
die koppelingen met deze bibliotheek zullen oud zijn .o bestand, en dat kan je
bouwt. (Ik raakte hier ooit grondig door in de war toen ik dode code probeerde te verwijderen
van een project: ik bleef bestanden verwijderen en het was nog steeds gekoppeld, dus ik dacht dat de code was
dood. Toen iemand anders het project echter helemaal opnieuw opbouwde, werd er geen gekoppeld
meer! Het probleem was dat de oude .o bestanden waren nog in het archief.)

Ook, afhankelijk van uw opties voor "ar" en uw implementatie van "ar" (bijv. als u
gebruik de "q" optie in plaats van "r"), je kunt eindigen met verschillende versies van de
dezelfde .o in de .a het dossier. Als de verschillende versies verschillende globalen definiëren,
linker kan proberen om beide naar binnen te trekken. Dit is waarschijnlijk een slechte zaak.

Dit is de reden waarom we eerst het bibliotheekbestand verwijderen en het helemaal opnieuw maken. Dit zal
iets langer duren dan alleen het bijwerken van modules in een bibliotheek, maar niet veel langer; Aan
een moderne computer, de hoeveelheid tijd die de ar programma is minuscuul vergeleken
naar wat de C-compiler opneemt in een typische build, dus het is niet de moeite waard om je zorgen te maken
over.

· Een van de manieren waarop makepp probeert om correcte builds te garanderen, is dat het zal
automatisch opnieuw opbouwen als de opdrachtregel voor het bouwen van een bepaald doel is gewijzigd. Maar
met behulp van de $? variabele kan problemen veroorzaken, omdat elke keer dat de bibliotheek wordt bijgewerkt,
het build-commando is anders. (Je kunt dit onderdrukken met
":build_check negeer_action"; zie makepp_build_check voor details.)

· Door het archief bij te werken in plaats van het opnieuw op te bouwen, wordt het voor makepp onmogelijk om
plaats het bestand correct in een buildcache (zie makepp_build_cache voor details).

Soms zult u merken dat het een beetje lastig is om alle bestanden op te sommen, vooral als
project ondergaat een snelle ontwikkeling en de lijst met bestanden verandert voortdurend. Het
is misschien gemakkelijker om de bibliotheek te bouwen met behulp van jokertekens, zoals deze:

libmine.a: $(only_targets *.o)
&rm $(uitvoer)
$(AR) cr $(uitvoer) $(invoer)

Dit zet alle .o bestanden in de huidige map naar de bibliotheek. het jokerteken
komt overeen met elke .o bestand dat bestaat of kan worden gebouwd, dus het zal ook werken als de bestanden dat niet doen
bestaan ​​nog.

De functie "only_targets" wordt gebruikt om uit te sluiten .o bestanden die geen corresponderende . hebben
bronbestanden niet meer. Stel dat u een bestand had met de naam xyz.c die je vroeger in je stopte
bibliotheek. Dit betekent dat er een xyz.o dossier rondslingeren. Nu verwijder je xyz.c
omdat het verouderd is, maar je vergeet te verwijderen xyz.o. Zonder de "only_targets"
functie, xyz.o zou nog steeds worden opgenomen in de lijst van .o bestanden die in de bibliotheek zijn opgenomen.

Gebouw a dynamisch bibliotheek

Het proces van het bouwen van dynamische bibliotheken is volledig systeemafhankelijk. ik zou het ten zeerste
raad aan om libtool te gebruiken om een ​​dynamische bibliotheek te bouwen (zie
<http://www.gnu.org/software/libtool/>), zodat u niet hoeft uit te zoeken hoe u het moet doen
uw platform, en zodat uw makefile blijft werken, zelfs wanneer u overschakelt naar een
ander besturingssysteem. Zie de libtool-documentatie voor details. Hier is een voorbeeld van Makefile:

LIBTOOL := libtool

libflick.la: $(only_targets *.lo)
$(LIBTOOL) --mode=link $(CC) $(invoer) -o $(uitvoer)

%.lo : %.c
$(LIBTOOL) --mode=compileren $(CC) $(CFLAGS) $(INCLUSIEF) -c $(invoer) -o $(uitvoer)

Gebouw on verscheidene anders machines or netwerken
Een van de meest vervelende problemen met makefiles is dat ze bijna nooit werken als je
overschakelen naar een andere machine of een ander netwerk. Als je makefiles aan moeten werken
elke mogelijke machine op de planeet, dan heb je waarschijnlijk een soort configuratie nodig
script. Maar als je maar op een paar verschillende machines hoeft te werken, zijn er verschillende manieren
je kunt dit probleem benaderen:

Te gebruiken a anders omvatten filet in allen de omgevingen

Aan het begin van elke makefile kun je een regel als deze opnemen:

inclusief system_defs.mk

Het bestand system_defs.mk zou normaal gesproken voor elk op een andere plaats zijn gevestigd
omgeving. Als je wilt dat je build-directory's op alle machines identiek zijn, zet dan
system_defs.mk in een map boven de build-mappen, of geef anders een include-pad op
om makepp te gebruiken met de opdrachtregeloptie "-I".

Dit is meestal een beetje pijnlijk om te doen, maar het werkt goed als er een groot aantal is
verschillen.

Te gebruiken if verklaringen

Dit is de lelijkste manier om het te doen, maar het zal meestal werken.

ifsys i386
CC := gcc
anders ifsys sun4u
CC := CC
anders ifsys hpux11
CC = c89
endif

Als u alleen maar een paar programma's of bibliotheken hoeft te vinden of bestanden in verschillende
plaatsen, kunnen er betere manieren zijn (zie hieronder).

zoek_programma, eerste_beschikbaar, vind bestand

Deze functies kunnen verschillende mappen in uw systeem doorzoeken om de
passende bestanden. Dit is natuurlijk niet zo krachtig als een configuratiescript, maar ik vind het
bruikbaar. Ik doe bijvoorbeeld het volgende:

CXX ;= $(vind_programma g++ c++ pg++ cxx CC aCC)
# Kies de eerste C++-compiler die beschikbaar is in PATH.
# (Overigens, als je CXX helemaal niet definieert, dit
# is de manier waarop het is gedefinieerd.)
TCL_INCLUDE ;= -I$(dir_noslash $(vindbestand tcl.h, \
/usr/local/stow/tcl-8.4.5-nothread/include \
/usr/include/tcl8.4 /usr/include/tcl \
/net/na1/tcl8.4a3/include /net/na1/tcl8.4a3/include))
# $(findfile ) zoekt naar tcl.h in elk van de aangegeven
# mappen en retourneert het volledige pad. Dit is dan
# omgezet in een compilatieoptie door de . te strippen
# bestandsnaam (de directory verlaten) en voorafgaan door -I.
%.o : %.cpp
$(CXX) $(CXXFLAGS) $(TCL_INCLUDE) $(invoer) -o $(uitvoer)

TCL_LIB ;= $((eerste_beschikbaar
/usr/local/stow/tcl-8.4.5-nothread/lib/libtcl8.4.so
/usr/lib/libtcl8.4.so /usr/lib/libtcl.so
/net/na1/tcl8.4a3/lib/libtcl8.4.a
/net/na1/tcl8.4a3/lib/libtcl8.4.sl))
# Zoek waar de Tcl-bibliotheek is. Dit is dan expliciet
# vermeld op het linkcommando:
mijn_programma : *.o
$(CXX) $(CXXFLAGS) $(invoer) -o $(uitvoer) $(TCL_LIB)

Nemen voordeel of Perl's config informatie

De bovenstaande technieken zijn mogelijk niet voldoende als u aanvullende informatie nodig heeft over:
uw systeem, zoals of er een lange dubbele bestaat, of wat de bytevolgorde is. Echter,
perl heeft deze dingen al berekend, dus je kunt gewoon de antwoorden gebruiken.

Perl's autoconfigure-script maakt alle configuratie-informatie beschikbaar via
de %Config-hash. Er is geen syntaxis om rechtstreeks in makepp toegang te krijgen tot een Perl-hash, maar dat kan wel:
drop in Perl en stel scalaire variabelen in, die direct toegankelijk zijn vanuit makepp:

perl_begin
# Haal waarden op uit de configuratiehash.
gebruik configuratie;
$CC = $Config{'cc'}; # C-compiler die perl gebruikt;
$byteorder_flags = "-DBYTEORDER=$Config{'byteorder'}";
$longdouble_defined = $Config{'d_longdbl'} eq 'definiëren';
$CFLAGS_for_shared_libs = $Config{'cccdlflags'};
$LDFLAGS_for_shared_libs = $Config{'ccdlflags'};
perl_end

Als je eenmaal 'use Config' hebt gedaan, kun je de instructie "$(perl )" gebruiken, zoals
deze:

SHARED_LIB_EXTENSION := $(perl $Config{'dlext'})

Typ "perldoc Config" om te zien welke informatie beschikbaar is via de %Config-hash.

De configuratie van Perl is een goede plek om dingen te krijgen zoals informatie over integer-types, byte
volgorde, en andere dingen die meestal een apart configuratiescript vereisen om te lokaliseren. Enkele van
de informatie die betrekking heeft op de aanwezigheid van dingen in het bestandssysteem is mogelijk niet:
Geldig. $Config{'cc'} verwijst bijvoorbeeld naar de C-compiler waarmee perl is gebouwd,
die misschien niet dezelfde C-compiler is die u wilt gebruiken. In feite bestaat het misschien niet eens
op uw systeem, aangezien u Perl waarschijnlijk via een binair pakket hebt geïnstalleerd.

Tips For gebruik wildcards
Bijpassende allen bestanden behalve a zeker subgroep

De jokertekens van Makepp kunnen momenteel op geen enkele manier alle bestanden matchen behalve een zekere
ingesteld, maar u kunt het doen met een combinatie van functies.

Stel bijvoorbeeld dat je een testprogramma hebt voor elke module in een bibliotheek, maar dat heb je niet
de testprogramma's in de bibliotheek wilt opnemen. Als alle testprogramma's beginnen met
proef, dan kun je ze als volgt uitsluiten:

libproduction.a: $(filter_out test*, $(jokerteken *.o))

De functies "$(filter)" en "$(filter_out)" zijn een zeer krachtige set filters om te doen
allerlei soorten snijpunt- en verschilbewerkingen. Bijvoorbeeld,

SUBDIRS ;= $(filter_out *test* *$(ARCH)*, $(shell find . -type d -print))
# Retourneert alle submappen die geen . hebben
# "test" of $(ARCH) erin.

$(filter $(patsubst test_dir/test_%.o, %.o, $(wildcard test_dir/*.o)), \
$(jokerteken *.o))
# Retourneert een lijst met .o-bestanden in de huidige
# directory waarvoor een corresponderend is
# test_*.o bestand in de test_dir submap.
$(filter_out $(patsubst man/man3/%.3, %.o, $(wildcard man/man3/*.3)), \
$(jokerteken *.o))
# Retourneert een lijst met .o-bestanden in de huidige
# directory waarvoor geen handmatige pagina bestaat
# met dezelfde bestandsnaam in de man/man3 submap.

gebruik de "$(only_targets )" functie naar elimineren oudbakken .o bestanden

Stel dat u een programma of een bibliotheek aan het bouwen bent met een build-opdracht zoals deze:

programma: *.o
$(CC) $(invoer) -o $(uitvoer)

Stel dat u nu een bronbestand verwijdert. Als u vergeet de corresponderende .o bestand
het zal nog steeds worden gekoppeld, ook al is er geen manier om het meer te bouwen. In de
In de toekomst zal makepp deze situatie waarschijnlijk automatisch herkennen en uitsluiten van
de lijst met jokertekens, maar op dit moment moet je hem vertellen om hem handmatig uit te sluiten:

programma: $(only_targets *.o)
$(CC) $(ingangen) -o $(uitgangen)

Makepp weet geen manier om de muffe op te bouwen .o bestand niet meer omdat het bronbestand is
verdwenen, dus de functie "$(only_targets)" zal het uitsluiten van de afhankelijkheidslijst.

Tips For meervoudig directories
Een van de belangrijkste redenen voor het schrijven van makepp was om de verwerking van meerdere
mappen. Makepp kan build-commando's van meerdere makefiles combineren, dus het kan:
correct omgaan met een regel in één makefile die afhankelijk is van een bestand dat is gebouwd door a
ander make-bestand.

Wat naar do in plaats of recursieve maken

Makepp ondersteunt recursive make voor achterwaartse compatibiliteit, maar het wordt sterk aanbevolen
Dat U niet gebruik het. Als je niet weet wat het is, prima.

Zie "Beter systeem voor hiërarchische builds" in makepp voor details over waarom je dat niet wilt
gebruik recursieve make, of zoek op internet naar "recursive make beschouwd als schadelijk".

In plaats van een recursieve make te doen om het "alle" doel in elke makefile te maken, is het:
meestal gemakkelijker om makepp te laten uitzoeken welke doelen er daadwerkelijk moeten worden gebouwd.
Bovendien, als u al uw .o en bibliotheekbestanden in dezelfde map als de
makefiles, dan zal makepp automatisch uitzoeken welke makefiles ook nodig zijn - de
het enige dat nodig is, is dat je op het hoogste niveau een lijst maakt met de bestanden die nodig zijn
voor de laatste koppelingsstap. Zie de voorbeelden hieronder.

One makefile For elk directory: Met stilzwijgend het laden

De meest gebruikelijke manier om met meerdere mappen om te gaan, is door een makefile in elke map te plaatsen
die beschrijft hoe alles in of vanuit die map moet worden gebouwd. Als je zet .o bestanden
dezelfde map als de bronbestanden, dan impliciet laden (zie "Impliciet laden" in
makepp_build_algorithm) vindt automatisch alle makefiles. Als je je .o
bestanden in een andere map (bijv. in een architectuurafhankelijke submap), dan kunt u
zal waarschijnlijk alle relevante makefiles moeten laden met het "load_makefile" statement.

Hier is een voorbeeld van een makefile op het hoogste niveau voor een directoryhiërarchie die impliciet laden gebruikt:
om een ​​programma te bouwen dat uit veel gedeelde bibliotheken bestaat (maar zie "Heeft u echt een
bibliotheek?" in makepp_cookbook, omdat het maken van een programma uit een aantal gedeelde bibliotheken
is niet per se een goed idee):

# Makefile op het hoogste niveau:
programma : main.o **/*.la # Link in gedeelde bibliotheken van alle submappen.
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(invoer) -o $(uitvoer) $(LIBS)

Dat is vrijwel alles wat je nodig hebt in de makefile op het hoogste niveau. In elke submap, u
zou waarschijnlijk zoiets doen:

# Makefile in elke submap:
include standard_defs.mk # Zoekopdrachten ., .., ../.., enz. totdat het
# vindt het aangegeven include-bestand.
# overschrijf hier enkele variabeledefinities
SPECIAL_FLAGS:= -doe_iets_anders

Elke makefile kan waarschijnlijk vrijwel hetzelfde zijn als de opdrachten om de doelen te bouwen
zijn vrij gelijkaardig.

Tot slot zou je het volgende in de standard_defs.mk bestand (wat waarschijnlijk zou moeten)
zich in de directory op het hoogste niveau bevinden):

# Algemene variabele instellingen en bouwregels voor alle mappen.
CLAGS := -g -O2
INCLUDE_DIR := $(find_upwards omvat)
# Zoekopdrachten ., .., ../.., enz. voor een bestand of
# directory genaamd omvat, dus als je zet
# al je include-bestanden daarin, dit zal
# vind ze.
INCLUSIEF := -I$(INCLUDE_DIR)

%.lo : %.c
$(LIBTOOL) --mode=compileren $(CC) $(CFLAGS) $(INCLUSIEF) -c $(invoer) -o $(uitvoer)

lib$(relatieve_naar., ..).la: $(alleen_doelen *.lo)
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(uitvoer) $(invoer)
# $(relative_to ., ..) geeft de naam van de huidige
# subdirectory ten opzichte van het bovenste niveau
# submap. Dus als deze makefile xyz/Makefile is,
# deze regel zal xyz/libxyz.la bouwen.

# Publiceer openbare include-bestanden in de include-directory op het hoogste niveau:
$(INCLUDE_DIR)/public_%.h: publieke_%.h
:build_check symbool
&ln -fr $(invoer) $(uitvoer)

One makefile For elk directory: uitdrukkelijk het laden

Als je al je .o bestanden in een architectuurafhankelijke submap, dan
het bovenstaande voorbeeld moet worden gewijzigd om iets als dit te zijn:

# Makefile op het hoogste niveau:
MAKEFILES := $(jokerteken **/Makeppfile) # Lijst met alle submappen naar
# makefiles ophalen van.

load_makefile $(MAKEFILES) # Laad ze allemaal in.

include standard_defs.mk # Haal het compile-commando op voor main.o.

programma : $(ARCH)/main.o */**/$(ARCH)/*.la
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(invoer) -o $(uitvoer) $(LIBS)
# */**/$(ARCH) sluit de submap uit
# $(ARCH), waar we niet willen bouwen
# een gedeelde bibliotheek.

Elke makefile zou precies hetzelfde zijn als voorheen:

# Makefile in elke submap:
inclusief standard_defs.mk
# ... variabele overschrijft hier

En tenslotte standard_defs.mk zou zoiets als het volgende bevatten:

# Algemene variabele instellingen en bouwregels voor alle mappen.
ARCH ;= $(shell uname -s)-$(shell uname -m)-$(shell uname -r)
# Soms gebruiken mensen alleen $(shell uname -m), maar
# dit zal hetzelfde zijn voor FreeBSD en Linux op
# een x86. De -r is niet echt handig op Linux,
# maar is belangrijk voor andere besturingssystemen: binaries voor
# SunOS 5.8 werkt doorgaans niet op SunOS 5.7.
&mkdir -p $(ARCH) # Zorg ervoor dat de uitvoermap bestaat.
CLAGS := -g -O2
INCLUDE_DIR := $(find_upwards omvat)
# Zoekopdrachten ., .., ../.., enz. voor een bestand of
# directory genaamd omvat, dus als je zet
# al je include-bestanden daarin, dit zal
# vind ze.
INCLUSIEF := -I$(INCLUDE_DIR)

$(ARCH)/%.lo: %.c
$(LIBTOOL) --mode=compileren $(CC) $(CFLAGS) $(INCLUSIEF) -c $(invoer) -o $(uitvoer)

$(ARCH)/ lib$(relative_to ., ..).la: $(only_targets *.lo)
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(uitvoer) $(invoer)
# $(relative_to ., ..) geeft de naam van de huidige
# subdirectory ten opzichte van het bovenste niveau
# submap. Dus als deze makefile xyz/Makefile is,
# deze regel zal xyz/$(ARCH)/libxyz.la bouwen.

# Kopieer openbare include-bestanden naar de include-directory op het hoogste niveau:
$(INCLUDE_DIR)/public_%.h: publieke_%.h
&cp $(invoer) $(uitvoer)

Automatisch het maken van de maakbestanden

Als je makefiles allemaal erg op elkaar lijken (zoals in het bovenstaande voorbeeld), kun je Makepp . vertellen
om ze automatisch te bouwen als ze niet bestaan. Voeg gewoon het volgende toe aan je topniveau
make-bestand:

SUBDIRS := $(filter_out ongewenste_dir1 ongewenste_dir2, $(jokerteken */**))
$(foreach)/Makeppfile: : foreach $(SUBDIRS)
&echo "inclusief standard_defs.mk" -o $(output)
&echo "_include additional_defs.mk" -o >>$(uitvoer)
# Als het bestand additional_defs.mk bestaat, dan:
# het zal worden opgenomen, maar als het niet bestaat,
# het _include-statement wordt genegeerd.

Nu worden de makefiles zelf automatisch gebouwd.

One makefile Slechts at de top niveau

Als al je makefiles identiek zijn, vraag je je misschien af: waarom zou ik bij elk een makefile hebben?
peil? Waarom zet je dat niet allemaal in de makefile op het hoogste niveau?

Ja, dit kan. Het grootste nadeel is dat het moeilijker wordt om te specificeren
verschillende build-opties voor elke submap. Een tweede nadeel is dat je
makefile zal waarschijnlijk een beetje moeilijker te lezen worden.

Hier is een voorbeeld om precies dat te doen:

# Makefile op het hoogste niveau voor directoryhiërarchie. Bouwt het programma
# uit een set gedeelde bibliotheken als voorbeeld. (Zie waarschuwingen hierboven)
# voor waarom u incrementele koppeling of iets anders zou willen gebruiken
# aanpak in plaats van gedeelde bibliotheken.)
makepp_percent_subdirs := 1 # Laat % overeenkomen met meerdere mappen.
SUBDIRS := $(filter_out *CVS* andere-ongewenste_dirs $(wildcard **))
CLAGS := -g -O2
INCLUSIEF := -Iincludes

%.lo: %.c
$(LIBTOOL) --mode=compileren $(CC) $(INCLUSIEF) $(CFLAGS) -c $(invoer) -o $(uitvoer)

$(voor)/ lib$(notdir $(foreach)).la: $(foreach)/*.lo : foreach $(SUBDIRS)
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(uitvoer) $(invoer)
# Regel om alle bibliotheken te maken.

programma : main.o **/*.la
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(uitvoer) $(invoer)

include/$(notdir $(foreach)) : $(foreach) : foreach **/public_*.h
&cp $(invoer) $(uitvoer)
# Voorbeeldregel voor het openbaar kopiëren
# toegankelijke .h-bestanden op de juiste plaats.

A schoon doel

Traditionele makefiles bevatten een schoon doel, waarmee alles kan worden verwijderd dat was
gebouwd. Er zijn drie redenen waarom je dit niet met makepp zou moeten doen:

1. Makepp doet er alles aan om een ​​correcte opbouw te garanderen. Dus het wanhopige "ik niet"
weet wat er aan de hand is", is het verleden tijd om helemaal opnieuw te willen beginnen.

2. Mensen proberen soms tijd te besparen door twee tegenstrijdige dingen tegelijk te doen:
"maak alles schoon". Dit kan het slimme wildcard-systeem van makepp in de war brengen, omdat het
verdiep je eerst in de feiten voordat je iets doet. Dan komt de schone actie, die wel
vertel makepp niet wat het doet (het kan inderdaad niet, omdat het iets ongedaan maakt -- de
in tegenstelling tot waar een build-tool voor is). Dan komt "alle", behalve de up-to-date bestanden,
die waar er zijn, op mysterieuze wijze verdwenen.

3. Er is de opdracht "makeppclean", die hetzelfde doet, en efficiënter.

Desalniettemin behouden we dit historische gedeelte, omdat het je wel iets vertelt over de
manier waarop makepp werkt: een nepdoel met de naam "schoon" is slechts een naam voor een reeks opdrachten om
verwijder alle bestanden die het resultaat zijn van het maakproces. Meestal ziet een schoon doelwit eruit
iets zoals dit:

$(nepschoon):
&rm -fm $(jokerteken *.o .makepp_log)
# -m en .makepp_log verwijderen alle makepp's rommel.

In plaats van expliciet de bestanden te vermelden die u wilt verwijderen, kunt u makepp ook vertellen om
verwijder alles wat het weet te bouwen, zoals dit:

$(nepschoon):
&rm -fm .makepp_log $(only_targets *)

Dit heeft het voordeel dat als een van uw bronbestanden kan worden opgebouwd uit andere bestanden,
ze worden ook verwijderd; aan de andere kant, oud .o bestanden (bestanden die vroeger
buildable maar waarvan het bronbestand inmiddels is verwijderd) worden niet verwijderd.

Als je een build hebt die makefiles in verschillende mappen bevat, is je top-
level makefile kan verwijzen naar het "schone" doel (of een ander nep doel) in een andere
make-bestand:

# Makefile op het hoogste niveau
SUBDIRS := sub1 sub2

# bouw regels hier

# Opruimen na de build:
$(nep-schoon): $(SUBDIRS)/schoon
&rm -fm .makepp_log $(only_targets *)

Als alternatief kunt u uw "schone" doel alleen in de makefile op het hoogste niveau plaatsen en het
verwerk alle mappen als volgt:

$(nepschoon):
&rm -fm $(only_targets **/*)

gebruik Qt's stroom voorverwerker
Dit voorbeeld toont een makefile voor een hulpprogramma dat de Qt GUI-bibliotheek van Nokia gebruikt (zie
<http://qt.nokia.com>). Het enige dat hier enigszins ongewoon aan is, is dat je
moet een preprocessor met de naam "moc" uitvoeren op de meeste ".h"-bestanden die widgetdefinities bevatten,
maar u wilt geen "moc" uitvoeren op ".h"-bestanden die de "Q_OBJECT"-macro niet gebruiken.

Automatisch bepalen welke bestanden genoodzaakt bent stroom bestanden

U kunt natuurlijk gewoon alle ".h"-bestanden vermelden waarop "moc" moet worden uitgevoerd.
Als u echter snel nieuwe widgets ontwikkelt, kan het vervelend zijn om
blijf de lijst in de makefile bijwerken. U kunt de noodzaak om de moc . op te sommen omzeilen
modules expliciet met zoiets als dit:

MOC:= $(QTDIR)/bin/moc
MODULES := welke modules je ook in je programma hebt
MOC_MODULES := $(patsubst %.h, moc_%, $(&grep -l /Q_OBJECT/ *.h))
# Scant alle .h-bestanden voor de Q_OBJECT-macro.

mijn_programma: $(MODULES).o $(MOC_MODULES).o
$(CXX) $(invoer) -o $(uitvoer)

moc_%.cxx: %.h # Maakt de moc-bestanden van de .h-bestanden.
$(MOC) $(invoer) -o $(uitvoer)

%.o: %.cxx
$(CXX) $(CXXFLAGS) -c $(invoer) -o $(uitvoer)

Deze aanpak scant elk van uw .h bestanden elke keer dat makepp wordt uitgevoerd, op zoek naar de
"Q_OBJECT"-macro. Dit klinkt duur, maar het zal waarschijnlijk niet lang duren. (De .h
bestanden zullen toch allemaal van schijf moeten worden geladen door het compilatieproces, dus ze zullen
in de cache worden opgeslagen.)

#include de .moc filet

Een andere benadering is om de uitvoer van de "moc" preprocessor in je widget te "#includen"
implementatie bestand. Dit betekent dat je moet onthouden om de "#include" te schrijven, maar het heeft
het voordeel dat er minder modules gecompileerd hoeven te worden, en dus sneller compileren.
(Voor de meeste C++-compilaties wordt het grootste deel van de tijd besteed aan het lezen van de headerbestanden, en
de uitvoer van de preprocessor moet bijna net zoveel bestanden bevatten als uw widget
hoe dan ook.) Bijvoorbeeld:

// mijn_widget.h
class MyWidget : openbare QWidget {
Q_OBJECT

}

// mijn_widget.cpp

#include "mijn_widget.h"
#include "my_widget.moc" // my_widget.moc is de uitvoer van de
// moc-preprocessor.
// Andere implementatie dingen hier.
MyWidget::MyWidget(QWidget * ouder, const char * naam) :
QWidget(ouder, naam)
{

}

Nu moet je een regel in je makefile hebben om alle ".moc"-bestanden te maken, zoals deze:

MOC:= $(QTDIR)/bin/moc
# Regel om .moc-bestanden te maken:
%.moc: %.h
$(MOC) $(invoer) -o $(uitvoer)

Makepp is slim genoeg om te beseffen dat het "my_widget.moc" moet maken als dat niet het geval is
al bestaat, of verouderd is.

Deze tweede benadering is degene die ik gewoonlijk gebruik omdat het de compilatie versnelt.

Onderdelen For deprecated maken idioom
MAAKCMDDOELEN

Soms hebben mensen regels in hun makefile, afhankelijk van het doel dat ze bouwen,
met behulp van de speciale variabele "MAKECMDGOALS". Je ziet bijvoorbeeld soms dingen als:
deze:

ifneq ($(filterproductie, $(MAKECMDGOALS)),)
CLAGS := -O2
anders
CLAGS := -g
endif

Dit werkt prima met makepp. Ik raad echter aan om "MAKECMDGOALS" hiervoor niet te gebruiken
gevallen (en dat doet de GNU handleiding). U kunt beter uw geoptimaliseerde en
debug-gecompileerd .o bestanden in aparte mappen, of geef ze verschillende voorvoegsels of
achtervoegsels, of het gebruik van repositories, om ze gescheiden te houden.

Waarschijnlijk de enige keer dat je echt naar "MAKECMDGOALS" wilt verwijzen, is als het
duurt lang om je makefiles te laden, en dat heb je niet nodig voor je "schone" doel
(maar je hebt geen schoon doelwit nodig). Bijvoorbeeld,

ifneq ($(MAKECMDGOALS),schoon)
load_makefile $(jokerteken **/Makeppfile)
anders
geen_impliciete_belasting . # Voorkom automatisch laden van andere makefiles.
endif

$(nepschoon):
&rm -f $(jokerteken **/*.o)

Recursieve maken naar bouw in anders directories

Zie "Tips voor meerdere mappen" in makepp_cookbook.

Recursieve maken naar verandering waarde of a variabele

Sommige makefiles roepen zichzelf opnieuw op met een andere waarde van een variabele, bijv. de debug
doel in het volgende makefile-fragment

.PHONY: alle debuggen

geoptimaliseerd:
$(MAKE) programma CFLAGS=-O2

debuggen:
$(MAKE) programma CFLAGS=-g

programma: oa bo
$(CC) $(CFLAGS) $^ -o $@

%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@

Als de gebruiker "make debug" typt, wordt het programma in standaardmodus gebouwd met debug ingeschakeld
in plaats van met optimalisatie.

Een betere manier om dit te doen, is door twee verschillende programma's te bouwen, met twee verschillende sets van
objectbestanden, zoals deze:

CLAGS := -O2
DEBUG_FLAGS := -g
MODULES := ab

programma: $(MODULES).o
$(CC) $(CFLAGS) $(invoer) -o $(uitvoer)

debug/programma: debug/$(MODULES).o
$(CC) $(DEBUG_FLAGS) $(invoer) -o $(uitvoer)

%.o: %.c
$(CC) $(CFLAGS) -c $(invoer) -o $(uitvoer)

debug/%.o: %.c
$(CC) $(DEBUG_FLAGS) -c $(invoer) -o $(uitvoer)

$(nep debug): debug/programma

Het voordeel om het op deze manier te doen is (a) dat je niet alles opnieuw hoeft te bouwen als je
overschakelen van debug naar geoptimaliseerd en weer terug; (B)

Bovenstaande kan wat beknopter worden geschreven met behulp van repositories. Het volgende
makefile is exact gelijk aan:

repository debug=. # Laat de debug-submap eruitzien als een kopie van
# de huidige submap.
load_makefile debuggen CFLAGS=-g
# Overschrijf CFLAGS wanneer aangeroepen in debug-submap
CFLAGS := -O2 # Waarde van CFLAGS wanneer aangeroepen in deze submap

programma: oa bo
$(CC) $(CFLAGS) $^ -o $@

%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@

$(nep debug): debug/programma
# Als de gebruiker "makepp debug" typt, bouwt
# debug/programma in plaats van programma.

Diversen tips
Hoe do I bouw een deel anders voor slechts een keer?

Makepp maakt dit moeilijk omdat het resultaat niet in overeenstemming is met de regels.
Maar er zijn situaties waarin u dit nodig kunt hebben, bijvoorbeeld om slechts één module te compileren met
zware foutopsporingsinformatie. U kunt dit in twee stappen bereiken door eerst de
afhankelijkheid afzonderlijk, en vervolgens uitsluiten van de link-fase:

makepp DEBUG=3 buggy.o # Bouw het met een andere optie.
makepp --dont-build=buggy.o buggy # Gebruik het, ondanks de "verkeerde" build-optie.

Hoe do I maken zeker my uitgang directories bestaan?

U kunt een regel specificeren om de uitvoermap te bouwen, en zorg er dan voor dat elk bestand dat
gaat in de uitvoermap hangt ervan af. Maar het is meestal gemakkelijker om iets te doen als
deze:

# De klassieke manier
dummy := $(shell-test -d $(OUTPUT_DIRECTORY) || mkdir -p $(OUTPUT_DIRECTORY))
# Dit is meestal makkelijker dan alle bestanden afhankelijk te maken van
# $(OUTPUT_DIRECTORY) en een regel hebben om het te maken.
# Merk op dat u := moet gebruiken in plaats van = om het te forceren
# onmiddellijk uitvoeren.
# Een alternatieve benadering: gebruik Perl-code, OUTPUT_DIRECTORY lokale var
perl_begin
-d $OUTPUT_DIRECTORY of mkdir $OUTPUT_DIRECTORY;
perl_end
# De moderne manier, doet niets voor bestaande mappen
&mkdir -p $(OUTPUT_DIRECTORY)

Een van deze instructies zou bovenaan je makefile moeten staan, zodat ze worden uitgevoerd
vóór alles dat de directory nodig zou kunnen hebben.

Hoe do I dwingen a commando naar uitvoeren on elk bouwen?

De gemakkelijkste manier is om het regelmechanisme helemaal niet te gebruiken, maar het gewoon uit te voeren, zoals
deze:

dummy := $(shell date > last_build_timestamp)

Of zet het in een perl-blok, zoals dit:

perl_begin
system("opdracht om uit te voeren");
perl_end

Deze aanpak heeft het nadeel dat het wordt uitgevoerd, zelfs als een niet-gerelateerd doel is
gerund wordt.

Een tweede benadering is om het bestand als een nepdoel te declareren, zelfs als het een echt bestand is.
Dit zal makepp dwingen om het commando om het elke keer te bouwen opnieuw uit te voeren, maar alleen als het
verschijnt in de afhankelijkheidslijst van een regel.

Hoe do I verkorten de weergegeven bouw commando's?

Vaak zijn er zoveel opties voor compilatiecommando's dat wat wordt weergegeven op de
scherm is onleesbaar. U kunt wijzigen wat wordt weergegeven door de weergave van de
volledige opdracht en druk vervolgens expliciet het interessante deel van de opdracht af. Zijn
eenvoudig om alleen het relevante deel van de opdracht af te drukken door "$(filter_out )" te gebruiken, zoals
deze:

ALL_CFLAGS = $(CFLAGS) $(INCLUSIEF) $(ADDL_CXX_FLAGS) $(DEBUG_FLAGS)

%.o: %.c
@&echo $(notdir $(CC)) ... \
$(filter_out -I* $(ADDL_CXX_FLAGS), $(ALL_CFLAGS)) \
-c $(invoer)
@$(CC) $(ALL_CFLAGS) -c $(invoer) -o $(uitvoer)

(De "@" voor de opdracht onderdrukt het afdrukken van de opdracht.)

Hierdoor kunt u de meeste interessante opties zien, maar niet alle
omvatten mappen (waarvan er vaak heel veel zijn!). Als het onderdeel waarin u geïnteresseerd bent:
in aangrenzend is in uw opdracht, kunt u ook de "print"-functie gebruiken (die een
newline, dus je wilt er niet meerdere):

doelwit:
@... $(interessant deel afdrukken) ...

Hoe do I converteren a filet in afhankelijkheden?

Voor sommige obscure bestandsformaten is het niet de moeite waard om een ​​scanner te implementeren. In één project
we hebben xml-bestanden, zeg maar foobar.xml die de afhankelijkheden bevat voor foobar.out:


een
B
C


We hebben besloten om deze eenvoudige lay-out aan te houden, zodat we XML niet hoeven te ontleden. Met de
ingebouwde &sed, dit is wat we doen met drie eenvoudige vervangingen voor de drie soorten
lijnen:

%.d: %.xml
&sed's! !$(stam).uit: \\! || s! (.+) !$$1 \\! || s! !# Leeg!' \
$(invoer) -o $(uitvoer)

inclusief foobar.d

Als je dit probeert op te nemen, wordt eerst "foobar.d" geproduceerd:

foobar.out: \
een \
B \
C \
# Leeg

Lege (alleen een opmerking of echt lege) regel voorkomt dat u zich zorgen hoeft te maken over de trailing
terugslag. Een alternatief voor het produceren van een lijst met meerdere regels is:

%.d: %.xml
&sed's! !$(stam).out: \$$((! || s! !))! || s!<.+?>!!g' \
$(invoer) -o $(uitvoer)

inclusief foobar.d

Dit levert een equivalent op:

foobar.out: $((
a
b
c
))

Als je meer complexe herschrijvingen te doen hebt, definieer dan een functie in de makefile of in a
module die u opneemt. Als u bijvoorbeeld $_ definieert, worden invoerregels overgeslagen:

sub mijnfilter {
retourneer $_ als /
mijn $stem = f_stem;
s! !$stem.out: \$((! || s! !))! || s!<.+?>!!g;
}

%.d: %.xml
&sed's! !$(stam).out: \$$((! || s! !))! || s!<.+?>!!g' \
$(invoer) -o $(uitvoer)

inclusief foobar.d

Gebruik makepp_cookbook online met onworks.net-services


Gratis servers en werkstations

Windows- en Linux-apps downloaden

Linux-commando's

Ad