Say one stage in your build process is a tool that writes out a series of source files and config files. You might have a rule that looks a bit like this:
glue.c glue.h net_conf.xml error_list.config: application.config context.xml target.conf
generate_glue.exe $^
The trouble is, this is exactly the same as specifying four rules, one for each of the goals. And, depending on the exact version of make you use, you may find that generate_glue.exe is run four times. If that takes more than 4 minutes then you’re going to get really mad. Especially if you have, say, 80 test applications to generate.
A pattern to get around this is to cause the dependency “tree” to reconverge around a marker that tells you when the generator was last run:
glue.c glue.h net_conf.xml error_list.config: glue_generated.touch
# nothing here
glue_generated.touch: application.config context.xml target.conf
generate_glue.exe $^
touch glue_generated.touch
This way, glue_generated.touch gets checked for its age a bunch of times, but the generation action is only executed if some config file is newer than the touch file.
ISTR that this can fail too (maybe it’s if the time granularity is too large) because somewhere I ended up with a horrible bunch of macrology that actually tested the file age explicitly in order to avoid it STILL running the tool n times.
Anyhow. This doesn’t seem to be anywhere on the internets, so it is now.
9 comments so far...
Steve,
Here’s another way (at least for GNU make), using the special support for this in a pattern rule.
i.
== Multiple targets made once ==
* Normally a rule with multiple targets is executed once for each target:
file1 file2 : otherfiles
commands
will execute commands twice, setting $@ to file1 and file2 individually.
Sometimes you want a single command to generate multiple targets. The trick to this is to transform the rule into a pattern rule, which has an exception to cope with things like:
%.tab.c %.tab.h: %.ybison -d $<
If you want to use this, but without a pattern then you need to create a phony target:
file1 file2 %.phony1 : otherfilescommands
You need to ensure that the
%.phonyXXtargets are unique within the invocation of make.What was that that just shot over my head?
Great find, ianb! Where was that?
** goes to change all his makefiles **
Originally, I think I saw it in TFM for GNU make.
But the juicy bits are just pulled from the our internal wiki :-)
addendum. Just tried it without joy on our system (gnu make 3.81 under cygwin under XP Pro). Back to the .touch dodge :-(
Works for me….is there a missing dependency somewhere else? Perhaps something updating your depenencies for the double target rule?
Your comment about your other approach requiring extra macrology makes me wonder about this too.
Also - with your first solution, remember to make the touch of the temp file dependent on successs of the real command. ” && touch ..”
There’s a neat trick to debug these kind of things (from http://www.ddj.com/development-tools/197003338)
Add these lines to the start of the file:
OLD_SHELL:=$(SHELL)
SHELL = $(warning [$@ ($^) ($?)])$(OLD_SHELL)
Every time you execute a command, you’ll see something like
Makefile:7: [t1 (f1 f2) (f1 f2)]
The syntax is:
file:linenumber of the rule
[ target
(full list of prerequisites of that target)
(which prerequisites are out of date) ]
I don’t think the
&&is necessary, as make will abandon the sequence of commands on failure (unless the command is prefixed by-)Nice debug trick, though!
I’ve checked and double checked and built yet more test cases. The rule with the %phony works. You surely have a missing dependency somewhere else that is triggering the rule twice.
Either that or just an embarrassing typo in a rule.
leave a reply
You must be logged in to post a comment.