DrBeco
DrBeco

Reputation: 11785

Running a binary using makefile

From all targets I have, I need one to run the program.

From command line, I would state:

$ make main_run.err

or

$ make main_time.err

If it was specific to a certain program, I would write the rule:

main_run.err main_time.err: main.x
    @echo ------------------- $(DEFSYM) Running ---------------------
    /usr/bin/time -p -o main_time.err ./main.x > main_run.err || true
    @echo -----------------------------------------------------------

Now, how to generalize it?

My first attempt was:

Command line:

$ make run main.x

And the rule:

run: $(filter-out $@,$(MAKECMDGOALS))
    @echo --------------- $(DEFSYM) Running -----------------
    /usr/bin/time -p -o time.err ./$< > run.err || true
    @echo ---------------------------------------------------

This gave me some strange errors:

make: Circular run <- run dependency dropped.
------------- BUILD_150729_162018 Running -----------------
/usr/bin/time -p -o time.err ./main.x > run.err || true
-----------------------------------------------------------
make: 'main.x' is up to date.

My second and best attempt, the one I'm asking for help (unless there is another really easier way), is:

%_run.err %_time.err: %.x
    @echo ------------------- $(DEFSYM) Running ---------------------
    /usr/bin/time -p -o $@_time.err ./$< > $@_run.err || true
    @echo -----------------------------------------------------------

This is almost ok! But filenames are wrong:

--------------- BUILD_150729_162239 Running -----------------
/usr/bin/time -p -o main_run.err_time.err ./main.x > main_run.err_run.err || true
-------------------------------------------------------------

And the command line is not good:

$ make main_run.err

I know I could use:

$ make run x=main

And define a variable $(x), but I prefer:

$ make run main.x

if possible.

Upvotes: 1

Views: 1280

Answers (1)

Etan Reisner
Etan Reisner

Reputation: 81012

This attempt:

%_run.err %_time.err: %.x
    @echo ------------------- $(DEFSYM) Running ---------------------
    /usr/bin/time -p -o $@_time.err ./$< > $@_run.err || true
    @echo -----------------------------------------------------------

Is exactly the right idea. You just need to adjust the automatic variables you are using to match the change to a pattern rule.

Specifically you want the $* (stem) variable instead of the $@ (target) variable.

From the manual:

$@

The file name of the target of the rule. If the target is an archive member, then ‘$@’ is the name of the archive file. In a pattern rule that has multiple targets (see Introduction to Pattern Rules), ‘$@’ is the name of whichever target caused the rule’s recipe to be run.

$*

The stem with which an implicit rule matches (see How Patterns Match). If the target is dir/a.foo.b and the target pattern is a.%.b then the stem is dir/foo. The stem is useful for constructing names of related files.

In a static pattern rule, the stem is part of the file name that matched the ‘%’ in the target pattern.

So you end up with:

%_run.err %_time.err: %.x
    @echo ------------------- $(DEFSYM) Running ---------------------
    /usr/bin/time -p -o $*_time.err ./$< > $*_run.err || true
    @echo -----------------------------------------------------------

The original original:

main_run.err main_time.err: main.x
    @echo ------------------- $(DEFSYM) Running ---------------------
    /usr/bin/time -p -o main_time.err ./main.x > main_run.err || true
    @echo -----------------------------------------------------------

Was incorrect because it did not tell make that both files were created by a single run of the target so make might have run it twice if you specified both main_run.err and main_time.err at the same time. The pattern rule version does tell make about that fact (see the last paragraph of the manual section on Pattern Rule Examples for an explanation of that).

To make the initial command line a bit prettier you could add:

.PHONY: run_%
run_%: %_run.err %_time.err

Which will let you run make run_main.

...

I say that but in a quick mock-up test that doesn't work and it needs to be:

.PHONY: run_%
run_%: %_run.err %_time.err ;

instead but I'm not at all sure why offhand.

Upvotes: 1

Related Questions