How to pass argument to Makefile from command line?
Solution 1
You probably shouldn't do this; you're breaking the basic pattern of how Make works. But here it is:
action:
@echo action $(filter-out $@,$(MAKECMDGOALS))
%: # thanks to chakrit
@: # thanks to William Pursell
EDIT:
To explain the first command,
$(MAKECMDGOALS)
is the list of "targets" spelled out on the command line, e.g. "action value1 value2".
$@
is an automatic variable for the name of the target of the rule, in this case "action".
filter-out
is a function that removes some elements from a list. So $(filter-out bar, foo bar baz)
returns foo baz
(it can be more subtle, but we don't need subtlety here).
Put these together and $(filter-out $@,$(MAKECMDGOALS))
returns the list of targets specified on the command line other than "action", which might be "value1 value2".
Solution 2
Here is a generic working solution based on @Beta's
I'm using GNU Make 4.1 with SHELL=/bin/bash
atop my Makefile, so YMMV!
This allows us to accept extra arguments (by doing nothing when we get a job that doesn't match, rather than throwing an error).
%:
@:
And this is a macro which gets the args for us:
args = `arg="$(filter-out $@,$(MAKECMDGOALS))" && echo $${arg:-${1}}`
Here is a job which might call this one:
test:
@echo $(call args,defaultstring)
The result would be:
$ make test
defaultstring
$ make test hi
hi
Note! You might be better off using a "Taskfile", which is a bash pattern that works similarly to make, only without the nuances of Maketools. See https://github.com/adriancooney/Taskfile
Solution 3
Much easier aproach. Consider a task:
provision:
ansible-playbook -vvvv \
-i .vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory \
--private-key=.vagrant/machines/default/virtualbox/private_key \
--start-at-task="$(AT)" \
-u vagrant playbook.yml
Now when I want to call it I just run something like:
AT="build assets" make provision
or just:
make provision
in this case AT
is an empty string
Solution 4
Few years later, want to suggest just
for this: https://github.com/casey/just
action v1 v2=default:
@echo 'take action on {{v1}} and {{v2}}...'
Related videos on Youtube
![Meng Lu](https://i.stack.imgur.com/0AnuY.png?s=256&g=1)
Meng Lu
Updated on November 19, 2021Comments
-
Meng Lu over 2 years
How to pass argument to Makefile from command line?
I understand I can do
$ make action VAR="value" $ value
with
Makefile
VAR = "default" action: @echo $(VAR)
How do I get the following behavior?
$ make action value value
How about
$make action value1 value2 value1 value2
-
glerYbo almost 9 yearsSimilar: Passing arguments to “make run”
-
Lesmana almost 7 yearsPossible duplicate of Passing arguments to "make run"
-
-
Evgeniy Generalov about 10 years
$(shell echo $(MAKECMDGOALS) | sed 's!^.* $@ !!')
to omit all targets before and just consider the following as arguments:make target1 target2 action value1 value2
-
Jon almost 10 yearsPardon my ignorance. I've tried googling
%:
and@:
and cannot find info on what those "directives" (or whatever they're called) do. Could you please explain? -
Beta almost 10 years@Jon: The manual is here. The part consisting of
%:
and@:
is a rule. The target name%
means that it is a rule that matches anything; that is, if Make can't find any other way to build the thing you tell it to build, it will execute that rule. The@:
is a recipe; the:
means do nothing, and the@
means do it silently. -
Jon almost 10 yearsThanks. I had been reading that manual and I didn't consider at first, that
%:
was actually% :
, a wildcard type target name. I don't see anything in that manual page regarding@:
though... it does suggest that a "do-nothing" rule would simply have a;
after the target specification, so, would it not be more accurate to write% : ;
as the "wildcard do-nothing" rule? -
Gingi almost 9 years
filter-out
doesn't work when the action is a dependency of the target specified on the command line, because$@
will be set to the dependency's name, not the original argument called on the command line. Instead, I assignMAKECMDGOALS
to a shell array and then remove the first element:@ args=($(MAKECMDGOALS)); args=("$${args[@]:1}")
-
patcon over 8 yearsfwiw, ":" is a synonym of "true" command, which is likely less cryptic. ":" isn't used often anymore, and brevity likely isn't worth it...?
-
Chris about 7 years@Beta using your solution (awesome, wow)--do you know how to suppress the output at the end of execution that says,
make: 'input1' is up to date
for the various inputs? -
Sion over 6 years@Beta this got me 90% of the way but if my arg is "le creuset" it will split on the space despite being wrapped in ". Is there a known workaround here?
-
Abdullah Al Maruf - Tuhin over 5 yearsIt worked!! TO other people trying it out, make sure there is
tab
before@echo
, notspace
. -
SergiyKolesnikov over 3 yearsThis also works if the action (e.g., test:) is a dependency of the target specified on the command line.
-
oz123 almost 3 yearsThis will execute the target
hi
if this target exist in the Makefile. Any idea how to avoid this? -
M3D almost 3 years@oz123 You might want to look into using environment variables, rather than positional arguments. You could also look at using a scripting language for this. As mentioned, bash has some useful patterns for this.
-
Big McLargeHuge over 2 years"you're breaking the basic pattern of how Make works" - what do you mean by this? And what is the "correct" pattern?
-
abc123 about 2 yearsThis works for me but it outputs
make: *** No rule to make target 'value'. Stop.
. How to prevent this?