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!
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
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
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
_
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
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.
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).
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) )
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
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:
make arg1="asdf" run
arg1="asdf" make run
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
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