Existe-t-il un meilleur moyen de générer un script, qui définit env vars, à partir d'un fichier Make?
FLAG ?= 0
ifeq ($(FLAG),0)
export FLAG=1
/bin/myshell -c '<source scripts here> ; $(MAKE) $@'
else
...targets...
endif
Pour répondre à la question telle que posée: vous ne pouvez pas .
Le problème fondamental est qu'un processus enfant ne peut pas modifier l'environnement du parent. Le shell résout ce problème en ne pas forger un nouveau processus lorsque source
'ing, mais en exécutant simplement ces commandes dans la version actuelle du Shell. Cela fonctionne bien, mais make
n'est pas /bin/sh
(ou quel que soit le shell pour lequel votre script est destiné) et ne comprend pas cette langue (à l'exception des bits qu'ils ont en commun).
Chris Dodd et Foo Bah ont abordé une solution de contournement possible. Je vais donc en suggérer une autre (en supposant que vous exécutiez GNU make]): post-traitement du script Shell en texte compatible et en incluant le résultat:
Shell-variable-setter.make: Shell-varaible-setter.sh
postprocess.py @^
# ...
else
include Shell-variable-setter.make
endif
détails en désordre laissés comme un exercice.
Le shell par défaut du Makefile est /bin/sh
qui n'implémente pas source
.
Changer Shell en /bin/bash
permet:
# Makefile
Shell := /bin/bash
rule:
source env.sh && YourCommand
Si votre objectif est simplement de définir des variables d'environnement pour Make, pourquoi ne pas le conserver dans la syntaxe Makefile et utiliser la commande include
?
include other_makefile
Si vous devez appeler le script Shell, capturez le résultat dans une commande Shell
:
JUST_DO_IT=$(Shell source_script)
la commande Shell doit être exécutée avant les cibles. Cependant, cela ne définira pas les variables d'environnement.
Si vous souhaitez définir des variables d'environnement dans la construction, écrivez un script Shell distinct qui identifie vos variables d'environnement et vos appels. Ensuite, dans le fichier Make, demandez aux cibles d’appeler le nouveau script Shell.
Par exemple, si votre makefile d'origine a pour cible a
, alors vous voulez faire quelque chose comme ceci:
# mysetenv.sh
#!/bin/bash
. <script to source>
export FLAG=1
make "$@"
# Makefile
ifeq($(FLAG),0)
export FLAG=1
a:
./mysetenv.sh a
else
a:
.. do it
endif
Avec GNU Make 3.81, je peux obtenir un script Shell à partir de make en utilisant:
rule:
<tab>source source_script.sh && build_files.sh
build_files.sh "récupère" les variables d'environnement exportées par source_script.sh.
Notez que l'utilisation de:
rule:
<tab>source source_script.sh
<tab>build_files.sh
ne fonctionnera pas. Chaque ligne est exécutée dans son propre sous-shell.
Cela fonctionne pour moi. Remplacez env.sh
par le nom du fichier que vous souhaitez créer. Cela fonctionne en recherchant le fichier dans bash et en sortant l'environnement modifié, après l'avoir formaté, dans un fichier appelé makeenv
qui est ensuite obtenu par le fichier makefile.
IGNORE := $(Shell bash -c "source env.sh; env | sed 's/=/:=/' | sed 's/^/export /' > makeenv")
include makeenv
Certaines constructions sont les mêmes dans Shell
et dans GNU Make
.
var=1234
text="Some text"
Vous pouvez modifier votre script Shell pour trouver la source. Ils doivent tous être de simples types name=value
.
C'est à dire,
[script.sh]
. ./vars.sh
[Makefile]
include vars.sh
Ensuite, le script shell et le Makefile peuvent partager la même "source" d'informations. J'ai trouvé cette question parce que je cherchais un manifeste de syntaxe commune pouvant être utilisé dans les scripts Gnu Make et Shell (peu importe le shell).
Éditer: Coquilles et faire comprendre $ {var} . Cela signifie que vous pouvez concaténer, etc., var = "Une chaîne" var = $ {var} "Deuxième chaîne"
J'aime beaucoup la réponse de Foo Bah où make appelle le script et le script rappelle. Pour développer cette réponse, j'ai fait ceci:
# Makefile
.DEFAULT_GOAL := all
ifndef SOME_DIR
%:
<tab>. ./setenv.sh $(MAKE) $@
else
all:
<tab>...
clean:
<tab>...
endif
-
# setenv.sh
export SOME_DIR=$PWD/path/to/some/dir
if [ -n "$1" ]; then
# The first argument is set, call back into make.
$1 $2
fi
Cela présente l'avantage supplémentaire d'utiliser $ (MAKE) au cas où quelqu'un utiliserait un programme de création unique et traiterait également toute règle spécifiée sur la ligne de commande, sans devoir dupliquer le nom de chaque règle dans le cas où SOME_DIR n'est pas défini .
Ma solution à ceci: (en supposant que vous avez bash
, la syntaxe pour $@
est différente pour tcsh
par exemple)
Avoir un script sourceThenExec.sh
, en tant que tel:
#!/bin/bash
source whatever.sh
$@
Ensuite, dans votre makefile, faites précéder vos cibles avec bash sourceThenExec.sh
, par exemple:
ExampleTarget:
bash sourceThenExec.sh gcc ExampleTarget.C
Vous pouvez bien sûr mettre quelque chose comme STE=bash sourceThenExec.sh
en haut de votre makefile et raccourcir ceci:
ExampleTarget:
$(STE) gcc ExampleTarget.C
Tout cela fonctionne car sourceThenExec.sh
ouvre un sous-shell, mais les commandes sont exécutées dans le même sous-shell.
L'inconvénient de cette méthode est que le fichier est recherché pour chaque cible, ce qui peut être indésirable.
Un autre moyen possible serait de créer un script sh, par exemple run.sh
, de rechercher les scripts requis et d'appeler make dans le script.
#!/bin/sh
source script1
source script2 and so on
make
Selon votre version de Make et le shell englobant, vous pouvez implémenter une solution Nice via eval
, cat
et le chaînage d'appels avec &&
:
ENVFILE=envfile
source-via-eval:
@echo "FOO: $${FOO}"
@echo "FOO=AMAZING!" > $(ENVFILE)
@eval `cat $(ENVFILE)` && echo "FOO: $${FOO}"
Et un test rapide:
> make source-via-eval
FOO:
FOO: AMAZING!
Si vous n'avez besoin que de quelques variables connues, l'exportation dans le fichier Make est un exemple, voici un exemple de ce que j'utilise.
$ grep ID /etc/os-release
ID=ubuntu
ID_LIKE=debian
$ cat Makefile
default: help rule/setup/lsb
source?=.
help:
-${MAKE} --version | head -n1
rule/setup/%:
echo ID=${@F}
rule/setup/lsb: /etc/os-release
${source} $< && export ID && ${MAKE} rule/setup/$${ID}
$ make
make --version | head -n1
GNU Make 3.81
. /etc/os-release && export ID && make rule/setup/${ID}
make[1]: Entering directory `/tmp'
echo ID=ubuntu
ID=ubuntu
Si vous souhaitez obtenir les variables dans environment , afin qu'elles soient transmises aux processus enfants, vous pouvez utiliser les codes set -a
et set +a
de bash. Le premier signifie "Lorsque je définis une variable, définissez également la variable d'environnement correspondante". Donc ça marche pour moi:
check:
bash -c "set -a && source .env.test && set +a && cargo test"
Cela passera tout dans .env.test
à cargo test
en tant que variables d'environnement.
Notez que cela vous permettra de transmettre un environnement à des sous-commandes, mais ne vous laissera pas définir de variables Makefile (qui sont de toute façon différentes). Si vous avez besoin de ce dernier, vous devriez essayer l'une des autres suggestions ici.