GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Was ist ein Makefile und wie funktioniert es?

Wenn Sie eine Aufgabe ausführen oder aktualisieren möchten, wenn bestimmte Dateien aktualisiert werden, muss die Datei make Dienstprogramm kann sich als nützlich erweisen. Das make Dienstprogramm erfordert eine Datei, Makefile (oder makefile ), die eine Reihe von auszuführenden Aufgaben definiert. Möglicherweise haben Sie make verwendet um ein Programm aus dem Quellcode zu kompilieren. Die meisten Open-Source-Projekte verwenden make um eine endgültige ausführbare Binärdatei zu kompilieren, die dann mit make install installiert werden kann .

In diesem Artikel untersuchen wir make und Makefile mit einfachen und fortgeschrittenen Beispielen. Bevor Sie beginnen, stellen Sie sicher, dass make auf Ihrem System installiert ist.

Einfache Beispiele

Beginnen wir damit, den Klassiker „Hello World“ auf dem Terminal auszudrucken. Erstellen Sie ein leeres Verzeichnis myproject enthält eine Datei Makefile mit diesem Inhalt:

say_hello:
        Echo "Hallo Welt"

Führen Sie nun die Datei aus, indem Sie make eingeben innerhalb des Verzeichnisses myproject . Die Ausgabe wird sein:

$ make
echo "Hallo Welt"
Hallo Welt

Im obigen Beispiel say_hello verhält sich wie ein Funktionsname, wie in jeder Programmiersprache. Dies wird als Ziel bezeichnet . Die Voraussetzungen oder Abhängigkeiten dem Ziel folgen. Der Einfachheit halber haben wir in diesem Beispiel keine Voraussetzungen definiert. Der Befehl echo "Hello World" wird das Rezept genannt . Das Rezept verwendet Voraussetzungen um ein Ziel zu machen . Das Ziel, die Voraussetzungen und die Rezepte ergeben zusammen eine Regel .

Zusammenfassend ist unten die Syntax einer typischen Regel:

Ziel:Voraussetzungen
Rezept

Beispielsweise könnte ein Ziel eine Binärdatei sein, die von Voraussetzungen abhängt (Quelldateien). Andererseits kann eine Voraussetzung auch ein Ziel sein, das von anderen Abhängigkeiten abhängt:

final_target:sub_target final_target.c
        Recipe_to_create_final_target

sub_target:sub_target.c
        Recipe_to_create_sub_target

Das Ziel muss keine Datei sein; es könnte auch nur ein Name für das Rezept sein, wie in unserem Beispiel. Wir nennen diese "falschen Ziele".

Zurück zum obigen Beispiel, wenn make ausgeführt wurde, der gesamte Befehl echo "Hello World" wurde angezeigt, gefolgt von der eigentlichen Befehlsausgabe. Das wollen wir oft nicht. Um das Echo des eigentlichen Befehls zu unterdrücken, müssen wir echo starten mit @ :

say_hello:
        @echo "Hallo Welt"

Versuchen Sie nun, make auszuführen wieder. Die Ausgabe sollte nur Folgendes anzeigen:

$ make
Hallo Welt

Lassen Sie uns ein paar weitere falsche Ziele hinzufügen:generate und clean zum Makefile :

say_hello:
        @echo "Hello World"

generate:
        @echo "Leere Textdateien erstellen..."
        touch file-{1. .10}.txt

clean:
        @echo "Aufräumen..."
        rm *.txt

Wenn wir versuchen, make auszuführen nach den Änderungen nur noch das Ziel say_hello wird durchgeführt. Das liegt daran, dass nur das erste Ziel im Makefile das Standardziel ist. Wird oft als Standardziel bezeichnet , das ist der Grund, warum Sie all sehen werden als erstes Ziel in den meisten Projekten. Es liegt in der Verantwortung von all andere Ziele anzurufen. Wir können dieses Verhalten überschreiben, indem wir ein spezielles falsches Ziel namens .DEFAULT_GOAL verwenden .

Fügen wir das am Anfang unseres Makefiles ein:

.DEFAULT_GOAL := generate 

Dadurch wird das Ziel generate ausgeführt als Standard:

$ make
Leere Textdateien erstellen...
touch file-{1..10}.txt

Wie der Name schon sagt, das falsche Ziel .DEFAULT_GOAL kann nur ein Ziel gleichzeitig ausführen. Aus diesem Grund enthalten die meisten Makefiles all als Ziel, das so viele Ziele wie nötig aufrufen kann.

Nehmen wir das falsche Ziel all auf und entfernen Sie .DEFAULT_GOAL :

all:say_hello generate

say_hello:
        @echo "Hello World"

generate:
        @echo "Leere Textdateien werden erstellt.. ."
        touch file-{1..10}.txt

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

Vor dem Ausführen von make , fügen wir ein weiteres spezielles falsches Ziel ein, .PHONY , wo wir alle Ziele definieren, die keine Dateien sind. make führt sein Rezept aus, unabhängig davon, ob eine Datei mit diesem Namen existiert oder wann sie zuletzt geändert wurde. Hier ist das komplette Makefile:

.PHONY:alle say_hello generieren sauber

all:say_hello generieren

say_hello:
        @echo "Hello World"

generate:
        @echo "Leere Textdateien erstellen..."
        touch file-{1..10}.txt

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

Das make sollte say_hello aufrufen und generate :

$ make
Hallo Welt
Leere Textdateien werden erstellt...
touch file-{1..10}.txt

Es empfiehlt sich, clean nicht aufzurufen in all oder setzen Sie es als erstes Ziel. clean sollte manuell aufgerufen werden, wenn eine Reinigung als erstes Argument für make erforderlich ist :

$ make clean
Aufräumen...
rm *.txt

Nachdem Sie nun eine Vorstellung davon haben, wie ein einfaches Makefile funktioniert und wie man ein einfaches Makefile schreibt, wollen wir uns einige fortgeschrittenere Beispiele ansehen.

Erweiterte Beispiele

Variablen

Weitere Linux-Ressourcen

  • Spickzettel für Linux-Befehle
  • Spickzettel für fortgeschrittene Linux-Befehle
  • Kostenloser Online-Kurs:RHEL Technical Overview
  • Spickzettel für Linux-Netzwerke
  • SELinux-Spickzettel
  • Spickzettel für allgemeine Linux-Befehle
  • Was sind Linux-Container?
  • Unsere neuesten Linux-Artikel

Im obigen Beispiel sind die meisten Ziel- und Voraussetzungswerte hartcodiert, aber in echten Projekten werden diese durch Variablen und Muster ersetzt.

Der einfachste Weg, eine Variable in einem Makefile zu definieren, ist die Verwendung von = Operator. Zum Beispiel, um den Befehl gcc zuzuweisen in eine Variable CC :

CC = gcc
 

Dies wird auch als rekursive erweiterte Variable bezeichnet , und es wird in einer Regel wie unten gezeigt verwendet:

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

Wie Sie vielleicht erraten haben, wird das Rezept wie folgt erweitert, wenn es an das Terminal übergeben wird:

gcc hello.c -o hello
 

Beide ${CC} und $(CC) sind gültige Verweise auf den Aufruf von gcc . Wenn man jedoch versucht, eine Variable sich selbst neu zuzuweisen, führt dies zu einer Endlosschleife. Lassen Sie uns das überprüfen:

CC =gcc
CC =${CC}

alle:
    @echo ${CC}

Ausführen von make ergibt:

$ make
Makefile:8:*** Rekursive Variable 'CC' verweist (eventuell) auf sich selbst. Stopp.

Um dieses Szenario zu vermeiden, können wir den := verwenden -Operator (dies wird auch als einfach erweiterte Variable bezeichnet ). Wir sollten kein Problem haben, das folgende Makefile auszuführen:

CC :=gcc
CC :=${CC}

alle:
    @echo ${CC}

Muster und Funktionen

Das folgende Makefile kann alle C-Programme kompilieren, indem es Variablen, Muster und Funktionen verwendet. Sehen wir es uns Zeile für Zeile an:

# Verwendung:
# make        # alle Binärdateien kompilieren
# make clean  # ALLE Binärdateien und Objekte entfernen

.PHONY =all clean

CC =gcc                        # zu verwendender Compiler

LINKERFLAG =-lm

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

alle:${BINS}

%:%.o
        @echo "Überprüfe...."
        ${CC} ${LINKERFLAG} $<-o $@

%.o:%.c
        @echo "Objekt erstellen.."
        ${CC} -c $<

clean:
        @echo "Aufräumen..."
        rm -rvf *.o ${BINS}
  • Zeilen beginnend mit # sind Kommentare.

  • Zeile .PHONY = all clean definiert falsche Ziele all und clean .

  • Variable LINKERFLAG definiert Flags, die mit gcc verwendet werden sollen in einem Rezept.

  • SRCS := $(wildcard *.c) :$(wildcard pattern) ist eine der Funktionen für Dateinamen . In diesem Fall alle Dateien mit dem .c Erweiterung wird in einer Variablen SRCS gespeichert .

  • BINS := $(SRCS:%.c=%) :Dies wird als Substitutionsreferenz bezeichnet . Wenn in diesem Fall SRCS hat Werte 'foo.c bar.c' , BINS wird 'foo bar' haben .

  • Zeile all: ${BINS} :Das falsche Ziel all ruft Werte in ${BINS} auf als individuelle Ziele.

  • Regel:

    %:%.o
      @echo "Überprüfe...."
      ${CC} ${LINKERFLAG} $< -o $@

    Schauen wir uns ein Beispiel an, um diese Regel zu verstehen. Angenommen foo ist einer der Werte in ${BINS} . Dann % entspricht foo (% kann mit jedem Zielnamen übereinstimmen). Unten ist die Regel in ihrer erweiterten Form:

    foo:foo.o
      @echo "Überprüfe...."
      gcc -lm foo.o -o foo

    Wie gezeigt, % wird durch foo ersetzt . $< wird durch foo.o ersetzt . $< ist so strukturiert, dass es den Voraussetzungen und $@ entspricht entspricht dem Ziel. Diese Regel wird für jeden Wert in ${BINS} aufgerufen

  • Regel:

    %.o:%.c
      @echo "Objekt erstellen.."
      ${CC} -c $<

    Jede Voraussetzung in der vorherigen Regel wird als Ziel für diese Regel betrachtet. Unten ist die Regel in ihrer erweiterten Form:

    foo.o:foo.c
      @echo "Objekt erstellen.."
      gcc -c foo.c
  • Schließlich entfernen wir alle Binärdateien und Objektdateien im Ziel clean .

Unten ist die Umschreibung des obigen Makefiles, vorausgesetzt, es befindet sich in dem Verzeichnis mit einer einzigen Datei foo.c:

# Verwendung:
# make        # alle Binärdateien kompilieren
# make clean  # ALLE Binärdateien und Objekte entfernen

.PHONY =all clean

CC =gcc                        # zu verwendender Compiler

LINKERFLAG =-lm

SRCS :=foo.c
BINS :=foo

all :foo

foo:foo.o
        @echo "Überprüfe...."
        gcc -lm foo.o -o foo

foo.o :foo.c
        @echo "Objekt erstellen.."
        gcc -c foo.c

clean:
        @echo "Aufräumen..."
        rm -rvf foo.o foo

Weitere Informationen zu Makefiles finden Sie im GNU Make-Handbuch, das eine vollständige Referenz und Beispiele bietet.

Sie können auch unsere Einführung in GNU Autotools lesen, um zu erfahren, wie Sie die Generierung eines Makefiles für Ihr Programmierprojekt automatisieren können.


Linux
  1. Was ist NGINX? Wie funktioniert es?

  2. Was ist ein Webserver und wie funktioniert ein Webserver?

  3. Ssh – Wie funktioniert TCP-Keepalive in Ssh?

  4. Wie funktioniert Rm? Was macht Rm?

  5. Wie funktioniert sig_atomic_t eigentlich?

Linux-Dateibefehl:Was macht er und wie wird er verwendet?

Was ist Docker? Wie funktioniert es?

Was ist Source Command in Linux und wie funktioniert es?

Was ist der Grep-Befehl unter Linux? Warum wird es verwendet und wie funktioniert es?

Wie funktioniert Swap-Speicher in Linux?

Wie funktioniert ein Load Balancer? Was ist Load-Balancing?