diff options
30 files changed, 1184 insertions, 56 deletions
diff --git a/Android.bp b/Android.bp index 4a06a113a..7f4fbca47 100644 --- a/Android.bp +++ b/Android.bp @@ -235,6 +235,7 @@ bootstrap_go_package { "java/java_resources.go", "java/proto.go", "java/sdk_library.go", + "java/support_libraries.go", "java/system_modules.go", ], testSrcs: [ diff --git a/android/config.go b/android/config.go index 40ba8c177..7ab5f5634 100644 --- a/android/config.go +++ b/android/config.go @@ -799,6 +799,22 @@ func (c *deviceConfig) PgoAdditionalProfileDirs() []string { return c.config.productVariables.PgoAdditionalProfileDirs } +func (c *deviceConfig) VendorSepolicyDirs() []string { + return c.config.productVariables.BoardVendorSepolicyDirs +} + +func (c *deviceConfig) OdmSepolicyDirs() []string { + return c.config.productVariables.BoardOdmSepolicyDirs +} + +func (c *deviceConfig) PlatPublicSepolicyDir() string { + return c.config.productVariables.BoardPlatPublicSepolicyDir +} + +func (c *deviceConfig) PlatPrivateSepolicyDir() string { + return c.config.productVariables.BoardPlatPrivateSepolicyDir +} + func (c *config) IntegerOverflowDisabledForPath(path string) bool { if c.productVariables.IntegerOverflowExcludePaths == nil { return false diff --git a/android/module.go b/android/module.go index 552d16517..4797c0c23 100644 --- a/android/module.go +++ b/android/module.go @@ -1015,6 +1015,13 @@ func (a *androidModuleContext) skipInstall(fullInstallPath OutputPath) bool { return true } + // We'll need a solution for choosing which of modules with the same name in different + // namespaces to install. For now, reuse the list of namespaces exported to Make as the + // list of namespaces to install in a Soong-only build. + if !a.module.base().commonProperties.NamespaceExportedToMake { + return true + } + if a.Device() { if a.Config().SkipDeviceInstall() { return true diff --git a/android/variable.go b/android/variable.go index 205790333..ce2cb232c 100644 --- a/android/variable.go +++ b/android/variable.go @@ -209,6 +209,11 @@ type productVariables struct { PgoAdditionalProfileDirs []string `json:",omitempty"` + BoardVendorSepolicyDirs []string `json:",omitempty"` + BoardOdmSepolicyDirs []string `json:",omitempty"` + BoardPlatPublicSepolicyDir string `json:",omitempty"` + BoardPlatPrivateSepolicyDir string `json:",omitempty"` + VendorVars map[string]map[string]string `json:",omitempty"` } diff --git a/androidmk/Android.bp b/androidmk/Android.bp index 442452f83..1d939b0c5 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/cmd/androidmk/androidmk_test.go b/androidmk/cmd/androidmk/androidmk_test.go index edf3d42fd..37e2427c0 100644 --- a/androidmk/cmd/androidmk/androidmk_test.go +++ b/androidmk/cmd/androidmk/androidmk_test.go @@ -576,6 +576,19 @@ include $(call all-makefiles-under,$(LOCAL_PATH)) } `, }, + { + desc: "cc_library shared_libs", + in: ` + include $(CLEAR_VARS) + LOCAL_SHARED_LIBRARIES := libfoo + include $(BUILD_SHARED_LIBRARY) + `, + expected: ` + cc_library_shared { + shared_libs: ["libfoo"], + } + `, + }, } func TestEndToEnd(t *testing.T) { diff --git a/androidmk/parser/make_strings.go b/androidmk/parser/make_strings.go index e6885a8cc..4b782a23c 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 8ad3d74c4..6995e8913 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 89ee308f7..89c1af904 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 000000000..f562c29e8 --- /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 7a514fae5..167e470b4 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/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go index e4d4e34e5..a610a1783 100644 --- a/bpfix/bpfix/bpfix.go +++ b/bpfix/bpfix/bpfix.go @@ -22,6 +22,7 @@ import ( "fmt" "io" "path/filepath" + "strings" "github.com/google/blueprint/parser" ) @@ -220,6 +221,10 @@ func (f *Fixer) rewriteIncorrectAndroidmkAndroidLibraries() error { continue } + if !strings.HasPrefix(mod.Type, "java_") && !strings.HasPrefix(mod.Type, "android_") { + continue + } + hasAndroidLibraries := hasNonEmptyLiteralListProperty(mod, "android_libs") hasStaticAndroidLibraries := hasNonEmptyLiteralListProperty(mod, "android_static_libs") hasResourceDirs := hasNonEmptyLiteralListProperty(mod, "resource_dirs") @@ -384,7 +389,7 @@ func (f *Fixer) removeMatchingModuleListProperties(canonicalName string, legacyN continue } legacyList, ok := getLiteralListProperty(mod, legacyName) - if !ok { + if !ok || len(legacyList.Values) == 0 { continue } canonicalList, ok := getLiteralListProperty(mod, canonicalName) @@ -392,6 +397,10 @@ func (f *Fixer) removeMatchingModuleListProperties(canonicalName string, legacyN continue } filterExpressionList(legacyList, canonicalList) + + if len(legacyList.Values) == 0 { + removeProperty(mod, legacyName) + } } return nil } diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go index 51708eb66..8fed0a2d8 100644 --- a/bpfix/bpfix/bpfix_test.go +++ b/bpfix/bpfix/bpfix_test.go @@ -73,20 +73,24 @@ func implFilterListTest(t *testing.T, local_include_dirs []string, export_includ // lookup legacy property mod := fixer.tree.Defs[0].(*parser.Module) - _, found := mod.GetProperty("local_include_dirs") - if !found { - t.Fatalf("failed to include key local_include_dirs in parse tree") + + expectedResultString := fmt.Sprintf("%q", expectedResult) + if expectedResult == nil { + expectedResultString = "unset" } // check that the value for the legacy property was updated to the correct value errorHeader := fmt.Sprintf("\nFailed to correctly simplify key 'local_include_dirs' in the presence of 'export_include_dirs.'\n"+ "original local_include_dirs: %q\n"+ "original export_include_dirs: %q\n"+ - "expected result: %q\n"+ + "expected result: %s\n"+ "actual result: ", - local_include_dirs, export_include_dirs, expectedResult) - result, ok := mod.GetProperty("local_include_dirs") - if !ok { + local_include_dirs, export_include_dirs, expectedResultString) + result, found := mod.GetProperty("local_include_dirs") + if !found { + if expectedResult == nil { + return + } t.Fatal(errorHeader + "property not found") } @@ -95,6 +99,10 @@ func implFilterListTest(t *testing.T, local_include_dirs []string, export_includ t.Fatalf("%sproperty is not a list: %v", errorHeader, listResult) } + if expectedResult == nil { + t.Fatalf("%sproperty exists: %v", errorHeader, listResult) + } + actualExpressions := listResult.Values actualValues := make([]string, 0) for _, expr := range actualExpressions { @@ -109,7 +117,7 @@ func implFilterListTest(t *testing.T, local_include_dirs []string, export_includ func TestSimplifyKnownVariablesDuplicatingEachOther(t *testing.T) { // TODO use []Expression{} once buildTree above can support it (which is after b/38325146 is done) - implFilterListTest(t, []string{"include"}, []string{"include"}, []string{}) + implFilterListTest(t, []string{"include"}, []string{"include"}, nil) implFilterListTest(t, []string{"include1"}, []string{"include2"}, []string{"include1"}) implFilterListTest(t, []string{"include1", "include2", "include3", "include4"}, []string{"include2"}, []string{"include1", "include3", "include4"}) diff --git a/bpfix/cmd/bpfix.go b/bpfix/cmd/bpfix.go index 2fde3836f..ccdae1656 100644 --- a/bpfix/cmd/bpfix.go +++ b/bpfix/cmd/bpfix.go @@ -65,7 +65,7 @@ func processFile(filename string, in io.Reader, out io.Writer, fixRequest bpfix. if err != nil { return err } - r := bytes.NewBuffer(src) + r := bytes.NewBuffer(append([]byte(nil), src...)) file, errs := parser.Parse(filename, r, parser.NewScope(nil)) if len(errs) > 0 { for _, err := range errs { diff --git a/cc/config/clang.go b/cc/config/clang.go index 132384950..ba1cd3c01 100644 --- a/cc/config/clang.go +++ b/cc/config/clang.go @@ -177,6 +177,10 @@ func init() { // warning are fixed. "-Wno-enum-compare", "-Wno-enum-compare-switch", + + // Disable c++98-specific warning since Android is not concerned with C++98 + // compatibility. + "-Wno-c++98-compat-extra-semi", }, " ")) } diff --git a/cc/config/global.go b/cc/config/global.go index d998ca2d9..06f6f9ae6 100644 --- a/cc/config/global.go +++ b/cc/config/global.go @@ -124,8 +124,8 @@ var ( // prebuilts/clang default settings. ClangDefaultBase = "prebuilts/clang/host" - ClangDefaultVersion = "clang-4679922" - ClangDefaultShortVersion = "7.0.1" + ClangDefaultVersion = "clang-r328903" + ClangDefaultShortVersion = "7.0.2" // Directories with warnings from Android.bp files. WarningAllowedProjects = []string{ @@ -19,6 +19,8 @@ import ( "path/filepath" "strings" + "github.com/google/blueprint/proptools" + "android/soong/android" "android/soong/cc/config" ) @@ -160,13 +162,8 @@ func (props *PgoProperties) addProfileUseFlags(ctx ModuleContext, flags Flags) F return flags } - // Skip -fprofile-use if 'enable_profile_use' property is set - if props.Pgo.Enable_profile_use != nil && *props.Pgo.Enable_profile_use == false { - return flags - } - - // If the profile file is found, add flags to use the profile - if profileFile := props.getPgoProfileFile(ctx); profileFile.Valid() { + if props.PgoCompile { + profileFile := props.getPgoProfileFile(ctx) profileFilePath := profileFile.Path() profileUseFlags := props.profileUseFlags(ctx, profileFilePath.String()) @@ -257,7 +254,8 @@ func (pgo *pgo) begin(ctx BaseModuleContext) { } } - if !ctx.Config().IsEnvTrue("ANDROID_PGO_NO_PROFILE_USE") { + if !ctx.Config().IsEnvTrue("ANDROID_PGO_NO_PROFILE_USE") && + proptools.BoolDefault(pgo.Properties.Pgo.Enable_profile_use, true) { if profileFile := pgo.Properties.getPgoProfileFile(ctx); profileFile.Valid() { pgo.Properties.PgoCompile = true } diff --git a/cc/proto.go b/cc/proto.go index 22e50ab48..6e6f95ee9 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 000000000..d2d1113d8 --- /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 000000000..64c97f52a --- /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 000000000..0a779b766 --- /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 000000000..bac3772be --- /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/androidmk.go b/java/androidmk.go index b168f2c7c..5a4a082b4 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -286,26 +286,52 @@ func (ddoc *Droiddoc) AndroidMk() android.AndroidMkData { if ddoc.Javadoc.stubsSrcJar != nil { fmt.Fprintln(w, "LOCAL_DROIDDOC_STUBS_SRCJAR := ", ddoc.Javadoc.stubsSrcJar.String()) } + if ddoc.checkCurrentApiTimestamp != nil { + fmt.Fprintln(w, ".PHONY:", ddoc.Name()+"-check-current-api") + fmt.Fprintln(w, ddoc.Name()+"-check-current-api:", + ddoc.checkCurrentApiTimestamp.String()) + + fmt.Fprintln(w, ".PHONY: checkapi") + fmt.Fprintln(w, "check-api:", + ddoc.checkCurrentApiTimestamp.String()) + + fmt.Fprintln(w, ".PHONY: droidcore") + fmt.Fprintln(w, "droidcore: checkapi") + } + if ddoc.updateCurrentApiTimestamp != nil { + fmt.Fprintln(w, ".PHONY:", ddoc.Name(), "-update-current-api") + fmt.Fprintln(w, ddoc.Name()+"-update-current-api:", + ddoc.updateCurrentApiTimestamp.String()) + + fmt.Fprintln(w, ".PHONY: update-api") + fmt.Fprintln(w, "update-api:", + ddoc.updateCurrentApiTimestamp.String()) + } + if ddoc.checkLastReleasedApiTimestamp != nil { + fmt.Fprintln(w, ".PHONY:", ddoc.Name()+"-check-last-released-api") + fmt.Fprintln(w, ddoc.Name()+"-check-last-released-api:", + ddoc.checkLastReleasedApiTimestamp.String()) + } apiFilePrefix := "INTERNAL_PLATFORM_" if String(ddoc.properties.Api_tag_name) != "" { apiFilePrefix += String(ddoc.properties.Api_tag_name) + "_" } - if String(ddoc.properties.Api_filename) != "" { + if ddoc.apiFile != nil { fmt.Fprintln(w, apiFilePrefix+"API_FILE := ", ddoc.apiFile.String()) } - if String(ddoc.properties.Private_api_filename) != "" { + if ddoc.privateApiFile != nil { fmt.Fprintln(w, apiFilePrefix+"PRIVATE_API_FILE := ", ddoc.privateApiFile.String()) } - if String(ddoc.properties.Private_dex_api_filename) != "" { + if ddoc.privateDexApiFile != nil { fmt.Fprintln(w, apiFilePrefix+"PRIVATE_DEX_API_FILE := ", ddoc.privateDexApiFile.String()) } - if String(ddoc.properties.Removed_api_filename) != "" { + if ddoc.removedApiFile != nil { fmt.Fprintln(w, apiFilePrefix+"REMOVED_API_FILE := ", ddoc.removedApiFile.String()) } - if String(ddoc.properties.Removed_dex_api_filename) != "" { + if ddoc.removedDexApiFile != nil { fmt.Fprintln(w, apiFilePrefix+"REMOVED_DEX_API_FILE := ", ddoc.removedDexApiFile.String()) } - if String(ddoc.properties.Exact_api_filename) != "" { + if ddoc.exactApiFile != nil { fmt.Fprintln(w, apiFilePrefix+"EXACT_API_FILE := ", ddoc.exactApiFile.String()) } }, diff --git a/java/config/config.go b/java/config/config.go index 6633f792b..d44315c5c 100644 --- a/java/config/config.go +++ b/java/config/config.go @@ -90,6 +90,7 @@ func init() { pctx.HostBinToolVariable("MergeZipsCmd", "merge_zips") pctx.HostBinToolVariable("Zip2ZipCmd", "zip2zip") pctx.HostBinToolVariable("ZipSyncCmd", "zipsync") + pctx.HostBinToolVariable("ApiCheckCmd", "apicheck") pctx.VariableFunc("DxCmd", func(ctx android.PackageVarContext) string { config := ctx.Config() if config.IsEnvFalse("USE_D8") { diff --git a/java/droiddoc.go b/java/droiddoc.go index 07042a115..202f22bc8 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -45,6 +45,24 @@ var ( }, "outDir", "srcJarDir", "stubsDir", "srcJars", "opts", "bootclasspathArgs", "classpathArgs", "sourcepath", "docZip") + + apiCheck = pctx.AndroidStaticRule("apiCheck", + blueprint.RuleParams{ + Command: `( ${config.ApiCheckCmd} -JXmx1024m -J"classpath $classpath" $opts ` + + `$apiFile $apiFileToCheck $removedApiFile $removedApiFileToCheck ` + + `&& touch $out ) || (echo $msg ; exit 38)`, + CommandDeps: []string{ + "${config.ApiCheckCmd}", + }, + }, + "classpath", "opts", "apiFile", "apiFileToCheck", "removedApiFile", "removedApiFileToCheck", "msg") + + updateApi = pctx.AndroidStaticRule("updateApi", + blueprint.RuleParams{ + Command: `( ( cp -f $apiFileToCheck $apiFile && cp -f $removedApiFileToCheck $removedApiFile ) ` + + `&& touch $out ) || (echo failed to update public API ; exit 38)`, + }, + "apiFile", "apiFileToCheck", "removedApiFile", "removedApiFileToCheck") ) func init() { @@ -94,6 +112,14 @@ type JavadocProperties struct { Sdk_version *string `android:"arch_variant"` } +type ApiToCheck struct { + Api_file *string + + Removed_api_file *string + + Args *string +} + type DroiddocProperties struct { // directory relative to top of the source tree that contains doc templates files. Custom_template *string @@ -157,6 +183,12 @@ type DroiddocProperties struct { // if set to false, don't allow droiddoc to generate stubs source files. Defaults to true. Create_stubs *bool + + Check_api struct { + Last_released ApiToCheck + + Current ApiToCheck + } } type Javadoc struct { @@ -189,6 +221,10 @@ type Droiddoc struct { removedApiFile android.WritablePath removedDexApiFile android.WritablePath exactApiFile android.WritablePath + + checkCurrentApiTimestamp android.WritablePath + updateCurrentApiTimestamp android.WritablePath + checkLastReleasedApiTimestamp android.WritablePath } func InitDroiddocModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) { @@ -420,6 +456,32 @@ func (j *Javadoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { }) } +func (d *Droiddoc) checkCurrentApi() bool { + if String(d.properties.Check_api.Current.Api_file) != "" && + String(d.properties.Check_api.Current.Removed_api_file) != "" { + return true + } else if String(d.properties.Check_api.Current.Api_file) != "" { + panic("check_api.current.removed_api_file: has to be non empty!") + } else if String(d.properties.Check_api.Current.Removed_api_file) != "" { + panic("check_api.current.api_file: has to be non empty!") + } + + return false +} + +func (d *Droiddoc) checkLastReleasedApi() bool { + if String(d.properties.Check_api.Last_released.Api_file) != "" && + String(d.properties.Check_api.Last_released.Removed_api_file) != "" { + return true + } else if String(d.properties.Check_api.Last_released.Api_file) != "" { + panic("check_api.last_released.removed_api_file: has to be non empty!") + } else if String(d.properties.Check_api.Last_released.Removed_api_file) != "" { + panic("check_api.last_released.api_file: has to be non empty!") + } + + return false +} + func (d *Droiddoc) DepsMutator(ctx android.BottomUpMutatorContext) { d.Javadoc.addDeps(ctx) @@ -435,6 +497,16 @@ func (d *Droiddoc) DepsMutator(ctx android.BottomUpMutatorContext) { // knowntags may contain filegroup or genrule. android.ExtractSourcesDeps(ctx, d.properties.Knowntags) + + if d.checkCurrentApi() { + android.ExtractSourceDeps(ctx, d.properties.Check_api.Current.Api_file) + android.ExtractSourceDeps(ctx, d.properties.Check_api.Current.Removed_api_file) + } + + if d.checkLastReleasedApi() { + android.ExtractSourceDeps(ctx, d.properties.Check_api.Last_released.Api_file) + android.ExtractSourceDeps(ctx, d.properties.Check_api.Last_released.Removed_api_file) + } } func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -547,12 +619,19 @@ func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { } var implicitOutputs android.WritablePaths - if String(d.properties.Api_filename) != "" { - d.apiFile = android.PathForModuleOut(ctx, String(d.properties.Api_filename)) + + if d.checkCurrentApi() || d.checkLastReleasedApi() || String(d.properties.Api_filename) != "" { + d.apiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.txt") args = args + " -api " + d.apiFile.String() implicitOutputs = append(implicitOutputs, d.apiFile) } + if d.checkCurrentApi() || d.checkLastReleasedApi() || String(d.properties.Removed_api_filename) != "" { + d.removedApiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_removed.txt") + args = args + " -removedApi " + d.removedApiFile.String() + implicitOutputs = append(implicitOutputs, d.removedApiFile) + } + if String(d.properties.Private_api_filename) != "" { d.privateApiFile = android.PathForModuleOut(ctx, String(d.properties.Private_api_filename)) args = args + " -privateApi " + d.privateApiFile.String() @@ -565,12 +644,6 @@ func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { implicitOutputs = append(implicitOutputs, d.privateDexApiFile) } - if String(d.properties.Removed_api_filename) != "" { - d.removedApiFile = android.PathForModuleOut(ctx, String(d.properties.Removed_api_filename)) - args = args + " -removedApi " + d.removedApiFile.String() - implicitOutputs = append(implicitOutputs, d.removedApiFile) - } - if String(d.properties.Removed_dex_api_filename) != "" { d.removedDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Removed_dex_api_filename)) args = args + " -removedDexApi " + d.removedDexApiFile.String() @@ -624,6 +697,91 @@ func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { "docZip": d.Javadoc.docZip.String(), }, }) + + java8Home := ctx.Config().Getenv("ANDROID_JAVA8_HOME") + + checkApiClasspath := classpath{jsilver, doclava, android.PathForSource(ctx, java8Home, "lib/tools.jar")} + + if d.checkCurrentApi() && !ctx.Config().IsPdkBuild() { + d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp") + + apiFile := ctx.ExpandSource(String(d.properties.Check_api.Current.Api_file), + "check_api.current.api_file") + removedApiFile := ctx.ExpandSource(String(d.properties.Check_api.Current.Removed_api_file), + "check_api.current_removed_api_file") + + ctx.Build(pctx, android.BuildParams{ + Rule: apiCheck, + Description: "Current API check", + Output: d.checkCurrentApiTimestamp, + Inputs: nil, + Implicits: append(android.Paths{apiFile, removedApiFile, d.apiFile, d.removedApiFile}, + checkApiClasspath...), + Args: map[string]string{ + "classpath": checkApiClasspath.FormJavaClassPath(""), + "opts": String(d.properties.Check_api.Current.Args), + "apiFile": apiFile.String(), + "apiFileToCheck": d.apiFile.String(), + "removedApiFile": removedApiFile.String(), + "removedApiFileToCheck": d.removedApiFile.String(), + "msg": fmt.Sprintf(`\n******************************\n`+ + `You have tried to change the API from what has been previously approved.\n\n`+ + `To make these errors go away, you have two choices:\n`+ + ` 1. You can add '@hide' javadoc comments to the methods, etc. listed in the\n`+ + ` errors above.\n\n`+ + ` 2. You can update current.txt by executing the following command:`+ + ` make %s-update-current-api\n\n`+ + ` To submit the revised current.txt to the main Android repository,`+ + ` you will need approval.\n`+ + `******************************\n`, ctx.ModuleName()), + }, + }) + + d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "update_current_api.timestamp") + + ctx.Build(pctx, android.BuildParams{ + Rule: updateApi, + Description: "update current API", + Output: d.updateCurrentApiTimestamp, + Implicits: append(android.Paths{}, apiFile, removedApiFile, d.apiFile, d.removedApiFile), + Args: map[string]string{ + "apiFile": apiFile.String(), + "apiFileToCheck": d.apiFile.String(), + "removedApiFile": removedApiFile.String(), + "removedApiFileToCheck": d.removedApiFile.String(), + }, + }) + } + + if d.checkLastReleasedApi() && !ctx.Config().IsPdkBuild() { + d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "check_last_released_api.timestamp") + + apiFile := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Api_file), + "check_api.last_released.api_file") + removedApiFile := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Removed_api_file), + "check_api.last_released.removed_api_file") + + ctx.Build(pctx, android.BuildParams{ + Rule: apiCheck, + Description: "Last Released API check", + Output: d.checkLastReleasedApiTimestamp, + Inputs: nil, + Implicits: append(android.Paths{apiFile, removedApiFile, d.apiFile, d.removedApiFile}, + checkApiClasspath...), + Args: map[string]string{ + "classpath": checkApiClasspath.FormJavaClassPath(""), + "opts": String(d.properties.Check_api.Last_released.Args), + "apiFile": apiFile.String(), + "apiFileToCheck": d.apiFile.String(), + "removedApiFile": removedApiFile.String(), + "removedApiFileToCheck": d.removedApiFile.String(), + "msg": `\n******************************\n` + + `You have tried to change the API from what has been previously released in\n` + + `an SDK. Please fix the errors listed above.\n` + + `******************************\n`, + }, + }) + } } var droiddocTemplateTag = dependencyTag{name: "droiddoc-template"} diff --git a/java/java_test.go b/java/java_test.go index ea524962f..4a0229eb0 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -175,6 +175,7 @@ func testContext(config android.Config, bp string, "jdk8/jre/lib/jce.jar": nil, "jdk8/jre/lib/rt.jar": nil, + "jdk8/lib/tools.jar": nil, "bar-doc/a.java": nil, "bar-doc/b.java": nil, diff --git a/java/proto.go b/java/proto.go index cfd733ab7..3ec2e8a6b 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/java/sdk_library.go b/java/sdk_library.go index 703401cf1..ee6998c39 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -68,10 +68,9 @@ var ( // // TODO: these are big features that are currently missing // 1) check for API consistency -// 2) install stubs libs as the dist artifacts -// 3) ensuring that apps have appropriate <uses-library> tag -// 4) disallowing linking to the runtime shared lib -// 5) HTML generation +// 2) ensuring that apps have appropriate <uses-library> tag +// 3) disallowing linking to the runtime shared lib +// 4) HTML generation func init() { android.RegisterModuleType("java_sdk_library", sdkLibraryFactory) @@ -155,15 +154,31 @@ func (module *sdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) } func (module *sdkLibrary) AndroidMk() android.AndroidMkData { - // Create a phony module that installs the impl library, for the case when this lib is - // in PRODUCT_PACKAGES. return android.AndroidMkData{ Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { + // Create a phony module that installs the impl library, for the case when this lib is + // in PRODUCT_PACKAGES. fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)") fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir) fmt.Fprintln(w, "LOCAL_MODULE :=", name) fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES := "+module.implName()) fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)") + // Create dist rules to install the stubs libs to the dist dir + if len(module.publicApiStubsPath) == 1 { + fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+ + module.publicApiStubsPath.Strings()[0]+ + ":"+path.Join("apistubs", "public", module.BaseModuleName()+".jar")+")") + } + if len(module.systemApiStubsPath) == 1 { + fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+ + module.systemApiStubsPath.Strings()[0]+ + ":"+path.Join("apistubs", "system", module.BaseModuleName()+".jar")+")") + } + if len(module.testApiStubsPath) == 1 { + fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+ + module.testApiStubsPath.Strings()[0]+ + ":"+path.Join("apistubs", "test", module.BaseModuleName()+".jar")+")") + } }, } } diff --git a/java/support_libraries.go b/java/support_libraries.go new file mode 100644 index 000000000..320afae19 --- /dev/null +++ b/java/support_libraries.go @@ -0,0 +1,66 @@ +// 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 java + +import ( + "sort" + "strings" + + "android/soong/android" +) + +func init() { + android.RegisterMakeVarsProvider(pctx, supportLibrariesMakeVarsProvider) +} + +func supportLibrariesMakeVarsProvider(ctx android.MakeVarsContext) { + var supportAars, supportJars []string + + sctx := ctx.SingletonContext() + sctx.VisitAllModules(func(module android.Module) { + dir := sctx.ModuleDir(module) + switch { + case strings.HasPrefix(dir, "prebuilts/sdk/current/extras"), + dir == "prebuilts/sdk/current/androidx", + dir == "prebuilts/sdk/current/car", + dir == "prebuilts/sdk/current/optional", + dir == "prebuilts/sdk/current/support": + // Support library + default: + // Not a support library + return + } + + name := sctx.ModuleName(module) + if strings.HasSuffix(name, "-nodeps") { + return + } + + switch module.(type) { + case *AndroidLibrary, *AARImport: + supportAars = append(supportAars, name) + case *Library, *Import: + supportJars = append(supportJars, name) + default: + sctx.ModuleErrorf(module, "unknown module type %t", module) + } + }) + + sort.Strings(supportAars) + sort.Strings(supportJars) + + ctx.Strict("SUPPORT_LIBRARIES_AARS", strings.Join(supportAars, " ")) + ctx.Strict("SUPPORT_LIBRARIES_JARS", strings.Join(supportJars, " ")) +} diff --git a/python/proto.go b/python/proto.go index 82ee3cb0d..42987fab9 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") ) |