508 Stimmen

Wie schreibe ich den Befehl 'cd' in ein Makefile?

Ich habe zum Beispiel so etwas in meinem Makefile:

all:
     cd some_directory

Aber als ich tippte make Ich sah nur 'cd some_directory', wie in der echo Befehl.

860voto

falstro Punkte 32879

Er führt den Befehl tatsächlich aus und ändert das Verzeichnis in some_directory wird jedoch in einer Unterprozess-Shell ausgeführt und beeinflusst weder make noch die Shell, in der Sie arbeiten.

Wenn Sie mehr Aufgaben innerhalb der some_directory , müssen Sie ein Semikolon hinzufügen und die anderen Befehle ebenfalls anhängen . Beachten Sie, dass Sie keine neuen Zeilen verwenden können, da diese von make als Ende der Regel interpretiert werden, so dass alle neuen Zeilen, die Sie aus Gründen der Übersichtlichkeit verwenden, mit einem Backslash versehen werden müssen.

Zum Beispiel:

all:
        cd some_dir; echo "I'm in some_dir"; \
          gcc -Wall -o myTest myTest.c

Beachten Sie auch, dass das Semikolon zwischen jedem Befehl notwendig ist, auch wenn Sie einen Backslash und einen Zeilenumbruch hinzufügen. Das liegt daran, dass die gesamte Zeichenkette von der Shell als eine einzige Zeile geparst wird. Wie in den Kommentaren erwähnt, sollten Sie '&&' verwenden, um Befehle zu verbinden, was bedeutet, dass sie nur ausgeführt werden, wenn der vorangegangene Befehl erfolgreich war.

all:
        cd some_dir && echo "I'm in some_dir" && \
          gcc -Wall -o myTest myTest.c

Dies ist besonders wichtig, wenn Sie zerstörerische Arbeiten, wie z. B. Aufräumarbeiten, durchführen, da Sie sonst das falsche Material zerstören, wenn die cd scheitern, aus welchem Grund auch immer.

Es ist jedoch üblich, make im Unterverzeichnis aufzurufen, was Sie sich vielleicht einmal ansehen sollten. Es gibt eine Kommandozeilenoption dafür, so dass Sie nicht cd selbst, so dass Ihre Regel wie folgt aussehen würde

all:
        $(MAKE) -C some_dir all

die sich dann in some_dir und führen die Makefile in diesem Verzeichnis, mit dem Ziel "all". Verwenden Sie als beste Praxis $(MAKE) statt eines Aufrufs make direkt, da es darauf achtet, die richtige make-Instanz aufzurufen (wenn Sie zum Beispiel eine spezielle make-Version für Ihre Build-Umgebung verwenden), sowie ein leicht abweichendes Verhalten bei der Ausführung mit bestimmten Schaltern, wie -t .

Für das Protokoll, machen Sie immer echot den Befehl, den es ausführt (es sei denn, er wird explizit unterdrückt), auch wenn er keine Ausgabe hat, was Sie gerade sehen.

148voto

Chnossos Punkte 9110

Ausgehend von GNU make 3.82 (Juli 2010), können Sie die .ONESHELL spezielles Ziel, um alle Rezepte in einer einzigen Instanzierung der Shell auszuführen (Hervorhebung durch mich):

  • Neues Spezialziel: .ONESHELL unterrichtet machen. aufzurufen eine einzelne Instanz der Shell und versorgen sie mit das gesamte Rezept , unabhängig davon, wie viele Zeilen er enthält.
.ONESHELL: # Applies to every targets in the file!

all:
    cd ~/some_dir
    pwd # Prints ~/some_dir if cd succeeded

another_rule:
    cd ~/some_dir
    pwd # Prints ~/some_dir if cd succeeded

Dies ist gleichbedeutend mit der manuellen Ausführung von

$(SHELL) $(.SHELLFLAGS) "cd ~/some_dir; pwd"
# Which gets replaced to this, most of the time:
/bin/sh -c "cd ~/some_dir; pwd"

Befehle sind nicht verknüpft mit && Wenn Sie also beim ersten fehlgeschlagenen Versuch aufhören wollen, sollten Sie auch die -e Flagge zu Ihrem .SHELLFLAGS :

.SHELLFLAGS += -e

Auch die -o pipefail Flagge könnte von Interesse sein:

Wenn diese Option gesetzt ist, ist der Rückgabewert einer Pipeline der Wert des letzten (ganz rechts stehenden) Befehls, der mit einem Status ungleich Null beendet wird, oder Null, wenn alle Befehle in der Pipeline erfolgreich beendet werden. Diese Option ist standardmäßig deaktiviert.

20voto

JoeS Punkte 381

Hier ist ein netter Trick, um mit Verzeichnissen und make umzugehen. Anstatt mehrzeilige Zeichenketten oder "cd ;" bei jedem Befehl zu verwenden, definieren Sie eine einfache chdir-Funktion wie folgt:

CHDIR_SHELL := $(SHELL)
define chdir
   $(eval _D=$(firstword $(1) $(@D)))
   $(info $(MAKE): cd $(_D)) $(eval SHELL = cd $(_D); $(CHDIR_SHELL))
endef

Dann müssen Sie es nur noch in Ihrer Regel so nennen:

all:
          $(call chdir,some_dir)
          echo "I'm now always in some_dir"
          gcc -Wall -o myTest myTest.c

Sie können sogar Folgendes tun:

some_dir/myTest:
          $(call chdir)
          echo "I'm now always in some_dir"
          gcc -Wall -o myTest myTest.c

9voto

Nadir SOUALEM Punkte 3397

Was soll es tun, wenn es dort angekommen ist? Jeder Befehl wird in einer Subshell ausgeführt, so dass die Subshell das Verzeichnis wechselt, aber das Endergebnis ist, dass sich der nächste Befehl immer noch im aktuellen Verzeichnis befindet.

Mit GNU make können Sie so etwas tun wie:

BIN=/bin
foo:
    $(shell cd $(BIN); ls)

6voto

Jasha Punkte 2740

Hier ist das Muster, das ich verwendet habe:

.PHONY: test_py_utils
PY_UTILS_DIR = py_utils
test_py_utils:
    cd $(PY_UTILS_DIR) && black .
    cd $(PY_UTILS_DIR) && isort .
    cd $(PY_UTILS_DIR) && mypy .
    cd $(PY_UTILS_DIR) && pytest -sl .
    cd $(PY_UTILS_DIR) && flake8 .

Meine Beweggründe für dieses Muster sind:

  • Die obige Lösung ist einfach und lesbar (wenn auch langatmig)
  • Ich habe das klassische Papier gelesen "Rekursive Marke gilt als schädlich" was mich davon abgehalten hat, die $(MAKE) -C some_dir all
  • Ich wollte nicht nur eine einzige Codezeile verwenden (mit Semikolons unterbrochen oder && ), weil es weniger lesbar ist und ich befürchte, dass ich beim Bearbeiten des Rezepts einen Tippfehler mache.
  • Ich wollte nicht die .ONESHELL besonderes Ziel, weil:
    • das ist eine globale Option, die alle Rezepte im Makefile betrifft
    • mit .ONESHELL bewirkt, dass alle Zeilen des Rezepts ausgeführt werden, auch wenn eine der früheren Zeilen mit einem Exit-Status ungleich Null fehlgeschlagen ist. Umgehungen wie der Aufruf von set -e sind möglich, aber solche Workarounds müssten für jedes Rezept im Makefile implementiert werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X