What is a makefile in Java?

Forstå Regler i en Makefile: En Komplet Guide

16/05/2006

Rating: 4.63 (13709 votes)

En Makefile er hjertet i mange softwareprojekter, især i C- og C++-verdenen, men dens anvendelighed strækker sig langt ud over det. Den automatiserer byggeprocessen, kompilerer kildekode, linker biblioteker og udfører en række andre opgaver. Kernen i enhver Makefile er dens regler. At forstå, hvordan man skriver effektive regler, er afgørende for at mestre make-værktøjet og skabe robuste, vedligeholdelsesvenlige bygge-systemer. Denne artikel vil guide dig gennem alt, hvad du behøver at vide om regler i en Makefile, fra de mest grundlæggende koncepter til avancerede teknikker.

Why do I need a rule for a makefile?
In the makefile for a program, many of the rules you need to write often say only that some object file depends on some header file. For example, if main.c uses defs.h via an #include, you would write: You need this rule so that make knows that it must remake main.o whenever defs.h changes.
Indholdsfortegnelse

Den Grundlæggende Anatomi af en Regel

En regel i en Makefile fortæller make, hvornår og hvordan man genopbygger bestemte filer, som kaldes regelens mål (targets). Den specificerer også de filer, som målet afhænger af, kendt som forudsætninger (prerequisites), samt den opskrift (recipe), der skal udføres for at skabe eller opdatere målet.

Den generelle syntaks for en regel ser således ud:

mål ...: forudsætninger ... <tab>opskrift <tab>...

Lad os bryde det ned med et klassisk eksempel:

foo.o: foo.c defs.h cc -c -g foo.c
  • Mål:foo.o er filen, vi ønsker at bygge.
  • Forudsætninger:foo.c og defs.h er de filer, som foo.o afhænger af.
  • Opskrift:cc -c -g foo.c er kommandoen, der køres for at generere foo.o. Bemærk, at linjen med opskriften skal starte med et tabulator-tegn, ikke mellemrum.

Denne regel fortæller make to ting: Hvordan den skal afgøre, om foo.o er forældet (den er forældet, hvis den ikke eksisterer, eller hvis foo.c eller defs.h er blevet ændret siden foo.o sidst blev bygget), og hvordan den skal opdatere filen ved at køre C-kompilatoren.

Typer af Forudsætninger

GNU make skelner mellem to typer af forudsætninger, hvilket giver mere finkornet kontrol over byggeprocessen.

Normale Forudsætninger

Dette er den mest almindelige type, som vi så i eksemplet ovenfor. De etablerer både en afhængighed og en rækkefølge. Hvis en normal forudsætning er nyere end målet, bliver målet genopbygget. Desuden sikrer make, at alle normale forudsætninger er opdaterede, før opskriften for målet køres.

Orden-Kun Forudsætninger (Order-Only)

Nogle gange ønsker man at sikre, at en fil eksisterer eller en opgave er udført, før en anden regel køres, men uden at skabe en tidsstempel-afhængighed. Her kommer orden-kun forudsætninger ind i billedet. De adskilles fra normale forudsætninger med et pipe-symbol (|).

mål: normale-forudsætninger | orden-kun-forudsætninger

Et godt eksempel er at sikre, at en output-mappe eksisterer, før man placerer filer i den:

OBJDIR := obj OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o) $(OBJS): | $(OBJDIR) $(OBJDIR): mkdir $(OBJDIR)

Her vil make sikre, at mappen obj bliver oprettet, før den forsøger at bygge foo.o eller bar.o. Men selvom tidsstemplet på obj-mappen ændres (hvilket det gør, hver gang en fil tilføjes), vil det ikke i sig selv udløse en genopbygning af objektfilerne.

Why do I need a rule for a makefile?
In the makefile for a program, many of the rules you need to write often say only that some object file depends on some header file. For example, if main.c uses defs.h via an #include, you would write: You need this rule so that make knows that it must remake main.o whenever defs.h changes.

Phony Mål: Regler uden Filer

Et .PHONY mål er et specielt mål, der ikke repræsenterer en rigtig fil. Det er i stedet et navn for en opskrift, der skal udføres, når målet eksplicit kaldes. Der er to primære grunde til at bruge phony-mål:

  1. Undgå konflikter: Hvis du har en regel for at rydde op i projektet, f.eks. clean, og der ved et uheld oprettes en fil ved navn clean, vil make tro, at målet er opdateret og vil ikke køre opskriften. Ved at erklære det som phony undgår man dette problem.
  2. Ydeevne:make springer søgningen efter implicitte regler over for phony-mål, hvilket kan give en lille forbedring i ydeevnen.

Sådan defineres et phony-mål:

.PHONY: clean clean: rm -f *.o temp

Nu vil kommandoen make clean altid køre rm-kommandoen, uanset om der findes en fil ved navn clean.

Et andet almindeligt brugsmønster er et all-mål, der bygger hele projektet:

.PHONY: all all: program1 program2 program1: program1.o utils.o cc -o program1 program1.o utils.o program2: program2.o cc -o program2 program2.o

Ved blot at køre make vil standardmålet (det første i filen), all, blive valgt, hvilket sikrer, at både program1 og program2 bliver bygget.

Dobbeltkolon Regler (::)

Dobbeltkolon-regler er en mere obskur, men potentielt kraftfuld funktion. Mens en almindelig regel kun kan have én opskrift for et givet mål, tillader dobbeltkolon-regler, at flere uafhængige regler defineres for det samme mål.

Syntaksen er mål :: forudsætninger.

Når et mål er defineret med dobbeltkolon, behandles hver regel for sig. Opskriften for en given dobbeltkolon-regel køres kun, hvis målet er ældre end en af den specifikke regels forudsætninger.

Overvej dette eksempel:

my_target :: file1.c @echo Bygger fra file1.c # ... opskrift 1 ... my_target :: file2.py @echo Bygger fra file2.py # ... opskrift 2 ...
  • Hvis file1.c er nyere end my_target, køres den første opskrift.
  • Hvis file2.py er nyere end my_target, køres den anden opskrift.
  • Hvis begge er nyere, køres begge opskrifter i den rækkefølge, de optræder i Makefile.

Dette er nyttigt i sjældne tilfælde, hvor metoden til at opdatere et mål afhænger af, hvilken forudsætning der udløste opdateringen.

What are double-colon rules in GNU make?
Section 4.13 of the GNU Make manual describes the so-called double-colon rules: Double-colon rules are rules written with ‘::’ instead of ‘:’ after the target names. They are handled differently from ordinary rules when the same target appears in more than one rule.

Statiske Mønsterregler

Statiske mønsterregler er en mere generel måde at skrive regler for flere mål på. De er mere eksplicitte end implicitte regler og giver dig mulighed for at specificere en gruppe af filer, som reglen gælder for. Syntaksen er:

mål ...: mål-mønster: forudsætnings-mønstre ... <tab>opskrift

Her er et eksempel, der kompilerer en liste af objektfiler fra deres tilsvarende C-filer:

objects = foo.o bar.o $(objects): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@
  • mål:$(objects), som udvides til foo.o bar.o.
  • mål-mønster:%.o. Procenttegnet (%) fungerer som et wildcard.
  • forudsætnings-mønster:%.c.

For hvert mål i listen (f.eks. foo.o), matcher make det med %.o. Den del, der matcher %, kaldes "stammen" (her, foo). Denne stamme indsættes derefter i forudsætningsmønsteret for at skabe forudsætningen (foo.c). De automatiske variabler $< (den første forudsætning) og $@ (målet) bruges i opskriften for at gøre den generisk.

Sammenligning af Regeltyper

For at give et hurtigt overblik er her en sammenligningstabel over de diskuterede regeltyper.

RegeltypeSyntaksPrimær Anvendelse
Almindelig Regeltarget: prereqsDefinerer en enkelt opskrift for et eller flere mål med de samme forudsætninger.
Dobbeltkolon Regeltarget :: prereqsTillader flere uafhængige opskrifter for det samme mål, hver med egne forudsætninger.
Statisk Mønsterregeltargets...: %pat: %prereqAnvender et generisk mønster på en specifik, foruddefineret liste af mål.
Phony Regel.PHONY: targetDefinerer et mål, der ikke er en fil, men en kommando, der altid skal køres.

Ofte Stillede Spørgsmål (FAQ)

Hvordan bruger jeg et bogstaveligt dollartegn ($) i en opskrift?

Dollartegnet har en speciel betydning i Makefiles, da det bruges til at referere til variabler (f.eks. $(CC)). Hvis du har brug for at sende et bogstaveligt dollartegn til shell'en (f.eks. for shell-variabler som $HOME eller i et script), skal du "escape" det ved at fordoble det. Brug $$.

show_home: @echo Hjemmemappen er: $$HOME

Her vil make i sin første læsefase omdanne $$HOME til $HOME. Derefter, når opskriften udføres, vil shell'en korrekt fortolke $HOME og udskrive stien til brugerens hjemmemappe.

Hvad er forskellen på `VPATH` og `vpath`?

Begge bruges til at fortælle make, hvor den skal lede efter forudsætningsfiler. VPATH (med store bogstaver) er en variabel, der angiver en global søgesti for alle filer, som make ikke kan finde i den nuværende mappe. vpath (med små bogstaver) er et direktiv, der giver en mere finkornet kontrol, hvor du kan specificere forskellige søgestier for forskellige filtyper baseret på et mønster (f.eks. vpath %.h headers/).

Kan et mål have flere regler?

Ja. Hvis du bruger almindelige regler (med enkelt kolon), bliver alle forudsætningerne fra alle regler for det samme mål slået sammen til én stor liste af forudsætninger. Der kan dog kun være én opskrift. Hvis flere regler for det samme mål har en opskrift, vil make bruge den sidst definerede og udskrive en advarsel. Hvis du har brug for flere uafhængige opskrifter, skal du bruge dobbeltkolon-regler.

Hvis du vil læse andre artikler, der ligner Forstå Regler i en Makefile: En Komplet Guide, kan du besøge kategorien Teknologi.

Go up