Why GNU make removes intermediate targets when using pattern rules? - makefile

I have a makefile like the following:
.PHONY: all
all: foo_1.txt foo_2.txt foo_xxx.txt
.PHONY: clean
clean:
rm -f foo_* bar_*
foo_%.txt: bar_%.txt
cp $< $#
#.PRECIOUS: bar_%.txt
bar_%.txt:
touch $#
bar_2.txt:
touch $#
The output of "make all" is folowing
touch bar_1.txt
cp bar_1.txt foo_1.txt
touch bar_2.txt
cp bar_2.txt foo_2.txt
touch bar_xxx.txt
cp bar_xxx.txt foo_xxx.txt
rm bar_xxx.txt bar_1.txt
The intermediate files created by the rule using pattern (bar_xxx.txt, bar_1.txt) are removed on the end. I have found that this behaviour can be supressed by .PRECIOUS (in the code is the line intentionally commented out).
Why are intermediate files created by rule with pattern removed by default and files created by rule without pattern are not?

By the definition of "intermediate files", you can't have an intermediate file created by an explicit rule (a rule "without a pattern").
See the section on Chains of Implicit Rules to understand this feature. If you have specific questions then please update your question.

Related

make simple file copying yields "Nothing to be done"

I'm trying to simply copy files that are modified using make. Here is the entire Makefile:
FILES = www/foo.html www/bar.html www/zap.php
all: $(FILES)
$(FILES): src/$#
cp src/$# $#
clean:
rm $(FILES)
After modifying a file src/www/bar.html, make does not copy the file:
$ make
make: Nothing to be done for 'all'.
$ make www/bar.html
make: 'www/bar.html' is up to date.
Why does make not see the prerequisite has been modified and that the file needs to be copied?
If I run make clean, make it works (copies all files).
src/$# is not well-defined. You want
$(FILES): %: src/%
which declares a pattern rule, and restricts its scope to the files in $(FILES). (You might want or even need to remove this restriction.)

How to determine if Make target is a PHONY?

I have a make target that depends on a variable, which contains both PHONY and real targets.
This target needs to depend only on the real targets in the variable.
How can I test a variable to determine if it is a PHONY or not, so I can filter them out?
(I can test for a file's existence inside the recipe, but I don't want my target to be triggered by execution of any of the PHONY targets.)
Thanks!
There is a way to do it, but I would strongly recommend against it. First of, phony targets can be also file targets. And there is no way to tell a phony file target from a non-phony file target.
It looks like the question implies that the phony targets the author wants to ignore are all non-file targets. In this case see the example below.
.PHONY: phony_target .FORCE
.FORCE:
ALL_TARGETS = phony_target file_target undetermined_target
-include detect_phony.inc
all: final_target
# All done
final_target: $(REAL_TARGETS)
# create $# triggered by $?
#touch $#
ifeq (,$(MAKE_RESTARTS))
# Generate the list of real file targets in make include file
detect_phony.inc: .FORCE
#echo 'REAL_TARGETS = ' `ls $(ALL_TARGETS) 2>/dev/null` > $# |:
endif
file_target:
touch $#
undetermined_target phony_target:
# process $#
clean:
rm -f file_target final_target
Here are the test results:
$make clean
rm -f file_target final_target
$ make
# create final_target triggered by
# All done
$ touch file_target
$ make
# create final_target triggered by file_target
# All done
$ make
# All done
As you can see it only triggers the final target when the file target is updated.
Before you criticize - Here are the flaws of this implementation:
make is always called twice, updating the generated detect_phony.inc include file at every run
if detect_phony.inc gets corrupted somehow, make execution will be locked by syntax errors, until you manually delete it.
it can't handle phony file targets as I mentioned before
if another generated include is added in this makefile that requires another restart before detect_phony.inc this functionality will break.
So it this method is hacky and has several gotchas. I would not use it in production environment. I would insist on changing the top level Makefile first.

gnu make "Removing intermediate files"

I have the following rules
define compile_c
$(ECHO) "CC $<"
$(Q)$(CC) $(CFLAGS) -c -MD -o $# $<
## The following fixes the dependency file.
## See http://make.paulandlesley.org/autodep.html for details.
## Regex adjusted from the above to play better with Windows paths, etc.
#$(CP) $(#:.o=.d) $(#:.o=.P); \
$(SED) -e 's/#.*//' -e 's/^.*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $(#:.o=.d) >> $(#:.o=.P); \
$(RM) -f $(#:.o=.d)
endef
vpath %.c . $(TOP)
$(BUILD)/%.o: %.c $(BUILD)/%.pp
$(call compile_c)
vpath %.c . $(TOP)
$(BUILD)/%.pp: %.c
$(ECHO) "PreProcess $<"
$(Q)$(CC) $(CFLAGS) -E -Wp,-C,-dD,-dI -o $# $<
When the build finishes, GNU make says
Removing intermediate files... and deletes all the .pp files which I do NOT want.
Why is it doing this?
How do I stop it?
Since you're using GNU Make, you can make the following adjustment to your Makefile:
.PRECIOUS: $(BUILD)/%.pp # ADD THIS LINE
$(BUILD)/%.pp: %.c
$(ECHO) "PreProcess $<"
$(Q)$(CC) $(CFLAGS) -E -Wp,-C,-dD,-dI -o $# $<
The documentation has this to say about .PRECIOUS directives:
The targets which .PRECIOUS depends on are given the following special treatment: if make is killed or interrupted during the execution of their recipes, the target is not deleted.
[...]
Also, if the target is an intermediate file, it will not be deleted after it is no longer needed, as is normally done.
[...]
You can also list the target pattern of an implicit rule (such as ‘%.o’) as a prerequisite file of the special target .PRECIOUS to preserve intermediate files created by rules whose target patterns match that file's name.
This has the benefit of not creating an unwanted additional rule. It's also clearer what you're trying to do: keep the precious intermediate files that might be expensive to recreate.
I think the best solution is to use the .SECONDARY special target. Just add this line:
.SECONDARY:
Quoting the manual:
.SECONDARY with no prerequisites causes all targets to be treated as secondary (i.e., no target is removed because it is considered intermediate).
Why is this better than making the targets prerequisites of a throw-away target? That's more clutter, and has to be done explicitly for every set of files that might be generated with pattern rules.
Why is this better than .PRECIOUS? That causes files to be retained even if their recipe fails when using .DELETE_ON_ERROR. The latter is important to avoid failing recipes leaving behind bad outputs that are then treated as current by subsequent make invocations. IMO, you always want .DELETE_ON_ERROR, but .PRECIOUS breaks it.
If you search for "gnu make intermediate files" you'll immediately find the answer as to why it's happening, in the GNU make manual section Chains of Implicit Rules.
It also tells you how to avoid it: a file cannot be intermediate if it is mentioned in the makefile as a target or prerequisite.
So, just list your .pp files as a prerequisite of some rule, somewhere. It doesn't have to be a rule that's ever invoked. You don't give enough of your makefile here for us to provide a complete answer, but it would be something like:
all_pps: $(ALL_OBJECTS:.o=.pp)
assuming you had a variable ALL_OBJECTS containing all your .o files.
Here is a detail that finally got PRECIOUS working for me. The pattern that you give to PRECIOUS has to be exactly the pattern that is being used in the rule that creates the intermediate file. I want to save all files prefixed by moc_. At first I used
.PRECIOUS: moc_%
to no avail. Then I tried
.PRECIOUS: moc_%.cpp
and this did the trick.

How do I get Make to do Second Expansion using two rules with the same target containing %?

I have a directory structure that looks like this:
$ tree
.
|-- dir
| `-- subdir
| `-- data
`-- makefile
where data is a file. My makefile looks like this:
all: dir/analysis
.SECONDEXPANSION:
%/analysis: %/subdir
%/analysis: $(addsuffix /data, $$^)
#echo TARGET: $# DEPS: $^
touch $#
When I run make, I would expect the result to look like this:
TARGET: dir/analysis DEPS: dir/subdir/data dir/subdir
touch dir/analysis
Instead, it just reports
make: *** No rule to make target `dir/analysis', needed by `all'. Stop.
If I change the first rule to dir/analysis: dir/subdir then it works as I expected. So I suspect that make ignores the first rule and skips straight to the second when the first rule is %/analysis: %/subdir. It also works as expected when both rules have dir/analysis as their target instead of just the first rule. MadScientists's answer to a different question here seemed to apply to my problem. I tried adding rules like
dummy_target: dir/subdir
mkdir -p dir/subdir
and
dir/subdir:
mkdir -p dir/subdir
to the end of makefile to try to make the dependency on the first rule an explicit target but this didn't change anything. I'm pretty new to make, so I'm probably missing something pretty stupid, but I can't for the life of me figure it out. How do I get the first and second rules to execute in the order they're written? I'm using Make version 3.81 in case that matters.
--EDIT--
If I actually add a command after the first rule, like #echo RULE 1 then that command executes and not the second one.
I think that you understand the makefile:
# Original
all: dir/analysis
.SECONDEXPANSION:
%/analysis: %/subdir
%/analysis: $(addsuffix /data, $$^)
#echo TARGET: $# DEPS: $^
touch $#
like this:
By default we make dir/analysis
We request secondary expansion
We say that whatever/analysis depends on whatever/subdir. So
that makes dir/subdir a prerequisite of dir/analysis
We say also that whatever/analysis depends on the thing we get by
suffixing /data to the prior prerequisites of whatever/analysis. (Secondary
expansion of $$^ gives us the prior prerequisites of the target). And
since the prior prerequisites of dir/analysis are dir/subdir, that now
makes dir/subdir dir/subdir/data the new prerequisites of dir/analysis.
Hence the output of the recipe ought to be:
TARGET: dir/analysis DEPS: dir/subdir/data dir/subdir
touch dir/analysis
This understanding is badly wrong. You're confusing and conflating the operation
of pattern rules with the operation or ordinary rules.
If a makefile says, e.g:
# A
xx: xa
xx: xb
xx: xc
touch $#
or even:
# B
xx: xa
xx: xb
%x: %c
touch $#
then the prerequisites of all the empty rules for the target xx are combined
with those of the (one) non-empty rule when xx is considered as a target. So
# A is equivalent to:
xx: xa xb xc
touch $#
and # B is equivalent to:
xx: xa xb
%x: %c
touch $#
and in both cases the prerequisites of xx are xa xb xc.
However:
%x: %a
%x: %b
touch $#
is not equivalent to:
%x: %a %b
touch $#
If you ever write an empty pattern rule, its effect is simply to delete
any prior pattern rule with the same target and prerequisite patterns. And if you
ever write a non-empty pattern rule, you simply replace
any prior pattern rule with the same target and prerequisite patterns. See
10.5.6 Canceling Implicit Rules
So:
.SECONDEXPANSION:
%/analysis: %/subdir
%/analysis: $(addsuffix /data, $$^)
#echo TARGET: $# DEPS: $^
touch $#
is equivalent to:
.SECONDEXPANSION:
%/analysis: $(addsuffix /data, $$^)
#echo TARGET: $# DEPS: $^
touch $#
Then when this pattern rule is considered with respect to dir/analysis,
there are no prior prerequisites: $^ will expand to the empty string, and
in secondary expansion, so will $$^. So finally the recipe is equivalent to:
# Residual
%/analysis: /data
#echo TARGET: $# DEPS: $^
touch $#
You can satisfy yourself of this by running both of the # Original and
# Residual makefiles in debug mode (-d) and comparing the outputs.
This explains why the outputs in both cases conclude:
make: *** No rule to make target 'dir/analysis', needed by 'all'. Stop.
A pattern rule will only be instantiated to make a target if doing so offers a way for the target to be made. Why else select it? Since the prerequisite /data does not exist, the pattern rule
is not viable and is discarded. Just as if you tried to make foo.o with the makefile:
%.o: %c
touch $#
when there is no foo.c in the directory.
If you really want to see the output you were expecting, then you can get it from:
.PHONY: all clean
all: dir/analysis
%/analysis: %/subdir %/subdir/data
#echo TARGET: $# DEPS: $^
touch $#
clean:
rm -f dir/analysis
But it is hard to believe that is what you really want to see. This makefile
makes the analysis dependent on the data and also on the directory where
the data resides. What can be the point of that?
%/analysis: %/subdir/data
presumably expresses the desired relationship between the data and the analysis:-
If the data doesn't exist then the analysis is out of date and cannot be made (till you get some data).
If the data exists and is older than the analysis then the analysis does not
need to be made.
If the data exists and is newer than the analysis then the analysis can and will be
made.
By making dir/subdir an independent prerequisite all that you achieve is to introduce
a requirement to remake the analysis if it becomes older than the directory in
which the data resides - regardless of the data. So if, e.g. I run touch dir/subdir,
that will require make to re-run the analysis, even though the data hasn't changed.
I can make sense of your motivation only by supposing that in
reality you also want make to make the data, and the directory where it resides, if
they happen not exist when an analysis is required. If that is the situation then
you want a makefile on the lines of:
.PHONY: all clean
all: dir/analysis
.SECONDARY:
%/subdir:
mkdir -p $#
%/subdir/data: | %/subdir
touch $#
%/analysis: %/subdir/data
#echo TARGET: $# DEPS: $^
touch $#
clean:
rm -f dir/analysis dir/subdir/data
Here, the pattern rule:
%/subdir/data: | %/subdir
makes %/subdir an order-only prerequisite
of %/subdir/data. It means that /dir/subdir is not considered when determining whether /dir/subdir/data needs
to be made, but if it is determined that /dir/subdir/data is to be made, then /dir/subdir will be made first. This
way, /dir/subdir will be created, if it does not exist, when you need it to put the data there, but will have no influence
on whether you need to make the data or the analysis.
The special target .SECONDARY is a technicality. It directs make not to automatically delete any subsequent targets
that it deduces to be intermediate artefacts that emerge from chained pattern rules. Without it, in this example make
would deduce that dir/subdir/data and dir/subdir/ are just waste-products of making dir/analysis and auto-delete them
at the end.
If you run this makefile initially with no dir/subdir/data and no dir/subdir/ you get:
$ make
mkdir -p dir/subdir
touch dir/subdir/data
TARGET: dir/analysis DEPS: dir/subdir/data
touch dir/analysis
And subsequently:
$ make clean
rm -f dir/analysis dir/subdir/data
$ make
touch dir/subdir/data
TARGET: dir/analysis DEPS: dir/subdir/data
touch dir/analysis
And then:
$ make
make: Nothing to be done for 'all'.
$ touch dir/subdir
$ make
make: Nothing to be done for 'all'.

How to target multiple directories with a single Makefile?

I'm using GNU Make to build three different editions of a static html document.
I use Less as a CSS preprocessor.
My directory structure looks like this:
Makefile
160x600/style.less
300x250/style.less
728x90/style.less
This is my Makefile:
LESSC=lessc -x # use -x for debugging
.PHONY: all clean
all: 160x600 300x250 728x90
%.css: %.less
$(LESSC) $< > $#
160x600: 160x600/style.css
300x250: 300x250/style.css
728x90: 728x90/style.css
clean:
rm -f 160x600/*.css
rm -f 300x250/*.css
rm -f 728x90/*.css
This way, I can use make 160x600 to build style.css from style.less.
But I don't want to explicitly list a target rule for each directory. Instead, I tried adding this rule instead of the three directory specific ones:
%: %/style.css
But that does not work. I assume it's clear from that example what my goal is. Is there a way to accept any directory as a target, so that I just have to list the directory names in the all: rule?
use static pattern rule:
res_dirs = 160x600 300x250 728x90
$(res_dirs): %: %/style.css

Resources