TL;DR don't try to do this
$ make run arg
instead create script build_and_run_prog.sh
:
#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"
and do this:
$ ./build_and_run_prog.sh arg
Read on for some explanation of why this is the most reasonable choice and why the other alternatives are best avoided
Answer to the stated question: how to pass arguments to a make target
you can use a variable in the recipe
run: prog
./prog $(var)
then pass a variable assignment as an argument to make
$ make run var=arg
this will execute ./prog arg
.
this is the most correct and straightforward way to "pass arguments to a recipe". see the gnu make manual on overriding variables
but while it can be used to run a program with arguments it is certainly not meant to be used that way.
Let me elaborate on some problems
what you want to do is run prog
with argument arg
. but instead of writing:
$ ./prog arg
you need to write:
$ make run var=arg
this gets even more awkward when trying to pass multiple arguments or arguments containing spaces.
instead of writing
$ ./prog foo "bar baz"
you need to write
$ make run var="foo bar\ baz"
or
$ make run var="foo \"bar baz\""
i hope you see how this will get quite awkward for anything but the simplest arguments.
also note that you should not put $(var)
in quotes in the makefile:
run: prog
./prog "$(var)"
because then prog
will always get just one argument.
Answer to the assumed intention behind your question: You want to run prog
with some arguments but have it rebuild before running if necessary.
Create a script which rebuilds if necessary then runs prog with args
build_and_run_prog.sh
:
#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"
This script makes the intention very clear. It uses make to do what it is good for: building/compiling. It uses a shell script to do what it is good for: batch processing.
Plus you can do whatever else you might need with the full flexibility and expressiveness of a shell script without all the caveats of a makefile.
Also the calling syntax is now practically identical:
$ ./build_and_run_prog.sh foo "bar baz"
compared to:
$ ./prog foo "bar baz"
contrast to
$ make run var="foo bar\ baz"
Background explanation of how make handles arguments:
Make is not designed to pass arguments to a target. All arguments on the command line are interpreted either as a goal (a.k.a. target), as an option, or as a variable assignment.
so if you run this:
$ make run foo --wat var=arg
make will interpret run
and foo
as goals (targets) to update according to their recipes. --wat
as an option for make. And var=arg
as a variable assignment for make.
i hope you can see the only method you have to pass information from the command line to use inside a recipe (without hacks) is via variable assignment.
for more details see the gnu manual on how to run make
For completeness here are some of the hacks to "pass arguments to make run".
Method 1:
run: prog
./prog $(filter-out $@, $(MAKECMDGOALS))
%:
@true
super short explanation: filter out current goal from list of goals. then pass list of goals as arguments to prog
. also create a catch all target (%
) which does nothing to silently ignore all the other "goals".
this will allow you to write something like this
$ make run arg1 arg2
problems of method 1:
Arguments that start with a dash will be interpreted by make and not passed as a goal.
$ make run --foo --bar
workaround
$ make run -- --foo --bar
Arguments with an equal sign will be interpreted by make and not passed
$ make run foo=bar
no workaround
Arguments with spaces is awkward
$ make run foo "bar\ baz"
no workaround
If an argument happens to be run
(equal to the target) it will also be removed
$ make run foo bar run
will run ./prog foo bar
instead of ./prog foo bar run
workaround possible with method 2
If an argument is a legitimate target it will also be run.
$ make run foo bar clean
will run ./prog foo bar clean
but also the recipe for the target clean
(assuming it exists).
workaround possible with method 2
When you mistype a legitimate target it will be silently ignored because of the catch all target.
$ make celan
will just silently ignore celan
.
workaround is to make everything verbose. so you see what happens. but that creates a lot of noise for the legitimate output.
Method 2:
ifeq (run, $(firstword $(MAKECMDGOALS)))
runargs := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
$(eval $(runargs):;@true)
endif
run:
./prog $(runargs)
super short explanation: if the target is run
then process the goal list and save in variable. also create do nothing targets for the remaining "goals" using eval
. later when running prog
pass the prepared variable as arguments.
problems of method 2:
If an argument has same name as an existing target then make will print a warning that it is being overwritten.
no workaround that I know of
Arguments with an equal sign will still be interpreted by make and not passed
no workaround
Arguments with spaces is still awkward
no workaround
Arguments with space breaks eval
trying to create do nothing targets.
workaround: create the global catch all target doing nothing as above. with the problem as above that it will again silently ignore mistyped legitimate targets.
it uses eval
to modify the makefile at runtime. how much worse can you go in terms of readability and debugability and the Principle of least astonishment.
workaround: don't!
I have only tested using gnu make. other makes may have different behaviour.
gnu make manual
https://www.gnu.org/software/make/manual/html_node/index.html