Come scrivo il comando 'cd' in un makefile?


354

Ad esempio, ho qualcosa di simile nel mio makefile:

all:
     cd some_directory

Ma quando ho scritto makeho visto solo 'cd some_directory', come nel echocomando.


5
Non è chiaro cosa vuoi fare, ma, nella mia esperienza con make, non ho mai voluto cambiare la directory in questo modo. Forse dovresti provare un altro approccio alla tua soluzione?
P Shved,

2
È un errore da principiante comune credere che la tua directory sia importante. Per la maggior parte delle cose non lo è; cd dir; cmd filepuò quasi sempre essere più utilmente espresso come cmd dir/file.
tripla il

34
È un comune errore da principiante ritenere che la tua directory di lavoro attuale sia irrilevante. Molti programmi, in particolare gli script di shell, sono scritti tenendo presente uno specifico valore .. È vero che la maggior parte degli strumenti sono progettati in modo tale da non dover cambiare il tuo pwd per questo. Ma questo non è sempre vero, e non credo sia una buona idea chiamarlo "errore" per credere che la tua directory possa essere importante.
Evelyn Kokemoor,

Suggerimento: se il comando cd dice "No such file or directory", anche quando la directory (relativa) non esistono, verificare che la variabile ambiente CDPATH è vuoto o comprende "". Make esegue i comandi con "sh", che troverà un percorso relativo solo tramite CDPATH se è impostato. Questo contrasta con bash, che ci proverà. prima di consultare CDPATH.
Denis Howe,

Risposte:


621

In realtà sta eseguendo il comando, cambiando la directory in some_directory, tuttavia, questo viene eseguito in una shell di processo secondario e non influenza né make né la shell da cui stai lavorando.

Se stai cercando di eseguire più attività all'interno some_directory, devi aggiungere un punto e virgola e aggiungere anche gli altri comandi. Si noti che non è possibile utilizzare le nuove righe poiché vengono interpretate da make come la fine della regola, quindi qualsiasi nuova riga utilizzata per chiarezza deve essere salvata da una barra rovesciata.

Per esempio:

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

Si noti inoltre che il punto e virgola è necessario tra ogni comando anche se si aggiunge una barra rovesciata e una nuova riga. Ciò è dovuto al fatto che l'intera stringa viene analizzata come una singola riga dalla shell. Come notato nei commenti, dovresti usare '&&' per unire i comandi, il che significa che vengono eseguiti solo se il comando precedente ha avuto successo.

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

Ciò è particolarmente cruciale quando si fa un lavoro distruttivo, come la pulizia, poiché altrimenti si distruggono le cose sbagliate, in caso di cdfallimento per qualsiasi motivo.

Un uso comune però è chiamare make nella sottodirectory, che potresti voler esaminare. C'è un'opzione da riga di comando per questo, quindi non devi chiamarti cd, quindi la tua regola sarebbe simile a questa

all:
        $(MAKE) -C some_dir all

che si trasformerà in some_dired eseguirà il Makefilelì dentro con il target "all". Come best practice, usa $(MAKE)invece di chiamare makedirettamente, poiché ti prenderà cura di chiamare la giusta istanza di make (se, ad esempio, usi una versione di make speciale per il tuo ambiente di build), oltre a fornire un comportamento leggermente diverso durante l'esecuzione utilizzando determinati interruttori, come ad esempio -t.

Per la cronaca, fai sempre eco al comando che esegue (a meno che non sia esplicitamente soppresso), anche se non ha output, che è quello che stai vedendo.


8
Beh, non sempre . Per sopprimere l'eco, basta inserire @ all'inizio della riga.
Beta

2
@Beta: bene sì, e un prefisso trattino ignora anche lo stato di errore. Forse mi sono lasciata trasportare un po ', volevo sottolineare il fatto che make fa eco al comando, indipendentemente dal tipo di comando. E in questo caso, è un comando senza output, che rende l'eco ancora più estraneo a qualcuno che non ha familiarità con make.
falstro,

46
Due nits: 1. I comandi dovrebbero davvero essere uniti &&, perché con ;se la directory non esiste e cdfallisce, la shell continuerà a eseguire il resto dei comandi nella directory corrente, il che può causare cose come il misterioso "file not trovato "messaggi per compilazioni, cicli infiniti quando si richiama make o disastro per regole come clean:: cd dir; rm -rf *. 2. Quando si invocano sub-marche, chiamare $(MAKE)invece makeche in modo che le opzioni vengano trasmesse correttamente .
Andrewdotn,

2
@perreal, di solito definisco una regola del modello in questo modo: %-recursive:con il body: @T="$@";$(MAKE) -C some_dir $${T%-*}(di solito ho anche un for-loop, per scorrere su un elenco di sottodir, $${T%-*}è un'espansione bash che rimuove la -recursiveparte del nome target) e quindi definire obiettivi a breve esplicito mano (e .PHONY) per ciascuno, come all: all-recursive, check: check-recursive, clean: clean-recursive.
falstro,

1
@ChristianStewart, vero, come è stato menzionato nei commenti 2 e 3.
falstro

95

A partire da GNU make 3.82 (luglio 2010), è possibile utilizzare il .ONESHELLtarget speciale per eseguire tutte le linee di ricetta in un'unica istanza della shell (grassetto il mio accento):

  • Nuovo target speciale: .ONESHELListruisce fanno invocare una singola istanza del guscio e forniscono con l'intera ricetta , indipendentemente dal numero di linee che contiene. [...]
.ONESHELL: # Only applies to all target
all:
    cd ~/some_dir
    pwd # Prints ~/some_dir if cd succeeded

6
Basta notare che pwdin sé funziona, così come `pwd`(con apici inversi), ma $(shell pwd)e $(PWD)sarà ancora restituire la directory prima di fare il cdcomando, in modo che non è possibile utilizzarli direttamente.
anol

3
Sì perché variabile ed espansione funzione vengono eseguite prima di eseguire i comandi per marca, mentre pwde `pwd`viene eseguito dalla shell stessa.
Chnossos,

2
Non tutti lavorano su makefile legacy, e anche allora questa risposta è sapere che esiste questa possibilità.
Chnossos,

1
Questa può essere un'opzione fastidiosa / pericolosa perché solo l'ultimo comando per una destinazione può causare un errore (eventuali errori di comando precedenti verranno ignorati) e probabilmente nessuno che lavora con te se lo aspetterà.
tobii,

1
Impostato SHELLFLAGSsu -e -ce la shell uscirà al primo comando che fallisce.
Timothy Baldwin,

21

Ecco un trucco carino per gestire le directory e creare. Invece di usare stringhe multilinea o "cd;" su ogni comando, definire una semplice funzione chdir in questo modo:

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

Quindi tutto ciò che devi fare è chiamarlo nella tua regola in questo modo:

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

Puoi anche fare quanto segue:

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

41
"Carina"? Più come una corda sufficiente per spararti al piede.
tripla il

1
Questa directory corrente è quindi impostata per i comandi proprio in quella regola o per tutte le regole eseguite successivamente? Inoltre, alcune varianti di questo lavoro funzioneranno su Windows?
user117529

8
Questo di interruzioni corso di esecuzione parallela ( -jn), che è il punto di make realmente.
bobbogo,

5
Questo è un brutto trucco. Se mai dovessi ricorrere a una cosa del genere, non stai usando Makefile per quello che sono.
Gatopeich,

4
Non sarò in disaccordo sul fatto che si tratti di un brutto hack, questo è certo. Ma dimostra alcune delle cose malvagie che puoi fare.
Joe

9

Cosa vuoi che faccia una volta arrivato? Ogni comando viene eseguito in una subshell, quindi la subshell cambia directory, ma il risultato finale è che il comando successivo è ancora nella directory corrente.

Con GNU make, puoi fare qualcosa del tipo:

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

5
Perché il $(shell ...)quando cd $(BIN); lso cd $(BIN) && ls(come sottolineato da @andrewdotn) sarebbe sufficiente.
Vapace,

1

Per cambiare dir

foo: 
    $(MAKE) -C mydir

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

-6

Come questo:

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

In bocca al lupo!


1
No, questo è sbagliato. In particolare, $(shell cd ....)viene eseguito quando il Makefile viene inizialmente analizzato, non quando viene eseguita questa particolare ricetta.
tripleee

@triplee Non proprio: $(shell)viene espanso solo quando make decide di costruire target . Se make non ha mai bisogno della ricetta, non la espanderà.
Bobbogo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.