3

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
2

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.

3

You might be able to use $(MAKECMDGOALS) to force run to be executed last among other targets specified in the command line. The following example uses C because I don't know Clean.

EXE = a b c

%: %.c
    $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)

all: $(EXE)

clean:
    rm -f $(EXE)

run: $(filter-out run, $(MAKECMDGOALS))
    @for i in $^; do ./$$i; done

.PHONY: all clean run

make run program might seem a little clumsy because it always puts unnecessary output program is up to date.

$ cat a.c
#include <stdio.h>
int main(int argc, char **argv) { printf("This is %s\n", argv[0]); return 0; }
$ cp a.c b.c
$ cp a.c c.c

$ make run a b
cc  -o a a.c
cc  -o b b.c
This is ./a
This is ./b
make: 'a' is up to date.
make: 'b' is up to date.

make program run makes no such output and it might be more natural as English.

$ make c run
cc  -o c c.c
This is ./c
  • Very nice, I wasn't aware of MAKECMDGOALS. Thanks a lot. – Keelan Aug 10 '15 at 12:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.