Passer des variables supplémentaires depuis la ligne de commande pour faire

615

Puis-je transmettre des variables à un Makefile GNU comme arguments de ligne de commande? En d'autres termes, je veux passer quelques arguments qui deviendront éventuellement des variables dans le Makefile.

Pablo
la source

Réponses:

754

Vous avez plusieurs options pour configurer des variables depuis l'extérieur de votre makefile:

  • De l'environnement - chaque variable d'environnement est transformée en une variable makefile avec le même nom et la même valeur.

    Vous pouvez également activer l' -eoption (aka --environments-override), et vos variables d'environnement remplaceront les affectations faites dans makefile (sauf si ces affectations elles-mêmes utilisent la overridedirective . Cependant, ce n'est pas recommandé, et il est beaucoup mieux et flexible d'utiliser l' ?=affectation (la variable conditionnelle) opérateur d'affectation, il n'a d'effet que si la variable n'est pas encore définie):

    FOO?=default_value_if_not_set_in_environment
    

    Notez que certaines variables ne sont pas héritées de l'environnement:

    • MAKE est obtenu à partir du nom du script
    • SHELLest soit défini dans un makefile, soit par défaut /bin/sh(justification: les commandes sont spécifiées dans le makefile, et elles sont spécifiques au shell).
  • Depuis la ligne de commande - makepeut accepter des affectations variables dans le cadre de sa ligne de commande, mêlées à des cibles:

    make target FOO=bar
    

    Mais alors toutes les affectations à la FOOvariable dans le makefile seront ignorées à moins que vous n'utilisiez la overridedirective dans l'affectation. (L'effet est le même qu'avec l' -eoption pour les variables d'environnement).

  • Exportation à partir du Make parent - si vous appelez Make à partir d'un Makefile, vous ne devriez généralement pas écrire explicitement des affectations de variables comme ceci:

    # Don't do this!
    target:
            $(MAKE) -C target CC=$(CC) CFLAGS=$(CFLAGS)
    

    Au lieu de cela, une meilleure solution pourrait être d'exporter ces variables. L'exportation d'une variable la fait entrer dans l'environnement de chaque appel de shell, et les appels à partir de ces commandes choisissent ces variables d'environnement comme spécifié ci-dessus.

    # Do like this
    CFLAGS=-g
    export CFLAGS
    target:
            $(MAKE) -C target
    

    Vous pouvez également exporter toutes les variables en utilisant exportsans arguments.

P Shved
la source
11
passer de la ligne de commande make A='"as df"'
quelque chose
4
On dirait que vous demandez des ennuis si vous vous souciez des variables d'environnement. D'une part, c'est un cauchemar de débogage s'il fonctionne à la place A et non à la place B, simplement parce qu'ils ont des environnements différents.
James Moore
12
Sur la base de l'expérience, l'exportation de trucs comme CFLAGS est une recette de cauchemar pour les grands projets. Les grands projets ont souvent des bibliothèques tierces qui ne compilent qu'avec un ensemble d'indicateurs donné (que personne ne prend la peine de réparer). Si vous exportez CFLAGS, les CFLAGS de votre projet finissent par remplacer les bibliothèques tierces et déclenchent des erreurs de compilation. Une autre façon pourrait être de la définir export PROJECT_MAKE_ARGS = CC=$(CC) CFLAGS=$(CFLAGS)et de la transmettre en tant que make -C folder $(PROJECT_MAKE_FLAGS). S'il y a un moyen de dire au makefile de la bibliothèque d'ignorer l'environnement, ce serait idéal (à l'opposé de -e).
RD
24
AVERTISSEMENT: dans la section "Exportation depuis la marque parent" ci-dessus, "Ne faites pas ça!" est trompeuse . La transmission de variables sur la ligne de commande remplace les affectations dans le sous-makefile mais les variables exportées ne remplacent pas les affectations dans le sous-makefile. Ces deux méthodes pour passer des variables à un sous-makefile ne sont pas équivalentes et ne doivent pas être confondues.
Jonathan Ben-Avraham
4
Toute différence ? make target FOO=bar make FOO=bar target?
gfan
213

La manière la plus simple est:

make foo=bar target

Ensuite, dans votre makefile, vous pouvez vous référer à $(foo) . Notez que cela ne se propagera pas automatiquement aux sous-marques.

Si vous utilisez des sous-marques, consultez cet article: Communiquer des variables à une sous-marque

Mark Byers
la source
2
par sous-marques, vous entendez les makefiles includeddans le makefile principal?
Pablo
2
@Michael: Cela signifie appeler à nouveau make depuis l'intérieur du makefile. J'ai mis à jour ma réponse car vous semblez intéressé par ce détail.
Mark Byers
2
"Notez que cela ne se propagera pas automatiquement aux sous-marques." Faux! "Par défaut, seules les variables provenant de l'environnement ou de la ligne de commande sont transmises à des appels récursifs. Vous pouvez utiliser la directive d'exportation pour transmettre d'autres variables." gnu.org/software/make/manual/html_node/…
ThomasMcLeod
82

Disons que vous avez un makefile comme celui-ci:

action:
    echo argument is $(argument)

Vous l'appeleriez alors make action argument=something

nc3b
la source
5
de sorte que la cible et les arguments peuvent être échangés en termes de position?
Pablo
4
@Michael: Oui (voir la réponse de Mark Byers)
nc3b
25

Du manuel :

Les variables dans make peuvent provenir de l'environnement dans lequel make est exécuté. Chaque variable d'environnement que make voit au démarrage est transformée en variable make avec le même nom et la même valeur. Cependant, une affectation explicite dans le makefile, ou avec un argument de commande, remplace l'environnement.

Vous pouvez donc faire (depuis bash):

FOOBAR=1 make

résultant en une variable FOOBARdans votre Makefile.

Thomas
la source
2
L'autre voie est en effet meilleure dans presque tous les cas. Je vais laisser ceci ici pour être complet.
Thomas
C'est la seule réponse qui montre l'affectation des variables avant la commande make sur la même ligne - il vaut la peine de savoir que ce n'est pas une erreur de syntaxe.
M_M
7

Il y a une autre option non citée ici qui est incluse dans le livre GNU Make de Stallman et McGrath (voir http://www.chemie.fu-berlin.de/chemnet/use/info/make/make_7.html ). Il fournit l'exemple:

archive.a: ...
ifneq (,$(findstring t,$(MAKEFLAGS)))
        +touch archive.a
        +ranlib -t archive.a
else
        ranlib archive.a
endif

Il s'agit de vérifier si un paramètre donné apparaît dans MAKEFLAGS. Par exemple .. supposons que vous étudiez les threads en c ++ 11 et que vous avez divisé votre étude sur plusieurs fichiers ( class01, ..., classNM) et que vous souhaitez: compiler puis tout et exécuter individuellement ou compiler un à la fois. et exécutez-le si un indicateur est spécifié ( -r, par exemple). Ainsi, vous pouvez proposer les éléments suivants Makefile:

CXX=clang++-3.5
CXXFLAGS = -Wall -Werror -std=c++11
LDLIBS = -lpthread

SOURCES = class01 class02 class03

%: %.cxx
    $(CXX) $(CXXFLAGS) -o [email protected] $^ $(LDLIBS)
ifneq (,$(findstring r,  $(MAKEFLAGS)))
    ./[email protected]
endif

all: $(SOURCES)

.PHONY: clean

clean:
    find . -name "*.out" -delete

Ayant cela, vous auriez:

  • construire et exécuter un fichier w / make -r class02;
  • construire tout w / makeou make all;
  • construire et exécuter tout w / make -r(supposons que tous contiennent un certain type de substance assert et que vous voulez juste tous les tester)
Ciro Costa
la source
6

il semble

commande args écraser la variable d'environnement

Makefile

send:
    echo $(MESSAGE1) $(MESSAGE2)

Exécuter l'exemple

$ MESSAGE1=YES MESSAGE2=NG  make send MESSAGE2=OK
echo YES OK
YES OK
YumaInaura
la source
5

Si vous créez un fichier appelé Makefile et ajoutez une variable comme celle-ci $ (unittest), vous pourrez utiliser cette variable dans le Makefile même avec des caractères génériques

exemple :

make unittest=*

J'utilise BOOST_TEST et en donnant un caractère générique au paramètre --run_test = $ (unittest), je pourrai utiliser une expression régulière pour filtrer le test que je veux que mon Makefile exécute

serup
la source
4
export ROOT_DIR=<path/value>

Utilisez ensuite la variable, $(ROOT_DIR)dans le Makefile.

parasir
la source