How to pass argument to Makefile from command line?

192,331

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}}...'
Share:
192,331

Related videos on Youtube

Meng Lu
Author by

Meng Lu

Updated on November 19, 2021

Comments

  • Meng Lu
    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
    
  • Evgeniy Generalov
    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
    Jon almost 10 years
    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?
  • Beta
    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
    Jon almost 10 years
    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?
  • Gingi
    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 assign MAKECMDGOALS to a shell array and then remove the first element: @ args=($(MAKECMDGOALS)); args=("$${args[@]:1}")
  • patcon
    patcon over 8 years
    fwiw, ":" is a synonym of "true" command, which is likely less cryptic. ":" isn't used often anymore, and brevity likely isn't worth it...?
  • Chris
    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
    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
    Abdullah Al Maruf - Tuhin over 5 years
    It worked!! TO other people trying it out, make sure there is tab before @echo, not space.
  • SergiyKolesnikov
    SergiyKolesnikov over 3 years
    This also works if the action (e.g., test:) is a dependency of the target specified on the command line.
  • oz123
    oz123 almost 3 years
    This will execute the target hi if this target exist in the Makefile. Any idea how to avoid this?
  • M3D
    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
    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
    abc123 about 2 years
    This works for me but it outputs make: *** No rule to make target 'value'. Stop.. How to prevent this?