Make target with two words

gnu-makemake

I'm using a Makefile to compile my Clean code. My Clean files have file names of the format *.icl and are compiled to binaries with the same name but without .icl. This is done with the rule:

$(EXE): % : %.icl | copy
    $(CLM) $(CLM_LIBS) $(CLM_INC) $(CLM_OPTS) $@ -o $@

I would now like to add a rule which allows me to run a binary. Currently, I'm often doing

make some_module && ./some_module

I would like to have a make target which depends on the rule above and runs the module. However, the name of the module is already a target itself for compilation alone and I'd like to keep it that way. What I would like is a target with two words that I can call with make run some_module which then depends on the rule above and runs ./some_module afterwards.

Is it possible to create targets with multiple words?

I tried to make a rule (now still without dependency) with the following:

run $(EXE):
    ./$@

Running make run some_module results in many recipes being 'overridden' and 'ignored' and finally ./run not existing.

Makefile:24: warning: overriding recipe for target 'tut7_2_2'
Makefile:21: warning: ignoring old recipe for target 'tut7_2_2'
Makefile:24: warning: overriding recipe for target 'support_check'
Makefile:21: warning: ignoring old recipe for target 'support_check'
[...]
Makefile:24: warning: overriding recipe for target 'drawingframe'
Makefile:21: warning: ignoring old recipe for target 'drawingframe'
./run
/bin/bash: ./run: No such file or directory
Makefile:24: recipe for target 'run' failed
make: *** [run] Error 127

Best Answer

You can inspect MAKECMDGOALS to detect the presence of one goal while building another goal. Either make the %: %.icl run detect the presence of the run goal, or make the run goal inspect what executables are mentioned as targets. If you pass more than one executable as a target, the first method causes each to be run right after it's built, while the second causes all the runs to happen at the end.

The downside of this approach is that it doesn't scale well with other features. For example, if you define a target with multiple executables as dependencies, the method with a run target won't work.

EXE = foo bar experimental
released: foo bar
run: $(filter $(EXE), $(MAKECMDGOALS))
        set -e; for x in $^; do ./$x; done

Here make released run won't run anything.

What I normally do for this case is to define a “run” target for each executable. This keeps each target expressed as a single word, which is a major advantage. It's simple and doesn't break other features.

$(EXE): %.run: %
        ./$(@:.run=)
all.run: $(EXE:=.run)

Then I run make {foo,bar}.run if I want to built and test foo and bar, or make all.run to build and run them all.

Related Question