Reputation: 4303
I have these recipes in my Makefile. They generate cross-compiled objects for ARM architecture and link them into an elf binary:
%.ao: %.c
$(ARM_CC) $(ARM_CPPFLAGS) $(ARM_FLAGS) $(CFLAGS) -c -o $@ $<
%.elf: %.ao startup_stm32f0xx.ao system_stm32f0xx.ao
$(ARM_CC) $(ARM_FLAGS) $other_arguments -o $@ $^
This works fine from a clean build.
Contrary to my expectation, if I then say touch foo.c; make foo.elf
, gmake responds with
make: 'foo.elf' is up to date.
If I try to make foo.ao
, gmake says that it, too , is up to date.
What am I missing?
TLDR: I did have multiple rules matching the same target, as John Bollinger alluded and HardcoreHenry said specifically.
In addition to the rules above, there's a rule for assembly sources so I can use those vendor files:
%.ao: %.s
$(ARM_CC) $(ARM_CPPFLAGS) $(ARM_FLAGS) $(CFLAGS) -c -o $@ $<
I had been debugging some macros, and used -save-temps
to look at preprocessor output. This option also writes .s
files. So after I'd run make foo.elf
, I'd have the following in my directory:
foo.c
foo.i
foo.s
foo.ao
foo.elf
I can touch foo.c
, but make
sees that there's a foo.s
which is older than foo.ao
, and produces the output that it does. On a clean build, there is no foo.s
, so make
finds the %.c:%.ao
rule and the build proceeds from foo.c
.
(BTW, .ao
stands for ARM object. In addition to cross-compiling for AMR, I compile many of the sources to run unit tests on the host, using the built-in .o:.c
rule)
Upvotes: 0
Views: 916
Reputation: 15483
I'm not a fan of pattern rules. Make can make very strange decisions on which rules apply depending on whatever is lying around on your hard disks. It's all a bit arbitrary.
Much better IMHO to tell make exactly what files you need for a target. It's pretty easy too. Just prefix your pattern rule with the list of targets you actually want it to apply to. This makes it a Static Pattern Rule.
objects := main.ao tools.ao devices.ao# etc
${objects}: %.ao: %.c
$(ARM_CC) $(ARM_CPPFLAGS) $(ARM_FLAGS) $(CFLAGS) -c -o $@ $<
%.elf: ${objects} startup_stm32f0xx.ao system_stm32f0xx.ao
$(ARM_CC) $(ARM_FLAGS) $other_arguments -o $@ $^
As an added bonus, make now won't try to create the pre-existing startup_stm32f0xx.ao
and system_stm32f0xx.ao
.
Usually I find it nicer to list the source files, but YMMV:
sources := main.c tools.c devices.c
objects := $(patsubst $.c,%.ao,${sources})
(P.S. Using a Static Pattern Rule doesn't really give you any advantage over a normal rule in this noddy case. I just wanted to show a small tweak that would make your makefiles much more consistent in their behaviour.)
Upvotes: 1
Reputation: 100781
I know it's bad form to use an answer to respond to another answer, but I ran out of space in a comment to @bobbogo's answer.
Sorry but I can't agree with your assessment of pattern rules. It's not true that you will get "strange decisions" based on "whatever is lying around on your harddisks", and it's certainly not arbitrary.
There is one advantage of static pattern rules over pattern rules, and that is also its downside: a static pattern rule is a shorthand for creating an explicit rule, so that rule will always be used to build that target. A pattern rule, on the other hand, is just one possible way to build a target: if the prerequisites of a pattern rule don't exist and can't be made, then make keeps going and looks for other pattern rules that might be able to build that target.
So if you have multiple possible ways you can build a target then an explicit rule cannot be used for that.
The problem with pattern rules is that if NO pattern rule applies then make just assumes there is no rule to build that target. If the target exists then make simply says "up to date" (as we see in the question) since there's no rule to build it. That can be confusing to users.
If you use an explicit rule (including a static pattern rule) and some prerequisite doesn't exist and can't be created, then make will exit with an error, which can make it easier to figure out what went wrong.
Upvotes: 0