402 votes

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

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

all:
     cd some_directory

Mais quand j'ai tapé make Je n'ai vu que 'cd certain_répertoire', comme dans l'exemple suivant echo commandement.

5 votes

Ce que vous voulez faire n'est pas clair, mais, d'après mon expérience avec make Je n'ai jamais voulu changer le répertoire comme ça. Peut-être devriez-vous essayer une autre approche pour votre solution ?

2 votes

C'est une erreur courante des débutants de croire que votre annuaire est important. Pour la plupart des choses, ce n'est pas le cas ; cd dir; cmd file peut presque toujours être exprimée plus utilement comme cmd dir/file .

60 votes

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

708voto

falstro Points 16545

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

Si vous cherchez à effectuer plus de tâches au sein de l'entreprise some_directory vous devez ajouter un point-virgule et ajouter les autres commandes également. Notez que vous ne pouvez pas utiliser de nouvelles lignes car elles sont interprétées par make comme la fin de la règle, donc toutes les nouvelles lignes que vous utilisez pour plus de clarté doivent être échappées par un backslash.

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. Ceci est dû au fait que la chaîne entière est analysée comme une seule ligne par le shell. Comme indiqué dans les commentaires, vous devez utiliser '&&' pour joindre les commandes, ce qui signifie qu'elles ne seront 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

C'est particulièrement important lorsque vous effectuez des travaux destructifs, comme le nettoyage, car vous risquez de détruire les mauvaises choses, si l'on vous demande d'effectuer un nettoyage. cd échouer pour une raison quelconque.

Une utilisation courante consiste cependant à appeler la marque dans le sous-répertoire, ce que vous pourriez vouloir examiner. Il existe une option en ligne de commande pour cela, ce qui vous évite d'avoir à appeler cd vous-même, de sorte que votre règle ressemblerait à ceci

all:
        $(MAKE) -C some_dir all

qui se transformera en some_dir et exécuter le Makefile avec la cible "all". Comme meilleure pratique, utilisez $(MAKE) au lieu d'appeler make directement, car il prendra soin d'appeler la bonne instance de make (si, par exemple, vous utilisez une version spéciale de make pour votre environnement de construction), ainsi que de fournir un comportement légèrement différent lors de l'exécution en utilisant certains commutateurs, tels que -t .

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

8 votes

Eh bien, pas toujours . Pour supprimer l'écho, il suffit de mettre @ au début de la ligne.

4 votes

@Beta : bien oui, et un préfixe tiret ignore également le statut d'erreur. Je me suis peut-être 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 rend l'écho encore plus étrange pour quelqu'un qui n'est pas familier avec make.

56 votes

Deux points : 1. Les commandements devraient vraiment être rejoints par && car avec ; si le répertoire n'existe pas et que le cd échoue, l'interpréteur de commandes continuera à exécuter le reste des commandes dans le répertoire courant, ce qui peut provoquer des choses comme des messages mystérieux "fichier non trouvé" pour les compilations, des boucles infinies lors de l'invocation de make, ou des désastres pour des règles telles que clean:: cd dir; rm -rf * . 2. Lorsque vous invoquez des sub-makes, appelez $(MAKE) au lieu de make de sorte que seront transmises correctement .

123voto

Chnossos Points 5510

À partir de GNU make 3.82 (juillet 2010), vous pouvez utiliser le .ONESHELL cible spéciale pour exécuter toutes les recettes dans une seule instanciation du shell (c'est moi qui souligne) :

  • Nouvelle cible spéciale : .ONESHELL donne des instructions à faire pour invoquer une seule instance du shell et lui fournir la recette complète quel que soit le nombre de lignes qu'il contient.
.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

Notez que cela sera équivalent à exécuter manuellement

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

Les commandes ne sont pas liées à && donc si vous voulez vous arrêter au premier qui échoue, vous devez également ajouter l'option -e à votre .SHELLFLAGS :

.SHELLFLAGS += -e

De même, le -o pipefail pourrait vous intéresser :

Si elle est définie, la valeur de retour d'un pipeline est la valeur de la dernière commande (la plus à droite) à sortir avec un état non nul, ou zéro si toutes les commandes du pipeline sortent avec succès. Cette option est désactivée par défaut.

7 votes

Notez simplement que pwd lui-même fonctionne, ainsi que `pwd` (avec des backticks), mais $(shell pwd) y $(PWD) retournera toujours le répertoire avant de faire le cd vous ne pouvez donc pas les utiliser directement.

3 votes

Oui, car l'expansion des variables et des fonctions se fait avant l'exécution des commandes par make, alors que pwd y `pwd` est exécuté par le shell lui-même.

2 votes

Tout le monde ne travaille pas sur des makefiles hérités, et même dans ce cas, cette réponse est à peu près la même. connaître cette possibilité existe.

20voto

JoeS Points 189

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

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

Il vous suffit alors de l'appeler ainsi dans votre règle :

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

Vous pouvez même faire ce qui suit :

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

49 votes

"Mignon" ? Plutôt assez de corde pour se tirer dans le pied.

1 votes

Ce répertoire courant est-il alors défini pour les commandes de cette seule règle, ou pour toutes les règles exécutées par la suite ? Par ailleurs, une variante de ce système fonctionnera-t-elle sous Windows ?

9 votes

Bien entendu, cela n'est pas compatible avec l'exécution parallèle ( -jn ), ce qui est tout l'intérêt de faire vraiment.

8voto

Nadir SOUALEM Points 2034

Que voulez-vous qu'il fasse une fois arrivé là ? 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 prochaine commande est toujours dans le répertoire actuel.

Avec GNU make, vous pouvez faire quelque chose comme :

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

7 votes

Pourquoi le $(shell ...) cuando cd $(BIN); ls o cd $(BIN) && ls (comme l'a souligné @andrewdotn) serait suffisant.

1voto

jackotonye Points 36

Pour changer de direction

foo: 
    $(MAKE) -C mydir

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X