web-dev-qa-db-fra.com

Passer des arguments à "make run"

J'utilise des Makefiles.

J'ai une cible appelée run qui exécute la cible de construction. Simplifié, il ressemble à ceci:

prog: ....
  ...

run: prog
  ./prog

Asseyez-vous. Je sais que c'est ingénieux, mais pas besoin d'une ovation debout.

Ma question est la suivante: existe-t-il un moyen de faire passer des arguments? Pour que

make run asdf --> ./prog asdf
make run the dog kicked the cat --> ./prog the dog kicked the cat

Merci!

318
anon

Je ne sais pas comment faire exactement ce que vous voulez, mais une solution de contournement pourrait être:

run: ./prog
    ./prog ${ARGS}

Ensuite:

make ARGS="asdf" run
224
Jakob Borg

Cette question a presque trois ans, mais de toute façon ...

Si vous utilisez GNU make, c'est facile à faire. Le seul problème est que make interprétera comme des cibles des arguments sans option dans la ligne de commande. La solution consiste à les transformer en cibles à ne rien faire, pour que make ne se plaint pas:

# If the first argument is "run"...
ifeq (run,$(firstword $(MAKECMDGOALS)))
  # use the rest as arguments for "run"
  RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
  # ...and turn them into do-nothing targets
  $(eval $(RUN_ARGS):;@:)
endif

prog: # ...
    # ...

.PHONY: run
run : prog
    @echo prog $(RUN_ARGS)

Courir cela donne:

$ make run foo bar baz
prog foo bar baz
174
Idelic

pour standard vous pouvez passer des arguments en définissant des macros comme celle-ci

make run arg1=asdf

puis les utiliser comme ça

run: ./prog $(arg1)
   etc

Références pour make Microsoft's NMake

44
John Knoeller

Vous pouvez passer la variable au Makefile comme ci-dessous:

_run:
    @echo ./prog $$FOO
_

Usage:

_$ make run FOO="the dog kicked the cat"
./prog the dog kicked the cat
_

ou:

_$ FOO="the dog kicked the cat" make run
./prog the dog kicked the cat
_

Vous pouvez également utiliser la solution fournie par Beta :

_run:
    @echo ./prog $(filter-out $@,$(MAKECMDGOALS))
%:
    @:
_

%: - règle qui correspond à n'importe quel nom de tâche; @: - recette vide = ne rien faire

Usage:

_$ make run the dog kicked the cat
./prog the dog kicked the cat
_
29
kenorb

TL; DR n'essayez pas de faire cela

$ make run arg

créez à la place un script:

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

et faire ceci:

$ ./buildandrunprog.sh arg

répondre à la question posée:

vous pouvez utiliser une variable dans la recette

run: prog
    ./prog $(var)

puis passez une assignation de variable comme argument pour faire

$ make run var=arg

ceci exécutera ./prog arg.

mais méfiez-vous des pièges. Je détaillerai les pièges de cette méthode et d’autres méthodes plus loin.


répondre à l'intention présumée derrière la question:

l'hypothèse: vous voulez exécuter prog avec quelques arguments, mais le reconstruire avant d'exécuter si nécessaire.

la réponse: créer un script qui reconstruit si nécessaire puis lance prog avec args

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

ce script rend l'intention très claire. il utilise make pour faire ce pour quoi il est bon: construire. il utilise un script shell pour faire ce qui est bon pour: le traitement par lots.

de plus, la syntaxe d'appel est maintenant pratiquement identique:

$ ./buildandrunprog.sh foo "bar baz"

comparer aux:

$ ./prog foo "bar baz"

de plus, vous pouvez faire tout ce dont vous avez besoin avec toute la flexibilité et l'expressivité d'un script Shell sans toutes les mises en garde d'un fichier makefile.


contexte:

make n'est pas conçu pour exécuter une cible et transmettre des arguments à cette cible. tous les arguments de la ligne de commande sont interprétés comme un objectif (a.k.a. target), comme une option ou comme une affectation de variable.

alors si vous lancez ceci:

$ make run foo bar --wat var=arg

make interprétera run, foo et bar comme des objectifs (cibles) à mettre à jour en fonction de leurs recettes. --wat comme option pour make. et var=arg en tant qu'affectation de variable.

pour plus de détails, voir: https://www.gnu.org/software/make/manual/html_node/Goals.html#Goals

pour la terminologie, voir: https://www.gnu.org/software/make/manual/html_node/Rule-Introduction.html#Rule-Introduction


sur la méthode d'attribution de variable et pourquoi je recommande contre elle

$ make run var=arg

et la variable dans la recette

run: prog
    ./prog $(var)

c'est le moyen le plus "correct" et le plus simple de passer des arguments à une recette. mais bien qu'il puisse être utilisé pour exécuter un programme avec des arguments, il n'est certainement pas conçu pour être utilisé de cette façon. voir https://www.gnu.org/software/make/manual/html_node/Overriding.html#Overriding

à mon avis, cela présente un inconvénient majeur: vous voulez exécuter prog avec l'argument arg. mais au lieu d'écrire:

$ ./prog arg

tu écris:

$ make run var=arg

cela devient encore plus gênant lorsque vous essayez de passer plusieurs arguments ou des arguments contenant des espaces:

$ make run var="foo bar\ baz"
./prog foo bar\ baz
argcount: 2
arg: foo
arg: bar baz

comparer aux:

$ ./prog foo "bar baz"
argcount: 2
arg: foo
arg: bar baz

pour mémoire, voici à quoi ressemble ma prog:

#! /bin/sh
echo "argcount: $#"
for arg in "$@"; do
  echo "arg: $arg"
done

notez que lorsque vous mettez $(var) entre guillemets dans le fichier makefile:

run: prog
    ./prog "$(var)"

alors prog n'aura toujours qu'un seul argument:

$ make run var="foo bar\ baz"
./prog "foo bar\ baz"
argcount: 1
arg: foo bar\ baz

tout cela est pourquoi je recommande contre cette route.


pour compléter, voici quelques autres méthodes pour "passer des arguments à exécuter".

méthode 1:

run: prog
    ./prog $(filter-out $@, $(MAKECMDGOALS))

%:
    @true

très courte explication: filtrez l'objectif actuel de la liste des objectifs. crée une capture de tous les objectifs (%) qui ne fait rien pour ignorer silencieusement les autres objectifs.

méthode 2:

ifeq (run, $(firstword $(MAKECMDGOALS)))
  runargs := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
  $(eval $(runargs):;@true)
endif

run:
    ./prog $(runargs)

très courte explication: si la cible est run, supprimez le premier objectif et créez des cibles ne rien faire pour les objectifs restants à l'aide de eval.

pour une explication plus approfondie, étudiez le manuel de make: https://www.gnu.org/software/make/manual/html_node/index.html

problèmes de méthode 1:

  • les arguments commençant par un tiret seront interprétés par make et ne seront pas passés comme objectif.

    $ make run --foo --bar
    

    workaround

    $ make run -- --foo --bar
    
  • si un argument est run (égal à la cible), il sera également supprimé

    $ make run foo bar run
    

    exécutera ./prog foo bar au lieu de ./prog foo bar run

    solution de contournement possible avec la méthode 2

  • si un argument est une cible légitime, il sera également exécuté.

    $ make run foo bar clean
    

    exécutera ./prog foo bar clean mais également la recette de la cible clean (en supposant qu'elle existe).

    solution de contournement possible avec la méthode 2

  • il est difficile de passer des arguments avec des espaces

    $ make run foo "bar\ baz"
    

    pas de solution de contournement

  • lorsque vous saisissez une cible légitime avec une erreur de frappe, elle est ignorée en raison de la capture de la cible.

    $ make celan
    

    ignorerai silencieusement celan.

    la solution consiste à tout rendre verbeux. alors vous voyez ce qui se passe. mais cela crée beaucoup de bruit pour la sortie légitime.

problèmes de la méthode 2:

  • si un argument a le même nom qu'une cible existante, alors make affichera un avertissement indiquant qu'il est en train d'être écrasé.

    pas de solution de contournement que je connaisse

  • passer des arguments avec de l'espace est toujours délicat.

    pas de solution de contournement

  • arguments avec espaces: eval essayant de créer ne cible rien.

    solution de contournement: créer la capture globale cible tout en ne faisant rien comme ci-dessus. avec le problème ci-dessus, il ignorera à nouveau silencieusement les cibles légitimes mal typées.

  • il utilise eval pour modifier le fichier make au moment de l'exécution. combien pire pouvez-vous aller en termes de lisibilité et de débogage et le principe du moindre étonnement .

    contournement: ne faites pas ceci! 1 Au lieu de cela, écrivez un script Shell qui exécute make, puis exécute prog.

j'ai seulement testé en utilisant GNU make. d'autres marques peuvent avoir un comportement différent.


TL; DR n'essayez pas de faire cela

$ make run arg

créez à la place un script:

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

et faire ceci:

$ ./buildandrunprog.sh arg
14
lesmana

Voici une autre solution qui pourrait aider avec certains de ces cas d'utilisation:

test-%:
    $(PYTHON) run-tests.py $@

En d'autres termes, choisissez un préfixe (test- dans ce cas), puis transmettez le nom de la cible directement au programme/programme d'exécution. Je suppose que cela est surtout utile si un script de coureur impliqué peut décompresser le nom de la cible en un élément utile pour le programme sous-jacent.

11
djc

Non. Consultation de la syntaxe à partir de la page de manuel de GNU make

make [-f makefile] [options] ... [cibles] ...

vous pouvez spécifier plusieurs cibles, d’où "non" (du moins non de la manière exacte que vous avez spécifiée).

9
ziya

Vous pouvez extraire explicitement chaque énième argument de la ligne de commande. Pour ce faire, vous pouvez utiliser la variable MAKECMDGOALS, elle contient la liste des arguments de ligne de commande donnés à 'make', qu’elle interprète comme une liste de cibles. Si vous voulez extraire le nième argument, vous pouvez utiliser cette variable combinée à la fonction "Word", par exemple, si vous voulez le deuxième argument, vous pouvez le stocker dans une variable comme suit:

second_argument := $(Word 2, $(MAKECMDGOALS) )
4
user5122888

Voici mon exemple. Notez que j'écris sous Windows 7 en utilisant le fichier mingw32-make.exe fourni avec Dev-Cpp. (J'ai c:\Windows\System32\make.bat, la commande s'appelle donc toujours "make".)

clean:
    $(RM) $(OBJ) $(BIN) 
    @echo off
    if "${backup}" NEQ "" ( mkdir ${backup} 2> nul && copy * ${backup} )

Utilisation pour le nettoyage régulier:

make clean

Utilisation pour nettoyer et créer une sauvegarde dans mydir /:

make clean backup=mydir
2
osa

anon, run: ./prog semble un peu étrange, car la partie droite devrait être une cible, donc run: prog paraît mieux.

Je suggérerais simplement:

.PHONY: run

run:
        prog $(arg1)

et je voudrais ajouter que ces arguments peuvent être passés:

  1. comme argument: make arg1="asdf" run
  2. ou être défini comme environnement: arg1="asdf" make run
2
dma_k

Pas très fier de cela, mais comme je ne voulais pas transmettre de variables d’environnement, j’ai inversé la méthode pour exécuter une commande fixe:

run:
    @echo command-you-want

ceci affichera la commande que vous voulez exécuter, donc évaluez-la dans un sous-shell:

$(make run) args to my command
2
Conrad.Dean

Une autre astuce que j'utilise est le drapeau -n, qui indique à make d'effectuer un essai. Par exemple,

$ make install -n 
# Outputs the string: helm install stable/airflow --name airflow -f values.yaml
$ eval $(make install -n) --dry-run --debug
# Runs: helm install stable/airflow --name airflow -f values.yaml --dry-run --debug
0
santon