Tue 20 Feb 2007 01:45:23 PM UTC, original submission:
[Tested on Windows with native (non-MSYS) MinGW build of GNU make 3.81, found in http://prdownloads.sourceforge.net/mingw/mingw32-make-3.81-1.tar.gz, and on Gentoo Linux with portage-compiled make 3.81.]
Hello,
I think I've found a bug in GNU make. When a pattern rule with multiple targets exists, and make determines that it needs that rule to built one matching target, it uses this rule even if the other targets implicitly built by this rule don't have their dependencies built yet.
As this is quite complicated to explain, I created a little example:
final: x
@echo "making final"
touch $@
x: x.tgt1 x.tgt2
@echo "making x"
touch $@
x.tgt2: dep
dep:
@echo "making dep"
sleep 5
touch $@
%.tgt1 %.tgt2: %.src
cp $< $(patsubst %.src,%.tgt1,$<)
cp $< $(patsubst %.src,%.tgt2,$<)
clean:
-rm -f final x dep x.tgt1 x.tgt2
(Note: To run this makefile successfully you have to create a file named "x.src" at first, to cause the pattern rule to be applied.)
This Makefile has some unwanted properties. At first, building "final" does not result in building "dep", although the dependency chain "final-->x-->x.tgt2-->dep" exists. Make traverses the dependency tree from left to right. It finds that x depends on x.tgt1 and that x.tgt1 can only be built by the pattern rule. However, at build time, this rule also builds x.tgt2. So x.tgt2's non-existent dependencies are ignored as it already exists.
If you reverse the order of x's prerequisites, you encounter an even stranger effect. Running make (after a proper "make clean", of course) seems to work. However, if you use "make -j" you will see that the "dep" target will be finished long after the "final" target. This is due to the fact that the dependency x.tgt2-->dep is not properly tracked across the pattern rule.
Additionally, in both cases (Makefile with original order of x's
prerequisites and Makefile with reversed order and using "make -j"), one has to run make a second time until everything is built.
I don't know whether this behaviour is by design. But I think that pattern rules with multiple (pattern) targets must only be run if make determines that all dependencies for all the targets the pattern rule would create are already in place. Currently, make seems to perform the dependency check only for that target that triggers the rule. This is not enough IMHO. In the example above, make should be running the pattern rule only after the dependencies of both x.tgt1 and x.tgt2 have been built successfully. This would also help to get rid of the "reordering changes semantics" effect described above.
The background: The real case was a system where the compiler (MSVC++) also creates a precompiled header file for a special dummy source file. To account for the fact that compiling all "normal" source files needs this precompiled headers I built a pattern rule like the following one (the pattern rule was necessary in order to tell "make" that the command build both files simultaneously):
OBJS := library-pchgen.obj <further library objects>
$(OBJS): library-pchgen.obj
%.pch %-pchgen.obj: %-pchgen.src
<compiler call to compile the pchgen and pch files>
$(TARGET): $(OBJS) library.pch
<linker call>
Because there were dependencies on other libraries, all the object files had additional prerequisites:
$(OBJS): other_lib/other_lib.dll another_lib/another_lib.dll
However, this did not work when using "make -j", as library-pchgen.obj was built before the other libraries were built. Stating explicitly that the pch file (library.pch) also depends on the other libraries via:
$(OBJS) library.pch: other_lib/other_lib.dll another_lib/another_lib.dll
solved the problem, but - as I already said - I don't think this should be necessary.)
Regards,
Christoph Schulz
|