GNU/Linux >> Linux Esercitazione >  >> Linux

Che cos'è un Makefile e come funziona?

Se desideri eseguire o aggiornare un'attività quando vengono aggiornati determinati file, make l'utilità può tornare utile. Il make l'utilità richiede un file, Makefile (o makefile ), che definisce l'insieme delle attività da eseguire. Potresti aver usato make per compilare un programma dal codice sorgente. La maggior parte dei progetti open source utilizza make per compilare un binario eseguibile finale, che può quindi essere installato utilizzando make install .

In questo articolo esploreremo make e Makefile utilizzando esempi di base e avanzati. Prima di iniziare, assicurati che make è installato nel tuo sistema.

Esempi di base

Iniziamo stampando sul terminale il classico "Hello World". Crea una directory vuota myproject contenente un file Makefile con questo contenuto:

say_hello:
        echo "Hello World"

Ora esegui il file digitando make all'interno della directory myproject . L'output sarà:

$ make
echo "Hello World"
Hello World

Nell'esempio sopra, say_hello si comporta come un nome di funzione, come in qualsiasi linguaggio di programmazione. Questo è chiamato obiettivo . I prerequisiti o dipendenze seguire l'obiettivo. Per semplicità, in questo esempio non abbiamo definito alcun prerequisito. Il comando echo "Hello World" è chiamata la ricetta . La ricetta utilizza prerequisiti per creare un bersaglio . L'obiettivo, i prerequisiti e le ricette insieme costituiscono una regola .

Per riassumere, di seguito è riportata la sintassi di una regola tipica:

target: prerequisites
<TAB> recipe

Ad esempio, una destinazione potrebbe essere un file binario che dipende dai prerequisiti (file di origine). D'altra parte, un prerequisito può anche essere una destinazione che dipende da altre dipendenze:

final_target: sub_target final_target.c
        Recipe_to_create_final_target

sub_target: sub_target.c
        Recipe_to_create_sub_target

Non è necessario che la destinazione sia un file; potrebbe essere solo un nome per la ricetta, come nel nostro esempio. Li chiamiamo "bersagli falsi".

Tornando all'esempio sopra, quando make è stato eseguito, l'intero comando echo "Hello World" è stato visualizzato, seguito dall'output del comando effettivo. Spesso non lo vogliamo. Per sopprimere l'eco del comando vero e proprio, dobbiamo avviare echo con @ :

say_hello:
        @echo "Hello World"

Ora prova a eseguire make ancora. L'output dovrebbe visualizzare solo questo:

$ make
Hello World

Aggiungiamo altri target fasulli:generate e clean al Makefile :

say_hello:
        @echo "Hello World"

generate:
        @echo "Creating empty text files..."
        touch file-{1..10}.txt

clean:
        @echo "Cleaning up..."
        rm *.txt

Se proviamo a eseguire make dopo le modifiche, solo il target say_hello sarà eseguito. Questo perché solo il primo target nel makefile è il target predefinito. Spesso chiamato obiettivo predefinito , questo è il motivo per cui vedrai all come primo obiettivo nella maggior parte dei progetti. È responsabilità di all chiamare altri bersagli. Possiamo ignorare questo comportamento utilizzando uno speciale target fasullo chiamato .DEFAULT_GOAL .

Includiamolo all'inizio del nostro makefile:

.DEFAULT_GOAL := generate

Questo eseguirà la destinazione generate come predefinito:

$ make
Creating empty text files...
touch file-{1..10}.txt

Come suggerisce il nome, il target fasullo .DEFAULT_GOAL può eseguire solo un target alla volta. Questo è il motivo per cui la maggior parte dei makefile include all come target che può richiamare tutti i target necessari.

Includiamo il target fasullo all e rimuovi .DEFAULT_GOAL :

all: say_hello generate

say_hello:
        @echo "Hello World"

generate:
        @echo "Creating empty text files..."
        touch file-{1..10}.txt

clean:
        @echo "Cleaning up..."
        rm *.txt

Prima di eseguire make , includiamo un altro target fasullo speciale, .PHONY , dove definiamo tutti i target che non sono file. make eseguirà la sua ricetta indipendentemente dal fatto che esista un file con quel nome o quale sia l'ora dell'ultima modifica. Ecco il makefile completo:

.PHONY: all say_hello generate clean

all: say_hello generate

say_hello:
        @echo "Hello World"

generate:
        @echo "Creating empty text files..."
        touch file-{1..10}.txt

clean:
        @echo "Cleaning up..."
        rm *.txt

Il make dovrebbe chiamare say_hello e generate :

$ make
Hello World
Creating empty text files...
touch file-{1..10}.txt

È buona norma non chiamare clean in all o mettilo come primo obiettivo. clean dovrebbe essere chiamato manualmente quando la pulizia è necessaria come primo argomento per make :

$ make clean
Cleaning up...
rm *.txt

Ora che hai un'idea di come funziona un makefile di base e di come scrivere un makefile semplice, diamo un'occhiata ad alcuni esempi più avanzati.

Esempi avanzati

Variabili

Più risorse Linux

  • Comandi Linux cheat sheet
  • Cheat sheet sui comandi avanzati di Linux
  • Corso online gratuito:Panoramica tecnica RHEL
  • Cheat sheet della rete Linux
  • Cheat sheet di SELinux
  • Cheat sheet dei comandi comuni di Linux
  • Cosa sono i container Linux?
  • I nostri ultimi articoli su Linux

Nell'esempio precedente, la maggior parte dei valori target e prerequisiti sono codificati, ma nei progetti reali questi vengono sostituiti con variabili e modelli.

Il modo più semplice per definire una variabile in un makefile è usare = operatore. Ad esempio, per assegnare il comando gcc a una variabile CC :

CC = gcc

Questa è anche chiamata variabile espansa ricorsiva , e viene utilizzato in una regola come mostrato di seguito:

hello: hello.c
    ${CC} hello.c -o hello

Come avrai intuito, la ricetta si espande come di seguito quando viene passata al terminale:

gcc hello.c -o hello

Entrambi ${CC} e $(CC) sono riferimenti validi per chiamare gcc . Ma se si tenta di riassegnare una variabile a se stessa, si verificherà un ciclo infinito. Verifichiamo questo:

CC = gcc
CC = ${CC}

all:
    @echo ${CC}

Esecuzione di make risulterà in:

$ make
Makefile:8: *** Recursive variable 'CC' references itself (eventually).  Stop.

Per evitare questo scenario, possiamo usare il := operatore (questo è anche chiamato la variabile semplicemente espansa ). Non dovremmo avere problemi a eseguire il makefile di seguito:

CC := gcc
CC := ${CC}

all:
    @echo ${CC}

Modelli e funzioni

Il makefile seguente può compilare tutti i programmi C utilizzando variabili, modelli e funzioni. Esploriamolo riga per riga:

# Usage:
# make        # compile all binary
# make clean  # remove ALL binaries and objects

.PHONY = all clean

CC = gcc                        # compiler to use

LINKERFLAG = -lm

SRCS := $(wildcard *.c)
BINS := $(SRCS:%.c=%)

all: ${BINS}

%: %.o
        @echo "Checking.."
        ${CC} ${LINKERFLAG} $< -o $@

%.o: %.c
        @echo "Creating object.."
        ${CC} -c $<

clean:
        @echo "Cleaning up..."
        rm -rvf *.o ${BINS}
  • Righe che iniziano con # sono commenti.

  • Riga .PHONY = all clean definisce gli obiettivi falsi all e clean .

  • Variabile LINKERFLAG definisce i flag da usare con gcc in una ricetta.

  • SRCS := $(wildcard *.c) :$(wildcard pattern) è una delle funzioni per i nomi di file . In questo caso, tutti i file con .c l'estensione verrà memorizzata in una variabile SRCS .

  • BINS := $(SRCS:%.c=%) :Questo è chiamato come riferimento di sostituzione . In questo caso, se SRCS ha valori 'foo.c bar.c' , BINS avrà 'foo bar' .

  • Riga all: ${BINS} :Il bersaglio fasullo all chiama i valori in${BINS} come obiettivi individuali.

  • Regola:

    %: %.o
      @echo "Checking.."
      ${CC} ${LINKERFLAG} $&lt; -o $@

    Diamo un'occhiata a un esempio per capire questa regola. Supponiamo foo è uno dei valori in ${BINS} . Quindi % corrisponderà a foo (% può corrispondere a qualsiasi nome di destinazione). Di seguito è riportata la regola nella sua forma estesa:

    foo: foo.o
      @echo "Checking.."
      gcc -lm foo.o -o foo

    Come mostrato, % è sostituito da foo . $< è sostituito da foo.o . $< è modellato per soddisfare i prerequisiti e $@ corrisponde all'obiettivo. Questa regola verrà chiamata per ogni valore in ${BINS}

  • Regola:

    %.o: %.c
      @echo "Creating object.."
      ${CC} -c $&lt;

    Ogni prerequisito nella regola precedente è considerato un obiettivo per questa regola. Di seguito è riportata la regola nella sua forma estesa:

    foo.o: foo.c
      @echo "Creating object.."
      gcc -c foo.c
  • Infine, rimuoviamo tutti i binari e i file oggetto nella destinazione clean .

Di seguito è riportata la riscrittura del makefile sopra, supponendo che sia posizionato nella directory con un unico file foo.c:

# Usage:
# make        # compile all binary
# make clean  # remove ALL binaries and objects

.PHONY = all clean

CC = gcc                        # compiler to use

LINKERFLAG = -lm

SRCS := foo.c
BINS := foo

all: foo

foo: foo.o
        @echo "Checking.."
        gcc -lm foo.o -o foo

foo.o: foo.c
        @echo "Creating object.."
        gcc -c foo.c

clean:
        @echo "Cleaning up..."
        rm -rvf foo.o foo

Per ulteriori informazioni sui makefile, fare riferimento al manuale GNU Make, che offre un riferimento completo ed esempi.

Puoi anche leggere la nostra Introduzione a GNU Autotools per imparare come automatizzare la generazione di un makefile per il tuo progetto di programmazione.


Linux
  1. Cos'è NGINX? Come funziona?

  2. Che cos'è un server Web e come funziona un server Web?

  3. Cos'è il DNS e come funziona?

  4. Come funziona rm? Cosa fa rm?

  5. Come funziona effettivamente sig_atomic_t?

Comando file Linux:cosa fa e come usarlo

Cos'è Docker? Come funziona?

Che cos'è il comando sorgente in Linux e come funziona?

Che cos'è il comando Grep in Linux? Perché viene utilizzato e come funziona?

Come funziona la memoria di scambio in Linux?

Come funziona un sistema di bilanciamento del carico? Che cos'è il bilanciamento del carico?