Merge "Disable dtor inlining for clang-tidy"
diff --git a/Android.bp b/Android.bp
index 4a06a11..2daa958 100644
--- a/Android.bp
+++ b/Android.bp
@@ -233,8 +233,10 @@
"java/jacoco.go",
"java/java.go",
"java/java_resources.go",
+ "java/prebuilt_apis.go",
"java/proto.go",
"java/sdk_library.go",
+ "java/support_libraries.go",
"java/system_modules.go",
],
testSrcs: [
@@ -334,6 +336,7 @@
name: "libatomic",
defaults: ["linux_bionic_supported"],
vendor_available: true,
+ recovery_available: true,
arch: {
arm: {
instruction_set: "arm",
@@ -345,6 +348,7 @@
name: "libgcc",
defaults: ["linux_bionic_supported"],
vendor_available: true,
+ recovery_available: true,
arch: {
arm: {
instruction_set: "arm",
diff --git a/android/arch.go b/android/arch.go
index 9f03f1b..ebe9897 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -956,6 +956,7 @@
{"arm", "armv7-a-neon", "cortex-a53", []string{"armeabi-v7a"}},
{"arm", "armv7-a-neon", "cortex-a53.a57", []string{"armeabi-v7a"}},
{"arm", "armv7-a-neon", "cortex-a73", []string{"armeabi-v7a"}},
+ {"arm", "armv7-a-neon", "cortex-a75", []string{"armeabi-v7a"}},
{"arm", "armv7-a-neon", "denver", []string{"armeabi-v7a"}},
{"arm", "armv7-a-neon", "krait", []string{"armeabi-v7a"}},
{"arm", "armv7-a-neon", "kryo", []string{"armeabi-v7a"}},
@@ -967,6 +968,7 @@
{"arm64", "armv8-a", "kryo", []string{"arm64-v8a"}},
{"arm64", "armv8-a", "exynos-m1", []string{"arm64-v8a"}},
{"arm64", "armv8-a", "exynos-m2", []string{"arm64-v8a"}},
+ {"arm64", "armv8-2a", "cortex-a75", []string{"arm64-v8a"}},
{"mips", "mips32-fp", "", []string{"mips"}},
{"mips", "mips32r2-fp", "", []string{"mips"}},
{"mips", "mips32r2-fp-xburst", "", []string{"mips"}},
diff --git a/android/config.go b/android/config.go
index 40ba8c1..7f2a3ac 100644
--- a/android/config.go
+++ b/android/config.go
@@ -468,6 +468,10 @@
return *c.productVariables.ResourceOverlays
}
+func (c *config) PlatformVersionName() string {
+ return String(c.productVariables.Platform_version_name)
+}
+
func (c *config) PlatformSdkVersionInt() int {
return *c.productVariables.Platform_sdk_version
}
@@ -799,6 +803,22 @@
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 552d165..3316a44 100644
--- a/android/module.go
+++ b/android/module.go
@@ -124,6 +124,7 @@
InstallInData() bool
InstallInSanitizerDir() bool
+ InstallInRecovery() bool
RequiredModuleNames() []string
@@ -176,6 +177,7 @@
Target() Target
InstallInData() bool
InstallInSanitizerDir() bool
+ InstallInRecovery() bool
SkipInstall()
ExportedToMake() bool
@@ -191,8 +193,6 @@
}
type commonProperties struct {
- Tags []string
-
// emit build rules for this module
Enabled *bool `android:"arch_variant"`
@@ -239,6 +239,9 @@
// /system/product if product partition does not exist).
Product_specific *bool
+ // Whether this module is installed to recovery partition
+ Recovery *bool
+
// init.rc files to be installed if this module is installed
Init_rc []string
@@ -562,6 +565,10 @@
return false
}
+func (p *ModuleBase) InstallInRecovery() bool {
+ return Bool(p.commonProperties.Recovery)
+}
+
func (a *ModuleBase) generateModuleTarget(ctx ModuleContext) {
allInstalledFiles := Paths{}
allCheckbuildFiles := Paths{}
@@ -1010,11 +1017,22 @@
return a.module.InstallInSanitizerDir()
}
+func (a *androidModuleContext) InstallInRecovery() bool {
+ return a.module.InstallInRecovery()
+}
+
func (a *androidModuleContext) skipInstall(fullInstallPath OutputPath) bool {
if a.module.base().commonProperties.SkipInstall {
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/paths.go b/android/paths.go
index 91dd9a6..8cc3182 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -47,6 +47,7 @@
InstallInData() bool
InstallInSanitizerDir() bool
+ InstallInRecovery() bool
}
var _ ModuleInstallPathContext = ModuleContext(nil)
@@ -948,6 +949,8 @@
var partition string
if ctx.InstallInData() {
partition = "data"
+ } else if ctx.InstallInRecovery() {
+ partition = "recovery/root"
} else if ctx.SocSpecific() {
partition = ctx.DeviceConfig().VendorPath()
} else if ctx.DeviceSpecific() {
diff --git a/android/paths_test.go b/android/paths_test.go
index cd9fbfd..b3dc9de 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -201,6 +201,7 @@
inData bool
inSanitizerDir bool
+ inRecovery bool
}
func (moduleInstallPathContextImpl) Fs() pathtools.FileSystem {
@@ -221,6 +222,10 @@
return m.inSanitizerDir
}
+func (m moduleInstallPathContextImpl) InstallInRecovery() bool {
+ return m.inRecovery
+}
+
func TestPathForModuleInstall(t *testing.T) {
testConfig := TestConfig("", nil)
diff --git a/android/variable.go b/android/variable.go
index 2057903..6599ca3 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -109,6 +109,7 @@
BuildNumberFromFile *string `json:",omitempty"`
DateFromFile *string `json:",omitempty"`
+ Platform_version_name *string `json:",omitempty"`
Platform_sdk_version *int `json:",omitempty"`
Platform_sdk_codename *string `json:",omitempty"`
Platform_sdk_final *bool `json:",omitempty"`
@@ -209,6 +210,11 @@
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 442452f..1d939b0 100644
--- a/androidmk/Android.bp
+++ b/androidmk/Android.bp
@@ -44,6 +44,6 @@
],
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 edf3d42..a54a4d2 100644
--- a/androidmk/cmd/androidmk/androidmk_test.go
+++ b/androidmk/cmd/androidmk/androidmk_test.go
@@ -331,7 +331,7 @@
`,
},
{
- desc: "Keep LOCAL_MODULE_TAGS non-optional",
+ desc: "Warn for LOCAL_MODULE_TAGS non-optional",
in: `
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := debug
@@ -340,7 +340,41 @@
expected: `
cc_library_shared {
- tags: ["debug"],
+ // WARNING: Module tags are not supported in Soong.
+ // Add this module to PRODUCT_PACKAGES_DEBUG in your product file if you want to
+ // force installation for -userdebug and -eng builds.
+}
+`,
+ },
+ {
+ desc: "Custom warning for LOCAL_MODULE_TAGS tests",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := debug tests
+include $(BUILD_SHARED_LIBRARY)
+`,
+
+ expected: `
+cc_library_shared {
+ // WARNING: Module tags are not supported in Soong.
+ // Add this module to PRODUCT_PACKAGES_DEBUG in your product file if you want to
+ // force installation for -userdebug and -eng builds.
+ // WARNING: Module tags are not supported in Soong.
+ // To make a shared library only for tests, use the "cc_test_library" module
+ // type. If you don't use gtest, set "gtest: false".
+}
+`,
+ },
+ {
+ desc: "Ignore LOCAL_MODULE_TAGS tests for cc_test",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_NATIVE_TEST)
+`,
+
+ expected: `
+cc_test {
}
`,
},
@@ -576,6 +610,19 @@
}
`,
},
+ {
+ 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 e6885a8..4b782a2 100644
--- a/androidmk/parser/make_strings.go
+++ b/androidmk/parser/make_strings.go
@@ -90,10 +90,10 @@
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) 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 @@
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 @@
}
}
- ret = append(ret, curMs)
+ if !curMs.Empty() {
+ ret = append(ret, curMs)
+ }
return ret
}
@@ -206,3 +218,64 @@
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 8ad3d74..6995e89 100644
--- a/androidmk/parser/make_strings_test.go
+++ b/androidmk/parser/make_strings_test.go
@@ -99,6 +99,78 @@
}
}
+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 89ee308..89c1af9 100644
--- a/androidmk/parser/parser.go
+++ b/androidmk/parser/parser.go
@@ -35,6 +35,10 @@
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 @@
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 @@
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 @@
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 0000000..f562c29
--- /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 7a514fa..167e470 100644
--- a/androidmk/parser/scope.go
+++ b/androidmk/parser/scope.go
@@ -71,7 +71,7 @@
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 e4d4e34..ee00907 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -22,6 +22,7 @@
"fmt"
"io"
"path/filepath"
+ "strings"
"github.com/google/blueprint/parser"
)
@@ -48,6 +49,8 @@
rewriteIncorrectAndroidmkPrebuilts bool
rewriteIncorrectAndroidmkAndroidLibraries bool
mergeMatchingModuleProperties bool
+ reorderCommonProperties bool
+ removeTags bool
}
func NewFixRequest() FixRequest {
@@ -60,6 +63,8 @@
result.rewriteIncorrectAndroidmkPrebuilts = true
result.rewriteIncorrectAndroidmkAndroidLibraries = true
result.mergeMatchingModuleProperties = true
+ result.reorderCommonProperties = true
+ result.removeTags = true
return result
}
@@ -144,11 +149,12 @@
func (f *Fixer) fixTreeOnce(config FixRequest) error {
if config.simplifyKnownRedundantVariables {
- err := f.simplifyKnownPropertiesDuplicatingEachOther()
+ err := f.runPatchListMod(simplifyKnownPropertiesDuplicatingEachOther)
if err != nil {
return err
}
}
+
if config.rewriteIncorrectAndroidmkPrebuilts {
err := f.rewriteIncorrectAndroidmkPrebuilts()
if err != nil {
@@ -164,7 +170,21 @@
}
if config.mergeMatchingModuleProperties {
- err := f.mergeMatchingModuleProperties()
+ err := f.runPatchListMod(mergeMatchingModuleProperties)
+ if err != nil {
+ return err
+ }
+ }
+
+ if config.reorderCommonProperties {
+ err := f.runPatchListMod(reorderCommonProperties)
+ if err != nil {
+ return err
+ }
+ }
+
+ if config.removeTags {
+ err := f.runPatchListMod(removeTags)
if err != nil {
return err
}
@@ -172,9 +192,10 @@
return nil
}
-func (f *Fixer) simplifyKnownPropertiesDuplicatingEachOther() error {
+func simplifyKnownPropertiesDuplicatingEachOther(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
// remove from local_include_dirs anything in export_include_dirs
- return f.removeMatchingModuleListProperties("export_include_dirs", "local_include_dirs")
+ return removeMatchingModuleListProperties(mod, patchList,
+ "export_include_dirs", "local_include_dirs")
}
func (f *Fixer) rewriteIncorrectAndroidmkPrebuilts() error {
@@ -220,6 +241,10 @@
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")
@@ -244,7 +269,7 @@
return nil
}
-func (f *Fixer) mergeMatchingModuleProperties() error {
+func (f *Fixer) runPatchListMod(modFunc func(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error) error {
// Make sure all the offsets are accurate
buf, err := f.reparse()
if err != nil {
@@ -258,7 +283,7 @@
continue
}
- err := mergeMatchingProperties(&mod.Properties, buf, &patchlist)
+ err := modFunc(mod, buf, &patchlist)
if err != nil {
return err
}
@@ -270,9 +295,12 @@
return err
}
+ // Save a copy of the buffer to print for errors below
+ bufCopy := append([]byte(nil), newBuf.Bytes()...)
+
newTree, err := parse(f.tree.Name, newBuf)
if err != nil {
- return err
+ return fmt.Errorf("Failed to parse: %v\nBuffer:\n%s", err, string(bufCopy))
}
f.tree = newTree
@@ -280,6 +308,129 @@
return nil
}
+var commonPropertyPriorities = []string{
+ "name",
+ "defaults",
+ "device_supported",
+ "host_supported",
+}
+
+func reorderCommonProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
+ if len(mod.Properties) == 0 {
+ return nil
+ }
+
+ pos := mod.LBracePos.Offset + 1
+ stage := ""
+
+ for _, name := range commonPropertyPriorities {
+ idx := propertyIndex(mod.Properties, name)
+ if idx == -1 {
+ continue
+ }
+ if idx == 0 {
+ err := patchlist.Add(pos, pos, stage)
+ if err != nil {
+ return err
+ }
+ stage = ""
+
+ pos = mod.Properties[0].End().Offset + 1
+ mod.Properties = mod.Properties[1:]
+ continue
+ }
+
+ prop := mod.Properties[idx]
+ mod.Properties = append(mod.Properties[:idx], mod.Properties[idx+1:]...)
+
+ stage += string(buf[prop.Pos().Offset : prop.End().Offset+1])
+
+ err := patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, "")
+ if err != nil {
+ return err
+ }
+ }
+
+ if stage != "" {
+ err := patchlist.Add(pos, pos, stage)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func removeTags(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
+ prop, ok := mod.GetProperty("tags")
+ if !ok {
+ return nil
+ }
+ list, ok := prop.Value.(*parser.List)
+ if !ok {
+ return nil
+ }
+
+ replaceStr := ""
+
+ for _, item := range list.Values {
+ str, ok := item.(*parser.String)
+ if !ok {
+ replaceStr += fmt.Sprintf("// ERROR: Unable to parse tag %q\n", item)
+ continue
+ }
+
+ switch str.Value {
+ case "optional":
+ continue
+ case "debug":
+ replaceStr += `// WARNING: Module tags are not supported in Soong.
+ // Add this module to PRODUCT_PACKAGES_DEBUG in your product file if you want to
+ // force installation for -userdebug and -eng builds.
+ `
+ case "eng":
+ replaceStr += `// WARNING: Module tags are not supported in Soong.
+ // Add this module to PRODUCT_PACKAGES_ENG in your product file if you want to
+ // force installation for -eng builds.
+ `
+ case "tests":
+ if strings.Contains(mod.Type, "cc_test") || strings.Contains(mod.Type, "cc_library_static") {
+ continue
+ } else if strings.Contains(mod.Type, "cc_lib") {
+ replaceStr += `// WARNING: Module tags are not supported in Soong.
+ // To make a shared library only for tests, use the "cc_test_library" module
+ // type. If you don't use gtest, set "gtest: false".
+ `
+ } else if strings.Contains(mod.Type, "cc_bin") {
+ replaceStr += `// WARNING: Module tags are not supported in Soong.
+ // For native test binaries, use the "cc_test" module type. Some differences:
+ // - If you don't use gtest, set "gtest: false"
+ // - Binaries will be installed into /data/nativetest[64]/<name>/<name>
+ // - Both 32 & 64 bit versions will be built (as appropriate)
+ `
+ } else if strings.Contains(mod.Type, "java_lib") {
+ replaceStr += `// WARNING: Module tags are not supported in Soong.
+ // For JUnit or similar tests, use the "java_test" module type. A dependency on
+ // Junit will be added by default, if it is using some other runner, set "junit: false".
+ `
+ } else {
+ replaceStr += `// WARNING: Module tags are not supported in Soong.
+ // In most cases, tests are now identified by their module type:
+ // cc_test, java_test, python_test
+ `
+ }
+ default:
+ replaceStr += fmt.Sprintf("// WARNING: Unknown module tag %q\n", str.Value)
+ }
+ }
+
+ return patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, replaceStr)
+}
+
+func mergeMatchingModuleProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
+ return mergeMatchingProperties(&mod.Properties, buf, patchlist)
+}
+
func mergeMatchingProperties(properties *[]*parser.Property, buf []byte, patchlist *parser.PatchList) error {
seen := make(map[string]*parser.Property)
for i := 0; i < len(*properties); i++ {
@@ -354,7 +505,7 @@
}
// removes from <items> every item present in <removals>
-func filterExpressionList(items *parser.List, removals *parser.List) {
+func filterExpressionList(patchList *parser.PatchList, items *parser.List, removals *parser.List) {
writeIndex := 0
for _, item := range items.Values {
included := true
@@ -371,28 +522,39 @@
if included {
items.Values[writeIndex] = item
writeIndex++
+ } else {
+ patchList.Add(item.Pos().Offset, item.End().Offset+2, "")
}
}
items.Values = items.Values[:writeIndex]
}
// Remove each modules[i].Properties[<legacyName>][j] that matches a modules[i].Properties[<canonicalName>][k]
-func (f *Fixer) removeMatchingModuleListProperties(canonicalName string, legacyName string) error {
- for _, def := range f.tree.Defs {
- mod, ok := def.(*parser.Module)
- if !ok {
- continue
- }
- legacyList, ok := getLiteralListProperty(mod, legacyName)
- if !ok {
- continue
- }
- canonicalList, ok := getLiteralListProperty(mod, canonicalName)
- if !ok {
- continue
- }
- filterExpressionList(legacyList, canonicalList)
+func removeMatchingModuleListProperties(mod *parser.Module, patchList *parser.PatchList, canonicalName string, legacyName string) error {
+ legacyProp, ok := mod.GetProperty(legacyName)
+ if !ok {
+ return nil
}
+ legacyList, ok := legacyProp.Value.(*parser.List)
+ if !ok || len(legacyList.Values) == 0 {
+ return nil
+ }
+ canonicalList, ok := getLiteralListProperty(mod, canonicalName)
+ if !ok {
+ return nil
+ }
+
+ localPatches := parser.PatchList{}
+ filterExpressionList(&localPatches, legacyList, canonicalList)
+
+ if len(legacyList.Values) == 0 {
+ patchList.Add(legacyProp.Pos().Offset, legacyProp.End().Offset+2, "")
+ } else {
+ for _, p := range localPatches {
+ patchList.Add(p.Start, p.End, p.Replacement)
+ }
+ }
+
return nil
}
@@ -410,6 +572,15 @@
return list, ok
}
+func propertyIndex(props []*parser.Property, propertyName string) int {
+ for i, prop := range props {
+ if prop.Name == propertyName {
+ return i
+ }
+ }
+ return -1
+}
+
func renameProperty(mod *parser.Module, from, to string) {
for _, prop := range mod.Properties {
if prop.Name == from {
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index 51708eb..654ccf9 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -66,27 +66,31 @@
fixer := NewFixer(tree)
// apply simplifications
- err := fixer.simplifyKnownPropertiesDuplicatingEachOther()
+ err := fixer.runPatchListMod(simplifyKnownPropertiesDuplicatingEachOther)
if len(errs) > 0 {
t.Fatal(err)
}
// 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 @@
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 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"})
@@ -117,6 +125,49 @@
implFilterListTest(t, []string{}, []string{}, []string{})
}
+func runPass(t *testing.T, in, out string, innerTest func(*Fixer) error) {
+ expected, err := Reformat(out)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ in, err = Reformat(in)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ tree, errs := parser.Parse("<testcase>", bytes.NewBufferString(in), parser.NewScope(nil))
+ if errs != nil {
+ t.Fatal(errs)
+ }
+
+ fixer := NewFixer(tree)
+
+ got := ""
+ prev := "foo"
+ passes := 0
+ for got != prev && passes < 10 {
+ err := innerTest(fixer)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ out, err := parser.Print(fixer.tree)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ prev = got
+ got = string(out)
+ passes++
+ }
+
+ if got != expected {
+ t.Errorf("output didn't match:\ninput:\n%s\n\nexpected:\n%s\ngot:\n%s\n",
+ in, expected, got)
+ }
+}
+
func TestMergeMatchingProperties(t *testing.T) {
tests := []struct {
name string
@@ -199,47 +250,250 @@
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
- expected, err := Reformat(test.out)
- if err != nil {
- t.Error(err)
- }
+ runPass(t, test.in, test.out, func(fixer *Fixer) error {
+ return fixer.runPatchListMod(mergeMatchingModuleProperties)
+ })
+ })
+ }
+}
- in, err := Reformat(test.in)
- if err != nil {
- t.Error(err)
- }
-
- tree, errs := parser.Parse("<testcase>", bytes.NewBufferString(in), parser.NewScope(nil))
- if errs != nil {
- t.Fatal(errs)
- }
-
- fixer := NewFixer(tree)
-
- got := ""
- prev := "foo"
- passes := 0
- for got != prev && passes < 10 {
- err := fixer.mergeMatchingModuleProperties()
- if err != nil {
- t.Fatal(err)
+func TestReorderCommonProperties(t *testing.T) {
+ var tests = []struct {
+ name string
+ in string
+ out string
+ }{
+ {
+ name: "empty",
+ in: `cc_library {}`,
+ out: `cc_library {}`,
+ },
+ {
+ name: "only priority",
+ in: `
+ cc_library {
+ name: "foo",
}
-
- out, err := parser.Print(fixer.tree)
- if err != nil {
- t.Fatal(err)
+ `,
+ out: `
+ cc_library {
+ name: "foo",
}
+ `,
+ },
+ {
+ name: "already in order",
+ in: `
+ cc_library {
+ name: "foo",
+ defaults: ["bar"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+ defaults: ["bar"],
+ }
+ `,
+ },
+ {
+ name: "reorder only priority",
+ in: `
+ cc_library {
+ defaults: ["bar"],
+ name: "foo",
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+ defaults: ["bar"],
+ }
+ `,
+ },
+ {
+ name: "reorder",
+ in: `
+ cc_library {
+ name: "foo",
+ srcs: ["a.c"],
+ host_supported: true,
+ defaults: ["bar"],
+ shared_libs: ["baz"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+ defaults: ["bar"],
+ host_supported: true,
+ srcs: ["a.c"],
+ shared_libs: ["baz"],
+ }
+ `,
+ },
+ }
- prev = got
- got = string(out)
- passes++
- }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ runPass(t, test.in, test.out, func(fixer *Fixer) error {
+ return fixer.runPatchListMod(reorderCommonProperties)
+ })
+ })
+ }
+}
- if got != expected {
- t.Errorf("failed testcase '%s'\ninput:\n%s\n\nexpected:\n%s\ngot:\n%s\n",
- test.name, in, expected, got)
- }
+func TestRemoveMatchingModuleListProperties(t *testing.T) {
+ var tests = []struct {
+ name string
+ in string
+ out string
+ }{
+ {
+ name: "simple",
+ in: `
+ cc_library {
+ name: "foo",
+ foo: ["a"],
+ bar: ["a"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+ bar: ["a"],
+ }
+ `,
+ },
+ {
+ name: "long",
+ in: `
+ cc_library {
+ name: "foo",
+ foo: [
+ "a",
+ "b",
+ ],
+ bar: ["a"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+ foo: [
+ "b",
+ ],
+ bar: ["a"],
+ }
+ `,
+ },
+ {
+ name: "long fully removed",
+ in: `
+ cc_library {
+ name: "foo",
+ foo: [
+ "a",
+ ],
+ bar: ["a"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+ bar: ["a"],
+ }
+ `,
+ },
+ {
+ name: "comment",
+ in: `
+ cc_library {
+ name: "foo",
+ // comment
+ foo: ["a"],
+
+ bar: ["a"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+
+ // comment
+
+ bar: ["a"],
+ }
+ `,
+ },
+ {
+ name: "inner comment",
+ in: `
+ cc_library {
+ name: "foo",
+ foo: [
+ // comment
+ "a",
+ ],
+ bar: ["a"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+ bar: ["a"],
+ }
+ `,
+ },
+ {
+ name: "eol comment",
+ in: `
+ cc_library {
+ name: "foo",
+ foo: ["a"], // comment
+ bar: ["a"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+ // comment
+ bar: ["a"],
+ }
+ `,
+ },
+ {
+ name: "eol comment with blank lines",
+ in: `
+ cc_library {
+ name: "foo",
+
+ foo: ["a"], // comment
+
+ // bar
+ bar: ["a"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+
+ // comment
+
+ // bar
+ bar: ["a"],
+ }
+ `,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ runPass(t, test.in, test.out, func(fixer *Fixer) error {
+ return fixer.runPatchListMod(func(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
+ return removeMatchingModuleListProperties(mod, patchList, "bar", "foo")
+ })
+ })
})
}
}
diff --git a/bpfix/cmd/bpfix.go b/bpfix/cmd/bpfix.go
index 2fde383..ccdae16 100644
--- a/bpfix/cmd/bpfix.go
+++ b/bpfix/cmd/bpfix.go
@@ -65,7 +65,7 @@
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/androidmk.go b/cc/androidmk.go
index cdd4a5a..f417de0 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -24,7 +24,8 @@
)
var (
- vendorSuffix = ".vendor"
+ vendorSuffix = ".vendor"
+ recoverySuffix = ".recovery"
)
type AndroidMkContext interface {
@@ -99,6 +100,8 @@
// .vendor suffix is added only when we will have two variants: core and vendor.
// The suffix is not added for vendor-only module.
ret.SubName += vendorSuffix
+ } else if c.inRecovery() && !c.onlyInRecovery() {
+ ret.SubName += recoverySuffix
}
return ret
@@ -345,7 +348,7 @@
func (c *llndkStubDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
ret.Class = "SHARED_LIBRARIES"
- ret.SubName = ".vendor"
+ ret.SubName = vendorSuffix
ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
c.libraryDecorator.androidMkWriteExportedFlags(w)
diff --git a/cc/binary.go b/cc/binary.go
index 9e7b70b..00fda06 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -353,6 +353,11 @@
}
func (binary *binaryDecorator) install(ctx ModuleContext, file android.Path) {
+ // <recovery>/bin is a symlink to /system/bin. Recovery binaries are all in /sbin.
+ if ctx.inRecovery() {
+ binary.baseInstaller.dir = "sbin"
+ }
+
binary.baseInstaller.install(ctx, file)
for _, symlink := range binary.Properties.Symlinks {
binary.symlinks = append(binary.symlinks,
diff --git a/cc/cc.go b/cc/cc.go
index 76e6645..a885c91 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -34,7 +34,7 @@
android.RegisterModuleType("cc_defaults", defaultsFactory)
android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.BottomUp("image", vendorMutator).Parallel()
+ ctx.BottomUp("image", imageMutator).Parallel()
ctx.BottomUp("link", linkageMutator).Parallel()
ctx.BottomUp("vndk", vndkMutator).Parallel()
ctx.BottomUp("ndk_api", ndkApiMutator).Parallel()
@@ -175,6 +175,11 @@
// *.logtags files, to combine together in order to generate the /system/etc/event-log-tags
// file
Logtags []string
+
+ // Make this module available when building for recovery
+ Recovery_available *bool
+
+ InRecovery bool `blueprint:"mutated"`
}
type VendorProperties struct {
@@ -206,10 +211,6 @@
Double_loadable *bool
}
-type UnusedProperties struct {
- Tags []string
-}
-
type ModuleContextIntf interface {
static() bool
staticBinary() bool
@@ -221,6 +222,7 @@
isVndk() bool
isVndkSp() bool
isVndkExt() bool
+ inRecovery() bool
createVndkSourceAbiDump() bool
selectedStl() string
baseModuleName() string
@@ -320,7 +322,6 @@
Properties BaseProperties
VendorProperties VendorProperties
- unused UnusedProperties
// initialize before calling Init
hod android.HostOrDeviceSupported
@@ -360,7 +361,7 @@
}
func (c *Module) Init() android.Module {
- c.AddProperties(&c.Properties, &c.VendorProperties, &c.unused)
+ c.AddProperties(&c.Properties, &c.VendorProperties)
if c.compiler != nil {
c.AddProperties(c.compiler.compilerProps()...)
}
@@ -458,6 +459,14 @@
return c.isVndk() || Bool(c.VendorProperties.Vendor_available)
}
+func (c *Module) inRecovery() bool {
+ return c.Properties.InRecovery || c.ModuleBase.InstallInRecovery()
+}
+
+func (c *Module) onlyInRecovery() bool {
+ return c.ModuleBase.InstallInRecovery()
+}
+
type baseModuleContext struct {
android.BaseContext
moduleContextImpl
@@ -505,7 +514,7 @@
}
func (ctx *moduleContextImpl) useSdk() bool {
- if ctx.ctx.Device() && !ctx.useVndk() {
+ if ctx.ctx.Device() && !ctx.useVndk() && !ctx.inRecovery() {
return String(ctx.mod.Properties.Sdk_version) != ""
}
return false
@@ -549,16 +558,20 @@
return ctx.mod.isVndkExt()
}
+func (ctx *moduleContextImpl) inRecovery() bool {
+ return ctx.mod.inRecovery()
+}
+
// Create source abi dumps if the module belongs to the list of VndkLibraries.
func (ctx *moduleContextImpl) createVndkSourceAbiDump() bool {
skipAbiChecks := ctx.ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS")
- isUnsanitizedVariant := true
+ isVariantOnProductionDevice := true
sanitize := ctx.mod.sanitize
if sanitize != nil {
- isUnsanitizedVariant = sanitize.isUnsanitizedVariant()
+ isVariantOnProductionDevice = sanitize.isVariantOnProductionDevice()
}
vendorAvailable := Bool(ctx.mod.VendorProperties.Vendor_available)
- return !skipAbiChecks && isUnsanitizedVariant && ctx.ctx.Device() && ((ctx.useVndk() && ctx.isVndk() && vendorAvailable) || inList(ctx.baseModuleName(), llndkLibraries))
+ return !skipAbiChecks && isVariantOnProductionDevice && ctx.ctx.Device() && ((ctx.useVndk() && ctx.isVndk() && vendorAvailable) || inList(ctx.baseModuleName(), llndkLibraries))
}
func (ctx *moduleContextImpl) selectedStl() string {
@@ -1074,6 +1087,10 @@
// Platform code can link to anything
return
}
+ if from.inRecovery() {
+ // Recovery code is not NDK
+ return
+ }
if _, ok := to.linker.(*toolchainLibraryDecorator); ok {
// These are always allowed
return
@@ -1365,6 +1382,8 @@
return libName + vendorSuffix
} else if (ctx.Platform() || ctx.ProductSpecific()) && isVendorPublicLib {
return libName + vendorPublicLibrarySuffix
+ } else if ccDep.inRecovery() && !ccDep.onlyInRecovery() {
+ return libName + recoverySuffix
} else {
return libName
}
@@ -1416,6 +1435,10 @@
return c.installer.inSanitizerDir()
}
+func (c *Module) InstallInRecovery() bool {
+ return c.inRecovery()
+}
+
func (c *Module) HostToolPath() android.OptionalPath {
if c.installer == nil {
return android.OptionalPath{}
@@ -1475,7 +1498,6 @@
&BinaryLinkerProperties{},
&TestProperties{},
&TestBinaryProperties{},
- &UnusedProperties{},
&StlProperties{},
&SanitizeProperties{},
&StripProperties{},
@@ -1502,6 +1524,8 @@
// vendorMode is the variant used for /vendor code that compiles
// against the VNDK.
vendorMode = "vendor"
+
+ recoveryMode = "recovery"
)
func squashVendorSrcs(m *Module) {
@@ -1514,7 +1538,17 @@
}
}
-func vendorMutator(mctx android.BottomUpMutatorContext) {
+func squashRecoverySrcs(m *Module) {
+ if lib, ok := m.compiler.(*libraryDecorator); ok {
+ lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
+ lib.baseCompiler.Properties.Target.Recovery.Srcs...)
+
+ lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
+ lib.baseCompiler.Properties.Target.Recovery.Exclude_srcs...)
+ }
+}
+
+func imageMutator(mctx android.BottomUpMutatorContext) {
if mctx.Os() != android.Android {
return
}
@@ -1583,43 +1617,70 @@
}
}
+ var coreVariantNeeded bool = false
+ var vendorVariantNeeded bool = false
+ var recoveryVariantNeeded bool = false
+
if mctx.DeviceConfig().VndkVersion() == "" {
// If the device isn't compiling against the VNDK, we always
// use the core mode.
- mctx.CreateVariations(coreMode)
+ coreVariantNeeded = true
} else if _, ok := m.linker.(*llndkStubDecorator); ok {
// LL-NDK stubs only exist in the vendor variant, since the
// real libraries will be used in the core variant.
- mctx.CreateVariations(vendorMode)
+ vendorVariantNeeded = true
} else if _, ok := m.linker.(*llndkHeadersDecorator); ok {
// ... and LL-NDK headers as well
- mod := mctx.CreateVariations(vendorMode)
- vendor := mod[0].(*Module)
- vendor.Properties.UseVndk = true
+ vendorVariantNeeded = true
} else if _, ok := m.linker.(*vndkPrebuiltLibraryDecorator); ok {
// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
// PRODUCT_EXTRA_VNDK_VERSIONS.
- mod := mctx.CreateVariations(vendorMode)
- vendor := mod[0].(*Module)
- vendor.Properties.UseVndk = true
+ vendorVariantNeeded = true
} else if m.hasVendorVariant() && !vendorSpecific {
// This will be available in both /system and /vendor
// or a /system directory that is available to vendor.
- mod := mctx.CreateVariations(coreMode, vendorMode)
- vendor := mod[1].(*Module)
- vendor.Properties.UseVndk = true
- squashVendorSrcs(vendor)
+ coreVariantNeeded = true
+ vendorVariantNeeded = true
} else if vendorSpecific && String(m.Properties.Sdk_version) == "" {
// This will be available in /vendor (or /odm) only
- mod := mctx.CreateVariations(vendorMode)
- vendor := mod[0].(*Module)
- vendor.Properties.UseVndk = true
- squashVendorSrcs(vendor)
+ vendorVariantNeeded = true
} else {
// This is either in /system (or similar: /data), or is a
// modules built with the NDK. Modules built with the NDK
// will be restricted using the existing link type checks.
- mctx.CreateVariations(coreMode)
+ coreVariantNeeded = true
+ }
+
+ if Bool(m.Properties.Recovery_available) {
+ recoveryVariantNeeded = true
+ }
+
+ if m.ModuleBase.InstallInRecovery() {
+ recoveryVariantNeeded = true
+ coreVariantNeeded = false
+ }
+
+ var variants []string
+ if coreVariantNeeded {
+ variants = append(variants, coreMode)
+ }
+ if vendorVariantNeeded {
+ variants = append(variants, vendorMode)
+ }
+ if recoveryVariantNeeded {
+ variants = append(variants, recoveryMode)
+ }
+ mod := mctx.CreateVariations(variants...)
+ for i, v := range variants {
+ if v == vendorMode {
+ m := mod[i].(*Module)
+ m.Properties.UseVndk = true
+ squashVendorSrcs(m)
+ } else if v == recoveryMode {
+ m := mod[i].(*Module)
+ m.Properties.InRecovery = true
+ squashRecoverySrcs(m)
+ }
}
}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 4f26827..1b12ad4 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -63,7 +63,7 @@
ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(objectFactory))
ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.BottomUp("image", vendorMutator).Parallel()
+ ctx.BottomUp("image", imageMutator).Parallel()
ctx.BottomUp("link", linkageMutator).Parallel()
ctx.BottomUp("vndk", vndkMutator).Parallel()
ctx.BottomUp("begin", beginMutator).Parallel()
diff --git a/cc/compiler.go b/cc/compiler.go
index 2ba19f1..b410115 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -142,6 +142,19 @@
// variant of the C/C++ module.
Cflags []string
}
+ Recovery struct {
+ // list of source files that should only be used in the
+ // recovery variant of the C/C++ module.
+ Srcs []string
+
+ // list of source files that should not be used to
+ // build the recovery variant of the C/C++ module.
+ Exclude_srcs []string
+
+ // List of additional cflags that should be used to build the recovery
+ // variant of the C/C++ module.
+ Cflags []string
+ }
}
Proto struct {
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 74c936f..73d9e3b 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -27,6 +27,15 @@
"-Werror=implicit-function-declaration",
}
+ arm64ArchVariantCflags = map[string][]string{
+ "armv8-a": []string{
+ "-march=armv8-a",
+ },
+ "armv8-2a": []string{
+ "-march=armv8.2a",
+ },
+ }
+
arm64Ldflags = []string{
"-Wl,-m,aarch64_elf64_le_vec",
"-Wl,--hash-style=gnu",
@@ -44,6 +53,17 @@
"cortex-a53": []string{
"-mcpu=cortex-a53",
},
+ "cortex-a55": []string{
+ // The cortex-a55 target is not yet supported,
+ // so use cortex-a53.
+ "-mcpu=cortex-a53",
+ },
+ "cortex-a75": []string{
+ // Use the cortex-a53 since it is similar to the little
+ // core (cortex-a55) and is sensitive to ordering.
+ // The cortex-a55 target is not yet supported.
+ "-mcpu=cortex-a53",
+ },
"kryo": []string{
// Use the cortex-a57 cpu since some compilers
// don't support a Kryo specific target yet.
@@ -67,8 +87,11 @@
func init() {
android.RegisterArchVariants(android.Arm64,
"armv8_a",
+ "armv8_2a",
"cortex-a53",
+ "cortex-a55",
"cortex-a73",
+ "cortex-a75",
"kryo",
"exynos-m1",
"exynos-m2",
@@ -93,11 +116,19 @@
pctx.StaticVariable("Arm64ClangLldflags", strings.Join(ClangFilterUnknownCflags(arm64Lldflags), " "))
pctx.StaticVariable("Arm64ClangCppflags", strings.Join(ClangFilterUnknownCflags(arm64Cppflags), " "))
+ pctx.StaticVariable("Arm64ClangArmv8ACflags", strings.Join(arm64ArchVariantCflags["armv8-a"], " "))
+ pctx.StaticVariable("Arm64ClangArmv82ACflags", strings.Join(arm64ArchVariantCflags["armv8-2a"], " "))
+
pctx.StaticVariable("Arm64CortexA53Cflags",
strings.Join(arm64CpuVariantCflags["cortex-a53"], " "))
pctx.StaticVariable("Arm64ClangCortexA53Cflags",
strings.Join(arm64ClangCpuVariantCflags["cortex-a53"], " "))
+ pctx.StaticVariable("Arm64CortexA55Cflags",
+ strings.Join(arm64CpuVariantCflags["cortex-a55"], " "))
+ pctx.StaticVariable("Arm64ClangCortexA55Cflags",
+ strings.Join(arm64ClangCpuVariantCflags["cortex-a55"], " "))
+
pctx.StaticVariable("Arm64KryoCflags",
strings.Join(arm64CpuVariantCflags["kryo"], " "))
pctx.StaticVariable("Arm64ClangKryoCflags",
@@ -118,16 +149,25 @@
arm64CpuVariantCflagsVar = map[string]string{
"": "",
"cortex-a53": "${config.Arm64CortexA53Cflags}",
+ "cortex-a55": "${config.Arm64CortexA55Cflags}",
"cortex-a73": "${config.Arm64CortexA53Cflags}",
+ "cortex-a75": "${config.Arm64CortexA55Cflags}",
"kryo": "${config.Arm64KryoCflags}",
"exynos-m1": "${config.Arm64ExynosM1Cflags}",
"exynos-m2": "${config.Arm64ExynosM2Cflags}",
}
+ arm64ClangArchVariantCflagsVar = map[string]string{
+ "armv8-a": "${config.Arm64ClangArmv8ACflags}",
+ "armv8-2a": "${config.Arm64ClangArmv82ACflags}",
+ }
+
arm64ClangCpuVariantCflagsVar = map[string]string{
"": "",
"cortex-a53": "${config.Arm64ClangCortexA53Cflags}",
+ "cortex-a55": "${config.Arm64ClangCortexA55Cflags}",
"cortex-a73": "${config.Arm64ClangCortexA53Cflags}",
+ "cortex-a75": "${config.Arm64ClangCortexA55Cflags}",
"kryo": "${config.Arm64ClangKryoCflags}",
"exynos-m1": "${config.Arm64ClangExynosM1Cflags}",
"exynos-m2": "${config.Arm64ClangExynosM2Cflags}",
@@ -206,13 +246,21 @@
}
func arm64ToolchainFactory(arch android.Arch) Toolchain {
- if arch.ArchVariant != "armv8-a" {
+ switch arch.ArchVariant {
+ case "armv8-a":
+ case "armv8-2a":
+ // Nothing extra for armv8-a/armv8-2a
+ default:
panic(fmt.Sprintf("Unknown ARM architecture version: %q", arch.ArchVariant))
}
+ toolchainClangCflags := []string{arm64ClangArchVariantCflagsVar[arch.ArchVariant]}
+ toolchainClangCflags = append(toolchainClangCflags,
+ variantOrDefault(arm64ClangCpuVariantCflagsVar, arch.CpuVariant))
+
return &toolchainArm64{
toolchainCflags: variantOrDefault(arm64CpuVariantCflagsVar, arch.CpuVariant),
- toolchainClangCflags: variantOrDefault(arm64ClangCpuVariantCflagsVar, arch.CpuVariant),
+ toolchainClangCflags: strings.Join(toolchainClangCflags, " "),
}
}
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index 02bd9eb..398df90 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -99,6 +99,24 @@
// better solution comes around. See Bug 27340895
"-D__ARM_FEATURE_LPAE=1",
},
+ "cortex-a55": []string{
+ "-mcpu=cortex-a53",
+ "-mfpu=neon-fp-armv8",
+ // Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+ // don't advertise.
+ // TODO This is a hack and we need to add it for each processor that supports LPAE until some
+ // better solution comes around. See Bug 27340895
+ "-D__ARM_FEATURE_LPAE=1",
+ },
+ "cortex-a75": []string{
+ "-mcpu=cortex-a53",
+ "-mfpu=neon-fp-armv8",
+ // Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+ // don't advertise.
+ // TODO This is a hack and we need to add it for each processor that supports LPAE until some
+ // better solution comes around. See Bug 27340895
+ "-D__ARM_FEATURE_LPAE=1",
+ },
"krait": []string{
"-mcpu=cortex-a15",
"-mfpu=neon-vfpv4",
@@ -143,7 +161,9 @@
"cortex-a15",
"cortex-a53",
"cortex-a53-a57",
+ "cortex-a55",
"cortex-a73",
+ "cortex-a75",
"krait",
"kryo",
"exynos-m1",
@@ -192,6 +212,7 @@
pctx.StaticVariable("ArmCortexA8Cflags", strings.Join(armCpuVariantCflags["cortex-a8"], " "))
pctx.StaticVariable("ArmCortexA15Cflags", strings.Join(armCpuVariantCflags["cortex-a15"], " "))
pctx.StaticVariable("ArmCortexA53Cflags", strings.Join(armCpuVariantCflags["cortex-a53"], " "))
+ pctx.StaticVariable("ArmCortexA55Cflags", strings.Join(armCpuVariantCflags["cortex-a55"], " "))
pctx.StaticVariable("ArmKraitCflags", strings.Join(armCpuVariantCflags["krait"], " "))
pctx.StaticVariable("ArmKryoCflags", strings.Join(armCpuVariantCflags["kryo"], " "))
@@ -225,6 +246,8 @@
strings.Join(armClangCpuVariantCflags["cortex-a15"], " "))
pctx.StaticVariable("ArmClangCortexA53Cflags",
strings.Join(armClangCpuVariantCflags["cortex-a53"], " "))
+ pctx.StaticVariable("ArmClangCortexA55Cflags",
+ strings.Join(armClangCpuVariantCflags["cortex-a55"], " "))
pctx.StaticVariable("ArmClangKraitCflags",
strings.Join(armClangCpuVariantCflags["krait"], " "))
pctx.StaticVariable("ArmClangKryoCflags",
@@ -245,7 +268,9 @@
"cortex-a15": "${config.ArmCortexA15Cflags}",
"cortex-a53": "${config.ArmCortexA53Cflags}",
"cortex-a53.a57": "${config.ArmCortexA53Cflags}",
+ "cortex-a55": "${config.ArmCortexA55Cflags}",
"cortex-a73": "${config.ArmCortexA53Cflags}",
+ "cortex-a75": "${config.ArmCortexA55Cflags}",
"krait": "${config.ArmKraitCflags}",
"kryo": "${config.ArmKryoCflags}",
"exynos-m1": "${config.ArmCortexA53Cflags}",
@@ -266,7 +291,9 @@
"cortex-a15": "${config.ArmClangCortexA15Cflags}",
"cortex-a53": "${config.ArmClangCortexA53Cflags}",
"cortex-a53.a57": "${config.ArmClangCortexA53Cflags}",
+ "cortex-a55": "${config.ArmClangCortexA55Cflags}",
"cortex-a73": "${config.ArmClangCortexA53Cflags}",
+ "cortex-a75": "${config.ArmClangCortexA55Cflags}",
"krait": "${config.ArmClangKraitCflags}",
"kryo": "${config.ArmClangKryoCflags}",
"exynos-m1": "${config.ArmClangCortexA53Cflags}",
diff --git a/cc/library.go b/cc/library.go
index b31fee2..c83e89a 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -477,6 +477,11 @@
deps.SharedLibs = removeListFromList(deps.SharedLibs, library.baseLinker.Properties.Target.Vendor.Exclude_shared_libs)
deps.StaticLibs = removeListFromList(deps.StaticLibs, library.baseLinker.Properties.Target.Vendor.Exclude_static_libs)
}
+ if ctx.inRecovery() {
+ deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, library.baseLinker.Properties.Target.Recovery.Exclude_static_libs)
+ deps.SharedLibs = removeListFromList(deps.SharedLibs, library.baseLinker.Properties.Target.Recovery.Exclude_shared_libs)
+ deps.StaticLibs = removeListFromList(deps.StaticLibs, library.baseLinker.Properties.Target.Recovery.Exclude_static_libs)
+ }
android.ExtractSourceDeps(ctx, library.Properties.Version_script)
android.ExtractSourceDeps(ctx, library.Properties.Unexported_symbols_list)
@@ -745,7 +750,7 @@
}
if Bool(library.Properties.Static_ndk_lib) && library.static() &&
- !ctx.useVndk() && ctx.Device() &&
+ !ctx.useVndk() && !ctx.inRecovery() && ctx.Device() &&
library.sanitize.isUnsanitizedVariant() {
installPath := getNdkSysrootBase(ctx).Join(
ctx, "usr/lib", config.NDKTriple(ctx.toolchain()), file.Base())
diff --git a/cc/linker.go b/cc/linker.go
index 71da09e..3e2ecc6 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -107,6 +107,15 @@
// variant of the C/C++ module.
Exclude_runtime_libs []string
}
+ Recovery struct {
+ // list of shared libs that should not be used to build
+ // the recovery variant of the C/C++ module.
+ Exclude_shared_libs []string
+
+ // list of static libs that should not be used to build
+ // the recovery variant of the C/C++ module.
+ Exclude_static_libs []string
+ }
}
// make android::build:GetBuildNumber() available containing the build ID.
@@ -166,6 +175,14 @@
deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor.Exclude_runtime_libs)
}
+ if ctx.inRecovery() {
+ deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Target.Recovery.Exclude_shared_libs)
+ deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, linker.Properties.Target.Recovery.Exclude_shared_libs)
+ deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
+ deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Recovery.Exclude_static_libs)
+ deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
+ }
+
if ctx.ModuleName() != "libcompiler_rt-extras" {
deps.LateStaticLibs = append(deps.LateStaticLibs, "libcompiler_rt-extras")
}
diff --git a/cc/pgo.go b/cc/pgo.go
index d39e429..a341ab9 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -19,6 +19,8 @@
"path/filepath"
"strings"
+ "github.com/google/blueprint/proptools"
+
"android/soong/android"
"android/soong/cc/config"
)
@@ -160,13 +162,8 @@
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 @@
}
}
- 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 22e50ab..6e6f95e 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -25,13 +25,17 @@
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 @@
}
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/cc/sanitize.go b/cc/sanitize.go
index 859d876..a704cf1 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -307,10 +307,12 @@
}
func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
- minimalRuntimePath := "${config.ClangAsanLibDir}/" + config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(ctx.toolchain()) + ".a"
+ minimalRuntimeLib := config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(ctx.toolchain()) + ".a"
+ minimalRuntimePath := "${config.ClangAsanLibDir}/" + minimalRuntimeLib
if ctx.Device() && sanitize.Properties.MinimalRuntimeDep {
flags.LdFlags = append(flags.LdFlags, minimalRuntimePath)
+ flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
}
if !sanitize.Properties.SanitizerEnabled && !sanitize.Properties.UbsanRuntimeDep {
return flags
@@ -448,6 +450,7 @@
if enableMinimalRuntime(sanitize) {
flags.CFlags = append(flags.CFlags, strings.Join(minimalRuntimeFlags, " "))
flags.libFlags = append([]string{minimalRuntimePath}, flags.libFlags...)
+ flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
}
}
}
@@ -539,6 +542,11 @@
!sanitize.isSanitizerEnabled(cfi)
}
+func (sanitize *sanitize) isVariantOnProductionDevice() bool {
+ return !sanitize.isSanitizerEnabled(asan) &&
+ !sanitize.isSanitizerEnabled(tsan)
+}
+
func (sanitize *sanitize) SetSanitizer(t sanitizerType, b bool) {
switch t {
case asan:
diff --git a/cc/test.go b/cc/test.go
index fef6367..5d0ef20 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -168,13 +168,17 @@
}
func (test *testDecorator) linkerInit(ctx BaseModuleContext, linker *baseLinker) {
- // add ../../lib[64] to rpath so that out/host/linux-x86/nativetest/<test dir>/<test> can
+ // 1. Add ../../lib[64] to rpath so that out/host/linux-x86/nativetest/<test dir>/<test> can
// find out/host/linux-x86/lib[64]/library.so
- runpath := "../../lib"
- if ctx.toolchain().Is64Bit() {
- runpath += "64"
+ // 2. Add ../../../lib[64] to rpath so that out/host/linux-x86/testcases/<test dir>/<CPU>/<test> can
+ // also find out/host/linux-x86/lib[64]/library.so
+ runpaths := []string{"../../lib", "../../../lib"}
+ for _, runpath := range runpaths {
+ if ctx.toolchain().Is64Bit() {
+ runpath += "64"
+ }
+ linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, runpath)
}
- linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, runpath)
// add "" to rpath so that test binaries can find libraries in their own test directory
linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "")
diff --git a/cmd/dep_fixer/Android.bp b/cmd/dep_fixer/Android.bp
new file mode 100644
index 0000000..d2d1113
--- /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 0000000..64c97f5
--- /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 0000000..0a779b7
--- /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 0000000..bac3772
--- /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/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 06c5626..ab82963 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -255,6 +255,11 @@
setMaxFiles(log)
+ finder := build.NewSourceFinder(buildCtx, config)
+ defer finder.Shutdown()
+
+ build.FindSources(buildCtx, config, finder)
+
vars, err := build.DumpMakeVars(buildCtx, config, nil, []string{"all_named_products"})
if err != nil {
log.Fatal(err)
@@ -303,9 +308,6 @@
var wg sync.WaitGroup
productConfigs := make(chan Product, len(products))
- finder := build.NewSourceFinder(buildCtx, config)
- defer finder.Shutdown()
-
// Run the product config for every product in parallel
for _, product := range products {
wg.Add(1)
diff --git a/java/aar.go b/java/aar.go
index 9e5cddb..66f1cab 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -150,8 +150,8 @@
if ctx.ModuleName() == "framework-res" {
// Some builds set AppsDefaultVersionName() to include the build number ("O-123456"). aapt2 copies the
// version name of framework-res into app manifests as compileSdkVersionCodename, which confuses things
- // if it contains the build number. Use the DefaultAppTargetSdk instead.
- versionName = ctx.Config().DefaultAppTargetSdk()
+ // if it contains the build number. Use the PlatformVersionName instead.
+ versionName = ctx.Config().PlatformVersionName()
} else {
versionName = ctx.Config().AppsDefaultVersionName()
}
diff --git a/java/androidmk.go b/java/androidmk.go
index b168f2c..88dc6ef 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -286,26 +286,52 @@
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, "checkapi:",
+ 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 6633f79..d44315c 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -90,6 +90,7 @@
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 07042a1..b34c393 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -45,6 +45,24 @@
},
"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 -e "$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,20 @@
Sdk_version *string `android:"arch_variant"`
}
+type ApiToCheck struct {
+ // path to the API txt file that the new API extracted from source code is checked
+ // against. The path can be local to the module or from other module (via :module syntax).
+ Api_file *string
+
+ // path to the API txt file that the new @removed API extractd from source code is
+ // checked against. The path can be local to the module or from other module (via
+ // :module syntax).
+ Removed_api_file *string
+
+ // Arguments to the apicheck tool.
+ Args *string
+}
+
type DroiddocProperties struct {
// directory relative to top of the source tree that contains doc templates files.
Custom_template *string
@@ -157,6 +189,12 @@
// 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 +227,10 @@
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 +462,32 @@
})
}
+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 +503,16 @@
// 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 +625,19 @@
}
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 +650,6 @@
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 +703,91 @@
"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:\n`+
+ ` make %s-update-current-api\n\n`+
+ ` To submit the revised current.txt to the main Android repository,\n`+
+ ` 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 ea52496..fe1c3d7 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -84,10 +84,12 @@
ctx.RegisterModuleType("droiddoc_host", android.ModuleFactoryAdaptor(DroiddocHostFactory))
ctx.RegisterModuleType("droiddoc_template", android.ModuleFactoryAdaptor(DroiddocTemplateFactory))
ctx.RegisterModuleType("java_sdk_library", android.ModuleFactoryAdaptor(sdkLibraryFactory))
+ ctx.RegisterModuleType("prebuilt_apis", android.ModuleFactoryAdaptor(prebuiltApisFactory))
ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
ctx.PreArchMutators(android.RegisterPrebuiltsPostDepsMutators)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.TopDown("prebuilt_apis", prebuiltApisMutator).Parallel()
ctx.TopDown("java_sdk_library", sdkLibraryMutator).Parallel()
})
ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(OverlaySingletonFactory))
@@ -141,19 +143,25 @@
}
mockFS := map[string][]byte{
- "Android.bp": []byte(bp),
- "a.java": nil,
- "b.java": nil,
- "c.java": nil,
- "b.kt": nil,
- "a.jar": nil,
- "b.jar": nil,
- "java-res/a/a": nil,
- "java-res/b/b": nil,
- "java-res2/a": nil,
- "java-fg/a.java": nil,
- "java-fg/b.java": nil,
- "java-fg/c.java": nil,
+ "Android.bp": []byte(bp),
+ "a.java": nil,
+ "b.java": nil,
+ "c.java": nil,
+ "b.kt": nil,
+ "a.jar": nil,
+ "b.jar": nil,
+ "java-res/a/a": nil,
+ "java-res/b/b": nil,
+ "java-res2/a": nil,
+ "java-fg/a.java": nil,
+ "java-fg/b.java": nil,
+ "java-fg/c.java": nil,
+ "api/current.txt": nil,
+ "api/removed.txt": nil,
+ "api/system-current.txt": nil,
+ "api/system-removed.txt": nil,
+ "api/test-current.txt": nil,
+ "api/test-removed.txt": nil,
"prebuilts/sdk/14/public/android.jar": nil,
"prebuilts/sdk/14/public/framework.aidl": nil,
@@ -163,6 +171,19 @@
"prebuilts/sdk/current/public/core.jar": nil,
"prebuilts/sdk/current/system/android.jar": nil,
"prebuilts/sdk/current/test/android.jar": nil,
+ "prebuilts/sdk/28/public/api/foo.txt": nil,
+ "prebuilts/sdk/28/system/api/foo.txt": nil,
+ "prebuilts/sdk/28/test/api/foo.txt": nil,
+ "prebuilts/sdk/28/public/api/foo-removed.txt": nil,
+ "prebuilts/sdk/28/system/api/foo-removed.txt": nil,
+ "prebuilts/sdk/28/test/api/foo-removed.txt": nil,
+ "prebuilts/sdk/28/public/api/bar.txt": nil,
+ "prebuilts/sdk/28/system/api/bar.txt": nil,
+ "prebuilts/sdk/28/test/api/bar.txt": nil,
+ "prebuilts/sdk/28/public/api/bar-removed.txt": nil,
+ "prebuilts/sdk/28/system/api/bar-removed.txt": nil,
+ "prebuilts/sdk/28/test/api/bar-removed.txt": nil,
+ "prebuilts/sdk/Android.bp": []byte(`prebuilt_apis { name: "prebuilt_apis",}`),
// For framework-res, which is an implicit dependency for framework
"AndroidManifest.xml": nil,
@@ -175,6 +196,7 @@
"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,
@@ -195,7 +217,7 @@
func run(t *testing.T, ctx *android.TestContext, config android.Config) {
t.Helper()
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp", "prebuilts/sdk/Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
android.FailIfErrored(t, errs)
@@ -1037,10 +1059,15 @@
ctx.ModuleForTests("foo", "android_common")
ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix, "android_common")
ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix+sdkSystemApiSuffix, "android_common")
+ ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix+sdkTestApiSuffix, "android_common")
ctx.ModuleForTests("foo"+sdkDocsSuffix, "android_common")
ctx.ModuleForTests("foo"+sdkDocsSuffix+sdkSystemApiSuffix, "android_common")
+ ctx.ModuleForTests("foo"+sdkDocsSuffix+sdkTestApiSuffix, "android_common")
ctx.ModuleForTests("foo"+sdkImplLibrarySuffix, "android_common")
ctx.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
+ ctx.ModuleForTests("foo.api.public.28", "")
+ ctx.ModuleForTests("foo.api.system.28", "")
+ ctx.ModuleForTests("foo.api.test.28", "")
bazJavac := ctx.ModuleForTests("baz", "android_common").Rule("javac")
// tests if baz is actually linked to the stubs lib
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
new file mode 100644
index 0000000..50318bb
--- /dev/null
+++ b/java/prebuilt_apis.go
@@ -0,0 +1,140 @@
+// 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 (
+ "android/soong/android"
+ "sort"
+ "strconv"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+// prebuilt_apis is a meta-module that generates filegroup modules for all
+// API txt files found under the directory where the Android.bp is located.
+// Specificaly, an API file located at ./<ver>/<scope>/api/<module>.txt
+// generates a filegroup module named <module>-api.<scope>.<ver>.
+//
+// It also creates <module>-api.<scope>.latest for the lastest <ver>.
+//
+func init() {
+ android.RegisterModuleType("prebuilt_apis", prebuiltApisFactory)
+
+ android.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.TopDown("prebuilt_apis", prebuiltApisMutator).Parallel()
+ })
+}
+
+type prebuiltApis struct {
+ android.ModuleBase
+}
+
+func (module *prebuiltApis) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // no need to implement
+}
+
+func (module *prebuiltApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // no need to implement
+}
+
+func parseApiFilePath(ctx android.BaseModuleContext, path string) (module string, apiver int, scope string) {
+ elements := strings.Split(path, "/")
+ ver, err := strconv.Atoi(elements[0])
+ if err != nil {
+ ctx.ModuleErrorf("invalid version %q found in path: %q", elements[0], path)
+ return
+ }
+ apiver = ver
+
+ scope = elements[1]
+ if scope != "public" && scope != "system" && scope != "test" {
+ ctx.ModuleErrorf("invalid scope %q found in path: %q", scope, path)
+ return
+ }
+
+ // elements[2] is string literal "api". skipping.
+ module = strings.TrimSuffix(elements[3], ".txt")
+ return
+}
+
+func createFilegroup(mctx android.TopDownMutatorContext, module string, scope string, apiver string, path string) {
+ fgName := module + ".api." + scope + "." + apiver
+ filegroupProps := struct {
+ Name *string
+ Srcs []string
+ }{}
+ filegroupProps.Name = proptools.StringPtr(fgName)
+ filegroupProps.Srcs = []string{path}
+ mctx.CreateModule(android.ModuleFactoryAdaptor(android.FileGroupFactory), &filegroupProps)
+}
+
+func prebuiltApisMutator(mctx android.TopDownMutatorContext) {
+ if _, ok := mctx.Module().(*prebuiltApis); ok {
+ mydir := mctx.ModuleDir() + "/"
+ // <apiver>/<scope>/api/<module>.txt
+ files, err := mctx.GlobWithDeps(mydir+"*/*/api/*.txt", nil)
+ if err != nil {
+ mctx.ModuleErrorf("failed to glob api txt files under %q: %s", mydir, err)
+ }
+ if len(files) == 0 {
+ mctx.ModuleErrorf("no api file found under %q", mydir)
+ }
+
+ // construct a map to find out the latest api file path
+ // for each (<module>, <scope>) pair.
+ type latestApiInfo struct {
+ module string
+ scope string
+ apiver int
+ path string
+ }
+ m := make(map[string]latestApiInfo)
+
+ for _, f := range files {
+ // create a filegroup for each api txt file
+ localPath := strings.TrimPrefix(f, mydir)
+ module, apiver, scope := parseApiFilePath(mctx, localPath)
+ createFilegroup(mctx, module, scope, strconv.Itoa(apiver), localPath)
+
+ // find the latest apiver
+ key := module + "." + scope
+ info, ok := m[key]
+ if !ok {
+ m[key] = latestApiInfo{module, scope, apiver, localPath}
+ } else if apiver > info.apiver {
+ info.apiver = apiver
+ info.path = localPath
+ }
+ }
+ // create filegroups for the latest version of (<module>, <scope>) pairs
+ // sort the keys in order to make build.ninja stable
+ keys := make([]string, 0, len(m))
+ for k := range m {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ info := m[k]
+ createFilegroup(mctx, info.module, info.scope, "latest", info.path)
+ }
+ }
+}
+
+func prebuiltApisFactory() android.Module {
+ module := &prebuiltApis{}
+ android.InitAndroidModule(module)
+ return module
+}
diff --git a/java/proto.go b/java/proto.go
index cfd733a..3ec2e8a 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -30,12 +30,14 @@
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 703401c..ba121ab 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -67,11 +67,9 @@
// classpath at runtime if requested via <uses-library>.
//
// 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
+// 1) ensuring that apps have appropriate <uses-library> tag
+// 2) disallowing linking to the runtime shared lib
+// 3) HTML generation
func init() {
android.RegisterModuleType("java_sdk_library", sdkLibraryFactory)
@@ -155,15 +153,31 @@
}
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")+")")
+ }
},
}
}
@@ -245,21 +259,32 @@
return apiTagName
}
-// returns the path (relative to this module) to the API txt file. Files are located
-// ./<api_dir>/<api_level>.txt where <api_level> is either current, system-current, removed,
-// or system-removed.
-func (module *sdkLibrary) apiFilePath(apiLevel string, apiScope apiScope) string {
- apiDir := "api"
- apiFile := apiLevel
+func (module *sdkLibrary) latestApiFilegroupName(apiScope apiScope) string {
+ name := ":" + module.BaseModuleName() + ".api."
switch apiScope {
+ case apiScopePublic:
+ name = name + "public"
case apiScopeSystem:
- apiFile = "system-" + apiFile
+ name = name + "system"
case apiScopeTest:
- apiFile = "test-" + apiFile
+ name = name + "test"
}
- apiFile = apiFile + ".txt"
+ name = name + ".latest"
+ return name
+}
- return path.Join(apiDir, apiFile)
+func (module *sdkLibrary) latestRemovedApiFilegroupName(apiScope apiScope) string {
+ name := ":" + module.BaseModuleName() + "-removed.api."
+ switch apiScope {
+ case apiScopePublic:
+ name = name + "public"
+ case apiScopeSystem:
+ name = name + "system"
+ case apiScopeTest:
+ name = name + "test"
+ }
+ name = name + ".latest"
+ return name
}
// Creates a static java library that has API stubs
@@ -316,6 +341,10 @@
Api_tag_name *string
Api_filename *string
Removed_api_filename *string
+ Check_api struct {
+ Current ApiToCheck
+ Last_released ApiToCheck
+ }
}{}
props.Name = proptools.StringPtr(module.docsName(apiScope))
@@ -340,7 +369,6 @@
// List of APIs identified from the provided source files are created. They are later
// compared against to the not-yet-released (a.k.a current) list of APIs and to the
// last-released (a.k.a numbered) list of API.
- // TODO: If any incompatible change is detected, break the build
currentApiFileName := "current.txt"
removedApiFileName := "removed.txt"
switch apiScope {
@@ -353,12 +381,31 @@
}
currentApiFileName = path.Join("api", currentApiFileName)
removedApiFileName = path.Join("api", removedApiFileName)
+ // TODO(jiyong): remove these three props
props.Api_tag_name = proptools.StringPtr(module.apiTagName(apiScope))
- // Note: the exact names of these two are not important because they are always
- // referenced by the make variable $(INTERNAL_PLATFORM_<TAG_NAME>_API_FILE)
props.Api_filename = proptools.StringPtr(currentApiFileName)
props.Removed_api_filename = proptools.StringPtr(removedApiFileName)
+ // check against the not-yet-release API
+ props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
+ props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
+ // any change is reported as error
+ props.Check_api.Current.Args = proptools.StringPtr("-error 2 -error 3 -error 4 -error 5 " +
+ "-error 6 -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 " +
+ "-error 14 -error 15 -error 16 -error 17 -error 18 -error 19 -error 20 " +
+ "-error 21 -error 23 -error 24 -error 25 -error 26 -error 27")
+
+ // check against the latest released API
+ props.Check_api.Last_released.Api_file = proptools.StringPtr(
+ module.latestApiFilegroupName(apiScope))
+ props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
+ module.latestRemovedApiFilegroupName(apiScope))
+ // backward incompatible changes are reported as error
+ props.Check_api.Last_released.Args = proptools.StringPtr("-hide 2 -hide 3 -hide 4 -hide 5 " +
+ "-hide 6 -hide 24 -hide 25 -hide 26 -hide 27 " +
+ "-error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 " +
+ "-error 15 -error 16 -error 17 -error 18")
+
// Include the part of the framework source. This is required for the case when
// API class is extending from the framework class. In that case, doclava needs
// to know whether the base class is hidden or not. Since that information is
diff --git a/java/support_libraries.go b/java/support_libraries.go
new file mode 100644
index 0000000..320afae
--- /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 82ee3cb..42987fa 100644
--- a/python/proto.go
+++ b/python/proto.go
@@ -29,12 +29,14 @@
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")
)
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index d150451..e3552a0 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -48,7 +48,7 @@
"Platform_sdk_version": ${PLATFORM_SDK_VERSION},
"Platform_version_active_codenames": ${PLATFORM_VERSION_ALL_CODENAMES},
- "DeviceName": "flounder",
+ "DeviceName": "generic_arm64",
"DeviceArch": "arm64",
"DeviceArchVariant": "armv8-a",
"DeviceCpuVariant": "denver64",
diff --git a/ui/build/util.go b/ui/build/util.go
index f698ccd..96088fe 100644
--- a/ui/build/util.go
+++ b/ui/build/util.go
@@ -62,9 +62,24 @@
func ensureEmptyDirectoriesExist(ctx Context, dirs ...string) {
// remove all the directories
for _, dir := range dirs {
- err := os.RemoveAll(dir)
- if err != nil {
- ctx.Fatalf("Error removing %s: %q\n", dir, err)
+ seenErr := map[string]bool{}
+ for {
+ err := os.RemoveAll(dir)
+ if err == nil {
+ break
+ }
+
+ if pathErr, ok := err.(*os.PathError); !ok ||
+ dir == pathErr.Path || seenErr[pathErr.Path] {
+
+ ctx.Fatalf("Error removing %s: %q\n", dir, err)
+ } else {
+ seenErr[pathErr.Path] = true
+ err = os.Chmod(filepath.Dir(pathErr.Path), 0700)
+ if err != nil {
+ ctx.Fatal(err)
+ }
+ }
}
}
// recreate all the directories
diff --git a/ui/build/util_test.go b/ui/build/util_test.go
index e85eada..0e0dbdf 100644
--- a/ui/build/util_test.go
+++ b/ui/build/util_test.go
@@ -14,7 +14,41 @@
package build
-import "testing"
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "android/soong/ui/logger"
+)
+
+func TestEnsureEmptyDirs(t *testing.T) {
+ ctx := testContext()
+ defer logger.Recover(func(err error) {
+ t.Error(err)
+ })
+
+ tmpDir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer func() {
+ err := os.RemoveAll(tmpDir)
+ if err != nil {
+ t.Errorf("Error removing tmpDir: %v", err)
+ }
+ }()
+
+ ensureEmptyDirectoriesExist(ctx, filepath.Join(tmpDir, "a/b"))
+
+ err = os.Chmod(filepath.Join(tmpDir, "a"), 0555)
+ if err != nil {
+ t.Fatalf("Failed to chown: %v", err)
+ }
+
+ ensureEmptyDirectoriesExist(ctx, filepath.Join(tmpDir, "a"))
+}
func TestStripAnsiEscapes(t *testing.T) {
testcases := []struct {