If I create an implicit rule with a prerequisite (but no recipe), then the dependency does not seem to be honored. If, on the other hand, I define the prerequisite in the block where I define the recipe, or if I specify a dependency of a particular instance of the target, it does seem to work. I have the following Makefile (GNU make 3.81)
all: foo.a foo.b bar.b bar.c
dep1:
#echo "running $#"
%.a: dep1
%.a:
#echo "running $# (depends on: $^)"
bar.b: dep1
%.b: dep1
#echo "running $# (depends on: $^)"
bar.c: dep1
bar.c:
#echo "running $# (depends on: $^)"
If I run make, I get:
~/tmp/tmp5> make
running foo.a (depends on: )
running dep1
running foo.b (depends on: dep1)
running bar.b (depends on: dep1)
running bar.c (depends on: dep1)
It seems that even though I have %.a depend on dep1, that foo.a can be built without dep1 being built. Is this a bug in make, or is there a reason for this behavior?
Thanks,
John
Pattern rules with the same target don't get combined into a single rule like non-pattern rules do. When you have two non-pattern rules for the same target, they get combined into a single rule with all the dependencies of the two rules and the actions from the rule that has actions (and it is an error for both rules to have actions). With pattern rules, that does not happen -- they are treated as two completely independent rules, either of which can be used to update the target.
The reason for that is pretty obvious when you think of the builtin pattern rules -- there are multiple rules for %.o which can compile a source file in a variety of languages. If they all got combined into a single rule, it simply wouldn't work.
Related
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?
Edit after reading the comments:
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)
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.)
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.
Example: let's say I have a bar.c and baz.c, I want to produce foo.a. So:
foo.a: bar.o baz.o
ar -rc $# $?
Now I decide I always want bar.o to be updated. I decide to make it a Phony target. This also requires an explicit recipe for it, since implicit rules don't get searched for Phony targets. So:
.PHONY: bar.o
bar.o: bar.c
cc -c -o $# $<
foo.a: bar.o baz.o
ar -rc $# $?
Now when I run make foo.a, bar.c always gets compiled. Since bar.o gets updated, foo.a's recipe always gets run.
But: $? has an empty value so foo.a doesn't get modified. Why?
Good question. I'm not sure exactly but I suspect it is because phony targets are exactly that "phony" as-in they are explicitly expected not to represent files and so having them in the list of prerequisites newer than the target doesn't really make all that much sense.
That said if you want to keep this behavior but handle things correctly you want to avoid a .PHONY target and instead use a forced target.
If a rule has no prerequisites or recipe, and the target of the rule is a nonexistent file, then make imagines this target to have been updated whenever its rule is run. This implies that all targets depending on this one will always have their recipe run.
An example will illustrate this:
clean: FORCE
rm $(objects)
FORCE:
Here the target ‘FORCE’ satisfies the special conditions, so the target clean that depends on it is forced to run its recipe. There is nothing special about the name ‘FORCE’, but that is one name commonly used this way.
As you can see, using ‘FORCE’ this way has the same results as using ‘.PHONY: clean’.
Using ‘.PHONY’ is more explicit and more efficient. However, other versions of make do not support ‘.PHONY’; thus ‘FORCE’ appears in many makefiles. See Phony Targets.
I have a makefile that does something like this:
.INTERMEDIATE: gen0.tmp gen1.tmp
.PHONY: %_test
%_test: tests/%.so
ln -fs $< test.so
tests/%.so: gen.o test_src/%.c
cc -shared $^ -o $#
gen.c: gen0.tmp gen1.tmp
cat $^ > $#
gen%.tmp:
seds and awks and non-relevant stuff
As far as i have understood make's documentation, all files created from implicit rules are treated as intermediate, but that is not true for pattern rules, yet whatever .so i create with %_test rule is being deleted with other intermediate files, unless it existed before calling make. What is wrong here?
Also
.SECONDARY: tests/%.so
Doesn't work and
.SECONDARY:
does, but then targets explicitly marked as .INTERMEDIATE aren't beeing deleted, and i don't think marking my main target as .SECONDARY is good practice.
PS: i use make version 3.81
I don't understand your statement all files created from implicit rules are treated as intermediate, but that is not true for pattern rules.
A pattern rule IS a (type of) implicit rule. It absolutely is the case that targets which are created as a result of a pattern rule may be considered intermediate.
Can someone shed light on the difference here:
$(tsdir)/proj has prerequisites $(tsdir)/proja and $(tsdir)/projb. I want proja's and projb's makefile to be called every time I have to build proj. If proja or projb are out of date and are updated, then their makefile will touch $(tsdir)/proja and $(tsdir)/projb respectively. If those files are then newer than $(tsdir)/proj, then rebuild proj.
I have this working by using the below code and the FORCE target. If I try and switch to use .PHONY targets, this doesn't work. I prefer .PHONY as that is supposedly the more 'correct' way of doing this. But it doesen't work and I don't know why. proja's and projb's makefiles aren't called with .PHONY targets, but proj is rebuilt.
I am using GNU make 3.81.
Thanks
Nachum
$(tsdir)/proj: $(tsdir)/proja $(tsdir)/projb
...
$(tsdir)/%: FORCE
make -C $(prereqdir)/$*
FORCE:
#or
$(tsdir)/proj: $(tsdir)/proja $(tsdir)/projb
...
.PHONY: $(addprefix $(tsdir)/, $(projects))
$(tsdir)/%:
make -C $(prereqdir)/$*
.PHONY targets are supposed to represent tasks, not real files, where implicit rule search works only for files. Thus, there is no way to build a phony target with an implicit rule.
From Phony Targets chapter:
Since it knows that phony targets do not name actual files that could be remade from other files, make skips the implicit rule search for phony targets
In your case I would just use explicit rule, may be with a static pattern:
.PHONY: $(addprefix $(tsdir)/, $(projects))
$(addprefix $(tsdir)/, $(projects)) : $(tsdir)/% :
make -C $(prereqdir)/$*
I suspect you can get the result you want by adding a phony dependency to the (real) time-stamp files, one removed from the master project proj.
.PHONY: phony
phony: ; : $#
ts := $(addprefix $(tsdir)/, $(projects))
${ts} : $(tsdir)/%: phony
make -C $(prereqdir)/$*
$(tsdir)/proj: $(tsdir)/proja $(tsdir)/projb
...
I'm just starting to really grok the inner workings of make. Yet I do not understand why the following doesn't work:
test%: test%.foo
#echo $#
#echo $<
all: test1 test2
.PHONY: all test1 test2
Expected behavior:
$ make
test1
test1.foo
test2
test2.foo
# 1,2 Order not important
However, I get:
$ make
make: Nothing to be done for `all'.
("make all", "make test1", etc make no difference).
Can someone explain why the PHONY test rules aren't being executed?
Excerpt from the GNU make manual.
Since it knows that phony targets do not name actual files that could
be remade from other files, make skips the implicit rule search for
phony targets (see section Using Implicit Rules). This is why
declaring a target phony is good for performance, even if you are not
worried about the actual file existing.
This means that as your test1 and test2 targets are phony, make does not search for implicit rules for them. Even if what you use is more accurately named pattern rules, all pattern rules are implicit rules.