How to pass argument to Makefile from command line?
Asked Answered
C

6

194

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
Congeries answered 8/6, 2011 at 2:32 Comment(2)
Similar: Passing arguments to “make run”Rustler
Possible duplicate of Passing arguments to "make run"Fiery
P
290

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".

Plassey answered 8/6, 2011 at 3:13 Comment(13)
$(shell echo $(MAKECMDGOALS) | sed 's!^.* $@ !!') to omit all targets before and just consider the following as arguments: make target1 target2 action value1 value2Acaudal
Pardon 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?Redheaded
@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.Plassey
Thanks. 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?Redheaded
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 assign MAKECMDGOALS to a shell array and then remove the first element: @ args=($(MAKECMDGOALS)); args=("$${args[@]:1}")Trussell
fwiw, ":" is a synonym of "true" command, which is likely less cryptic. ":" isn't used often anymore, and brevity likely isn't worth it...?Anaclinal
@Plassey 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?Assemblyman
@Plassey 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?Riojas
"you're breaking the basic pattern of how Make works" - what do you mean by this? And what is the "correct" pattern?Calcine
This works for me but it outputs make: *** No rule to make target 'value'. Stop.. How to prevent this?Wally
If the argument is something like make foo bar -- --param this works fine, it resolves to bar --param -- but if we try to do make foo bar -- --param=10 to pass in a value, for some reason it is just bar with --param=10 stripped out. Anyone have any ideas?Andorra
Aha, got it -- if you require that flags be passed down as well, you'd want: $(filter-out $@,$(MAKECMDGOALS)) $(MAKEFLAGS)Andorra
@Wally that's because you have to add a tab before the @: instead of space.Rapids
M
42

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

Materially answered 30/10, 2017 at 5:16 Comment(5)
It worked!! TO other people trying it out, make sure there is tab before @echo, not space.Dickdicken
This also works if the action (e.g., test:) is a dependency of the target specified on the command line.Tergal
This will execute the target hi if this target exist in the Makefile. Any idea how to avoid this?Sarge
@Sarge 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.Materially
@Materially This only worked for me when I did: make test args=hi - am I being thick?Sunglasses
M
27

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

Malonis answered 17/2, 2016 at 10:39 Comment(0)
K
10

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}}...'
Kev answered 27/11, 2020 at 14:17 Comment(0)
C
6

You will be better of defining variables and calling your make instead of using parameters:

Makefile

action: ## My action helper
    @echo $$VAR_NAME

Terminal

> VAR_NAME="Hello World" make action
Hello World
Crispation answered 7/11, 2021 at 14:6 Comment(0)
F
2

don't try to do this

$ make action value1 value2

instead create script:

#! /bin/sh
# rebuild if necessary
make
# do action with arguments
action "$@"

and do this:

$ ./buildthenaction.sh value1 value2

for more explanation why do this and caveats of makefile hackery read my answer to another very similar but seemingly not duplicate question: Passing arguments to "make run"

Fiery answered 8/10, 2019 at 11:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.