diff options
author | Dan Willemsen <dwillemsen@google.com> | 2018-02-21 02:10:29 -0800 |
---|---|---|
committer | Dan Willemsen <dwillemsen@google.com> | 2018-05-07 16:21:59 -0700 |
commit | 4339853a20bc300968d0389f4f9307ec415d540d (patch) | |
tree | 3258e9b29d26cafc74123ad21cc557f28cf0c9b1 | |
parent | 470969df19079ad1c2eeb94fcc984ca2ce5e68ab (diff) | |
download | build_soong-4339853a20bc300968d0389f4f9307ec415d540d.tar.gz build_soong-4339853a20bc300968d0389f4f9307ec415d540d.tar.bz2 build_soong-4339853a20bc300968d0389f4f9307ec415d540d.zip |
Add a dependency fixer for proto deps
protoc dependency files, at least for C++ outputs, uses the form of:
a/b.c \
a/b.h: <dep1> <dep2>...
Ninja will fail the command when it parses a dep file and there's more
than one output file (even though it doesn't care what the output file
name is). So this tool will parse the original file, and output a
version with only a single output file.
Bug: 67329638
Test: NINJA_ARGS="-t deps ...pb.c" m
Test: NINJA_ARGS="-t deps ...srcjar" m
Test: NINJA_ARGS="-t deps ...srcszip" m
Test: Run dep_fixer across all of taimen's dep files, no failures.
Test: Run dep_fixer against the processed files, no changes.
Test: Run androidmk across all of our Android.mk files, inspect the diffs
Change-Id: I4263b7d5faea37285afa6b24dedf5964aa7d19dc
-rw-r--r-- | androidmk/Android.bp | 2 | ||||
-rw-r--r-- | androidmk/parser/make_strings.go | 81 | ||||
-rw-r--r-- | androidmk/parser/make_strings_test.go | 72 | ||||
-rw-r--r-- | androidmk/parser/parser.go | 15 | ||||
-rw-r--r-- | androidmk/parser/parser_test.go | 61 | ||||
-rw-r--r-- | androidmk/parser/scope.go | 2 | ||||
-rw-r--r-- | cc/proto.go | 17 | ||||
-rw-r--r-- | cmd/dep_fixer/Android.bp | 23 | ||||
-rw-r--r-- | cmd/dep_fixer/deps.go | 95 | ||||
-rw-r--r-- | cmd/dep_fixer/deps_test.go | 389 | ||||
-rw-r--r-- | cmd/dep_fixer/main.go | 67 | ||||
-rw-r--r-- | java/proto.go | 4 | ||||
-rw-r--r-- | python/proto.go | 4 |
13 files changed, 816 insertions, 16 deletions
diff --git a/androidmk/Android.bp b/androidmk/Android.bp index 442452f8..1d939b0c 100644 --- a/androidmk/Android.bp +++ b/androidmk/Android.bp @@ -44,6 +44,6 @@ bootstrap_go_package { ], testSrcs: [ "parser/make_strings_test.go", + "parser/parser_test.go", ], } - diff --git a/androidmk/parser/make_strings.go b/androidmk/parser/make_strings.go index e6885a8c..4b782a23 100644 --- a/androidmk/parser/make_strings.go +++ b/androidmk/parser/make_strings.go @@ -90,10 +90,10 @@ func (ms *MakeString) Value(scope Scope) string { if len(ms.Strings) == 0 { return "" } else { - ret := ms.Strings[0] + ret := unescape(ms.Strings[0]) for i := range ms.Strings[1:] { ret += ms.Variables[i].Value(scope) - ret += ms.Strings[i+1] + ret += unescape(ms.Strings[i+1]) } return ret } @@ -125,6 +125,16 @@ func (ms *MakeString) Split(sep string) []*MakeString { } func (ms *MakeString) SplitN(sep string, n int) []*MakeString { + return ms.splitNFunc(n, func(s string, n int) []string { + return splitAnyN(s, sep, n) + }) +} + +func (ms *MakeString) Words() []*MakeString { + return ms.splitNFunc(-1, splitWords) +} + +func (ms *MakeString) splitNFunc(n int, splitFunc func(s string, n int) []string) []*MakeString { ret := []*MakeString{} curMs := SimpleMakeString("", ms.Pos()) @@ -133,7 +143,7 @@ func (ms *MakeString) SplitN(sep string, n int) []*MakeString { var s string for i, s = range ms.Strings { if n != 0 { - split := splitAnyN(s, sep, n) + split := splitFunc(s, n) if n != -1 { if len(split) > n { panic("oops!") @@ -156,7 +166,9 @@ func (ms *MakeString) SplitN(sep string, n int) []*MakeString { } } - ret = append(ret, curMs) + if !curMs.Empty() { + ret = append(ret, curMs) + } return ret } @@ -206,3 +218,64 @@ func splitAnyN(s, sep string, n int) []string { ret = append(ret, s) return ret } + +func splitWords(s string, n int) []string { + ret := []string{} + preserve := "" + for n == -1 || n > 1 { + index := strings.IndexAny(s, " \t") + if index == 0 && len(preserve) == 0 { + s = s[1:] + } else if index >= 0 { + escapeCount := 0 + for i := index - 1; i >= 0; i-- { + if s[i] != '\\' { + break + } + escapeCount += 1 + } + + if escapeCount%2 == 1 { + preserve += s[0 : index+1] + s = s[index+1:] + continue + } + + ret = append(ret, preserve+s[0:index]) + s = s[index+1:] + preserve = "" + if n > 0 { + n-- + } + } else { + break + } + } + if preserve != "" || s != "" || len(ret) == 0 { + ret = append(ret, preserve+s) + } + return ret +} + +func unescape(s string) string { + ret := "" + for { + index := strings.IndexByte(s, '\\') + if index < 0 { + break + } + + if index+1 == len(s) { + break + } + + switch s[index+1] { + case ' ', '\\', '#', ':', '*', '[', '|', '\t', '\n', '\r': + ret += s[:index] + s[index+1:index+2] + default: + ret += s[:index+2] + } + s = s[index+2:] + } + return ret + s +} diff --git a/androidmk/parser/make_strings_test.go b/androidmk/parser/make_strings_test.go index 8ad3d74c..6995e891 100644 --- a/androidmk/parser/make_strings_test.go +++ b/androidmk/parser/make_strings_test.go @@ -99,6 +99,78 @@ func TestMakeStringSplitN(t *testing.T) { } } +var valueTestCases = []struct { + in *MakeString + expected string +}{ + { + in: SimpleMakeString("a b", NoPos), + expected: "a b", + }, + { + in: SimpleMakeString("a\\ \\\tb\\\\", NoPos), + expected: "a \tb\\", + }, + { + in: SimpleMakeString("a\\b\\", NoPos), + expected: "a\\b\\", + }, +} + +func TestMakeStringValue(t *testing.T) { + for _, test := range valueTestCases { + got := test.in.Value(nil) + if got != test.expected { + t.Errorf("\nwith: %q\nwant: %q\n got: %q", test.in.Dump(), test.expected, got) + } + } +} + +var splitWordsTestCases = []struct { + in *MakeString + expected []*MakeString +}{ + { + in: SimpleMakeString("", NoPos), + expected: []*MakeString{}, + }, + { + in: SimpleMakeString(" a b\\ c d", NoPos), + expected: []*MakeString{ + SimpleMakeString("a", NoPos), + SimpleMakeString("b\\ c", NoPos), + SimpleMakeString("d", NoPos), + }, + }, + { + in: SimpleMakeString(" a\tb\\\t\\ c d ", NoPos), + expected: []*MakeString{ + SimpleMakeString("a", NoPos), + SimpleMakeString("b\\\t\\ c", NoPos), + SimpleMakeString("d", NoPos), + }, + }, + { + in: SimpleMakeString(`a\\ b\\\ c d`, NoPos), + expected: []*MakeString{ + SimpleMakeString(`a\\`, NoPos), + SimpleMakeString(`b\\\ c`, NoPos), + SimpleMakeString("d", NoPos), + }, + }, +} + +func TestMakeStringWords(t *testing.T) { + for _, test := range splitWordsTestCases { + got := test.in.Words() + gotString := dumpArray(got) + expectedString := dumpArray(test.expected) + if gotString != expectedString { + t.Errorf("with:\n%q\nexpected:\n%s\ngot:\n%s", test.in.Dump(), expectedString, gotString) + } + } +} + func dumpArray(a []*MakeString) string { ret := make([]string, len(a)) diff --git a/androidmk/parser/parser.go b/androidmk/parser/parser.go index 89ee308f..89c1af90 100644 --- a/androidmk/parser/parser.go +++ b/androidmk/parser/parser.go @@ -35,6 +35,10 @@ func (e *ParseError) Error() string { return fmt.Sprintf("%s: %s", e.Pos, e.Err) } +const builtinDollar = "__builtin_dollar" + +var builtinDollarName = SimpleMakeString(builtinDollar, NoPos) + func (p *parser) Parse() ([]Node, []error) { defer func() { if r := recover(); r != nil { @@ -326,7 +330,11 @@ loop: case '$': var variable Variable variable = p.parseVariable() - value.appendVariable(variable) + if variable.Name == builtinDollarName { + value.appendString("$") + } else { + value.appendVariable(variable) + } case scanner.EOF: break loop case '(': @@ -357,7 +365,8 @@ func (p *parser) parseVariable() Variable { case '{': return p.parseBracketedVariable('{', '}', pos) case '$': - name = SimpleMakeString("__builtin_dollar", NoPos) + name = builtinDollarName + p.accept(p.tok) case scanner.EOF: p.errorf("expected variable name, found %s", scanner.TokenString(p.tok)) @@ -457,6 +466,8 @@ func (p *parser) parseRulePrerequisites(target *MakeString) (*MakeString, bool) case '=': p.parseAssignment("=", target, prerequisites) return nil, true + case scanner.EOF: + // do nothing default: p.errorf("unexpected token %s after rule prerequisites", scanner.TokenString(p.tok)) } diff --git a/androidmk/parser/parser_test.go b/androidmk/parser/parser_test.go new file mode 100644 index 00000000..f562c29e --- /dev/null +++ b/androidmk/parser/parser_test.go @@ -0,0 +1,61 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "bytes" + "testing" +) + +var parserTestCases = []struct { + name string + in string + out []Node +}{ + { + name: "Escaped $", + in: `a$$ b: c`, + out: []Node{ + &Rule{ + Target: SimpleMakeString("a$ b", NoPos), + Prerequisites: SimpleMakeString("c", NoPos), + }, + }, + }, +} + +func TestParse(t *testing.T) { + for _, test := range parserTestCases { + t.Run(test.name, func(t *testing.T) { + p := NewParser(test.name, bytes.NewBufferString(test.in)) + got, errs := p.Parse() + + if len(errs) != 0 { + t.Fatalf("Unexpected errors while parsing: %v", errs) + } + + if len(got) != len(test.out) { + t.Fatalf("length mismatch, expected %d nodes, got %d", len(test.out), len(got)) + } + + for i := range got { + if got[i].Dump() != test.out[i].Dump() { + t.Errorf("incorrect node %d:\nexpected: %#v (%s)\n got: %#v (%s)", + i, test.out[i], test.out[i].Dump(), got[i], got[i].Dump()) + } + } + }) + } +} diff --git a/androidmk/parser/scope.go b/androidmk/parser/scope.go index 7a514fae..167e470b 100644 --- a/androidmk/parser/scope.go +++ b/androidmk/parser/scope.go @@ -71,7 +71,7 @@ var builtinScope map[string]string func init() { builtinScope := make(map[string]string) - builtinScope["__builtin_dollar"] = "$" + builtinScope[builtinDollar] = "$" } func (v Variable) EvalFunction(scope Scope) (string, bool) { diff --git a/cc/proto.go b/cc/proto.go index 22e50ab4..6e6f95ee 100644 --- a/cc/proto.go +++ b/cc/proto.go @@ -25,13 +25,17 @@ import ( func init() { pctx.HostBinToolVariable("protocCmd", "aprotoc") + pctx.HostBinToolVariable("depFixCmd", "dep_fixer") } var ( proto = pctx.AndroidStaticRule("protoc", blueprint.RuleParams{ - Command: "$protocCmd --cpp_out=$protoOutParams:$outDir -I $protoBase $protoFlags $in", - CommandDeps: []string{"$protocCmd"}, + Command: "$protocCmd --cpp_out=$protoOutParams:$outDir --dependency_out=$out.d -I $protoBase $protoFlags $in && " + + `$depFixCmd $out.d`, + CommandDeps: []string{"$protocCmd", "$depFixCmd"}, + Depfile: "${out}.d", + Deps: blueprint.DepsGCC, }, "protoFlags", "protoOutParams", "protoBase", "outDir") ) @@ -53,10 +57,11 @@ func genProto(ctx android.ModuleContext, protoFile android.Path, } ctx.Build(pctx, android.BuildParams{ - Rule: proto, - Description: "protoc " + protoFile.Rel(), - Outputs: android.WritablePaths{ccFile, headerFile}, - Input: protoFile, + Rule: proto, + Description: "protoc " + protoFile.Rel(), + Output: ccFile, + ImplicitOutput: headerFile, + Input: protoFile, Args: map[string]string{ "outDir": android.ProtoDir(ctx).String(), "protoFlags": protoFlags, diff --git a/cmd/dep_fixer/Android.bp b/cmd/dep_fixer/Android.bp new file mode 100644 index 00000000..d2d1113d --- /dev/null +++ b/cmd/dep_fixer/Android.bp @@ -0,0 +1,23 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +blueprint_go_binary { + name: "dep_fixer", + deps: ["androidmk-parser"], + srcs: [ + "main.go", + "deps.go", + ], + testSrcs: ["deps_test.go"], +} diff --git a/cmd/dep_fixer/deps.go b/cmd/dep_fixer/deps.go new file mode 100644 index 00000000..64c97f52 --- /dev/null +++ b/cmd/dep_fixer/deps.go @@ -0,0 +1,95 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bytes" + "fmt" + "io" + "strings" + + "android/soong/androidmk/parser" +) + +type Deps struct { + Output string + Inputs []string +} + +func Parse(filename string, r io.Reader) (*Deps, error) { + p := parser.NewParser(filename, r) + nodes, errs := p.Parse() + + if len(errs) == 1 { + return nil, errs[0] + } else if len(errs) > 1 { + return nil, fmt.Errorf("many errors: %v", errs) + } + + pos := func(node parser.Node) string { + return p.Unpack(node.Pos()).String() + ": " + } + + ret := &Deps{} + + for _, node := range nodes { + switch x := node.(type) { + case *parser.Comment: + // Do nothing + case *parser.Rule: + if x.Recipe != "" { + return nil, fmt.Errorf("%sunexpected recipe in rule: %v", pos(node), x) + } + + if !x.Target.Const() { + return nil, fmt.Errorf("%sunsupported variable expansion: %v", pos(node), x.Target.Dump()) + } + outputs := x.Target.Words() + if len(outputs) == 0 { + return nil, fmt.Errorf("%smissing output: %v", pos(node), x) + } + ret.Output = outputs[0].Value(nil) + + if !x.Prerequisites.Const() { + return nil, fmt.Errorf("%sunsupported variable expansion: %v", pos(node), x.Prerequisites.Dump()) + } + for _, input := range x.Prerequisites.Words() { + ret.Inputs = append(ret.Inputs, input.Value(nil)) + } + default: + return nil, fmt.Errorf("%sunexpected line: %#v", pos(node), node) + } + } + + return ret, nil +} + +func (d *Deps) Print() []byte { + // We don't really have to escape every \, but it's simpler, + // and ninja will handle it. + replacer := strings.NewReplacer(" ", "\\ ", + ":", "\\:", + "#", "\\#", + "$", "$$", + "\\", "\\\\") + + b := &bytes.Buffer{} + fmt.Fprintf(b, "%s:", replacer.Replace(d.Output)) + for _, input := range d.Inputs { + fmt.Fprintf(b, " %s", replacer.Replace(input)) + } + fmt.Fprintln(b) + return b.Bytes() +} diff --git a/cmd/dep_fixer/deps_test.go b/cmd/dep_fixer/deps_test.go new file mode 100644 index 00000000..0a779b76 --- /dev/null +++ b/cmd/dep_fixer/deps_test.go @@ -0,0 +1,389 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bytes" + "io" + "io/ioutil" + "os" + "testing" +) + +func TestParse(t *testing.T) { + testCases := []struct { + name string + input string + output Deps + err error + }{ + // These come from the ninja test suite + { + name: "Basic", + input: "build/ninja.o: ninja.cc ninja.h eval_env.h manifest_parser.h", + output: Deps{ + Output: "build/ninja.o", + Inputs: []string{ + "ninja.cc", + "ninja.h", + "eval_env.h", + "manifest_parser.h", + }, + }, + }, + { + name: "EarlyNewlineAndWhitespace", + input: ` \ + out: in`, + output: Deps{ + Output: "out", + Inputs: []string{"in"}, + }, + }, + { + name: "Continuation", + input: `foo.o: \ + bar.h baz.h +`, + output: Deps{ + Output: "foo.o", + Inputs: []string{"bar.h", "baz.h"}, + }, + }, + { + name: "CarriageReturnContinuation", + input: "foo.o: \\\r\n bar.h baz.h\r\n", + output: Deps{ + Output: "foo.o", + Inputs: []string{"bar.h", "baz.h"}, + }, + }, + { + name: "BackSlashes", + input: `Project\Dir\Build\Release8\Foo\Foo.res : \ + Dir\Library\Foo.rc \ + Dir\Library\Version\Bar.h \ + Dir\Library\Foo.ico \ + Project\Thing\Bar.tlb \ +`, + output: Deps{ + Output: `Project\Dir\Build\Release8\Foo\Foo.res`, + Inputs: []string{ + `Dir\Library\Foo.rc`, + `Dir\Library\Version\Bar.h`, + `Dir\Library\Foo.ico`, + `Project\Thing\Bar.tlb`, + }, + }, + }, + { + name: "Spaces", + input: `a\ bc\ def: a\ b c d`, + output: Deps{ + Output: `a bc def`, + Inputs: []string{"a b", "c", "d"}, + }, + }, + { + name: "Escapes", + input: `\!\@\#$$\%\^\&\\:`, + output: Deps{ + Output: `\!\@#$\%\^\&\`, + }, + }, + { + name: "SpecialChars", + // Ninja includes a number of '=', but our parser can't handle that, + // since it sees the equals and switches over to assuming it's an + // assignment. + // + // We don't have any files in our tree that contain an '=' character, + // and Kati can't handle parsing this either, so for now I'm just + // going to remove all the '=' characters below. + // + // It looks like make will only do this for the first + // dependency, but not later dependencies. + input: `C\:/Program\ Files\ (x86)/Microsoft\ crtdefs.h: \ + en@quot.header~ t+t-x!1 \ + openldap/slapd.d/cnconfig/cnschema/cn{0}core.ldif \ + Fu` + "\303\244ball", + output: Deps{ + Output: "C:/Program Files (x86)/Microsoft crtdefs.h", + Inputs: []string{ + "en@quot.header~", + "t+t-x!1", + "openldap/slapd.d/cnconfig/cnschema/cn{0}core.ldif", + "Fu\303\244ball", + }, + }, + }, + // Ninja's UnifyMultipleOutputs and RejectMultipleDifferentOutputs tests have been omitted, + // since we don't want the same behavior. + + // Our own tests + { + name: "Multiple outputs", + input: `a b: c +a: d +b: e`, + output: Deps{ + Output: "b", + Inputs: []string{ + "c", + "d", + "e", + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + out, err := Parse("test.d", bytes.NewBufferString(tc.input)) + if err != tc.err { + t.Fatalf("Unexpected error: %v (expected %v)", err, tc.err) + } + + if out.Output != tc.output.Output { + t.Errorf("output file doesn't match:\n"+ + " str: %#v\n"+ + "want: %#v\n"+ + " got: %#v", tc.input, tc.output.Output, out.Output) + } + + matches := true + if len(out.Inputs) != len(tc.output.Inputs) { + matches = false + } else { + for i := range out.Inputs { + if out.Inputs[i] != tc.output.Inputs[i] { + matches = false + } + } + } + if !matches { + t.Errorf("input files don't match:\n"+ + " str: %#v\n"+ + "want: %#v\n"+ + " got: %#v", tc.input, tc.output.Inputs, out.Inputs) + } + }) + } +} + +func BenchmarkParsing(b *testing.B) { + // Write it out to a file to most closely match ninja's perftest + tmpfile, err := ioutil.TempFile("", "depfile") + if err != nil { + b.Fatal("Failed to create temp file:", err) + } + defer os.Remove(tmpfile.Name()) + _, err = io.WriteString(tmpfile, `out/soong/.intermediates/external/ninja/ninja/linux_glibc_x86_64/obj/external/ninja/src/ninja.o: \ + external/ninja/src/ninja.cc external/libcxx/include/errno.h \ + external/libcxx/include/__config \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/features.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/predefs.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/sys/cdefs.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/wordsize.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/gnu/stubs.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/errno.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/errno.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/linux/errno.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/asm/errno.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/asm-generic/errno.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/asm-generic/errno-base.h \ + external/libcxx/include/limits.h \ + prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/limits.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/limits.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/posix1_lim.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/local_lim.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/linux/limits.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/posix2_lim.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/xopen_lim.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ + external/libcxx/include/stdio.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/stdio.h \ + external/libcxx/include/stddef.h \ + prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/stddef.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/types.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/typesizes.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/libio.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/_G_config.h \ + external/libcxx/include/wchar.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/wchar.h \ + prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/stdarg.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/sys_errlist.h \ + external/libcxx/include/stdlib.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/stdlib.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/waitflags.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/waitstatus.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/endian.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/endian.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/byteswap.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/xlocale.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/sys/types.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/time.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/sys/select.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/select.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/sigset.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/time.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/select2.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/sys/sysmacros.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/alloca.h \ + external/libcxx/include/string.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/string.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/getopt.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/unistd.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/posix_opt.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/environments.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/confname.h \ + external/ninja/src/browse.h external/ninja/src/build.h \ + external/libcxx/include/cstdio external/libcxx/include/map \ + external/libcxx/include/__tree external/libcxx/include/iterator \ + external/libcxx/include/iosfwd \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/wchar.h \ + external/libcxx/include/__functional_base \ + external/libcxx/include/type_traits external/libcxx/include/cstddef \ + prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/__stddef_max_align_t.h \ + external/libcxx/include/__nullptr external/libcxx/include/typeinfo \ + external/libcxx/include/exception external/libcxx/include/cstdlib \ + external/libcxx/include/cstdint external/libcxx/include/stdint.h \ + prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/stdint.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/stdint.h \ + external/libcxx/include/new external/libcxx/include/utility \ + external/libcxx/include/__tuple \ + external/libcxx/include/initializer_list \ + external/libcxx/include/cstring external/libcxx/include/__debug \ + external/libcxx/include/memory external/libcxx/include/limits \ + external/libcxx/include/__undef_macros external/libcxx/include/tuple \ + external/libcxx/include/stdexcept external/libcxx/include/cassert \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/assert.h \ + external/libcxx/include/atomic external/libcxx/include/algorithm \ + external/libcxx/include/functional external/libcxx/include/queue \ + external/libcxx/include/deque external/libcxx/include/__split_buffer \ + external/libcxx/include/vector external/libcxx/include/__bit_reference \ + external/libcxx/include/climits external/libcxx/include/set \ + external/libcxx/include/string external/libcxx/include/string_view \ + external/libcxx/include/__string external/libcxx/include/cwchar \ + external/libcxx/include/cwctype external/libcxx/include/cctype \ + external/libcxx/include/ctype.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/ctype.h \ + external/libcxx/include/wctype.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/wctype.h \ + external/ninja/src/graph.h external/ninja/src/eval_env.h \ + external/ninja/src/string_piece.h external/ninja/src/timestamp.h \ + external/ninja/src/util.h external/ninja/src/exit_status.h \ + external/ninja/src/line_printer.h external/ninja/src/metrics.h \ + external/ninja/src/build_log.h external/ninja/src/hash_map.h \ + external/libcxx/include/unordered_map \ + external/libcxx/include/__hash_table external/libcxx/include/cmath \ + external/libcxx/include/math.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/math.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/huge_val.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/huge_valf.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/huge_vall.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/inf.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/nan.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/mathdef.h \ + prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/mathcalls.h \ + external/ninja/src/deps_log.h external/ninja/src/clean.h \ + external/ninja/src/debug_flags.h external/ninja/src/disk_interface.h \ + external/ninja/src/graphviz.h external/ninja/src/manifest_parser.h \ + external/ninja/src/lexer.h external/ninja/src/state.h \ + external/ninja/src/version.h`) + tmpfile.Close() + if err != nil { + b.Fatal("Failed to write dep file:", err) + } + b.ResetTimer() + + for n := 0; n < b.N; n++ { + depfile, err := ioutil.ReadFile(tmpfile.Name()) + if err != nil { + b.Fatal("Failed to read dep file:", err) + } + + _, err = Parse(tmpfile.Name(), bytes.NewBuffer(depfile)) + if err != nil { + b.Fatal("Failed to parse:", err) + } + } +} + +func TestDepPrint(t *testing.T) { + testCases := []struct { + name string + input Deps + output string + }{ + { + name: "Empty", + input: Deps{ + Output: "a", + }, + output: "a:", + }, + { + name: "Basic", + input: Deps{ + Output: "a", + Inputs: []string{"b", "c"}, + }, + output: "a: b c", + }, + { + name: "Escapes", + input: Deps{ + Output: `\!\@#$\%\^\&\`, + }, + output: `\\!\\@\#$$\\%\\^\\&\\:`, + }, + { + name: "Spaces", + input: Deps{ + Output: "a b", + Inputs: []string{"c d", "e f "}, + }, + output: `a\ b: c\ d e\ f\ `, + }, + { + name: "SpecialChars", + input: Deps{ + Output: "C:/Program Files (x86)/Microsoft crtdefs.h", + Inputs: []string{ + "en@quot.header~", + "t+t-x!1", + "openldap/slapd.d/cnconfig/cnschema/cn{0}core.ldif", + "Fu\303\244ball", + }, + }, + output: `C\:/Program\ Files\ (x86)/Microsoft\ crtdefs.h: en@quot.header~ t+t-x!1 openldap/slapd.d/cnconfig/cnschema/cn{0}core.ldif Fu` + "\303\244ball", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + out := tc.input.Print() + outStr := string(out) + want := tc.output + "\n" + + if outStr != want { + t.Errorf("output doesn't match:\nwant:%q\n got:%q", want, outStr) + } + }) + } +} diff --git a/cmd/dep_fixer/main.go b/cmd/dep_fixer/main.go new file mode 100644 index 00000000..bac3772b --- /dev/null +++ b/cmd/dep_fixer/main.go @@ -0,0 +1,67 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This tool reads "make"-like dependency files, and outputs a canonical version +// that can be used by ninja. Ninja doesn't support multiple output files (even +// though it doesn't care what the output file is, or whether it matches what is +// expected). +package main + +import ( + "bytes" + "flag" + "fmt" + "io/ioutil" + "log" + "os" +) + +func main() { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "Usage: %s <depfile.d>") + flag.PrintDefaults() + } + output := flag.String("o", "", "Optional output file (defaults to rewriting source if necessary)") + flag.Parse() + + if flag.NArg() != 1 { + log.Fatal("Expected a single file as an argument") + } + + old, err := ioutil.ReadFile(flag.Arg(0)) + if err != nil { + log.Fatalf("Error opening %q: %v", flag.Arg(0), err) + } + + deps, err := Parse(flag.Arg(0), bytes.NewBuffer(append([]byte(nil), old...))) + if err != nil { + log.Fatalf("Failed to parse: %v", err) + } + + new := deps.Print() + + if *output == "" || *output == flag.Arg(0) { + if !bytes.Equal(old, new) { + err := ioutil.WriteFile(flag.Arg(0), new, 0666) + if err != nil { + log.Fatalf("Failed to write: %v", err) + } + } + } else { + err := ioutil.WriteFile(*output, new, 0666) + if err != nil { + log.Fatalf("Failed to write to %q: %v", *output, err) + } + } +} diff --git a/java/proto.go b/java/proto.go index cfd733ab..3ec2e8a6 100644 --- a/java/proto.go +++ b/java/proto.go @@ -30,12 +30,14 @@ var ( proto = pctx.AndroidStaticRule("protoc", blueprint.RuleParams{ Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` + - `$protocCmd $protoOut=$protoOutParams:$out.tmp -I $protoBase $protoFlags $in && ` + + `$protocCmd $protoOut=$protoOutParams:$out.tmp --dependency_out=$out.d -I $protoBase $protoFlags $in && ` + `${config.SoongZipCmd} -jar -o $out -C $out.tmp -D $out.tmp && rm -rf $out.tmp`, CommandDeps: []string{ "$protocCmd", "${config.SoongZipCmd}", }, + Depfile: "${out}.d", + Deps: blueprint.DepsGCC, }, "protoBase", "protoFlags", "protoOut", "protoOutParams") ) diff --git a/python/proto.go b/python/proto.go index 82ee3cb0..42987fab 100644 --- a/python/proto.go +++ b/python/proto.go @@ -29,12 +29,14 @@ var ( proto = pctx.AndroidStaticRule("protoc", blueprint.RuleParams{ Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` + - `$protocCmd --python_out=$out.tmp -I $protoBase $protoFlags $in && ` + + `$protocCmd --python_out=$out.tmp --dependency_out=$out.d -I $protoBase $protoFlags $in && ` + `$parCmd -o $out -P $pkgPath -C $out.tmp -D $out.tmp && rm -rf $out.tmp`, CommandDeps: []string{ "$protocCmd", "$parCmd", }, + Depfile: "${out}.d", + Deps: blueprint.DepsGCC, }, "protoBase", "protoFlags", "pkgPath") ) |