Définir la variable make au moment de l'exécution de la règle

209

Dans mon GNUmakefile, je voudrais avoir une règle qui utilise un répertoire temporaire. Par exemple:

out.tar: TMP := $(shell mktemp -d)
        echo hi $(TMP)/hi.txt
        tar -C $(TMP) cf $@ .
        rm -rf $(TMP)

Comme écrit, la règle ci-dessus crée le répertoire temporaire au moment où la règle est analysée . Cela signifie que, même si je ne distingue pas tout le temps, de nombreux répertoires temporaires sont créés. Je voudrais éviter que mon / tmp soit jonché de répertoires temporaires inutilisés.

Existe-t-il un moyen de faire en sorte que la variable soit définie uniquement lorsque la règle est déclenchée, par opposition à chaque fois qu'elle est définie?

Ma pensée principale est de vider le mktemp et le tar dans un script shell mais cela semble quelque peu disgracieux.

Emil Sit
la source

Réponses:

322

Dans votre exemple, la TMPvariable est définie (et le répertoire temporaire créé) chaque fois que les règles de out.tarsont évaluées. Afin de créer le répertoire uniquement lorsqu'il out.tarest réellement déclenché, vous devez déplacer la création du répertoire vers le bas dans les étapes:

out.tar : 
    $(eval TMP := $(shell mktemp -d))
    @echo hi $(TMP)/hi.txt
    tar -C $(TMP) cf $@ .
    rm -rf $(TMP)

La fonction eval évalue une chaîne comme si elle avait été tapée manuellement dans le makefile. Dans ce cas, il définit la TMPvariable sur le résultat de l' shellappel de fonction.

modifier (en réponse aux commentaires):

Pour créer une variable unique, vous pouvez procéder comme suit:

out.tar : 
    $(eval $@_TMP := $(shell mktemp -d))
    @echo hi $($@_TMP)/hi.txt
    tar -C $($@_TMP) cf $@ .
    rm -rf $($@_TMP)

Cela ajouterait le nom de la cible (out.tar, dans ce cas) à la variable, produisant une variable avec le nom out.tar_TMP. J'espère que cela suffit pour prévenir les conflits.

e.James
la source
2
Cool quelques clarifications ... cela ne permettra pas à TMP d'atteindre cet objectif, n'est-ce pas? Donc, s'il y a d'autres règles qui ont leur propre utilisation de $ (TMP) (éventuellement en parallèle avec -j), il peut y avoir des conflits? De plus, le @echo est-il même nécessaire? Il semble que vous puissiez simplement le laisser de côté.
Emil Sit
3
Semble faire l'affaire (bien qu'un peu opaque pour le gourou non-make moyen :-) Merci!
Emil Sit
29
Soyez prudent avec cette solution! $(eval $@_TMP := $(shell mktemp -d))se produira lorsque le Makefile est évalué pour la première fois et non dans l'ordre des procédures de règles. En d'autres termes, $(eval ...)cela se produit plus tôt que vous ne le pensez. Bien que cela puisse convenir à cet exemple, cette méthode entraînera des problèmes pour certaines opérations séquentielles.
JamesThomasMoon1979
1
@ JamesThomasMoon1979 savez-vous comment surmonter ces limitations, par exemple évaluer certaines variables qui devraient être le résultat d'étapes exécutées plus tôt dans la règle?
Vadim Kotov
2
Répondre à ma propre question: la solution de contournement pour moi consistait à créer des fichiers lors de l'exécution de la première règle et à essayer de les évaluer lors de la première étape de la deuxième règle.
Vadim Kotov
63

Une manière relativement simple de procéder consiste à écrire la séquence entière sous forme de script shell.

out.tar:
   set -e ;\
   TMP=$$(mktemp -d) ;\
   echo hi $$TMP/hi.txt ;\
   tar -C $$TMP cf $@ . ;\
   rm -rf $$TMP ;\

J'ai regroupé quelques conseils connexes ici: https://stackoverflow.com/a/29085684/86967

nobar
la source
3
Ceci est sans aucun doute le plus simple et donc meilleure réponse (éviter @et evalet faire le même travail). Notez que dans votre sortie, vous voyez $TMP(par exemple tar -C $TMP ...) bien que la valeur soit correctement transmise à la commande.
Karl Richter
Voici comment cela se fait normalement; J'espère que les gens verront cette réponse car dans la pratique, personne ne passe par tous les efforts de l'accepté.
pmos
Et si vous utilisez des commandes spécifiques au shell? Dans ce cas, les commandes shell doivent être dans le même langage shell que celui qui appelle make, non? J'avais vu un makefile avec shebang.
ptitpion
@ptitpion: Vous pouvez également souhaiter SHELL := /bin/bashdans votre makefile activer des fonctionnalités spécifiques à BASH.
nobar
31

Une autre possibilité consiste à utiliser des lignes distinctes pour configurer les variables Make lorsqu'une règle se déclenche.

Par exemple, voici un makefile avec deux règles. Si une règle se déclenche, elle crée un répertoire temporaire et définit TMP sur le nom du répertoire temporaire.

PHONY = ruleA ruleB display

all: ruleA

ruleA: TMP = $(shell mktemp -d testruleA_XXXX)
ruleA: display

ruleB: TMP = $(shell mktemp -d testruleB_XXXX)
ruleB: display

display:
    echo ${TMP}

L'exécution du code produit le résultat attendu:

$ ls
Makefile
$ make ruleB
echo testruleB_Y4Ow
testruleB_Y4Ow
$ ls
Makefile  testruleB_Y4Ow
user3124434
la source
Pour rappel, GNU make a une syntaxe pour les prérequis d'ordre uniquement qui peuvent être nécessaires pour compléter cette approche.
anol
11
Soyez prudent avec cette solution! ruleA: TMP = $(shell mktemp -d testruleA_XXXX)et ruleB: TMP = $(shell mktemp -d testruleB_XXXX)se produira lors de la première évaluation du Makefile. En d'autres termes, ruleA: TMP = $(shell ...cela se produit plus tôt que vous ne le pensez. Bien que cela puisse fonctionner dans ce cas particulier, cette méthode entraînera des problèmes pour certaines opérations séquentielles.
JamesThomasMoon1979
1

Je n'aime pas les réponses "Ne pas", mais ... non.

makeLes variables de sont globales et sont censées être évaluées pendant la phase "d'analyse" du makefile, pas pendant la phase d'exécution.

Dans ce cas, tant que la variable est locale à une seule cible, suivez la réponse de @ nobar et faites-en une variable shell.

Les variables spécifiques à la cible sont également considérées comme nuisibles par d'autres implémentations de marque: kati , Mozilla pymake . Grâce à eux, une cible peut être construite différemment selon qu'elle est construite de manière autonome ou en tant que dépendance d'une cible parent avec une variable spécifique à la cible. Et vous ne saurez pas de quelle façon c'était , parce que vous ne savez pas ce qui est déjà construit.

Victor Sergienko
la source