Comment écrire la commande 'cd' dans un makefile?


354

Par exemple, j'ai quelque chose comme ça dans mon makefile:

all:
     cd some_directory

Mais quand j'ai tapé, makeje n'ai vu que 'cd some_directory', comme dans la echocommande.


5
On ne sait pas trop ce que vous voulez faire, mais, d'après mon expérience avec make, je n'ai jamais voulu changer le répertoire comme ça. Vous devriez peut-être essayer une autre approche de votre solution?
P Shved

2
C'est une erreur courante pour les débutants de croire que votre répertoire est important. Pour la plupart des choses, ce n'est pas le cas; cd dir; cmd filepeut presque toujours être exprimé plus utilement par cmd dir/file.
tripleee

34
C'est une erreur courante pour les débutants de croire que votre répertoire de travail actuel est sans conséquence. De nombreux programmes, en particulier les scripts shell, sont écrits avec une valeur spécifique .à l'esprit. Il est vrai que la plupart des outils sont conçus de telle manière que vous n'avez pas besoin de changer votre pwd pour cela. Mais ce n'est pas toujours vrai, et je ne pense pas que ce soit une bonne idée d'appeler cela une "erreur" pour croire que votre répertoire peut être important.
Evelyn Kokemoor

Astuce: si votre commande cd dit « Aucun fichier ou répertoire », même si le répertoire (relatif) ne exist, vérifiez que votre variable d'environnement CDPATH est vide ou comprend « ». Make exécute les commandes avec "sh", qui ne trouvera un chemin relatif via CDPATH que s'il est défini. Cela contraste avec bash, qui va essayer. avant de consulter CDPATH.
Denis Howe

Réponses:


621

Il exécute en fait la commande, changeant le répertoire en some_directory, cependant, cela est effectué dans un shell de sous-processus, et n'affecte ni la marque ni le shell à partir duquel vous travaillez.

Si vous cherchez à effectuer plus de tâches à l'intérieur some_directory, vous devez ajouter un point-virgule et ajouter également les autres commandes. Notez que vous ne pouvez pas utiliser de sauts de ligne car ils sont interprétés par make comme la fin de la règle, donc tout saut de ligne que vous utilisez pour plus de clarté doit être échappé par une barre oblique inverse.

Par exemple:

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

Notez également que le point-virgule est nécessaire entre chaque commande même si vous ajoutez une barre oblique inverse et une nouvelle ligne. Cela est dû au fait que la chaîne entière est analysée en une seule ligne par le shell. Comme indiqué dans les commentaires, vous devez utiliser '&&' pour joindre les commandes, ce qui signifie qu'elles ne sont exécutées que si la commande précédente a réussi.

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

Ceci est particulièrement crucial lorsque vous effectuez des travaux destructeurs, tels que le nettoyage, car vous détruiriez autrement les mauvaises choses, en cas d' cdéchec pour une raison quelconque.

Une utilisation courante consiste cependant à appeler make dans le sous-répertoire, que vous voudrez peut-être examiner. Il y a une option en ligne de commande pour que vous n'ayez pas à cdvous appeler , donc votre règle ressemblerait à ceci

all:
        $(MAKE) -C some_dir all

qui se transformera en some_diret exécutera le Makefilededans avec la cible "all". En tant que meilleure pratique, utilisez $(MAKE)plutôt que d'appeler makedirectement, car cela prendra soin d'appeler la bonne instance de création (si vous, par exemple, utilisez une version spéciale de création pour votre environnement de génération), ainsi qu'un comportement légèrement différent lors de l'exécution. en utilisant certains commutateurs, tels que -t.

Pour mémoire, faites toujours en écho la commande qu'elle exécute (sauf si elle est explicitement supprimée), même si elle n'a pas de sortie, ce que vous voyez.


8
Enfin, pas toujours . Pour supprimer l'écho, il suffit de mettre @ au début de la ligne.
Bêta

2
@Beta: eh bien oui, et un préfixe de tiret ignore également le statut d'erreur. Peut-être que je me suis un peu emporté, je voulais souligner le fait que make fait écho à la commande, quel que soit le type de commande. Et dans ce cas, c'est une commande sans sortie, ce qui fait que l'écho semble encore plus étrange à quelqu'un qui n'est pas familier avec make.
falstro

46
Deux nits: 1. Les commandes doivent vraiment être jointes par &&, car avec ;si le répertoire n'existe pas et cdéchoue, le shell continuera à exécuter le reste des commandes dans le répertoire actuel, ce qui peut provoquer des choses comme un mystérieux «fichier non messages trouvés pour les compilations, boucles infinies lors de l'appel de make ou catastrophe pour les règles comme clean:: cd dir; rm -rf *. 2. Lorsque vous appelez des sous-marques, appelez $(MAKE)au lieu de makepour que les options soient transmises correctement .
andrewdotn

2
@perreal, je définis généralement une règle de modèle comme ceci: %-recursive:avec le corps: @T="$@";$(MAKE) -C some_dir $${T%-*}(j'ai généralement une boucle for aussi, pour parcourir une liste de sous-répertoires, $${T%-*}c'est une extension bash qui supprime la -recursivepartie du nom cible), puis définir des objectifs à court main explicite (et .PHONY) pour chacun, comme all: all-recursive, check: check-recursive, clean: clean-recursive.
falstro

1
@ChristianStewart, vrai, comme cela a été mentionné dans les commentaires 2 et 3.
falstro

95

À partir de GNU make 3.82 (juillet 2010), vous pouvez utiliser la .ONESHELLcible spéciale pour exécuter toutes les lignes de recette en une seule instanciation du shell (gras en gras):

  • Nouvelle cible spéciale: .ONESHELLdemande à make d'appeler une seule instance du shell et de lui fournir la recette entière , quel que soit le nombre de lignes qu'il contient. [...]
.ONESHELL: # Only applies to all target
all:
    cd ~/some_dir
    pwd # Prints ~/some_dir if cd succeeded

6
Il suffit de noter que pwdlui - même fonctionne, ainsi que `pwd`(avec des accents graves), mais $(shell pwd)et $(PWD)retournera encore le répertoire avant de faire la cdcommande, vous ne pouvez pas les utiliser directement.
anol

3
Oui car l'expansion des variables et des fonctions se fait avant d'exécuter les commandes par make, tandis que pwdet `pwd`est exécutée par le shell lui-même.
Chnossos

2
Tout le monde ne travaille pas sur les makefiles hérités, et même alors, cette réponse consiste à savoir que cette possibilité existe.
Chnossos

1
Cela peut être une option ennuyeuse / dangereuse car seule la dernière commande pour une cible peut provoquer un échec (tout échec de commande antérieur sera ignoré), et probablement personne ne travaillant avec vous ne s’y attendra.
tobii

1
Définissez SHELLFLAGSsur -e -cet le shell se fermera à la première commande qui échoue.
Timothy Baldwin

21

Voici une astuce mignonne pour gérer les répertoires et les créer. Au lieu d'utiliser des chaînes multilignes, ou "cd;" sur chaque commande, définissez une fonction chdir simple comme suit:

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

Ensuite, tout ce que vous avez à faire est de l'appeler dans votre règle comme suit:

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

Vous pouvez même effectuer les opérations suivantes:

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

41
"Mignonne"? Plus comme assez de corde pour vous tirer dans le pied.
tripleee

1
Ce répertoire actuel est-il alors défini pour les commandes uniquement dans cette règle, ou pour toutes les règles exécutées ultérieurement? De plus, certaines variantes de ce fonctionnement fonctionneront-elles sous Windows?
user117529

8
Bien sûr, cela rompt pour l'exécution parallèle ( -jn), ce qui est vraiment le but de make .
bobbogo

5
C'est un mauvais hack. Si jamais vous devez recourir à une telle chose, vous n'utilisez pas les Makefiles pour ce qu'ils sont.
gatopeich

4
Je ne dirai pas que c'est un mauvais hack c'est sûr. Mais démontre certaines des choses méchantes que vous pouvez faire.
JoeS

9

Que voulez-vous qu'il fasse une fois sur place? Chaque commande est exécutée dans un sous-shell, donc le sous-shell change de répertoire, mais le résultat final est que la commande suivante est toujours dans le répertoire actuel.

Avec GNU make, vous pouvez faire quelque chose comme:

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

5
Pourquoi le $(shell ...)quand cd $(BIN); lsou cd $(BIN) && ls(comme l'a souligné @andrewdotn) serait suffisant.
vapace

1

Pour changer de dir

foo: 
    $(MAKE) -C mydir

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

-6

Comme ça:

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

Bonne chance!


1
Non, c'est faux. Plus précisément, le $(shell cd ....)est exécuté lorsque le Makefile est initialement analysé, pas lorsque cette recette particulière est exécutée.
tripleee

@triplee Pas tout à fait - le $(shell)n'est développé que lorsque make décide de construire la cible . Si make n'a jamais besoin de la recette, il ne l'étendra pas.
bobbogo
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.