google / kati

An experimental GNU make clone

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Multiple ninja targets generated for same file

stefanb2 opened this issue · comments

The test case is stripped-down from real-life case of including .P files in Android build processed by kati. Please keep this in mind when reading the example :-)

Test case:

$ mkdir -p external
$ touch dummy.h
$ cat Makefile.canonical-path
.PHONY: default

default: dummy1.o dummy2.o

dummy1.o: dummy.h
          touch $@

dummy.h:

dummy2.o: external/../dummy.h
          touch $@

external/../dummy.h:

.PHONY: clean
clean:
        rm -f dummy1.o dummy2.o

GNU make:

$ rm -f dummy?.o
$ make -f Makefile.canonical-path
touch dummy1.o
touch dummy2.o

kati + ninja:

$ rm -f build.ninja dummy?.o
$ ckati --warn --gen_all_targets --regen --ninja -f Makefile.canonical-path
./build.ninja is missing, regenerating...
$ ninja
ninja: warning: multiple rules generate dummy.h. builds involving this target will not be correct; continuing anyway [-w dupbuild=warn]
...
$ fgrep dummy.h build.ninja
build dummy1.o: rule2 dummy.h
build dummy.h: phony
build dummy2.o: rule3 external/../dummy.h
build external/../dummy.h: phony

GNU make and ninja consider both paths to be pointing to same file. kati should probably canonicalize the paths to detect targets pointing to the same file.

We don't directly include the .P files in the Android build -- the includes aren't even present anymore (they've been replaced by .KATI_DEPFILE). Before then, they were being converted to depfiles using the equivalent of --detect-depfile (which was the default before f3ad9e0), and --ignore_optional_include=$(OUT_DIR)/%.P.

Once they're passed as a depfile to ninja, ninja takes care of the path canonicalization.

Make doesn't treat the two as the same target either -- it just doesn't attempt to do any path canonicalization. Both of the targets above are independent, and if they do happen to generate the target, the result is undefined, as the rules could run in any order, and could even overlap:

.PHONY: default

default: dummy1.o dummy2.o

dummy1.o: dummy.h
        touch $@

dummy.h:
        echo "dummy.h"

dummy2.o: external/../dummy.h
        touch $@

external/../dummy.h:
        echo "external/../dummy.h"

.PHONY: clean
clean:
        rm -f dummy1.o dummy2.o

Make will run both echo statements, since they are different targets:

$ make
echo "dummy.h"
dummy.h
touch dummy1.o
echo "external/../dummy.h"
external/../dummy.h
touch dummy2.o

Ninja recognizes they are the same logical file and only runs one rule:

$ make clean
$ ckati --warn --gen_all_targets --regen --ninja -f Makefile
$ ninja -v
ninja: warning: multiple rules generate dummy.h. builds involving this target will not be correct; continuing anyway [-w dupbuild=warn]
[1/3] /bin/sh -c "echo \"dummy.h\""
dummy.h
[3/3] /bin/sh -c "touch dummy2.o"
[3/3] /bin/sh -c "touch dummy1.o"

(That warning is an error in Android builds, -w dupbuild=err, as there were a few cases where this was happening with real rules)

Thanks Dan for the explanation! A tiny nitpicky correction:

it just doesn't attempt to do any path canonicalization

...except GNU make strips leading "./". The document (http://www.gnu.org/software/make/manual/make.html#Features) says:

Strip leading sequences of ‘./’ from file names, so that ./file and file are considered to be the same file.

So yeah, I think this is working as intended.

We don't directly include the .P files in the Android build -- the includes aren't even present anymore (they've been replaced by .KATI_DEPFILE).

That is only true if the branch for build/core includes the commits 037c4225a484130a5510671e788794d23204262 and 72904774a35d395116bb784b5d2adc1a8d637e8e. As far as I can tell that is currently only the case for aosp/master.

After cherry-picking those two changes to the branch I'm working on, I can confirm that including dependency files by kati is no longer necessary.

Before then, they were being converted to depfiles using the equivalent of --detect-depfile (which was the default before f3ad9e0), and --ignore_optional_include=$(OUT_DIR)/%.P.

I wonder why --ignore_optional_include=$(OUT_DIR)/%.P --ignore_dirty=$(OUT_DIR)/% is still necessary after all -include's have been converted to .KATI_DEPFILE's. There is nothing to ignore any more.

Yeah, we'll want to remove these flags, but I'm not sure if we should do it now. AOSP changes will be automatically merged to other branches. I'm not sure one of such branches still needs these flags.

please help me in this

image