Makefile pattern rule limitation - makefile

Consider the following simplified Makefile :
target :
(recipe)
target-mt : (add some flags)
target-mt : target
This example works as intended : make target-mt add a few flags and proceeds with compiling target.
Now, since the *-mt pattern happens quite often, with always the same flags, I want to make this effort common. So let's simplify with a Makefile pattern rule :
target :
(recipe)
%-mt : (add some flags)
target-mt : target
This works as intended, and now all later *-mt targets receive the same additional set of flags.
Now, notice that the dependency of something-mt is always something. So I'm tempted to generalize this relation even more :
target :
(recipe)
%-mt : (add some flags)
%-mt : %
This one doesn't work.
make: *** No rule to make target `target-mt'. Stop.
This seems at odd with general formulation of pattern rules, such as the canonical example :
%.o : %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $#
I presume there must be a reason for this failure, and I presume it might be related to the fact that the dependency is a pure %, not something more defined like %.c. But I couldn't find anything related to such a restriction in the documentation (linked above).
Questions are :
1) Is there an explanation for this behavior ?
2) Is there another way to achieve the same objective ?

You can't declare a pattern rule with no recipe. Or rather, you can, but it doesn't do what you want here: instead it cancels the pattern rule.
You could add a dummy recipe, like this:
%-mt: % ; #:
(: is a shell no-op operator).
Or you could just not use this and go back to the way it was. It's a little hard to understand the full context from this limited example.

Related

Missing dependency in Makefile

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.

How to limit Makefile pattern matching to a list of options

I would like to use an implicit rule (of the %.final: %.intermediate type) in a makefile, but only for certain files (only a.final and b.final, but not x.final)
I've tried to use
targets=a b c
${targets} %.intermediate: %.original
process1 $< > $#
${targets}: %.final: %.intermediate
process2 $< > $#
all: ${targets}
but I'm just not making make happy. I'm not sure exactly what these implicit rules are doing, and if targets needs to include '.final', or how to really make it come together.
If I do
a.intermediate: a.original
process1 a.original > a.intermediate
a.final: a.intermediate
process2 a.intermediate > a.final
#and continue for b, c, etc.
Then things are just fine and dandy.
Here's the general idea. It's not in good form because I'm not used to writing makefiles with pattern substitution and so forth.
#Reference data
CHIP_WCE_ACCESSION=SRR713343
CHIP_BLANK_ACCESSION=SRR713344
#Experiment data
CHIP_NANOG_ACCESSION=SRR713342
CHIP_SOX2_ACCESSION=SRR713341
CHIP_OCT4_ACCESSION=SRR713340
CHIP_TARGETS=${CHIP_NANOG_ACCESSION} ${CHIP_SOX2_ACCESSION} ${CHIP_OCT4_ACCESSION}
#Note that CHIP_WCE_ACCESSION is *not* processed with these.
CHIP_REFERENCE=${CHIP_BLANK_ACCESSION} ${CHIP_WCE_ACCESSION}
BAM_COVERAGE=bamCoverage --numberOfProcessors 16 --binSize 10
#This is a specific bioinformatics program.
${CHIP_REFERENCE}: %.sort.bam
echo hello > $# #Not the same way the CHIP_TARGETS are created.
${CHIP_TARGETS}: %.sort.bam
touch $# #There's actually a long chain of processing, but touch works for the minimal example.
${CHIP_TARGETS}: %.bw: %.sort.bam ${CHIP_BLANK_ACCESSION}.sort.bam
${BAM_COVERAGE} --use-reference ${CHIP_BLANK_ACCESSION}.sort.bam --bam $< -o $#
chip: ${CHIP_NANOG_ACCESSION}.bw ${CHIP_SOX2_ACCESSION}.bw ${CHIP_OCT4_ACCESSION}.bw
Expected result: ${BAM_COVERAGE} is executed on all of the CHIP_TARGETS, with appropriate execution of CHIP_REFERENCE rules for the $CHIP_BLANK_ACCESSION.
Current results: I don't know how to write this rule.
This:
${targets} %.intermediate: %.original
is not valid because all targets in a pattern rule must contain the pattern.
This:
${targets}: %.final: %.intermediate
is a static pattern rule, which is what you want, BUT every target in the static pattern rule must match the target. So you want this:
$(targets:%=%.final) : %.final : %.intermediate

Makefile: Pattern rule as first rule

I know that make usually executes the first target if called without any arguments. But what happens if the first target is a pattern rule? I have a Makefile here that looks as follows:
%.o: %.cc
gcc -c -o $# $<
main: main.o helper.o
gcc main.o helper.o -o $#
From my understanding of make, just calling it w/o any arguments should probably lead to some kind of error in this case because the first target, which is as far as I understood always the default target, does not make sense if make is not given any arguments. But when I call make with this Makefile, it instead builds the main target (and, of course, recursively the targets main.o and helper.o as well).
So, is it always true that make will ignore the pattern rules when looking for the first target? And is it somehow considered bad style to put those in front of the target that one really wants to be the default one? In my opinion, this is somehow confusing.
From the GNU make manual:
The order of rules is not significant, except for determining the
default goal: the target for make to consider, if you do not otherwise
specify one. The default goal is the target of the first rule in the
first makefile. If the first rule has multiple targets, only the first
target is taken as the default. There are two exceptions: a target
starting with a period is not a default unless it contains one or more
slashes, ‘/’, as well; and, a target that defines a pattern rule has
no effect on the default goal. (See Defining and Redefining Pattern
Rules.)

Prioritizing pattern rules in Make

I have (roughly) this Makefile:
.PHONY: all
.SUFFIXES:
OUT = /www/web
all: $(OUT)/index.html
# rule 1
%.html: %.in
build_html $< $#
# rule 2
$(OUT)/%: %
cp $< $#
This Makefile has a problem, since there are two different ways to build $(OUT)/index.html:
build ./index.html (rule 1), then copy it to $(OUT) (rule 2).
copy ./index.in to $(OUT) (rule 2), then build $(OUT)/index.html (rule 1).
I want make to always prefer option 1. How can I indicate that there is a preferred order between these two pattern rules?
(I can think of a few hacky ways to accomplish it for this particular case, but I want a solution that is as general as possible---for instance, changing the pattern of rule 2 to $(OUT)/%.html: %.html will fix the problem, but loses generality since I need to repeat myself if I want to handle other kinds of files in the same way later.)
A quote from the GNU Makefile Manual:
It is possible that more than one pattern rule will meet these criteria. In that case, make will choose the rule with the shortest stem (that is, the pattern that matches most specifically). If more than one pattern rule has the shortest stem, make will choose the first one found in the makefile.
So, you can try to create rules which ensure shorter stems to take priority. Alternatively, you could use static pattern rules to limit the scope of what gets copied where, as so:
%.html: %.in
build_html $# $<
$(expected_out) : (OBJS)/% : %
cp $# $<
and then prepopulate $(expected_out) with what you want in there. Finally, you can add:
$(OUT)/index.html : index.html
somewhere in your makefile, as make prefers the 'shortest path' to building an object, which would only be one pattern rule in this case.
While #John's answer best fits my use-case (I know exactly what files belong in $(OUT)), there is also an alternative solution: mark the desired intermediate file as "precious".
.PRECIOUS: index.html
This will also instruct Make not to delete index.html, which it would otherwise do for you.
This works thanks to Make's algorithm for choosing implicit rules. Make favors rules whose dependencies exist or ought to exist, and a file "ought to exist" if it has an explicit rule or is a dependency of another rule. This applies even if it is a dependency of a special target like .SECONDARY, .INTERMEDIATE, or .PRECIOUS. For more info, also see the manual section on "Chains of Implicit Rules".

Makefile static pattern rule matching issue

I think there is something kind of basic I am missing about the gnu make (I am using 3.81 if it matters) static pattern rule matching (which apparently someone else did as well where I work because this was discovered as I was trying to fix a rule that had been commented out). I have tried to simplify my example down to the crux of it (hopefully I haven't' missed anything essential in the real example).
So this appears to work as I'd expect
JUNK:=foo bar
BINS:=$(patsubst %,bin/%,$(JUNK))
all : $(BINS)
.PHONY : all
# This works
$(BINS) : bin/% : %
mkdir -p bin && cp $< $#
But this (which is closer to what I found in the real Makefile) does not
JUNK:=foo bar
BINS:=$(patsubst %,bin/%,$(JUNK))
all : $(BINS)
.PHONY : all
# This doesn't work
$(JUNK) : bin/% : %
mkdir -p bin && cp $< $#
# This doesn't work either
#bin/$(JUNK) : bin/% : %
# mkdir -p bin && cp $< $#
Based on my understanding of what should be happening in both cases I would have expected that both Makefiles would behave exactly the same; however, only the first one behaves as I expected (i.e., properly copies the files to bin) and the second gives the following output
Makefile:12: target `foo' doesn't match the target pattern
Makefile:12: target `bar' doesn't match the target pattern
make: *** No rule to make target `bin/foo', needed by `all'. Stop.
What is more confusing is there are nearly identical other static pattern rules in the make file I was examining that were working.
So I obviously know how to "work around" the issue if need be but I'd like to understand why the second (and the commented out portion in the second code block as well) do not do what I expect them to.
Thanks in advance for any help/insight.
The first one doesn't work because, after expanding the JUNK variable, make sees this:
foo bar : bin/% : %
The way static pattern rules work is that the first pattern (the target pattern) must match each word in the list of targets. That tells make which part of the target name is the stem (the part that matches the %). If the target pattern doesn't match, make doesn't know what the stem is. The pattern bin/% does not match the text foo, (there is no bin/ in the text foo) so you get the error you see.
The second one doesn't work because the result of exanding the JUNK variable in this example (bin/$(JUNK) : bin/% : %) looks like this:
bin/foo bar : bin/% : %
Here, bin/foo matches the pattern, but bar doesn't match and so you get the same error as you did in the previous one.
To make it work you must have bin/ prepended to every target, not just the first target, hence the use of patsubst (or, addprefix would work as well).

Resources