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.
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.
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.
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.
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
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)
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:
$(MAKE) -C some_dir all
&&
), weil es weniger lesbar ist und ich befürchte, dass ich beim Bearbeiten des Rezepts einen Tippfehler mache..ONESHELL
besonderes Ziel, weil:
.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 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.