4

I know you can pass arguments like parameters, for example, make foo=bar. I am wondering if I can do this with only one argument, without the variable. For example, this is what I would like to do:

hello:
    echo "Hello, $1!"

In Bash, $1 is the first argument passed through a function. If I ran make hello 'world', then it should output Hello, world!. Is this possible?

2
  • 5
    You're attempting to misuse Make. The positional arguments denote recipes to invoke/ targets to build. You can perform some nasty hacks to coerce Make to do what you're asking (see stackoverflow.com/questions/2214575/…), but you would do much better to use Make as it was intended and pass Message="Dont abuse Make" from the command line
    – DWD
    Commented May 24, 2022 at 21:04
  • 2
    also, don't confuse make and Makefiles with Bash scripts - they're completely different and as such makefiles are NOT designed to be used like Bash scripting.
    – Thomas Ward
    Commented May 24, 2022 at 22:13

1 Answer 1

3

I fully agree with DWD and Thomas. But if, for any reason, you are forced to use make and a Makefile, please remember that make will consider all the arguments which are neither definitions nor execution flags as targets. It will attempt to update all of them in sequence, one at a time. So:

make hello VAR=value how are -d you --debug=n

Will consider -d and --debug=n as execution flags (or options) and VAR=value a definition of variable VAR. All other arguments will be treated as targets.

If you are using GNU make, you can benefit from certain special variables, such as MAKECMDGOALS which contains a complete list of targets to be updated. With the assistance of functions such as firstword, wordlist and words, we can isolate the first target from the rest:

# Takes the first target as command
Command := $(firstword $(MAKECMDGOALS))
# Skips the first word
Arguments := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))

The rule for hello is pretty simple:

hello:
    @echo "Hello, "$(Arguments)"!";

If you run make hello 'world', you will get what you expect, but make will also attempt to build world as a target, and so with any other strings that you pass after hello.

The solution will come with the last resource rule: %::. It will be evaluated when no other rule can be applied. To stay quiet (no output, no errors) the recipe could be @true:

%::
    @true

Wrapping up everything, the final Makefile would look like:

Command := $(firstword $(MAKECMDGOALS))
Arguments := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))

hello:
    @echo "Hello, "$(Arguments)"!";

%::
    @true

This has many limitations. The first one is that if you extend the repertoire beyond a simple hello, those new targets will be evaluated in addition to being echoed by hello. Another limitation has to do with the invalid command handling. You will have to use the filter function to determine if the target is the first one in the line (actual command) or an argument to the command.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .