Innføring

I C programmering, er et program ofte delt på flere kildefiler og header-filer for å forbedre organisasjon, reussabilitet og samlehastighet. Men, denne modulariteten introduserer en utfordring: når en header filendringer, hver kildefil som inkluderer det må rekomponeres. Gjøre dette manuelt er feil ⁇ prone og tid ⁇ krevende. Makefiles, drevet av [[FLT: 0]]make[FLT: 1] bygge automatiseringsverktøy, løse dette problemet ved å kode avhengigheter og automatisere byggeprosessen. Mastering Makefiles er et ritual for passasje for enhver alvorlig C utvikler, men mange nykommere behandler dem som svarte bokser. Denne artikkelen forklarer hvordan avhengigheter fungerer i Makefiles, hvordan du genererer dem automatisk, og hvordan du kan strukturere en Makefil for pålitelighet og hastighet.

Hva er Makefiles?

En Makefile er en vanlig ⁇ tekstfil som definerer et sett regler for å bygge et prosjekt. gjør] verktøyet leser disse reglene, kontrollerer filtidsstempler, og utfører bare kommandoene som kreves for å bringe prosjektet oppdatert. Kjerneideen er enkel: hver regel har et mål (forutsetninger] (filer som trengs for å bygge målet), og ] (skalkommandoer å kjøre). Ved å ta opp hvilke filer som er avhengige av hvilket, kan gradvis gjenoppbygge bare deler som har endret seg.

Makefiles har vært en del av Unix siden 1970-tallet, og GNU Make er de facto standard på Linux og macOS. Syntaksen er konsistent, men kan være subtil; å få avhengighet riktig er den primære ferdigheten en C utvikler trenger.

Forståelse av avhengigheter

I et C-prosjekt er avhengighet ikke begrenset til filer. Hver kildefil inneholder en eller flere header-filer (f.eks. ]). Hvis en header er endret, må alle -filer som inkluderer den ombestilles. På samme måte er objektfilene avhengige av deres tilsvarende -filer, og den endelige kjøringen avhenger av alle objektfiler.

Utforske vs implicit avhengighet

I tidlige Makefiles, programmerere oppført alle forutsetninger manuelt. Den tilnærmingen er skjøre: å glemme en overskrift betyr trappe bygg, mens oppføring for mange utløser unødvendige rekompilasjoner. Verre, etter hvert som prosjektet vokser, manuelle lister blir uholdbar. Den moderne løsningen er å ha kompilatoren generere avhengighetene automatisk, gjøre dem til implicite, maskin - kontrollerte forutsetninger.

En velutformet Makefil behandler avhengighet som en førsteklasses bekymring. Målet er aldri å gjenoppbygge noe som ikke trenger å gjenoppbygge, og å alltid gjenoppbygge alt som gjør det. Dette er essensen av korrekthet og effektivitet i integrerte bygg.

Grunnleggende struktur av en Makefil

En typisk Makefil inneholder variable definisjoner, regler og falske mål. Her er et minimalt men funksjonelt eksempel for et prosjekt med og :

CC = gcc
CFLAGS = -Wall -Wextra -O2

main: main.o utils.o
 $(CC) $(CFLAGS) -o main main.o utils.o

main.o: main.c
 $(CC) $(CFLAGS) -c main.c

utils.o: utils.c
 $(CC) $(CFLAGS) -c utils.c

clean:
 rm -f main main.o utils.o

Denne Makefilen har fire mål: (kjørbar), to objektfiler og et falskt mål . Avhengighetslinjene etter kolonfortel ] gjør hvilke filer som skal sjekkes før du bestemmer deg for å gjenoppbygge målet.

Fony mål

Mål som eller representerer ikke filer. For å hindre ] å forvirre dem med filnavn, bør de erklæres som ]phony:

.PHONY: clean all

Uten dette, hvis en fil som heter eksisterte, gjør ville vurdere det opp til ⁇ dato og hoppe over oppskriften.

Håndtering av avhengigheter effektivt

Den manuelle Makefilen ovenfor har en alvorlig feil: avhengigheten av på er riktig, men hva med overskrifter? Hvis endres, ] (som inkluderer det) må gjenoppbygges, men regelen sier det bare avhenger av . Rettelsen er å la kompilatoren produsere den virkelige avhengighetslisten.

Automatisk avhengighet generasjon med GCC

GCC (og Clang) kan generere avhengighetsinformasjon ved hjelp av flaggfamilien. Den mest praktiske kombinasjonen for de fleste prosjekter er :

  • ]] ⁇ skriver en avhengighetsfil (]) under sammenstilling, lister bare bruker ⁇ definerte overskrifter (ikke systemoverskrifter).
  • ]] ⁇ angir navnet på avhengighetsfilen.
  • ]] ⁇ legger til falske mål for hver overskrift, og forhindrer feil når et header fjernes.

Her er hvordan du kan inkludere automatisk avhengighetssporing i en Makefil:

CC = gcc
CFLAGS = -Wall -Wextra -O2 -MMD -MP
SRCDIR = src
OBJDIR = obj
SRCS = $(wildcard $(SRCDIR)/*.c)
OBJS = $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(SRCS))
DEPS = $(OBJS:.o=.d)

all: myprogram

myprogram: $(OBJS)
 $(CC) $(CFLAGS) -o $@ $^

$(OBJDIR)/%.o: $(SRCDIR)/%.c
 @mkdir -p $(OBJDIR)
 $(CC) $(CFLAGS) -c $< -o $@

-include $(DEPS)

.PHONY: all clean
clean:
 rm -rf $(OBJDIR) myprogram

Forklaring:

  • samler alle filer i kildekatalogen.
  • forvandler dem til objektfilstier.
  • lister opp avhengighetsfilene (f.eks. ).
  • Mønsterregelen sammenstiller hver fil og på grunn av genererer en fil som en bivirkning.
  • Linjen leser de genererte filer, som gjør dem til virkelige Makefile forutsetninger. Stroket (]) undertrykker feil når filer ikke eksisterer enda (f.eks. på den første bygg).

Hvis endres, vil den neste påkallelsen av automatisk komme i stand til å bli som en forutsetning.

Avanserte teknikker

Håndtering Generert avhengighet trygt

Når en overskrift slettes, kan filen fortsatt referere til den, noe som forårsaker gjør å mislykkes med en manglende målfeil. flagget adresserer dette ved å legge til tomme falske regler for hver avhengighetshode. Hvis overskriften er borte, gjør rett og slett kjører den falske regelen (som ikke gjør noe) og fortsetter.

Inkludert avhengighetsfiler etter kildeendringer

En subtilitet: Hvis en kildefil legger til eller fjerner en , må den tilsvarende filen regenereres. Fordi filen selv er en forutsetning for objektfilen, [[FLT: 0]] gjør vil legge merke til den endret tidsstempel og rekompilering, som regenererer filen. Denne recitasjonen fungerer automatisk når den første bygningen er fullført.

Bruke Bestilling ⁇ Bare Forutsetninger

Noen ganger trenger du en katalog å eksistere før bygging, men du vil ikke at tidsstemplet skal utløse en gjenoppbygging. Det er rollen som - bare forutsetninger (separert av ]). I mønsterregelen ovenfor, brukte vi inne i oppskriften; et alternativ er:

$(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR)
 $(CC) $(CFLAGS) -c $< -o $@

$(OBJDIR):
 mkdir -p $@

Dette sikrer at katalogen opprettes før noen sammendrag, men en endring i selve katalogen (f.eks. en ny fil inne) vil ikke utløse rekompilasjon.

Beste praksis for å administrere avhengigheter

  1. Bruk automatisk avhengighet generasjon fra dag ett. Selv for et enkelt-fil prosjekt, er det en god vane. Det koster ingenting og forhindrer fremtidige feil.
  2. Hold avhengighetsfilene adskilt fra kildefiler. Legg dem i en eller ] katalog. Dette gjør det lettere å rengjøre og unngå å rote kildetreet.
  3. Inkluderer de genererte filer etter reglene som skaper dem. Direktivet er fint, men plasserer det etter mønsterregelen sikrer at gjør først vet hvordan man bygger objektfiler før man prøver å lese filer.
  4. Bruk falske mål for renhold. , ], og ] er vanlige. Alltid erklære dem med .
  5. Leveringsvariabler for kompilatorflagg, kildemapper og lister over filer. Dette gjør Makefilen gjenbrukbar på tvers av prosjekter og enklere å tilpasse.
  6. Test makefilen din med en bevisst overskriftsendring. Endre et header, kjør , og bekreft at bare de berørte objektfilene er rekompilert. Hvis en full gjenoppbygging skjer, er noe galt med avhengighetssporingen.
  7. Behold Makefilen enkelt, men ikke enklere enn nødvendig. Over-engineering med avanserte funksjoner som ] kan gjøre feilsøking smertefullt. Start med mønsteret vist i denne artikkelen; det skalerer godt til dusinvis av filer.

Eksterne ressurser

For å utdype din forståelse, konsulter disse autoritative referansene:

Konklusjon

Administrering av C-programavhengigheter med Makefiles er ikke en luksus, men en nødvendighet for ethvert prosjekt som utløper en enkelt fil. Ved å kombinere mønsterregler, automatisk avhengighet generering med ] og nøye medhold av de genererte filer, kan du opprette et byggesystem som er både rask og korrekt. Teknikkene som vises her eliminere manuell sporing, redusere byggtider, og hindre subtile feil forårsaket av stale objektfiler. Når du internererer disse mønstrene, vil du aldri skrive en manuell avhengighetsliste igjen. Investeringen i å lære riktig avhengighetshåndtering betaler seg hver gang du kjører og se bare på de endret filene kompilere.