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

354

Zum Beispiel habe ich so etwas in meinem Makefile:

all:
     cd some_directory

Aber als ich tippte, makesah ich nur 'cd some_directory', wie im echoBefehl.

Davit Siradeghyan
quelle
5
Es ist unklar, was Sie tun möchten, aber meiner Erfahrung nach makewollte ich das Verzeichnis nie so ändern. Vielleicht sollten Sie einen anderen Ansatz für Ihre Lösung ausprobieren?
P Shved
2
Es ist ein häufiger Anfängerfehler zu glauben, dass Ihr Verzeichnis wichtig ist. Für die meisten Dinge ist es nicht; cd dir; cmd filekann fast immer sinnvoller ausgedrückt werden als cmd dir/file.
Tripleee
34
Es ist ein häufiger Anfängerfehler zu glauben, dass Ihr derzeitiges Arbeitsverzeichnis keine Rolle spielt. Viele Programme, insbesondere Shell-Skripte, wurden unter .Berücksichtigung eines bestimmten Werts geschrieben. Es ist wahr, dass die meisten Tools so konzipiert sind, dass Sie Ihr pwd dafür nicht ändern müssen. Dies ist jedoch nicht immer der Fall, und ich halte es nicht für eine gute Idee, es als "Fehler" zu bezeichnen, um zu glauben, dass Ihr Verzeichnis wichtig sein könnte.
Evelyn Kokemoor
Tipp: Wenn Ihr Befehl cd sagt „Keine solche Datei oder das Verzeichnis“, auch wenn das (relative) Verzeichnis tut exist, überprüfen Sie, ob Ihre CDPATH Umgebungsvariable entweder leer oder enthält „“. Make führt Befehle mit "sh" aus, die nur dann einen relativen Pfad über CDPATH finden, wenn dieser festgelegt ist. Dies steht im Gegensatz zu Bash, das es versuchen wird. vor Rücksprache mit CDPATH.
Denis Howe

Antworten:

620

Es führt den Befehl tatsächlich aus und ändert das Verzeichnis in some_directory. Dies wird jedoch in einer Unterprozess-Shell ausgeführt und wirkt sich weder auf make noch auf die Shell aus, von der aus Sie arbeiten.

Wenn Sie mehr Aufgaben ausführen möchten some_directory, müssen Sie ein Semikolon hinzufügen und auch die anderen Befehle anhängen. Beachten Sie, dass Sie keine Zeilenumbrüche verwenden können, da diese von make als Ende der Regel interpretiert werden. Daher müssen alle Zeilenumbrüche, die Sie aus Gründen der Übersichtlichkeit verwenden, durch einen Backslash maskiert werden.

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 erforderlich ist, obwohl Sie einen Backslash und eine Newline hinzufügen. Dies liegt an der Tatsache, dass die gesamte Zeichenfolge von der Shell als einzelne Zeile analysiert wird. Wie in den Kommentaren erwähnt, sollten Sie '&&' verwenden, um Befehle zu verbinden. Dies bedeutet, dass sie nur ausgeführt werden, wenn der vorhergehende 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 ausführen, z. B. Aufräumen, da Sie sonst das falsche Material zerstören, falls dies cdaus irgendeinem Grund fehlschlägt.

Eine häufige Verwendung ist jedoch das Aufrufen von make im Unterverzeichnis, in das Sie möglicherweise schauen möchten. Hierfür gibt es eine Befehlszeilenoption, sodass Sie sich nicht selbst anrufen müssen cd, sodass Ihre Regel so aussehen würde

all:
        $(MAKE) -C some_dir all

das wird sich in some_dirdas Makefiledort mit dem Ziel "all" ändern und ausführen . Verwenden Sie als bewährte Methode, $(MAKE)anstatt makedirekt aufzurufen , da Sie die richtige make-Instanz aufrufen müssen (wenn Sie beispielsweise eine spezielle make-Version für Ihre Build-Umgebung verwenden) und beim Ausführen ein etwas anderes Verhalten bereitstellen mit bestimmten Schaltern, wie z -t.

Machen Sie für den Datensatz immer den Befehl, den er ausführt (sofern er nicht ausdrücklich unterdrückt wird), auch wenn er keine Ausgabe hat, was Sie sehen.

falstro
quelle
8
Nun, nicht immer . Um das Echo zu unterdrücken, setzen Sie einfach @ am Anfang der Zeile.
Beta
2
@Beta: Nun ja, und ein Bindestrich-Präfix ignoriert auch den Fehlerstatus. Vielleicht wurde ich ein wenig mitgerissen, ich wollte darauf hinweisen, dass make den Befehl wiedergibt, unabhängig davon, um welche Art von Befehl es sich handelt. In diesem Fall handelt es sich um einen Befehl ohne Ausgabe, wodurch das Echo für jemanden, der mit make nicht vertraut ist, noch seltsamer erscheint.
Falstro
46
Zwei Nits: 1. Die Befehle sollten wirklich verbunden werden &&, denn ;wenn das Verzeichnis nicht existiert und das cdfehlschlägt, führt die Shell den Rest der Befehle im aktuellen Verzeichnis weiter aus, was zu mysteriösen „Datei nicht“ führen kann gefundene Nachrichten für Kompilierungen, Endlosschleifen beim Aufrufen von make oder Katastrophen für Regeln wie clean:: cd dir; rm -rf *. 2. Rufen Sie beim Aufrufen von Untermarken $(MAKE)stattdessen auf, makedamit die Optionen korrekt weitergegeben werden .
Andrewdotn
2
@perreal, ich definiere normalerweise eine Musterregel wie folgt: %-recursive:mit dem Körper: @T="$@";$(MAKE) -C some_dir $${T%-*}(Ich habe normalerweise auch eine for-Schleife, um eine Liste von Unterverzeichnissen zu durchlaufen, das $${T%-*}ist eine Bash-Erweiterung, die den -recursiveTeil des Zielnamens entfernt ) und dann definiert explizite Kurz Hand (und .PHONY) Ziele für jede, wie all: all-recursive, check: check-recursive, clean: clean-recursive.
Falstro
1
@ChristianStewart, wahr, wie in Kommentar 2 und 3 erwähnt.
Falstro
94

Ab GNU make 3.82 (Juli 2010) können Sie mit dem .ONESHELLspeziellen Ziel alle Rezeptzeilen in einer einzigen Instanziierung der Shell ausführen (fett hervorgehobene Mine):

  • Neues spezielles Ziel: .ONESHELLAnweisungen machen , eine einzelne Instanz der Shell aufzurufen und ihr das gesamte Rezept bereitzustellen , unabhängig davon, wie viele Zeilen sie enthält. [...]
.ONESHELL: # Only applies to all target
all:
    cd ~/some_dir
    pwd # Prints ~/some_dir if cd succeeded
Chnossos
quelle
6
Es genügt bemerken , dass pwdsich, wie auch funktioniert `pwd`(mit Backticks), aber $(shell pwd)und $(PWD)wird noch das Verzeichnis zurück , bevor das tun cdBefehl, so dass Sie sie nicht direkt nutzen können.
Anol
3
Ja, da die Variablen- und Funktionserweiterung vor der Ausführung der Befehle durch make erfolgt, während pwdund `pwd`von der Shell selbst ausgeführt wird.
Chnossos
2
Nicht jeder arbeitet an Legacy-Makefiles, und selbst dann geht es bei dieser Antwort darum, zu wissen, dass diese Möglichkeit besteht.
Chnossos
1
Dies kann eine ärgerliche / gefährliche Option sein, da nur der letzte Befehl für ein Ziel einen Fehler verursachen kann (frühere Befehlsfehler werden ignoriert), und wahrscheinlich wird dies niemand erwarten, der mit Ihnen zusammenarbeitet.
Tobii
1
Setzen Sie SHELLFLAGSauf -e -cund die Schale auf den ersten Befehl verlassen wird , die fehlschlägt.
Timothy Baldwin
21

Hier ist ein süßer Trick, um mit Verzeichnissen umzugehen und sie zu erstellen. Anstatt mehrzeilige Zeichenfolgen oder "cd" zu verwenden; Definieren Sie für jeden Befehl 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 in Ihrer Regel nur noch 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
JoeS
quelle
41
"Niedlich"? Eher genug Seil, um sich in den Fuß zu schießen.
Tripleee
1
Ist dieses aktuelle Verzeichnis dann für die Befehle nur in dieser Regel oder für alle nachfolgend ausgeführten Regeln festgelegt? Funktioniert eine Variation davon auch unter Windows?
user117529
8
Dies ist natürlich Pausen für die parallele Ausführung ( -jn), das der ganze Sinn ist make wirklich.
Bobbogo
5
Das ist ein schlechter Hack. Wenn Sie jemals auf so etwas zurückgreifen müssen, verwenden Sie Makefiles nicht für das, wofür sie sind.
Gatopeich
4
Ich werde nicht widersprechen, dass es ein schlechter Hack ist, das ist sicher. Aber zeigt einige der bösen Dinge, die Sie tun können.
JoeS
9

Was soll es tun, wenn es dort ankommt? Jeder Befehl wird in einer Unterschale ausgeführt, sodass die Unterschale das Verzeichnis wechselt. Das Endergebnis ist jedoch, dass sich der nächste Befehl noch im aktuellen Verzeichnis befindet.

Mit GNU make können Sie Folgendes tun:

BIN=/bin
foo:
    $(shell cd $(BIN); ls)
Nadir SOUALEM
quelle
5
Warum das $(shell ...)Wann cd $(BIN); lsoder cd $(BIN) && ls(wie @andrewdotn betonte) ausreichen würde.
Vapace
1

Dir ändern

foo: 
    $(MAKE) -C mydir

multi:
    $(MAKE) -C / -C my-custom-dir   ## Equivalent to /my-custom-dir
Jackotonye
quelle
-6

So was:

target:
    $(shell cd ....); \
    # ... commands execution in this directory
    # ... no need to go back (using "cd -" or so)
    # ... next target will be automatically in prev dir

Viel Glück!

Zauberwürfel
quelle
1
Nein, das ist falsch. Insbesondere wird das $(shell cd ....)ausgeführt, wenn das Makefile zum ersten Mal analysiert wird, nicht, wenn dieses bestimmte Rezept ausgeführt wird.
Tripleee
@triplee Nicht ganz - das $(shell)wird nur erweitert, wenn make beschließt, ein Ziel zu erstellen . Wenn make das Rezept nie benötigt, wird es nicht erweitert.
Bobbogo