Pasar argumentos para "hacer correr"


354

Yo uso Makefiles.

Tengo un objetivo llamado runque ejecuta el objetivo de compilación. Simplificado, se parece a lo siguiente:

prog: ....
  ...

run: prog
  ./prog

¿Hay alguna forma de pasar argumentos? Así que eso

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

¡Gracias!


Respuestas:


264

No sé cómo hacer exactamente lo que quieres, pero una solución podría ser:

run: ./prog
    ./prog $(ARGS)

Entonces:

make ARGS="asdf" run
# or
make run ARGS="asdf"

27
@Rob: $ () es más portátil, funciona tanto en Nmake como en make.
John Knoeller

77
@Rob: Nmake nunca ha admitido $ {} para la expansión de macros, y parece ser una forma arcaica ahora en proceso. $ () es recomendado por cada tutorial en línea que he visto. $ () también es más consistente con otras herramientas como bash.
John Knoeller

10
Quizás sea arcaico. Siempre he usado $ {}, pero el manual para GNU Make indica "Para sustituir el valor de una variable, escriba un signo de dólar seguido del nombre de la variable entre paréntesis o llaves: $(foo)' or $ {foo} 'es una referencia válida para la variable 'foo' ". y procede a dar ejemplos donde solo se usa $ (). Ah bueno.
Jakob Borg

77
Saludos John y calmh, volví y vi que la sugerencia venía de mi copia de la primera edición del libro de OReilly "Gestión de proyectos con marca". El autor establece la regla sobre la sustitución de archivos usando () 's y macros capaces de hacer ambas cosas, pero sugiere usar {}' para distinguir. Pero ... La nueva edición ahora retitulada como "Gestión de proyectos con GNU Make" utiliza () en todo momento. Vaya figura ... Supongo que tendré que modernizar! (-: Todavía estoy sorprendido de que MS NMake vomite en {} 's.
Rob Wells

1
@xealits Seguro que sí, hay un ejemplo en la pregunta aquí
helvete el

198

Esta pregunta tiene casi tres años, pero de todos modos ...

Si está utilizando GNU make, esto es fácil de hacer. El único problema es que makeinterpretará los argumentos no opcionales en la línea de comandos como objetivos. La solución es convertirlos en objetivos de no hacer nada, por makelo que no se quejará:

# 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)

Ejecutar esto da:

$ make run foo bar baz
prog foo bar baz

2
Esto es genial, excepto que no parece funcionar para argumentos que comienzan con un guión:prog foo bar --baz
ingydotnet

22
Que funciona en ese caso también, pero hay que decir makeque no interpreten --bazcomo una opción de línea de comandos: make -- prog foo bar --baz. El --significa "todo después de esto es un argumento, no una opción".
Ideal

¿Cómo definiría un valor predeterminado para RUN_ARGSusar esto?
Bouke

Tal vez agregar una elserama a la ifeqy establecer RUN_ARGSallí?
Ideal

1
Buen punto azulado! Pero hay una solución para eso: reemplace la línea 'eval' por $(eval $(RUN_ARGS):dummy;@:), sin un objetivo ficticio definido.
Lucas Cimon

52

para el estándar, puede pasar argumentos definiendo macros como este

make run arg1=asdf

luego úsalos así

run: ./prog $(arg1)
   etc

Referencias para hacer NMake de Microsoft


34

Puede pasar la variable al Makefile como se muestra a continuación:

run:
    @echo ./prog $$FOO

Uso:

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

o:

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

Alternativamente, use la solución provista por Beta :

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

%:- regla que coincide con cualquier nombre de tarea; @:- receta vacía = no hacer nada

Uso:

$ make run the dog kicked the cat
./prog the dog kicked the cat

23

TL; DR no intentes hacer esto

$ make run arg

en su lugar crea script:

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

y haz esto:

$ ./buildandrunprog.sh arg

Responda a la pregunta planteada:

puedes usar una variable en la receta

run: prog
    ./prog $(var)

luego pasa una asignación variable como argumento para hacer

$ make run var=arg

Esto se ejecutará ./prog arg.

pero cuidado con las trampas. Voy a elaborar sobre las trampas de este método y otros métodos más adelante.


Responda a la intención asumida detrás de la pregunta:

la suposición: desea ejecutar progcon algunos argumentos pero debe reconstruir antes de ejecutar si es necesario.

la respuesta: cree un script que se reconstruya si es necesario y luego ejecute prog con args

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

Este guión deja muy clara la intención. utiliza make para hacer lo que es bueno: construir. utiliza un script de shell para hacer lo que es bueno: procesamiento por lotes.

Además, puede hacer cualquier otra cosa que necesite con la total flexibilidad y expresividad de un script de shell sin todas las advertencias de un archivo MAKE.

También la sintaxis de llamada ahora es prácticamente idéntica:

$ ./buildandrunprog.sh foo "bar baz"

comparar con:

$ ./prog foo "bar baz"

contraste con

$ make run var="foo bar\ baz"

antecedentes:

make no está diseñado para pasar argumentos a un objetivo. Todos los argumentos en la línea de comando se interpretan como un objetivo (también conocido como objetivo), como una opción o como una asignación variable.

así que si ejecutas esto:

$ make run foo --wat var=arg

make interpretará runy foocomo objetivos (objetivos) actualizar según sus recetas. --watcomo una opción para hacer. y var=argcomo una asignación variable.

Para obtener más detalles, consulte: https://www.gnu.org/software/make/manual/html_node/Goals.html#Goals

para ver la terminología, consulte: https://www.gnu.org/software/make/manual/html_node/Rule-Introduction.html#Rule-Introduction


sobre el método de asignación de variables y por qué lo recomiendo

$ make run var=arg

y la variable en la receta

run: prog
    ./prog $(var)

Esta es la forma más "correcta" y directa de pasar argumentos a una receta. pero aunque puede usarse para ejecutar un programa con argumentos, ciertamente no está diseñado para usarse de esa manera. ver https://www.gnu.org/software/make/manual/html_node/Overriding.html#Overriding

En mi opinión, esto tiene una gran desventaja: lo que quieres hacer es correr progcon argumentos arg. pero en lugar de escribir:

$ ./prog arg

estas escribiendo:

$ make run var=arg

esto se vuelve aún más incómodo cuando se intenta pasar múltiples argumentos o argumentos que contienen espacios:

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

comparar con:

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

para que conste, así es progcomo se ve mi :

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

También tenga en cuenta que no debe poner $(var)comillas en el archivo MAKE:

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

porque entonces progsiempre obtendremos un solo argumento:

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

Todo esto es por lo que recomiendo contra esta ruta.


para completar aquí hay algunos otros métodos para "pasar argumentos para hacer correr".

Método 1:

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

%:
    @true

explicación súper corta: filtrar el objetivo actual de la lista de objetivos. create catch all target ( %) que no hace nada para ignorar silenciosamente los otros objetivos.

método 2:

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

run:
    ./prog $(runargs)

explicación súper corta: si el objetivo es runeliminar el primer objetivo y crear objetivos de no hacer nada para los objetivos restantes usando eval.

ambos te permitirán escribir algo como esto

$ make run arg1 arg2

Para una explicación más profunda, estudie el manual de make: https://www.gnu.org/software/make/manual/html_node/index.html

problemas del método 1:

  • Los argumentos que comienzan con un guión serán interpretados por make y no se pasarán como un objetivo.

    $ make run --foo --bar
    

    solución alterna

    $ make run -- --foo --bar
    
  • los argumentos con un signo igual se interpretarán por make y no se pasarán

    $ make run foo=bar
    

    sin solución

  • argumentos con espacios es incómodo

    $ make run foo "bar\ baz"
    

    sin solución

  • si resulta que un argumento es run(igual al objetivo), también se eliminará

    $ make run foo bar run
    

    correrá en ./prog foo barlugar de./prog foo bar run

    posible solución alternativa con el método 2

  • Si un argumento es un objetivo legítimo, también se ejecutará.

    $ make run foo bar clean
    

    correrá ./prog foo bar cleanpero también la receta para el objetivo clean(suponiendo que exista).

    posible solución alternativa con el método 2

  • cuando escribes mal un objetivo legítimo, se ignorará en silencio debido a la captura de todos los objetivos.

    $ make celan
    

    simplemente ignorará en silencio celan.

    La solución es hacer que todo sea detallado. para que veas lo que pasa. pero eso crea mucho ruido para la salida legítima.

problemas del método 2:

  • si un argumento tiene el mismo nombre que un objetivo existente, make imprimirá una advertencia de que se está sobrescribiendo.

    no hay solución que yo sepa

  • los argumentos con un signo igual seguirán siendo interpretados por make y no se pasarán

    sin solución

  • argumentos con espacios sigue siendo incómodo

    sin solución

  • Los argumentos con saltos de espacio que evalintentan crear no hacen nada.

    solución alternativa: cree el objetivo global de capturar todo sin hacer nada como se indicó anteriormente. con el problema anterior, que nuevamente ignorará silenciosamente los objetivos legítimos mal escritos.

  • Se utiliza evalpara modificar el archivo MAKE en tiempo de ejecución. cuánto peor puede ir en términos de legibilidad y debugabilidad y el Principio de menor asombro .

    solución alternativa: ¡no hagas esto! 1 en su lugar, escribe un script de shell que ejecute make y luego se ejecute prog.

Solo he probado usando gnu make. otras marcas pueden tener un comportamiento diferente.


TL; DR no intentes hacer esto

$ make run arg

en su lugar crea script:

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

y haz esto:

$ ./buildandrunprog.sh arg

12

Aquí hay otra solución que podría ayudar con algunos de estos casos de uso:

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

En otras palabras, elija algún prefijo ( test-en este caso) y luego pase el nombre del objetivo directamente al programa / corredor. Supongo que esto es principalmente útil si hay algún script de corredor involucrado que pueda desenvolver el nombre del objetivo en algo útil para el programa subyacente.


2
También puede usar $*para pasar solo la parte del objetivo que coincide con el %.
Malvineous

9

No. Mirando la sintaxis de la página del manual para GNU make

make [-f makefile] [opciones] ... [objetivos] ...

puede especificar múltiples objetivos, por lo tanto, 'no' (al menos no de la manera exacta que especificó).


4

Puede extraer explícitamente cada enésimo argumento en la línea de comando. Para hacer esto, puede usar la variable MAKECMDGOALS, contiene la lista de argumentos de la línea de comando dada a 'make', que interpreta como una lista de objetivos. Si desea extraer el enésimo argumento, puede usar esa variable combinada con la función "palabra", por ejemplo, si desea el segundo argumento, puede almacenarlo en una variable de la siguiente manera:

second_argument := $(word 2, $(MAKECMDGOALS) )

Esto también ejecuta el comando make para ese argumento. make: *** No rule to make target 'arg'. Stop.
ThomasReggi

2

anon , se run: ./progve un poco extraño, ya que la parte derecha debe ser un objetivo, por lo que se run: progve mejor.

Sugeriría simplemente:

.PHONY: run

run:
        prog $(arg1)

y me gustaría agregar que se pueden pasar argumentos:

  1. como argumento: make arg1="asdf" run
  2. o ser definido como entorno: arg1="asdf" make run

2

Aquí está mi ejemplo. Tenga en cuenta que estoy escribiendo en Windows 7, usando mingw32-make.exe que viene con Dev-Cpp. (Tengo c: \ Windows \ System32 \ make.bat, por lo que el comando todavía se llama "make").

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

Uso para limpieza regular:

make clean

Uso para limpiar y crear una copia de seguridad en mydir /:

make clean backup=mydir

2

No estoy muy orgulloso de esto, pero no quería pasar variables de entorno, así que invertí la forma de ejecutar un comando fijo:

run:
    @echo command-you-want

esto imprimirá el comando que desea ejecutar, así que simplemente evalúelo en una subshell:

$(make run) args to my command

44
Mirando hacia atrás a esta respuesta dos años después, ¿por qué era tan terco que no quería usar variables de entorno y por qué pensé que era mejor incluir la generación de otro comando?
Conrad

0

¡Encontré una manera de obtener los argumentos con un signo igual (=)! La respuesta es especialmente una adición a la respuesta de @lesmana (ya que es la más completa y explicada aquí), pero sería demasiado grande para escribirla como comentario. Nuevamente, repito su mensaje: TL; ¡DR no intente hacer esto!

Necesitaba una forma de tratar mi argumento --xyz-enabled=false(ya que el valor predeterminado es verdadero), que todos sabemos ahora que este no es un objetivo de creación y, por lo tanto, no forma parte de él $(MAKECMDGOALS).

Mientras miraba todas las variables de make haciendo eco, $(.VARIABLES)obtuve estos resultados interesantes:

[...] -*-command-variables-*- --xyz-enabled [...]

Esto nos permite ir de dos maneras: ya sea comenzando con una --(si eso se aplica a su caso) o analizando la variable específica de GNU (probablemente no está destinada a que la usemos)-*-command-variables-*- . ** Ver pie de página para opciones adicionales ** En mi caso, esta variable contenía:

--xyz-enabled=false

Con esta variable podemos combinarla con la solución ya existente $(MAKECMDGOALS)y, por lo tanto, definiendo:

# the other technique to invalidate other targets is still required, see linked post
run:
    @echo ./prog $(-*-command-variables-*-) $(filter-out $@,$(MAKECMDGOALS))`

y usarlo con (mezclar explícitamente el orden de los argumentos):

make run -- config --xyz-enabled=false over=9000 --foo=bar show  isit=alwaysreversed? --help

devuelto:

./prog isit=alwaysreversed? --foo=bar over=9000 --xyz-enabled=false config show --help

Como puede ver, perdemos el orden total de los args. La parte con los "args" de asignación parece haber sido revertida, el orden de los "args" de destino se mantiene. Coloqué las "asignaciones" -args al principio, espero que a su programa no le importe dónde se coloca el argumento.


Actualización: a continuación, las variables también parecen prometedoras:

MAKEFLAGS =  -- isit=alwaysreverse? --foo=bar over=9000 --xyz-enabled=false
MAKEOVERRIDES = isit=alwaysreverse? --foo=bar over=9000 --xyz-enabled=false

-2

Otro truco que uso es la -nbandera, que dice makeque haga una carrera en seco. Por ejemplo,

$ 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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.