152 lines
3.5 KiB
Markdown
152 lines
3.5 KiB
Markdown
|
# Ninja File Canonicalizer
|
||
|
|
||
|
Suppose we have a tool that generates a Ninja file from some other description (think Kati and makefiles), and during
|
||
|
the testing we discovered a regression. Furthermore, suppose that the generated Ninja file is large (think millions of
|
||
|
lines). And, the new Ninja file has build statements and rules in a slightly different order. As the tool generates the
|
||
|
rule names, the real differences in the output of the `diff` command are drowned in noise. Enter Canoninja.
|
||
|
|
||
|
Canoninja renames each Ninja rule to the hash of its contents. After that, we can just sort the build statements, and a
|
||
|
simple `comm` command immediately reveal the essential difference between the files.
|
||
|
|
||
|
## Example
|
||
|
|
||
|
Consider the following makefile
|
||
|
|
||
|
```makefile
|
||
|
second :=
|
||
|
first: foo
|
||
|
foo:
|
||
|
@echo foo
|
||
|
second: bar
|
||
|
bar:
|
||
|
@echo bar
|
||
|
```
|
||
|
|
||
|
Depending on Kati version converting it to Ninja file will yield either:
|
||
|
|
||
|
```
|
||
|
$ cat /tmp/1.ninja
|
||
|
# Generated by kati 06f2569b2d16628608c000a76e3d495a5a5528cb
|
||
|
|
||
|
pool local_pool
|
||
|
depth = 72
|
||
|
|
||
|
build _kati_always_build_: phony
|
||
|
|
||
|
build first: phony foo
|
||
|
rule rule0
|
||
|
description = build $out
|
||
|
command = /bin/sh -c "echo foo"
|
||
|
build foo: rule0
|
||
|
build second: phony bar
|
||
|
rule rule1
|
||
|
description = build $out
|
||
|
command = /bin/sh -c "echo bar"
|
||
|
build bar: rule1
|
||
|
|
||
|
default first
|
||
|
```
|
||
|
|
||
|
or
|
||
|
|
||
|
```
|
||
|
$ cat 2.ninja
|
||
|
# Generated by kati 371194da71b3e191fea6f2ccceb7b061bd0de310
|
||
|
|
||
|
pool local_pool
|
||
|
depth = 72
|
||
|
|
||
|
build _kati_always_build_: phony
|
||
|
|
||
|
build second: phony bar
|
||
|
rule rule0
|
||
|
description = build $out
|
||
|
command = /bin/sh -c "echo bar"
|
||
|
build bar: rule0
|
||
|
build first: phony foo
|
||
|
rule rule1
|
||
|
description = build $out
|
||
|
command = /bin/sh -c "echo foo"
|
||
|
build foo: rule1
|
||
|
|
||
|
default first
|
||
|
```
|
||
|
|
||
|
This is a quirk in Kati, see https://github.com/google/kati/issues/238
|
||
|
|
||
|
Trying to find out the difference between the targets even after sorting them isn't too helpful:
|
||
|
|
||
|
```
|
||
|
diff <(grep '^build' /tmp/1.ninja|sort) <(grep '^build' /tmp/2.ninja | sort)
|
||
|
1c1
|
||
|
< build bar: rule1
|
||
|
---
|
||
|
> build bar: rule0
|
||
|
3c3
|
||
|
< build foo: rule0
|
||
|
---
|
||
|
> build foo: rule1
|
||
|
```
|
||
|
|
||
|
However, running these files through `canoninja` yields
|
||
|
|
||
|
```
|
||
|
$ canoninja /tmp/1.ninja
|
||
|
# Generated by kati 06f2569b2d16628608c000a76e3d495a5a5528cb
|
||
|
|
||
|
pool local_pool
|
||
|
depth = 72
|
||
|
|
||
|
build _kati_always_build_: phony
|
||
|
|
||
|
build first: phony foo
|
||
|
rule R2f9981d3c152fc255370dc67028244f7bed72a03
|
||
|
description = build $out
|
||
|
command = /bin/sh -c "echo foo"
|
||
|
build foo: R2f9981d3c152fc255370dc67028244f7bed72a03
|
||
|
build second: phony bar
|
||
|
rule R62640f3f9095cf2da5b9d9e2a82f746cc710c94c
|
||
|
description = build $out
|
||
|
command = /bin/sh -c "echo bar"
|
||
|
build bar: R62640f3f9095cf2da5b9d9e2a82f746cc710c94c
|
||
|
|
||
|
default first
|
||
|
```
|
||
|
|
||
|
and
|
||
|
|
||
|
```
|
||
|
~/go/bin/canoninja /tmp/2.ninja
|
||
|
# Generated by kati 371194da71b3e191fea6f2ccceb7b061bd0de310
|
||
|
|
||
|
pool local_pool
|
||
|
depth = 72
|
||
|
|
||
|
build _kati_always_build_: phony
|
||
|
|
||
|
build second: phony bar
|
||
|
rule R62640f3f9095cf2da5b9d9e2a82f746cc710c94c
|
||
|
description = build $out
|
||
|
command = /bin/sh -c "echo bar"
|
||
|
build bar: R62640f3f9095cf2da5b9d9e2a82f746cc710c94c
|
||
|
build first: phony foo
|
||
|
rule R2f9981d3c152fc255370dc67028244f7bed72a03
|
||
|
description = build $out
|
||
|
command = /bin/sh -c "echo foo"
|
||
|
build foo: R2f9981d3c152fc255370dc67028244f7bed72a03
|
||
|
|
||
|
default first
|
||
|
```
|
||
|
|
||
|
and when we extract only build statements and sort them, we see that both Ninja files define the same graph:
|
||
|
|
||
|
```shell
|
||
|
$ diff <(~/go/bin/canoninja /tmp/1.ninja | grep '^build' | sort) \
|
||
|
<(~/go/bin/canoninja /tmp/2.ninja | grep '^build' | sort)
|
||
|
```
|
||
|
|
||
|
# Todo
|
||
|
|
||
|
* Optionally output only the build statements, optionally sorted
|
||
|
* Handle continuation lines correctly
|