Merge "Revert "Re-enable ASAN globals instrumentation for devices.""
diff --git a/Android.bp b/Android.bp
index 1b68adb..4e44a0d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -81,6 +81,7 @@
],
testSrcs: [
"android/android_test.go",
+ "android/androidmk_test.go",
"android/arch_test.go",
"android/config_test.go",
"android/expand_test.go",
@@ -177,6 +178,7 @@
"cc/linker.go",
"cc/binary.go",
+ "cc/fuzz.go",
"cc/library.go",
"cc/object.go",
"cc/test.go",
@@ -280,6 +282,7 @@
"java/jdeps.go",
"java/java_resources.go",
"java/kotlin.go",
+ "java/platform_compat_config.go",
"java/plugin.go",
"java/prebuilt_apis.go",
"java/proto.go",
@@ -291,6 +294,7 @@
"java/testing.go",
],
testSrcs: [
+ "java/androidmk_test.go",
"java/app_test.go",
"java/device_host_converter_test.go",
"java/dexpreopt_test.go",
diff --git a/README.md b/README.md
index 531ef4c..18422ea 100644
--- a/README.md
+++ b/README.md
@@ -236,6 +236,11 @@
If no `default_visibility` property can be found then the module uses the
global default of `//visibility:legacy_public`.
+The `visibility` property has no effect on a defaults module although it does
+apply to any non-defaults module that uses it. To set the visibility of a
+defaults module, use the `defaults_visibility` property on the defaults module;
+not to be confused with the `default_visibility` property on the package module.
+
Once the build has been completely switched over to soong it is possible that a
global refactoring will be done to change this to `//visibility:private` at
which point all packages that do not currently specify a `default_visibility`
@@ -350,6 +355,18 @@
dlv connect :1234
```
+If you see an error:
+```
+Could not attach to pid 593: this could be caused by a kernel
+security setting, try writing "0" to /proc/sys/kernel/yama/ptrace_scope
+```
+you can temporarily disable
+[Yama's ptrace protection](https://www.kernel.org/doc/Documentation/security/Yama.txt)
+using:
+```bash
+sudo sysctl -w kernel.yama.ptrace_scope=0
+```
+
## Contact
Email android-building@googlegroups.com (external) for any questions, or see
diff --git a/android/androidmk.go b/android/androidmk.go
index 9bc2692..1f1bd70 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -191,15 +191,7 @@
// Make cannot identify LOCAL_MODULE_TARGET_ARCH:= common.
if archStr != "common" {
if amod.Target().NativeBridge {
- // TODO: Unhardcode these rules.
- guestArchStr := archStr
- hostArchStr := ""
- if guestArchStr == "arm" {
- hostArchStr = "x86"
- } else if guestArchStr == "arm64" {
- hostArchStr = "x86_64"
- }
-
+ hostArchStr := amod.Target().NativeBridgeHostArchName
if hostArchStr != "" {
a.SetString("LOCAL_MODULE_TARGET_ARCH", hostArchStr)
}
@@ -391,6 +383,31 @@
return nil
}
+func (data *AndroidMkData) fillInData(config Config, bpPath string, mod blueprint.Module) {
+ // Get the preamble content through AndroidMkEntries logic.
+ entries := AndroidMkEntries{
+ Class: data.Class,
+ SubName: data.SubName,
+ DistFile: data.DistFile,
+ OutputFile: data.OutputFile,
+ Disabled: data.Disabled,
+ Include: data.Include,
+ Required: data.Required,
+ Host_required: data.Host_required,
+ Target_required: data.Target_required,
+ }
+ entries.fillInEntries(config, bpPath, mod)
+
+ // preamble doesn't need the footer content.
+ entries.footer = bytes.Buffer{}
+ entries.write(&data.preamble)
+
+ // copy entries back to data since it is used in Custom
+ data.Required = entries.Required
+ data.Host_required = entries.Host_required
+ data.Target_required = entries.Target_required
+}
+
func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
provider AndroidMkDataProvider) error {
@@ -404,22 +421,7 @@
data.Include = "$(BUILD_PREBUILT)"
}
- // Get the preamble content through AndroidMkEntries logic.
- entries := AndroidMkEntries{
- Class: data.Class,
- SubName: data.SubName,
- DistFile: data.DistFile,
- OutputFile: data.OutputFile,
- Disabled: data.Disabled,
- Include: data.Include,
- Required: data.Required,
- Host_required: data.Host_required,
- Target_required: data.Target_required,
- }
- entries.fillInEntries(ctx.Config(), ctx.BlueprintFile(mod), mod)
- // preamble doesn't need the footer content.
- entries.footer = bytes.Buffer{}
- entries.write(&data.preamble)
+ data.fillInData(ctx.Config(), ctx.BlueprintFile(mod), mod)
prefix := ""
if amod.ArchSpecific() {
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
new file mode 100644
index 0000000..0bb455b
--- /dev/null
+++ b/android/androidmk_test.go
@@ -0,0 +1,82 @@
+// Copyright 2019 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 android
+
+import (
+ "io"
+ "reflect"
+ "testing"
+)
+
+type customModule struct {
+ ModuleBase
+ data AndroidMkData
+}
+
+func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+func (m *customModule) AndroidMk() AndroidMkData {
+ return AndroidMkData{
+ Custom: func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData) {
+ m.data = data
+ },
+ }
+}
+
+func customModuleFactory() Module {
+ module := &customModule{}
+ InitAndroidModule(module)
+ return module
+}
+
+func TestAndroidMkSingleton_PassesUpdatedAndroidMkDataToCustomCallback(t *testing.T) {
+ config := TestConfig(buildDir, nil)
+ config.inMake = true // Enable androidmk Singleton
+
+ ctx := NewTestContext()
+ ctx.RegisterSingletonType("androidmk", SingletonFactoryAdaptor(AndroidMkSingleton))
+ ctx.RegisterModuleType("custom", ModuleFactoryAdaptor(customModuleFactory))
+ ctx.Register()
+
+ bp := `
+ custom {
+ name: "foo",
+ required: ["bar"],
+ host_required: ["baz"],
+ target_required: ["qux"],
+ }
+ `
+
+ ctx.MockFileSystem(map[string][]byte{
+ "Android.bp": []byte(bp),
+ })
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ m := ctx.ModuleForTests("foo", "").Module().(*customModule)
+
+ assertEqual := func(expected interface{}, actual interface{}) {
+ if !reflect.DeepEqual(expected, actual) {
+ t.Errorf("%q expected, but got %q", expected, actual)
+ }
+ }
+ assertEqual([]string{"bar"}, m.data.Required)
+ assertEqual([]string{"baz"}, m.data.Host_required)
+ assertEqual([]string{"qux"}, m.data.Target_required)
+}
diff --git a/android/arch.go b/android/arch.go
index 46e582c..907c58b 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -557,6 +557,10 @@
return archType
}
+func ArchTypeList() []ArchType {
+ return append([]ArchType(nil), archTypeList...)
+}
+
func (a ArchType) String() string {
return a.Name
}
@@ -691,9 +695,11 @@
)
type Target struct {
- Os OsType
- Arch Arch
- NativeBridge NativeBridgeSupport
+ Os OsType
+ Arch Arch
+ NativeBridge NativeBridgeSupport
+ NativeBridgeHostArchName string
+ NativeBridgeRelativePath string
}
func (target Target) String() string {
@@ -1403,7 +1409,8 @@
var targetErr error
addTarget := func(os OsType, archName string, archVariant, cpuVariant *string, abi []string,
- nativeBridgeEnabled NativeBridgeSupport) {
+ nativeBridgeEnabled NativeBridgeSupport, nativeBridgeHostArchName *string,
+ nativeBridgeRelativePath *string) {
if targetErr != nil {
return
}
@@ -1413,12 +1420,21 @@
targetErr = err
return
}
+ nativeBridgeRelativePathStr := String(nativeBridgeRelativePath)
+ nativeBridgeHostArchNameStr := String(nativeBridgeHostArchName)
+
+ // Use guest arch as relative install path by default
+ if nativeBridgeEnabled && nativeBridgeRelativePathStr == "" {
+ nativeBridgeRelativePathStr = arch.ArchType.String()
+ }
targets[os] = append(targets[os],
Target{
- Os: os,
- Arch: arch,
- NativeBridge: nativeBridgeEnabled,
+ Os: os,
+ Arch: arch,
+ NativeBridge: nativeBridgeEnabled,
+ NativeBridgeHostArchName: nativeBridgeHostArchNameStr,
+ NativeBridgeRelativePath: nativeBridgeRelativePathStr,
})
}
@@ -1426,14 +1442,14 @@
return nil, fmt.Errorf("No host primary architecture set")
}
- addTarget(BuildOs, *variables.HostArch, nil, nil, nil, NativeBridgeDisabled)
+ addTarget(BuildOs, *variables.HostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
if variables.HostSecondaryArch != nil && *variables.HostSecondaryArch != "" {
- addTarget(BuildOs, *variables.HostSecondaryArch, nil, nil, nil, NativeBridgeDisabled)
+ addTarget(BuildOs, *variables.HostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
}
if Bool(config.Host_bionic) {
- addTarget(LinuxBionic, "x86_64", nil, nil, nil, NativeBridgeDisabled)
+ addTarget(LinuxBionic, "x86_64", nil, nil, nil, NativeBridgeDisabled, nil, nil)
}
if String(variables.CrossHost) != "" {
@@ -1446,10 +1462,10 @@
return nil, fmt.Errorf("No cross-host primary architecture set")
}
- addTarget(crossHostOs, *variables.CrossHostArch, nil, nil, nil, NativeBridgeDisabled)
+ addTarget(crossHostOs, *variables.CrossHostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
if variables.CrossHostSecondaryArch != nil && *variables.CrossHostSecondaryArch != "" {
- addTarget(crossHostOs, *variables.CrossHostSecondaryArch, nil, nil, nil, NativeBridgeDisabled)
+ addTarget(crossHostOs, *variables.CrossHostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
}
}
@@ -1460,12 +1476,12 @@
}
addTarget(target, *variables.DeviceArch, variables.DeviceArchVariant,
- variables.DeviceCpuVariant, variables.DeviceAbi, NativeBridgeDisabled)
+ variables.DeviceCpuVariant, variables.DeviceAbi, NativeBridgeDisabled, nil, nil)
if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" {
addTarget(Android, *variables.DeviceSecondaryArch,
variables.DeviceSecondaryArchVariant, variables.DeviceSecondaryCpuVariant,
- variables.DeviceSecondaryAbi, NativeBridgeDisabled)
+ variables.DeviceSecondaryAbi, NativeBridgeDisabled, nil, nil)
deviceArches := targets[Android]
if deviceArches[0].Arch.ArchType.Multilib == deviceArches[1].Arch.ArchType.Multilib {
@@ -1476,7 +1492,8 @@
if variables.NativeBridgeArch != nil && *variables.NativeBridgeArch != "" {
addTarget(Android, *variables.NativeBridgeArch,
variables.NativeBridgeArchVariant, variables.NativeBridgeCpuVariant,
- variables.NativeBridgeAbi, NativeBridgeEnabled)
+ variables.NativeBridgeAbi, NativeBridgeEnabled, variables.DeviceArch,
+ variables.NativeBridgeRelativePath)
}
if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" &&
@@ -1484,7 +1501,10 @@
addTarget(Android, *variables.NativeBridgeSecondaryArch,
variables.NativeBridgeSecondaryArchVariant,
variables.NativeBridgeSecondaryCpuVariant,
- variables.NativeBridgeSecondaryAbi, NativeBridgeEnabled)
+ variables.NativeBridgeSecondaryAbi,
+ NativeBridgeEnabled,
+ variables.DeviceSecondaryArch,
+ variables.NativeBridgeSecondaryRelativePath)
}
}
diff --git a/android/config.go b/android/config.go
index c0ed50f..cb1bdf5 100644
--- a/android/config.go
+++ b/android/config.go
@@ -240,10 +240,10 @@
config := testConfig.config
config.Targets[Android] = []Target{
- {Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled},
- {Android, Arch{ArchType: X86, ArchVariant: "silvermont", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled},
- {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled},
- {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled},
+ {Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", ""},
+ {Android, Arch{ArchType: X86, ArchVariant: "silvermont", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", ""},
+ {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled, "x86_64", "arm64"},
+ {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled, "x86", "arm"},
}
return testConfig
@@ -255,10 +255,10 @@
config.Targets = map[OsType][]Target{
Fuchsia: []Target{
- {Fuchsia, Arch{ArchType: Arm64, ArchVariant: "", Native: true}, NativeBridgeDisabled},
+ {Fuchsia, Arch{ArchType: Arm64, ArchVariant: "", Native: true}, NativeBridgeDisabled, "", ""},
},
BuildOs: []Target{
- {BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled},
+ {BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
},
}
@@ -272,12 +272,12 @@
config.Targets = map[OsType][]Target{
Android: []Target{
- {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled},
- {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled},
+ {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", ""},
+ {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", ""},
},
BuildOs: []Target{
- {BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled},
- {BuildOs, Arch{ArchType: X86}, NativeBridgeDisabled},
+ {BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
+ {BuildOs, Arch{ArchType: X86}, NativeBridgeDisabled, "", ""},
},
}
@@ -689,10 +689,6 @@
return c.Targets[Android][0].Arch.ArchType
}
-func (c *config) SkipDeviceInstall() bool {
- return c.EmbeddedInMake()
-}
-
func (c *config) SkipMegaDeviceInstall(path string) bool {
return Bool(c.Mega_device) &&
strings.HasPrefix(path, filepath.Join(c.buildDir, "target", "product"))
@@ -748,10 +744,22 @@
return Bool(c.productVariables.UseGoma)
}
+func (c *config) UseRBE() bool {
+ return Bool(c.productVariables.UseRBE)
+}
+
func (c *config) RunErrorProne() bool {
return c.IsEnvTrue("RUN_ERROR_PRONE")
}
+func (c *config) XrefCorpusName() string {
+ return c.Getenv("XREF_CORPUS")
+}
+
+func (c *config) EmitXrefRules() bool {
+ return c.XrefCorpusName() != ""
+}
+
// Returns true if -source 1.9 -target 1.9 is being passed to javac
func (c *config) TargetOpenJDK9() bool {
return c.targetOpenJDK9
@@ -840,6 +848,10 @@
return ExistentPathForSource(ctx, "frameworks", "base").Valid()
}
+func (c *config) VndkSnapshotBuildArtifacts() bool {
+ return Bool(c.productVariables.VndkSnapshotBuildArtifacts)
+}
+
func (c *deviceConfig) Arches() []Arch {
var arches []Arch
for _, target := range c.config.Targets[Android] {
@@ -1065,7 +1077,7 @@
}
func (c *config) FlattenApex() bool {
- return Bool(c.productVariables.FlattenApex)
+ return Bool(c.productVariables.Flatten_apex)
}
func (c *config) EnforceSystemCertificate() bool {
diff --git a/android/defaults.go b/android/defaults.go
index ae2c820..f489c02 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -42,9 +42,16 @@
d.defaultableProperties = props
}
+// Interface that must be supported by any module to which defaults can be applied.
type Defaultable interface {
+ // Get a pointer to the struct containing the Defaults property.
defaults() *defaultsProperties
+
+ // Set the property structures into which defaults will be added.
setProperties([]interface{})
+
+ // Apply defaults from the supplied Defaults to the property structures supplied to
+ // setProperties(...).
applyDefaults(TopDownMutatorContext, []Defaults)
}
@@ -56,13 +63,25 @@
var _ Defaultable = (*DefaultableModuleBase)(nil)
func InitDefaultableModule(module DefaultableModule) {
- module.(Defaultable).setProperties(module.(Module).GetProperties())
+ module.setProperties(module.(Module).GetProperties())
module.AddProperties(module.defaults())
}
+// The Defaults_visibility property.
+type DefaultsVisibilityProperties struct {
+
+ // Controls the visibility of the defaults module itself.
+ Defaults_visibility []string
+}
+
type DefaultsModuleBase struct {
DefaultableModuleBase
+
+ // Container for defaults of the common properties
+ commonProperties commonProperties
+
+ defaultsVisibilityProperties DefaultsVisibilityProperties
}
// The common pattern for defaults modules is to register separate instances of
@@ -87,33 +106,75 @@
// rather than disabling the defaults module itself.
type Defaults interface {
Defaultable
+
+ // Although this function is unused it is actually needed to ensure that only modules that embed
+ // DefaultsModuleBase will type-assert to the Defaults interface.
isDefaults() bool
+
+ // Get the structures containing the properties for which defaults can be provided.
properties() []interface{}
+
+ // Return the defaults common properties.
+ common() *commonProperties
+
+ // Return the defaults visibility properties.
+ defaultsVisibility() *DefaultsVisibilityProperties
}
func (d *DefaultsModuleBase) isDefaults() bool {
return true
}
+type DefaultsModule interface {
+ Module
+ Defaults
+}
+
func (d *DefaultsModuleBase) properties() []interface{} {
return d.defaultableProperties
}
+func (d *DefaultsModuleBase) common() *commonProperties {
+ return &d.commonProperties
+}
+
+func (d *DefaultsModuleBase) defaultsVisibility() *DefaultsVisibilityProperties {
+ return &d.defaultsVisibilityProperties
+}
+
func (d *DefaultsModuleBase) GenerateAndroidBuildActions(ctx ModuleContext) {
}
-func InitDefaultsModule(module DefaultableModule) {
+func InitDefaultsModule(module DefaultsModule) {
+ commonProperties := module.common()
+
module.AddProperties(
&hostAndDeviceProperties{},
- &commonProperties{},
+ commonProperties,
&variableProperties{})
InitArchModule(module)
InitDefaultableModule(module)
- module.AddProperties(&module.base().nameProperties)
+ // Add properties that will not have defaults applied to them.
+ base := module.base()
+ defaultsVisibility := module.defaultsVisibility()
+ module.AddProperties(&base.nameProperties, defaultsVisibility)
- module.base().module = module
+ // The defaults_visibility property controls the visibility of a defaults module.
+ base.primaryVisibilityProperty =
+ newVisibilityProperty("defaults_visibility", &defaultsVisibility.Defaults_visibility)
+
+ // Unlike non-defaults modules the visibility property is not stored in m.base().commonProperties.
+ // Instead it is stored in a separate instance of commonProperties created above so use that.
+ // The visibility property needs to be checked (but not parsed) by the visibility module during
+ // its checking phase and parsing phase.
+ base.visibilityPropertyInfo = []visibilityProperty{
+ base.primaryVisibilityProperty,
+ newVisibilityProperty("visibility", &commonProperties.Visibility),
+ }
+
+ base.module = module
}
var _ Defaults = (*DefaultsModuleBase)(nil)
diff --git a/android/expand.go b/android/expand.go
index 527c4ac..67fb4ee 100644
--- a/android/expand.go
+++ b/android/expand.go
@@ -18,12 +18,30 @@
"fmt"
"strings"
"unicode"
+
+ "github.com/google/blueprint/proptools"
)
+// ExpandNinjaEscaped substitutes $() variables in a string
+// $(var) is passed to mapping(var), which should return the expanded value, a bool for whether the result should
+// be left unescaped when using in a ninja value (generally false, true if the expanded value is a ninja variable like
+// '${in}'), and an error.
+// $$ is converted to $, which is escaped back to $$.
+func ExpandNinjaEscaped(s string, mapping func(string) (string, bool, error)) (string, error) {
+ return expand(s, true, mapping)
+}
+
// Expand substitutes $() variables in a string
-// $(var) is passed to Expander(var)
-// $$ is converted to $
+// $(var) is passed to mapping(var), which should return the expanded value and an error.
+// $$ is converted to $.
func Expand(s string, mapping func(string) (string, error)) (string, error) {
+ return expand(s, false, func(s string) (string, bool, error) {
+ s, err := mapping(s)
+ return s, false, err
+ })
+}
+
+func expand(s string, ninjaEscape bool, mapping func(string) (string, bool, error)) (string, error) {
// based on os.Expand
buf := make([]byte, 0, 2*len(s))
i := 0
@@ -33,10 +51,13 @@
return "", fmt.Errorf("expected character after '$'")
}
buf = append(buf, s[i:j]...)
- value, w, err := getMapping(s[j+1:], mapping)
+ value, ninjaVariable, w, err := getMapping(s[j+1:], mapping)
if err != nil {
return "", err
}
+ if !ninjaVariable && ninjaEscape {
+ value = proptools.NinjaEscape(value)
+ }
buf = append(buf, value...)
j += w
i = j + 1
@@ -45,26 +66,26 @@
return string(buf) + s[i:], nil
}
-func getMapping(s string, mapping func(string) (string, error)) (string, int, error) {
+func getMapping(s string, mapping func(string) (string, bool, error)) (string, bool, int, error) {
switch s[0] {
case '(':
// Scan to closing brace
for i := 1; i < len(s); i++ {
if s[i] == ')' {
- ret, err := mapping(strings.TrimSpace(s[1:i]))
- return ret, i + 1, err
+ ret, ninjaVariable, err := mapping(strings.TrimSpace(s[1:i]))
+ return ret, ninjaVariable, i + 1, err
}
}
- return "", len(s), fmt.Errorf("missing )")
+ return "", false, len(s), fmt.Errorf("missing )")
case '$':
- return "$$", 1, nil
+ return "$", false, 1, nil
default:
i := strings.IndexFunc(s, unicode.IsSpace)
if i == 0 {
- return "", 0, fmt.Errorf("unexpected character '%c' after '$'", s[0])
+ return "", false, 0, fmt.Errorf("unexpected character '%c' after '$'", s[0])
} else if i == -1 {
i = len(s)
}
- return "", 0, fmt.Errorf("expected '(' after '$', did you mean $(%s)?", s[:i])
+ return "", false, 0, fmt.Errorf("expected '(' after '$', did you mean $(%s)?", s[:i])
}
}
diff --git a/android/expand_test.go b/android/expand_test.go
index 128de8a..12179ed 100644
--- a/android/expand_test.go
+++ b/android/expand_test.go
@@ -20,88 +20,111 @@
)
var vars = map[string]string{
- "var1": "abc",
- "var2": "",
- "var3": "def",
- "💩": "😃",
+ "var1": "abc",
+ "var2": "",
+ "var3": "def",
+ "💩": "😃",
+ "escape": "${in}",
}
-func expander(s string) (string, error) {
+func expander(s string) (string, bool, error) {
if val, ok := vars[s]; ok {
- return val, nil
+ return val, s == "escape", nil
} else {
- return "", fmt.Errorf("unknown variable %q", s)
+ return "", false, fmt.Errorf("unknown variable %q", s)
}
}
var expandTestCases = []struct {
- in string
- out string
- err bool
+ in string
+ out string
+ out_escaped string
+ err bool
}{
{
- in: "$(var1)",
- out: "abc",
+ in: "$(var1)",
+ out: "abc",
+ out_escaped: "abc",
},
{
- in: "$( var1 )",
- out: "abc",
+ in: "$( var1 )",
+ out: "abc",
+ out_escaped: "abc",
},
{
- in: "def$(var1)",
- out: "defabc",
+ in: "def$(var1)",
+ out: "defabc",
+ out_escaped: "defabc",
},
{
- in: "$(var1)def",
- out: "abcdef",
+ in: "$(var1)def",
+ out: "abcdef",
+ out_escaped: "abcdef",
},
{
- in: "def$(var1)def",
- out: "defabcdef",
+ in: "def$(var1)def",
+ out: "defabcdef",
+ out_escaped: "defabcdef",
},
{
- in: "$(var2)",
- out: "",
+ in: "$(var2)",
+ out: "",
+ out_escaped: "",
},
{
- in: "def$(var2)",
- out: "def",
+ in: "def$(var2)",
+ out: "def",
+ out_escaped: "def",
},
{
- in: "$(var2)def",
- out: "def",
+ in: "$(var2)def",
+ out: "def",
+ out_escaped: "def",
},
{
- in: "def$(var2)def",
- out: "defdef",
+ in: "def$(var2)def",
+ out: "defdef",
+ out_escaped: "defdef",
},
{
- in: "$(var1)$(var3)",
- out: "abcdef",
+ in: "$(var1)$(var3)",
+ out: "abcdef",
+ out_escaped: "abcdef",
},
{
- in: "$(var1)g$(var3)",
- out: "abcgdef",
+ in: "$(var1)g$(var3)",
+ out: "abcgdef",
+ out_escaped: "abcgdef",
},
{
- in: "$$",
- out: "$$",
+ in: "$$",
+ out: "$",
+ out_escaped: "$$",
},
{
- in: "$$(var1)",
- out: "$$(var1)",
+ in: "$$(var1)",
+ out: "$(var1)",
+ out_escaped: "$$(var1)",
},
{
- in: "$$$(var1)",
- out: "$$abc",
+ in: "$$$(var1)",
+ out: "$abc",
+ out_escaped: "$$abc",
},
{
- in: "$(var1)$$",
- out: "abc$$",
+ in: "$(var1)$$",
+ out: "abc$",
+ out_escaped: "abc$$",
},
{
- in: "$(💩)",
- out: "😃",
+ in: "$(💩)",
+ out: "😃",
+ out_escaped: "😃",
+ },
+ {
+ in: "$$a$(escape)$$b",
+ out: "$a${in}$b",
+ out_escaped: "$$a${in}$$b",
},
// Errors
@@ -141,7 +164,10 @@
func TestExpand(t *testing.T) {
for _, test := range expandTestCases {
- got, err := Expand(test.in, expander)
+ got, err := Expand(test.in, func(s string) (string, error) {
+ s, _, err := expander(s)
+ return s, err
+ })
if err != nil && !test.err {
t.Errorf("%q: unexpected error %s", test.in, err.Error())
} else if err == nil && test.err {
@@ -151,3 +177,16 @@
}
}
}
+
+func TestExpandNinjaEscaped(t *testing.T) {
+ for _, test := range expandTestCases {
+ got, err := ExpandNinjaEscaped(test.in, expander)
+ if err != nil && !test.err {
+ t.Errorf("%q: unexpected error %s", test.in, err.Error())
+ } else if err == nil && test.err {
+ t.Errorf("%q: expected error, got %q", test.in, got)
+ } else if !test.err && got != test.out_escaped {
+ t.Errorf("%q: expected %q, got %q", test.in, test.out, got)
+ }
+ }
+}
diff --git a/android/module.go b/android/module.go
index adb9454..0ab9be7 100644
--- a/android/module.go
+++ b/android/module.go
@@ -71,6 +71,7 @@
OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{})
OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
OtherModuleExists(name string) bool
+ OtherModuleType(m blueprint.Module) string
GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module
GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module
@@ -155,6 +156,7 @@
InstallInData() bool
InstallInSanitizerDir() bool
InstallInRecovery() bool
+ InstallBypassMake() bool
RequiredModuleNames() []string
HostRequiredModuleNames() []string
@@ -192,6 +194,7 @@
InstallInData() bool
InstallInSanitizerDir() bool
InstallInRecovery() bool
+ InstallBypassMake() bool
SkipInstall()
ExportedToMake() bool
NoticeFile() OptionalPath
@@ -211,6 +214,9 @@
// Get information about the properties that can contain visibility rules.
visibilityProperties() []visibilityProperty
+
+ // Get the visibility rules that control the visibility of this module.
+ visibility() []string
}
// Qualified id for a module
@@ -302,6 +308,11 @@
// If no `default_visibility` property can be found then the module uses the
// global default of `//visibility:legacy_public`.
//
+ // The `visibility` property has no effect on a defaults module although it does
+ // apply to any non-defaults module that uses it. To set the visibility of a
+ // defaults module, use the `defaults_visibility` property on the defaults module;
+ // not to be confused with the `default_visibility` property on the package module.
+ //
// See https://android.googlesource.com/platform/build/soong/+/master/README.md#visibility for
// more details.
Visibility []string
@@ -503,6 +514,12 @@
&base.variableProperties)
base.generalProperties = m.GetProperties()
base.customizableProperties = m.GetProperties()
+
+ // The default_visibility property needs to be checked and parsed by the visibility module during
+ // its checking and parsing phases.
+ base.primaryVisibilityProperty =
+ newVisibilityProperty("visibility", &base.commonProperties.Visibility)
+ base.visibilityPropertyInfo = []visibilityProperty{base.primaryVisibilityProperty}
}
func InitAndroidArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib Multilib) {
@@ -582,6 +599,13 @@
archProperties [][]interface{}
customizableProperties []interface{}
+ // Information about all the properties on the module that contains visibility rules that need
+ // checking.
+ visibilityPropertyInfo []visibilityProperty
+
+ // The primary visibility property, may be nil, that controls access to the module.
+ primaryVisibilityProperty visibilityProperty
+
noAddressSanitizer bool
installFiles Paths
checkbuildFiles Paths
@@ -668,10 +692,15 @@
}
func (m *ModuleBase) visibilityProperties() []visibilityProperty {
- return []visibilityProperty{
- newVisibilityProperty("visibility", func() []string {
- return m.base().commonProperties.Visibility
- }),
+ return m.visibilityPropertyInfo
+}
+
+func (m *ModuleBase) visibility() []string {
+ // The soong_namespace module does not initialize the primaryVisibilityProperty.
+ if m.primaryVisibilityProperty != nil {
+ return m.primaryVisibilityProperty.getStrings()
+ } else {
+ return nil
}
}
@@ -811,6 +840,10 @@
return Bool(m.commonProperties.Recovery)
}
+func (m *ModuleBase) InstallBypassMake() bool {
+ return false
+}
+
func (m *ModuleBase) Owner() string {
return String(m.commonProperties.Owner)
}
@@ -1467,6 +1500,10 @@
return m.module.InstallInRecovery()
}
+func (m *moduleContext) InstallBypassMake() bool {
+ return m.module.InstallBypassMake()
+}
+
func (m *moduleContext) skipInstall(fullInstallPath OutputPath) bool {
if m.module.base().commonProperties.SkipInstall {
return true
@@ -1480,7 +1517,7 @@
}
if m.Device() {
- if m.Config().SkipDeviceInstall() {
+ if m.Config().EmbeddedInMake() && !m.InstallBypassMake() {
return true
}
diff --git a/android/mutator.go b/android/mutator.go
index b799432..8e4343d 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -143,6 +143,7 @@
CreateVariations(...string) []blueprint.Module
CreateLocalVariations(...string) []blueprint.Module
SetDependencyVariation(string)
+ SetDefaultDependencyVariation(*string)
AddVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string)
AddFarVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string)
AddInterVariantDependency(tag blueprint.DependencyTag, from, to blueprint.Module)
@@ -243,7 +244,8 @@
}
func (t *topDownMutatorContext) CreateModule(factory blueprint.ModuleFactory, props ...interface{}) {
- t.bp.CreateModule(factory, props...)
+ common := []interface{}{&t.Module().base().commonProperties}
+ t.bp.CreateModule(factory, append(common, props...)...)
}
func (b *bottomUpMutatorContext) MutatorName() string {
@@ -291,6 +293,10 @@
b.bp.SetDependencyVariation(variation)
}
+func (b *bottomUpMutatorContext) SetDefaultDependencyVariation(variation *string) {
+ b.bp.SetDefaultDependencyVariation(variation)
+}
+
func (b *bottomUpMutatorContext) AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag,
names ...string) {
diff --git a/android/neverallow.go b/android/neverallow.go
index 1893e8b..8355bb3 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -48,6 +48,7 @@
var neverallows = []Rule{}
func init() {
+ AddNeverAllowRules(createIncludeDirsRules()...)
AddNeverAllowRules(createTrebleRules()...)
AddNeverAllowRules(createLibcoreRules()...)
AddNeverAllowRules(createJavaDeviceForHostRules()...)
@@ -58,6 +59,42 @@
neverallows = append(neverallows, rules...)
}
+func createIncludeDirsRules() []Rule {
+ // The list of paths that cannot be referenced using include_dirs
+ paths := []string{
+ "art",
+ "libcore",
+ "libnativehelper",
+ "external/apache-harmony",
+ "external/apache-xml",
+ "external/boringssl",
+ "external/bouncycastle",
+ "external/conscrypt",
+ "external/icu",
+ "external/okhttp",
+ "external/vixl",
+ "external/wycheproof",
+ "system/core/libnativebridge",
+ "system/core/libnativehelper",
+ }
+
+ // Create a composite matcher that will match if the value starts with any of the restricted
+ // paths. A / is appended to the prefix to ensure that restricting path X does not affect paths
+ // XY.
+ rules := make([]Rule, 0, len(paths))
+ for _, path := range paths {
+ rule :=
+ NeverAllow().
+ WithMatcher("include_dirs", StartsWith(path+"/")).
+ Because("include_dirs is deprecated, all usages of '" + path + "' have been migrated" +
+ " to use alternate mechanisms and so can no longer be used.")
+
+ rules = append(rules, rule)
+ }
+
+ return rules
+}
+
func createTrebleRules() []Rule {
return []Rule{
NeverAllow().
@@ -138,6 +175,8 @@
dir := ctx.ModuleDir() + "/"
properties := m.GetProperties()
+ osClass := ctx.Module().Target().Os.Class
+
for _, r := range neverallows {
n := r.(*rule)
if !n.appliesToPath(dir) {
@@ -152,13 +191,63 @@
continue
}
+ if !n.appliesToOsClass(osClass) {
+ continue
+ }
+
+ if !n.appliesToDirectDeps(ctx) {
+ continue
+ }
+
ctx.ModuleErrorf("violates " + n.String())
}
}
+type ValueMatcher interface {
+ test(string) bool
+ String() string
+}
+
+type equalMatcher struct {
+ expected string
+}
+
+func (m *equalMatcher) test(value string) bool {
+ return m.expected == value
+}
+
+func (m *equalMatcher) String() string {
+ return "=" + m.expected
+}
+
+type anyMatcher struct {
+}
+
+func (m *anyMatcher) test(value string) bool {
+ return true
+}
+
+func (m *anyMatcher) String() string {
+ return "=*"
+}
+
+var anyMatcherInstance = &anyMatcher{}
+
+type startsWithMatcher struct {
+ prefix string
+}
+
+func (m *startsWithMatcher) test(value string) bool {
+ return strings.HasPrefix(value, m.prefix)
+}
+
+func (m *startsWithMatcher) String() string {
+ return ".starts-with(" + m.prefix + ")"
+}
+
type ruleProperty struct {
- fields []string // e.x.: Vndk.Enabled
- value string // e.x.: true
+ fields []string // e.x.: Vndk.Enabled
+ matcher ValueMatcher
}
// A NeverAllow rule.
@@ -167,14 +256,22 @@
NotIn(path ...string) Rule
+ InDirectDeps(deps ...string) Rule
+
+ WithOsClass(osClasses ...OsClass) Rule
+
ModuleType(types ...string) Rule
NotModuleType(types ...string) Rule
With(properties, value string) Rule
+ WithMatcher(properties string, matcher ValueMatcher) Rule
+
Without(properties, value string) Rule
+ WithoutMatcher(properties string, matcher ValueMatcher) Rule
+
Because(reason string) Rule
}
@@ -185,6 +282,10 @@
paths []string
unlessPaths []string
+ directDeps map[string]bool
+
+ osClasses []OsClass
+
moduleTypes []string
unlessModuleTypes []string
@@ -194,7 +295,7 @@
// Create a new NeverAllow rule.
func NeverAllow() Rule {
- return &rule{}
+ return &rule{directDeps: make(map[string]bool)}
}
func (r *rule) In(path ...string) Rule {
@@ -207,6 +308,18 @@
return r
}
+func (r *rule) InDirectDeps(deps ...string) Rule {
+ for _, d := range deps {
+ r.directDeps[d] = true
+ }
+ return r
+}
+
+func (r *rule) WithOsClass(osClasses ...OsClass) Rule {
+ r.osClasses = append(r.osClasses, osClasses...)
+ return r
+}
+
func (r *rule) ModuleType(types ...string) Rule {
r.moduleTypes = append(r.moduleTypes, types...)
return r
@@ -218,21 +331,36 @@
}
func (r *rule) With(properties, value string) Rule {
+ return r.WithMatcher(properties, selectMatcher(value))
+}
+
+func (r *rule) WithMatcher(properties string, matcher ValueMatcher) Rule {
r.props = append(r.props, ruleProperty{
- fields: fieldNamesForProperties(properties),
- value: value,
+ fields: fieldNamesForProperties(properties),
+ matcher: matcher,
})
return r
}
func (r *rule) Without(properties, value string) Rule {
+ return r.WithoutMatcher(properties, selectMatcher(value))
+}
+
+func (r *rule) WithoutMatcher(properties string, matcher ValueMatcher) Rule {
r.unlessProps = append(r.unlessProps, ruleProperty{
- fields: fieldNamesForProperties(properties),
- value: value,
+ fields: fieldNamesForProperties(properties),
+ matcher: matcher,
})
return r
}
+func selectMatcher(expected string) ValueMatcher {
+ if expected == "*" {
+ return anyMatcherInstance
+ }
+ return &equalMatcher{expected: expected}
+}
+
func (r *rule) Because(reason string) Rule {
r.reason = reason
return r
@@ -253,10 +381,16 @@
s += " -type:" + v
}
for _, v := range r.props {
- s += " " + strings.Join(v.fields, ".") + "=" + v.value
+ s += " " + strings.Join(v.fields, ".") + v.matcher.String()
}
for _, v := range r.unlessProps {
- s += " -" + strings.Join(v.fields, ".") + "=" + v.value
+ s += " -" + strings.Join(v.fields, ".") + v.matcher.String()
+ }
+ for k := range r.directDeps {
+ s += " deps:" + k
+ }
+ for _, v := range r.osClasses {
+ s += " os:" + v.String()
}
if len(r.reason) != 0 {
s += " which is restricted because " + r.reason
@@ -270,6 +404,36 @@
return includePath && !excludePath
}
+func (r *rule) appliesToDirectDeps(ctx BottomUpMutatorContext) bool {
+ if len(r.directDeps) == 0 {
+ return true
+ }
+
+ matches := false
+ ctx.VisitDirectDeps(func(m Module) {
+ if !matches {
+ name := ctx.OtherModuleName(m)
+ matches = r.directDeps[name]
+ }
+ })
+
+ return matches
+}
+
+func (r *rule) appliesToOsClass(osClass OsClass) bool {
+ if len(r.osClasses) == 0 {
+ return true
+ }
+
+ for _, c := range r.osClasses {
+ if c == osClass {
+ return true
+ }
+ }
+
+ return false
+}
+
func (r *rule) appliesToModuleType(moduleType string) bool {
return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
}
@@ -280,6 +444,10 @@
return includeProps && !excludeProps
}
+func StartsWith(prefix string) ValueMatcher {
+ return &startsWithMatcher{prefix}
+}
+
// assorted utils
func cleanPaths(paths []string) []string {
@@ -338,8 +506,8 @@
continue
}
- check := func(v string) bool {
- return prop.value == "*" || prop.value == v
+ check := func(value string) bool {
+ return prop.matcher.test(value)
}
if matchValue(propertiesValue, check) {
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index ee3c94f..920b9a5 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -16,13 +16,66 @@
import (
"testing"
+
+ "github.com/google/blueprint"
)
+func init() {
+ // Add extra rules needed for testing.
+ AddNeverAllowRules(
+ NeverAllow().InDirectDeps("not_allowed_in_direct_deps"),
+ )
+}
+
var neverallowTests = []struct {
name string
fs map[string][]byte
expectedError string
}{
+ // Test General Functionality
+
+ // in direct deps tests
+ {
+ name: "not_allowed_in_direct_deps",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ cc_library {
+ name: "not_allowed_in_direct_deps",
+ }`),
+ "other/Blueprints": []byte(`
+ cc_library {
+ name: "libother",
+ static_libs: ["not_allowed_in_direct_deps"],
+ }`),
+ },
+ expectedError: `module "libother": violates neverallow deps:not_allowed_in_direct_deps`,
+ },
+
+ // Test specific rules
+
+ // include_dir rule tests
+ {
+ name: "include_dir not allowed to reference art",
+ fs: map[string][]byte{
+ "other/Blueprints": []byte(`
+ cc_library {
+ name: "libother",
+ include_dirs: ["art/libdexfile/include"],
+ }`),
+ },
+ expectedError: "all usages of 'art' have been migrated",
+ },
+ {
+ name: "include_dir can reference another location",
+ fs: map[string][]byte{
+ "other/Blueprints": []byte(`
+ cc_library {
+ name: "libother",
+ include_dirs: ["another/include"],
+ }`),
+ },
+ },
+ // Treble rule tests
{
name: "no vndk.enabled under vendor directory",
fs: map[string][]byte{
@@ -217,7 +270,9 @@
}
type mockCcLibraryProperties struct {
+ Include_dirs []string
Vendor_available *bool
+ Static_libs []string
Vndk struct {
Enabled *bool
@@ -248,6 +303,19 @@
return m
}
+type neverallowTestDependencyTag struct {
+ blueprint.BaseDependencyTag
+ name string
+}
+
+var staticDepTag = neverallowTestDependencyTag{name: "static"}
+
+func (c *mockCcLibraryModule) DepsMutator(ctx BottomUpMutatorContext) {
+ for _, lib := range c.properties.Static_libs {
+ ctx.AddDependency(ctx.Module(), staticDepTag, lib)
+ }
+}
+
func (p *mockCcLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
}
diff --git a/android/notices.go b/android/notices.go
index 8503593..7b61d65 100644
--- a/android/notices.go
+++ b/android/notices.go
@@ -42,8 +42,8 @@
})
generateNoticeRule = pctx.AndroidStaticRule("generateNoticeRule", blueprint.RuleParams{
- Command: `rm -rf $$(dirname $txtOut) $$(dirname htmlOut) $$(dirname $out) && ` +
- `mkdir -p $$(dirname $txtOut) $$(dirname htmlOut) $$(dirname $out) && ` +
+ Command: `rm -rf $$(dirname $txtOut) $$(dirname $htmlOut) $$(dirname $out) && ` +
+ `mkdir -p $$(dirname $txtOut) $$(dirname $htmlOut) $$(dirname $out) && ` +
`${generate_notice} --text-output $txtOut --html-output $htmlOut -t "$title" -s $inputDir && ` +
`${minigzip} -c $htmlOut > $out`,
CommandDeps: []string{"${generate_notice}", "${minigzip}"},
diff --git a/android/onceper.go b/android/onceper.go
index ff865c2..481cdea 100644
--- a/android/onceper.go
+++ b/android/onceper.go
@@ -95,6 +95,16 @@
return s[0], s[1]
}
+// OncePath is the same as Once, but returns the value cast to a Path
+func (once *OncePer) OncePath(key OnceKey, value func() Path) Path {
+ return once.Once(key, func() interface{} { return value() }).(Path)
+}
+
+// OncePath is the same as Once, but returns the value cast to a SourcePath
+func (once *OncePer) OnceSourcePath(key OnceKey, value func() SourcePath) SourcePath {
+ return once.Once(key, func() interface{} { return value() }).(SourcePath)
+}
+
// OnceKey is an opaque type to be used as the key in calls to Once.
type OnceKey struct {
key interface{}
diff --git a/android/package.go b/android/package.go
index 03f6a1e..880d6a9 100644
--- a/android/package.go
+++ b/android/package.go
@@ -64,16 +64,6 @@
return newPackageId(ctx.ModuleDir())
}
-// Override to ensure that the default_visibility rules are checked by the visibility module during
-// its checking phase.
-func (p *packageModule) visibilityProperties() []visibilityProperty {
- return []visibilityProperty{
- newVisibilityProperty("default_visibility", func() []string {
- return p.properties.Default_visibility
- }),
- }
-}
-
func (p *packageModule) Name() string {
return p.properties.Name
}
@@ -97,6 +87,13 @@
module.properties.Name = name
module.AddProperties(&module.properties)
+
+ // The default_visibility property needs to be checked and parsed by the visibility module during
+ // its checking and parsing phases.
+ module.primaryVisibilityProperty =
+ newVisibilityProperty("default_visibility", &module.properties.Default_visibility)
+ module.visibilityPropertyInfo = []visibilityProperty{module.primaryVisibilityProperty}
+
return module
}
diff --git a/android/path_properties.go b/android/path_properties.go
index af7af59..6b1cdb3 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -35,16 +35,17 @@
props := m.base().generalProperties
+ var pathProperties []string
for _, ps := range props {
- pathProperties := pathPropertiesForPropertyStruct(ctx, ps)
- pathProperties = FirstUniqueStrings(pathProperties)
+ pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ctx, ps)...)
+ }
- for _, s := range pathProperties {
- if m, t := SrcIsModuleWithTag(s); m != "" {
- ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(t), m)
- }
+ pathProperties = FirstUniqueStrings(pathProperties)
+
+ for _, s := range pathProperties {
+ if m, t := SrcIsModuleWithTag(s); m != "" {
+ ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(t), m)
}
-
}
}
diff --git a/android/path_properties_test.go b/android/path_properties_test.go
index e98c136..59bfa6c 100644
--- a/android/path_properties_test.go
+++ b/android/path_properties_test.go
@@ -28,12 +28,17 @@
Qux string
}
+ // A second property struct with a duplicate property name
+ props2 struct {
+ Foo string `android:"path"`
+ }
+
sourceDeps []string
}
func pathDepsMutatorTestModuleFactory() Module {
module := &pathDepsMutatorTestModule{}
- module.AddProperties(&module.props)
+ module.AddProperties(&module.props, &module.props2)
InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
return module
}
@@ -44,6 +49,13 @@
p.sourceDeps = append(p.sourceDeps, ctx.OtherModuleName(dep))
}
})
+
+ if p.props.Foo != "" {
+ // Make sure there is only one dependency on a module listed in a property present in multiple property structs
+ if ctx.GetDirectDepWithTag(SrcIsModule(p.props.Foo), sourceOrOutputDepTag("")) == nil {
+ ctx.ModuleErrorf("GetDirectDepWithTag failed")
+ }
+ }
}
func TestPathDepsMutator(t *testing.T) {
diff --git a/android/paths.go b/android/paths.go
index 0ea4447..5110617 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -46,6 +46,7 @@
InstallInData() bool
InstallInSanitizerDir() bool
InstallInRecovery() bool
+ InstallBypassMake() bool
}
var _ ModuleInstallPathContext = ModuleContext(nil)
@@ -230,7 +231,7 @@
// references to OutputFileProducer modules using the ":name{.tag}" syntax. Properties passed as the paths or excludes
// argument must have been annotated with struct tag `android:"path"` so that dependencies on SourceFileProducer modules
// will have already been handled by the path_properties mutator. If ctx.Config().AllowMissingDependencies() is
-// truethen any missing SourceFileProducer or OutputFileProducer dependencies will cause the module to be marked as
+// true then any missing SourceFileProducer or OutputFileProducer dependencies will cause the module to be marked as
// having missing dependencies.
func PathsForModuleSrcExcludes(ctx ModuleContext, paths, excludes []string) Paths {
ret, missingDeps := PathsAndMissingDepsForModuleSrcExcludes(ctx, paths, excludes)
@@ -818,6 +819,17 @@
return OutputPath{basePath{path, ctx.Config(), ""}}
}
+// pathForInstallInMakeDir is used by PathForModuleInstall when the module returns true
+// for InstallBypassMake to produce an OutputPath that installs to $OUT_DIR instead of
+// $OUT_DIR/soong.
+func pathForInstallInMakeDir(ctx PathContext, pathComponents ...string) OutputPath {
+ path, err := validatePath(pathComponents...)
+ if err != nil {
+ reportPathError(ctx, err)
+ }
+ return OutputPath{basePath{"../" + path, ctx.Config(), ""}}
+}
+
// PathsForOutput returns Paths rooted from buildDir
func PathsForOutput(ctx PathContext, paths []string) WritablePaths {
ret := make(WritablePaths, len(paths))
@@ -984,7 +996,7 @@
// PathForVndkRefAbiDump returns an OptionalPath representing the path of the
// reference abi dump for the given module. This is not guaranteed to be valid.
func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string,
- isLlndkOrNdk, isVndk, isGzip bool) OptionalPath {
+ isNdk, isLlndkOrVndk, isGzip bool) OptionalPath {
arches := ctx.DeviceConfig().Arches()
if len(arches) == 0 {
@@ -997,9 +1009,9 @@
}
var dirName string
- if isLlndkOrNdk {
+ if isNdk {
dirName = "ndk"
- } else if isVndk {
+ } else if isLlndkOrVndk {
dirName = "vndk"
} else {
dirName = "platform" // opt-in libs
@@ -1123,6 +1135,9 @@
outPaths = append([]string{"debug"}, outPaths...)
}
outPaths = append(outPaths, pathComponents...)
+ if ctx.InstallBypassMake() && ctx.Config().EmbeddedInMake() {
+ return pathForInstallInMakeDir(ctx, outPaths...)
+ }
return PathForOutput(ctx, outPaths...)
}
diff --git a/android/paths_test.go b/android/paths_test.go
index 8286e9a..f2996bf 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -227,6 +227,10 @@
return m.inRecovery
}
+func (m moduleInstallPathContextImpl) InstallBypassMake() bool {
+ return false
+}
+
func TestPathForModuleInstall(t *testing.T) {
testConfig := TestConfig("", nil)
diff --git a/android/prebuilt.go b/android/prebuilt.go
index ad7c4aa..8559df9 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -29,7 +29,7 @@
blueprint.BaseDependencyTag
}
-var prebuiltDepTag prebuiltDependencyTag
+var PrebuiltDepTag prebuiltDependencyTag
type PrebuiltProperties struct {
// When prefer is set to true the prebuilt will be used instead of any source module with
@@ -127,7 +127,7 @@
p := m.Prebuilt()
name := m.base().BaseModuleName()
if ctx.OtherModuleExists(name) {
- ctx.AddReverseDependency(ctx.Module(), prebuiltDepTag, name)
+ ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name)
p.properties.SourceExists = true
} else {
ctx.Rename(name)
@@ -147,7 +147,7 @@
p.properties.UsePrebuilt = p.usePrebuilt(ctx, nil)
}
} else if s, ok := ctx.Module().(Module); ok {
- ctx.VisitDirectDepsWithTag(prebuiltDepTag, func(m Module) {
+ ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(m Module) {
p := m.(PrebuiltInterface).Prebuilt()
if p.usePrebuilt(ctx, s) {
p.properties.UsePrebuilt = true
@@ -215,3 +215,7 @@
}
return value.String()
}
+
+func (p *Prebuilt) SourceExists() bool {
+ return p.properties.SourceExists
+}
diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go
index 069e1f5..9722a25 100644
--- a/android/prebuilt_etc.go
+++ b/android/prebuilt_etc.go
@@ -91,6 +91,10 @@
return PathForModuleSrc(ctx, String(p.properties.Src))
}
+func (p *PrebuiltEtc) InstallDirPath() OutputPath {
+ return p.installDirPath
+}
+
// This allows other derivative modules (e.g. prebuilt_etc_xml) to perform
// additional steps (like validating the src) before the file is installed.
func (p *PrebuiltEtc) SetAdditionalDependencies(paths Paths) {
@@ -165,15 +169,16 @@
}
}
-func InitPrebuiltEtcModule(p *PrebuiltEtc) {
+func InitPrebuiltEtcModule(p *PrebuiltEtc, dirBase string) {
+ p.installDirBase = dirBase
p.AddProperties(&p.properties)
}
// prebuilt_etc is for a prebuilt artifact that is installed in
// <partition>/etc/<sub_dir> directory.
func PrebuiltEtcFactory() Module {
- module := &PrebuiltEtc{installDirBase: "etc"}
- InitPrebuiltEtcModule(module)
+ module := &PrebuiltEtc{}
+ InitPrebuiltEtcModule(module, "etc")
// This module is device-only
InitAndroidArchModule(module, DeviceSupported, MultilibFirst)
return module
@@ -182,8 +187,8 @@
// prebuilt_etc_host is for a host prebuilt artifact that is installed in
// $(HOST_OUT)/etc/<sub_dir> directory.
func PrebuiltEtcHostFactory() Module {
- module := &PrebuiltEtc{installDirBase: "etc"}
- InitPrebuiltEtcModule(module)
+ module := &PrebuiltEtc{}
+ InitPrebuiltEtcModule(module, "etc")
// This module is host-only
InitAndroidArchModule(module, HostSupported, MultilibCommon)
return module
@@ -192,8 +197,8 @@
// prebuilt_usr_share is for a prebuilt artifact that is installed in
// <partition>/usr/share/<sub_dir> directory.
func PrebuiltUserShareFactory() Module {
- module := &PrebuiltEtc{installDirBase: "usr/share"}
- InitPrebuiltEtcModule(module)
+ module := &PrebuiltEtc{}
+ InitPrebuiltEtcModule(module, "usr/share")
// This module is device-only
InitAndroidArchModule(module, DeviceSupported, MultilibFirst)
return module
@@ -202,8 +207,8 @@
// prebuild_usr_share_host is for a host prebuilt artifact that is installed in
// $(HOST_OUT)/usr/share/<sub_dir> directory.
func PrebuiltUserShareHostFactory() Module {
- module := &PrebuiltEtc{installDirBase: "usr/share"}
- InitPrebuiltEtcModule(module)
+ module := &PrebuiltEtc{}
+ InitPrebuiltEtcModule(module, "usr/share")
// This module is host-only
InitAndroidArchModule(module, HostSupported, MultilibCommon)
return module
@@ -254,8 +259,8 @@
// prebuilt_font installs a font in <partition>/fonts directory.
func PrebuiltFontFactory() Module {
- module := &PrebuiltEtc{installDirBase: "fonts"}
- InitPrebuiltEtcModule(module)
+ module := &PrebuiltEtc{}
+ InitPrebuiltEtcModule(module, "fonts")
// This module is device-only
InitAndroidArchModule(module, DeviceSupported, MultilibFirst)
return module
@@ -265,8 +270,9 @@
// If soc_specific property is set to true, the firmware file is installed to the vendor <partition>/firmware
// directory for vendor image.
func PrebuiltFirmwareFactory() Module {
- module := &PrebuiltEtc{installDirBase: "etc/firmware", socInstallDirBase: "firmware"}
- InitPrebuiltEtcModule(module)
+ module := &PrebuiltEtc{}
+ module.socInstallDirBase = "firmware"
+ InitPrebuiltEtcModule(module, "etc/firmware")
// This module is device-only
InitAndroidArchModule(module, DeviceSupported, MultilibFirst)
return module
diff --git a/android/proto.go b/android/proto.go
index 5247c68..c8ade45 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -135,7 +135,7 @@
}
rule.Command().
- Tool(ctx.Config().HostToolPath(ctx, "aprotoc")).
+ BuiltTool(ctx, "aprotoc").
FlagWithArg(flags.OutTypeFlag+"=", strings.Join(flags.OutParams, ",")+":"+outDir.String()).
FlagWithDepFile("--dependency_out=", depFile).
FlagWithArg("-I ", protoBase).
@@ -145,5 +145,5 @@
ImplicitOutputs(outputs)
rule.Command().
- Tool(ctx.Config().HostToolPath(ctx, "dep_fixer")).Flag(depFile.String())
+ BuiltTool(ctx, "dep_fixer").Flag(depFile.String())
}
diff --git a/android/rule_builder.go b/android/rule_builder.go
index e53eb0d..48d070e 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -263,11 +263,36 @@
return toolsList
}
-// Commands returns a slice containing a the built command line for each call to RuleBuilder.Command.
+// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
+func (r *RuleBuilder) RspFileInputs() Paths {
+ var rspFileInputs Paths
+ for _, c := range r.commands {
+ if c.rspFileInputs != nil {
+ if rspFileInputs != nil {
+ panic("Multiple commands in a rule may not have rsp file inputs")
+ }
+ rspFileInputs = c.rspFileInputs
+ }
+ }
+
+ return rspFileInputs
+}
+
+// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
func (r *RuleBuilder) Commands() []string {
var commands []string
for _, c := range r.commands {
- commands = append(commands, c.buf.String())
+ commands = append(commands, c.String())
+ }
+ return commands
+}
+
+// NinjaEscapedCommands returns a slice containin the built command line after ninja escaping for each call to
+// RuleBuilder.Command.
+func (r *RuleBuilder) NinjaEscapedCommands() []string {
+ var commands []string
+ for _, c := range r.commands {
+ commands = append(commands, c.NinjaEscapedString())
}
return commands
}
@@ -284,7 +309,7 @@
func (r *RuleBuilder) depFileMergerCmd(ctx PathContext, depFiles WritablePaths) *RuleBuilderCommand {
return r.Command().
- Tool(ctx.Config().HostToolPath(ctx, "dep_fixer")).
+ BuiltTool(ctx, "dep_fixer").
Inputs(depFiles.Paths())
}
@@ -324,7 +349,7 @@
}
tools := r.Tools()
- commands := r.Commands()
+ commands := r.NinjaEscapedCommands()
outputs := r.Outputs()
if len(commands) == 0 {
@@ -334,7 +359,7 @@
panic("No outputs specified from any Commands")
}
- commandString := strings.Join(proptools.NinjaEscapeList(commands), " && ")
+ commandString := strings.Join(commands, " && ")
if r.sbox {
sboxOutputs := make([]string, len(outputs))
@@ -352,7 +377,7 @@
}
sboxCmd := &RuleBuilderCommand{}
- sboxCmd.Tool(ctx.Config().HostToolPath(ctx, "sbox")).
+ sboxCmd.BuiltTool(ctx, "sbox").
Flag("-c").Text(commandString).
Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(ctx).String())).
Flag("--output-root").Text(r.sboxOutDir.String()).
@@ -363,17 +388,27 @@
}
// Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
- // ImplicitOutputs. RuleBuilder never uses "$out", so the distinction between Outputs and ImplicitOutputs
- // doesn't matter.
+ // ImplicitOutputs. RuleBuilder only uses "$out" for the rsp file location, so the distinction between Outputs and
+ // ImplicitOutputs doesn't matter.
output := outputs[0]
implicitOutputs := outputs[1:]
+ var rspFile, rspFileContent string
+ rspFileInputs := r.RspFileInputs()
+ if rspFileInputs != nil {
+ rspFile = "$out.rsp"
+ rspFileContent = "$in"
+ }
+
ctx.Build(pctx, BuildParams{
Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
- Command: commandString,
- CommandDeps: tools.Strings(),
- Restat: r.restat,
+ Command: commandString,
+ CommandDeps: tools.Strings(),
+ Restat: r.restat,
+ Rspfile: rspFile,
+ RspfileContent: rspFileContent,
}),
+ Inputs: rspFileInputs,
Implicits: r.Inputs(),
Output: output,
ImplicitOutputs: implicitOutputs,
@@ -388,11 +423,15 @@
// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
// space as a separator from the previous method.
type RuleBuilderCommand struct {
- buf strings.Builder
- inputs Paths
- outputs WritablePaths
- depFiles WritablePaths
- tools Paths
+ buf strings.Builder
+ inputs Paths
+ outputs WritablePaths
+ depFiles WritablePaths
+ tools Paths
+ rspFileInputs Paths
+
+ // spans [start,end) of the command that should not be ninja escaped
+ unescapedSpans [][2]int
sbox bool
sboxOutDir WritablePath
@@ -439,6 +478,16 @@
return c.Text(flag)
}
+// OptionalFlag adds the specified raw text to the command line if it is not nil. The text should not contain input or
+// output paths or the rule will not have them listed in its dependencies or outputs.
+func (c *RuleBuilderCommand) OptionalFlag(flag *string) *RuleBuilderCommand {
+ if flag != nil {
+ c.Text(*flag)
+ }
+
+ return c
+}
+
// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
// rule will not have them listed in its dependencies or outputs.
func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
@@ -478,6 +527,24 @@
return c.Text(path.String())
}
+// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
+// be also added to the dependencies returned by RuleBuilder.Tools.
+//
+// It is equivalent to:
+// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
+func (c *RuleBuilderCommand) BuiltTool(ctx PathContext, tool string) *RuleBuilderCommand {
+ return c.Tool(ctx.Config().HostToolPath(ctx, tool))
+}
+
+// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
+// dependencies returned by RuleBuilder.Tools.
+//
+// It is equivalent to:
+// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
+func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
+ return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
+}
+
// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
// RuleBuilder.Inputs.
func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
@@ -606,11 +673,56 @@
return c.Text(flag + c.outputStr(path))
}
+// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator
+// between them. The paths will be written to the rspfile.
+func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, paths Paths) *RuleBuilderCommand {
+ if c.rspFileInputs != nil {
+ panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided")
+ }
+
+ // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
+ // generated.
+ if paths == nil {
+ paths = Paths{}
+ }
+
+ c.rspFileInputs = paths
+
+ rspFile := "$out.rsp"
+ c.FlagWithArg(flag, rspFile)
+ c.unescapedSpans = append(c.unescapedSpans, [2]int{c.buf.Len() - len(rspFile), c.buf.Len()})
+ return c
+}
+
// String returns the command line.
func (c *RuleBuilderCommand) String() string {
return c.buf.String()
}
+// String returns the command line.
+func (c *RuleBuilderCommand) NinjaEscapedString() string {
+ return ninjaEscapeExceptForSpans(c.String(), c.unescapedSpans)
+}
+
+func ninjaEscapeExceptForSpans(s string, spans [][2]int) string {
+ if len(spans) == 0 {
+ return proptools.NinjaEscape(s)
+ }
+
+ sb := strings.Builder{}
+ sb.Grow(len(s) * 11 / 10)
+
+ i := 0
+ for _, span := range spans {
+ sb.WriteString(proptools.NinjaEscape(s[i:span[0]]))
+ sb.WriteString(s[span[0]:span[1]])
+ i = span[1]
+ }
+ sb.WriteString(proptools.NinjaEscape(s[i:]))
+
+ return sb.String()
+}
+
func ninjaNameEscape(s string) string {
b := []byte(s)
escaped := false
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index cfbc2ab..6eba4f1 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -38,6 +38,7 @@
"ls": nil,
"turbine": nil,
"java": nil,
+ "javac": nil,
})
}
@@ -235,6 +236,34 @@
// ls --sort=time,size
}
+func ExampleRuleBuilderCommand_FlagWithRspFileInputList() {
+ ctx := pathContext()
+ fmt.Println(NewRuleBuilder().Command().
+ Tool(PathForSource(ctx, "javac")).
+ FlagWithRspFileInputList("@", PathsForTesting("a.java", "b.java")).
+ NinjaEscapedString())
+ // Output:
+ // javac @$out.rsp
+}
+
+func ExampleRuleBuilderCommand_String() {
+ fmt.Println(NewRuleBuilder().Command().
+ Text("FOO=foo").
+ Text("echo $FOO").
+ String())
+ // Output:
+ // FOO=foo echo $FOO
+}
+
+func ExampleRuleBuilderCommand_NinjaEscapedString() {
+ fmt.Println(NewRuleBuilder().Command().
+ Text("FOO=foo").
+ Text("echo $FOO").
+ NinjaEscapedString())
+ // Output:
+ // FOO=foo echo $$FOO
+}
+
func TestRuleBuilder(t *testing.T) {
fs := map[string][]byte{
"dep_fixer": nil,
@@ -503,3 +532,77 @@
"cp bar "+outFile, outFile, outFile+".d", true, nil)
})
}
+
+func Test_ninjaEscapeExceptForSpans(t *testing.T) {
+ type args struct {
+ s string
+ spans [][2]int
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ }{
+ {
+ name: "empty",
+ args: args{
+ s: "",
+ },
+ want: "",
+ },
+ {
+ name: "unescape none",
+ args: args{
+ s: "$abc",
+ },
+ want: "$$abc",
+ },
+ {
+ name: "unescape all",
+ args: args{
+ s: "$abc",
+ spans: [][2]int{{0, 4}},
+ },
+ want: "$abc",
+ },
+ {
+ name: "unescape first",
+ args: args{
+ s: "$abc$",
+ spans: [][2]int{{0, 1}},
+ },
+ want: "$abc$$",
+ },
+ {
+ name: "unescape last",
+ args: args{
+ s: "$abc$",
+ spans: [][2]int{{4, 5}},
+ },
+ want: "$$abc$",
+ },
+ {
+ name: "unescape middle",
+ args: args{
+ s: "$a$b$c$",
+ spans: [][2]int{{2, 5}},
+ },
+ want: "$$a$b$c$$",
+ },
+ {
+ name: "unescape multiple",
+ args: args{
+ s: "$a$b$c$",
+ spans: [][2]int{{2, 3}, {4, 5}},
+ },
+ want: "$$a$b$c$$",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := ninjaEscapeExceptForSpans(tt.args.s, tt.args.spans); got != tt.want {
+ t.Errorf("ninjaEscapeExceptForSpans() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/android/testing.go b/android/testing.go
index 44bee4b..b59f399 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -376,9 +376,20 @@
var p AndroidMkEntriesProvider
var ok bool
if p, ok = mod.(AndroidMkEntriesProvider); !ok {
- t.Errorf("module does not implmement AndroidMkEntriesProvider: " + mod.Name())
+ t.Errorf("module does not implement AndroidMkEntriesProvider: " + mod.Name())
}
entries := p.AndroidMkEntries()
entries.fillInEntries(config, bpPath, mod)
return entries
}
+
+func AndroidMkDataForTest(t *testing.T, config Config, bpPath string, mod blueprint.Module) AndroidMkData {
+ var p AndroidMkDataProvider
+ var ok bool
+ if p, ok = mod.(AndroidMkDataProvider); !ok {
+ t.Errorf("module does not implement AndroidMkDataProvider: " + mod.Name())
+ }
+ data := p.AndroidMk()
+ data.fillInData(config, bpPath, mod)
+ return data
+}
diff --git a/android/util.go b/android/util.go
index 3b8bc78..e02cca1 100644
--- a/android/util.go
+++ b/android/util.go
@@ -115,6 +115,17 @@
return false
}
+// IndexListPred returns the index of the element which in the given `list` satisfying the predicate, or -1 if there is no such element.
+func IndexListPred(pred func(s string) bool, list []string) int {
+ for i, l := range list {
+ if pred(l) {
+ return i
+ }
+ }
+
+ return -1
+}
+
func FilterList(list []string, filter []string) (remainder []string, filtered []string) {
for _, l := range list {
if InList(l, filter) {
@@ -188,6 +199,13 @@
return list[totalSkip:]
}
+// SortedUniqueStrings returns what the name says
+func SortedUniqueStrings(list []string) []string {
+ unique := FirstUniqueStrings(list)
+ sort.Strings(unique)
+ return unique
+}
+
// checkCalledFromInit panics if a Go package's init function is not on the
// call stack.
func checkCalledFromInit() {
diff --git a/android/variable.go b/android/variable.go
index fcd92d4..0931db8 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -59,17 +59,6 @@
Cflags []string
}
- // Product_is_iot is true for Android Things devices.
- Product_is_iot struct {
- Cflags []string
- Enabled bool
- Exclude_srcs []string
- Init_rc []string
- Shared_libs []string
- Srcs []string
- Static_libs []string
- }
-
// treble_linker_namespaces is true when the system/vendor linker namespace separation is
// enabled.
Treble_linker_namespaces struct {
@@ -165,15 +154,17 @@
DeviceSecondaryCpuVariant *string `json:",omitempty"`
DeviceSecondaryAbi []string `json:",omitempty"`
- NativeBridgeArch *string `json:",omitempty"`
- NativeBridgeArchVariant *string `json:",omitempty"`
- NativeBridgeCpuVariant *string `json:",omitempty"`
- NativeBridgeAbi []string `json:",omitempty"`
+ NativeBridgeArch *string `json:",omitempty"`
+ NativeBridgeArchVariant *string `json:",omitempty"`
+ NativeBridgeCpuVariant *string `json:",omitempty"`
+ NativeBridgeAbi []string `json:",omitempty"`
+ NativeBridgeRelativePath *string `json:",omitempty"`
- NativeBridgeSecondaryArch *string `json:",omitempty"`
- NativeBridgeSecondaryArchVariant *string `json:",omitempty"`
- NativeBridgeSecondaryCpuVariant *string `json:",omitempty"`
- NativeBridgeSecondaryAbi []string `json:",omitempty"`
+ NativeBridgeSecondaryArch *string `json:",omitempty"`
+ NativeBridgeSecondaryArchVariant *string `json:",omitempty"`
+ NativeBridgeSecondaryCpuVariant *string `json:",omitempty"`
+ NativeBridgeSecondaryAbi []string `json:",omitempty"`
+ NativeBridgeSecondaryRelativePath *string `json:",omitempty"`
HostArch *string `json:",omitempty"`
HostSecondaryArch *string `json:",omitempty"`
@@ -204,6 +195,7 @@
HostStaticBinaries *bool `json:",omitempty"`
Binder32bit *bool `json:",omitempty"`
UseGoma *bool `json:",omitempty"`
+ UseRBE *bool `json:",omitempty"`
Debuggable *bool `json:",omitempty"`
Eng *bool `json:",omitempty"`
Treble_linker_namespaces *bool `json:",omitempty"`
@@ -259,8 +251,6 @@
Override_rs_driver *string `json:",omitempty"`
- Product_is_iot *bool `json:",omitempty"`
-
Fuchsia *bool `json:",omitempty"`
DeviceKernelHeaders []string `json:",omitempty"`
@@ -271,7 +261,8 @@
PgoAdditionalProfileDirs []string `json:",omitempty"`
- VndkUseCoreVariant *bool `json:",omitempty"`
+ VndkUseCoreVariant *bool `json:",omitempty"`
+ VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
BoardVendorSepolicyDirs []string `json:",omitempty"`
BoardOdmSepolicyDirs []string `json:",omitempty"`
@@ -286,7 +277,7 @@
Ndk_abis *bool `json:",omitempty"`
Exclude_draft_ndk_apis *bool `json:",omitempty"`
- FlattenApex *bool `json:",omitempty"`
+ Flatten_apex *bool `json:",omitempty"`
DexpreoptGlobalConfig *string `json:",omitempty"`
diff --git a/android/visibility.go b/android/visibility.go
index 94af343..a7e718b 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -67,8 +67,8 @@
// Describes the properties provided by a module that contain visibility rules.
type visibilityPropertyImpl struct {
- name string
- stringsGetter func() []string
+ name string
+ stringsProperty *[]string
}
type visibilityProperty interface {
@@ -76,10 +76,10 @@
getStrings() []string
}
-func newVisibilityProperty(name string, stringsGetter func() []string) visibilityProperty {
+func newVisibilityProperty(name string, stringsProperty *[]string) visibilityProperty {
return visibilityPropertyImpl{
- name: name,
- stringsGetter: stringsGetter,
+ name: name,
+ stringsProperty: stringsProperty,
}
}
@@ -88,7 +88,7 @@
}
func (p visibilityPropertyImpl) getStrings() []string {
- return p.stringsGetter()
+ return *p.stringsProperty
}
// A compositeRule is a visibility rule composed from a list of atomic visibility rules.
@@ -211,16 +211,7 @@
// Checks the per-module visibility rule lists before defaults expansion.
func visibilityRuleChecker(ctx BottomUpMutatorContext) {
qualified := createQualifiedModuleName(ctx)
- if d, ok := ctx.Module().(Defaults); ok {
- // Defaults modules don't store the payload properties in m.base().
- for _, props := range d.properties() {
- if cp, ok := props.(*commonProperties); ok {
- if visibility := cp.Visibility; visibility != nil {
- checkRules(ctx, qualified.pkg, "visibility", visibility)
- }
- }
- }
- } else if m, ok := ctx.Module().(Module); ok {
+ if m, ok := ctx.Module().(Module); ok {
visibilityProperties := m.visibilityProperties()
for _, p := range visibilityProperties {
if visibility := p.getStrings(); visibility != nil {
@@ -294,14 +285,12 @@
qualifiedModuleId := m.qualifiedModuleId(ctx)
currentPkg := qualifiedModuleId.pkg
- // Parse all the properties into rules and store them.
- visibilityProperties := m.visibilityProperties()
- for _, p := range visibilityProperties {
- if visibility := p.getStrings(); visibility != nil {
- rule := parseRules(ctx, currentPkg, visibility)
- if rule != nil {
- moduleToVisibilityRuleMap(ctx).Store(qualifiedModuleId, rule)
- }
+ // Parse the visibility rules that control access to the module and store them by id
+ // for use when enforcing the rules.
+ if visibility := m.visibility(); visibility != nil {
+ rule := parseRules(ctx, currentPkg, m.visibility())
+ if rule != nil {
+ moduleToVisibilityRuleMap(ctx).Store(qualifiedModuleId, rule)
}
}
}
diff --git a/android/visibility_test.go b/android/visibility_test.go
index af6acf4..c44dc9e 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -658,6 +658,40 @@
` visible to this module`,
},
},
+
+ // Defaults module's defaults_visibility tests
+ {
+ name: "defaults_visibility invalid",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ mock_defaults {
+ name: "top_defaults",
+ defaults_visibility: ["//visibility:invalid"],
+ }`),
+ },
+ expectedErrors: []string{
+ `defaults_visibility: unrecognized visibility rule "//visibility:invalid"`,
+ },
+ },
+ {
+ name: "defaults_visibility overrides package default",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_visibility: ["//visibility:private"],
+ }
+ mock_defaults {
+ name: "top_defaults",
+ defaults_visibility: ["//visibility:public"],
+ }`),
+ "outsider/Blueprints": []byte(`
+ mock_library {
+ name: "liboutsider",
+ defaults: ["top_defaults"],
+ }`),
+ },
+ },
+
// Package default_visibility tests
{
name: "package default_visibility property is checked",
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index 2def179..fcadd03 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -173,6 +173,8 @@
// Jacoco filters:
"LOCAL_JACK_COVERAGE_INCLUDE_FILTER": "jacoco.include_filter",
"LOCAL_JACK_COVERAGE_EXCLUDE_FILTER": "jacoco.exclude_filter",
+
+ "LOCAL_FULL_LIBS_MANIFEST_FILES": "additional_manifests",
})
addStandardProperties(bpparser.BoolType,
diff --git a/apex/TEST_MAPPING b/apex/TEST_MAPPING
new file mode 100644
index 0000000..d0223d1
--- /dev/null
+++ b/apex/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+ {
+ "path": "system/apex/apexd"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/apex/apex.go b/apex/apex.go
index f95bd05..574604b 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -46,13 +46,21 @@
Description: "fs_config ${out}",
}, "ro_paths", "exec_paths")
+ injectApexDependency = pctx.StaticRule("injectApexDependency", blueprint.RuleParams{
+ Command: `rm -f $out && ${jsonmodify} $in ` +
+ `-a provideNativeLibs ${provideNativeLibs} ` +
+ `-a requireNativeLibs ${requireNativeLibs} -o $out`,
+ CommandDeps: []string{"${jsonmodify}"},
+ Description: "Inject dependency into ${out}",
+ }, "provideNativeLibs", "requireNativeLibs")
+
// TODO(b/113233103): make sure that file_contexts is sane, i.e., validate
// against the binary policy using sefcontext_compiler -p <policy>.
// TODO(b/114327326): automate the generation of file_contexts
apexRule = pctx.StaticRule("apexRule", blueprint.RuleParams{
Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
- `(${copy_commands}) && ` +
+ `(. ${out}.copy_commands) && ` +
`APEXER_TOOL_PATH=${tool_path} ` +
`${apexer} --force --manifest ${manifest} ` +
`--file_contexts ${file_contexts} ` +
@@ -62,18 +70,22 @@
CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}",
"${mke2fs}", "${resize2fs}", "${sefcontext_compile}",
"${soong_zip}", "${zipalign}", "${aapt2}", "prebuilts/sdk/current/public/android.jar"},
- Description: "APEX ${image_dir} => ${out}",
+ Rspfile: "${out}.copy_commands",
+ RspfileContent: "${copy_commands}",
+ Description: "APEX ${image_dir} => ${out}",
}, "tool_path", "image_dir", "copy_commands", "manifest", "file_contexts", "canned_fs_config", "key", "opt_flags")
zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
- `(${copy_commands}) && ` +
+ `(. ${out}.copy_commands) && ` +
`APEXER_TOOL_PATH=${tool_path} ` +
`${apexer} --force --manifest ${manifest} ` +
`--payload_type zip ` +
`${image_dir} ${out} `,
- CommandDeps: []string{"${apexer}", "${merge_zips}", "${soong_zip}", "${zipalign}", "${aapt2}"},
- Description: "ZipAPEX ${image_dir} => ${out}",
+ CommandDeps: []string{"${apexer}", "${merge_zips}", "${soong_zip}", "${zipalign}", "${aapt2}"},
+ Rspfile: "${out}.copy_commands",
+ RspfileContent: "${copy_commands}",
+ Description: "ZipAPEX ${image_dir} => ${out}",
}, "tool_path", "image_dir", "copy_commands", "manifest")
apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule",
@@ -139,6 +151,7 @@
pctx.HostBinToolVariable("soong_zip", "soong_zip")
pctx.HostBinToolVariable("zip2zip", "zip2zip")
pctx.HostBinToolVariable("zipalign", "zipalign")
+ pctx.HostBinToolVariable("jsonmodify", "jsonmodify")
android.RegisterModuleType("apex", apexBundleFactory)
android.RegisterModuleType("apex_test", testApexBundleFactory)
@@ -280,6 +293,10 @@
// List of sanitizer names that this APEX is enabled for
SanitizerNames []string `blueprint:"mutated"`
+ PreventInstall bool `blueprint:"mutated"`
+
+ HideFromMake bool `blueprint:"mutated"`
+
// Indicates this APEX provides C++ shared libaries to other APEXes. Default: false.
Provide_cpp_shared_libs *bool
@@ -354,7 +371,7 @@
case both:
panic(fmt.Errorf("must be either zip or image"))
default:
- panic(fmt.Errorf("unkonwn APEX type %d", a))
+ panic(fmt.Errorf("unknown APEX type %d", a))
}
}
@@ -367,7 +384,7 @@
case both:
panic(fmt.Errorf("must be either zip or image"))
default:
- panic(fmt.Errorf("unkonwn APEX type %d", a))
+ panic(fmt.Errorf("unknown APEX type %d", a))
}
}
@@ -384,7 +401,7 @@
case nativeTest:
return "NATIVE_TESTS"
default:
- panic(fmt.Errorf("unkonwn class %d", class))
+ panic(fmt.Errorf("unknown class %d", class))
}
}
@@ -410,6 +427,8 @@
outputFiles map[apexPackaging]android.WritablePath
installDir android.OutputPath
+ prebuiltFileToDelete string
+
public_key_file android.Path
private_key_file android.Path
@@ -425,6 +444,9 @@
flattened bool
testApex bool
+
+ // intermediate path for apex_manifest.json
+ manifestOut android.WritablePath
}
func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
@@ -449,6 +471,7 @@
ctx.AddFarVariationDependencies([]blueprint.Variation{
{Mutator: "arch", Variation: arch},
{Mutator: "image", Variation: imageVariation},
+ {Mutator: "test_per_src", Variation: ""}, // "" is the all-tests variant
}, testTag, tests...)
}
@@ -492,6 +515,7 @@
ctx.AddFarVariationDependencies([]blueprint.Variation{
{Mutator: "arch", Variation: target.String()},
{Mutator: "image", Variation: a.getImageVariation(config)},
+ {Mutator: "test_per_src", Variation: ""}, // "" is the all-tests variant
}, testTag, a.properties.Tests...)
// Add native modules targetting both ABIs
@@ -611,7 +635,7 @@
}
func (a *apexBundle) installable() bool {
- return a.properties.Installable == nil || proptools.Bool(a.properties.Installable)
+ return !a.properties.PreventInstall && (a.properties.Installable == nil || proptools.Bool(a.properties.Installable))
}
func (a *apexBundle) getImageVariation(config android.DeviceConfig) string {
@@ -646,6 +670,18 @@
return android.InList(sanitizerName, globalSanitizerNames)
}
+func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
+ return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
+}
+
+func (a *apexBundle) PreventInstall() {
+ a.properties.PreventInstall = true
+}
+
+func (a *apexBundle) HideFromMake() {
+ a.properties.HideFromMake = true
+}
+
func getCopyManifestForNativeLibrary(cc *cc.Module, handleSpecialLibs bool) (fileToCopy android.Path, dirInApex string) {
// Decide the APEX-local directory by the multilib of the library
// In the future, we may query this to the module.
@@ -656,8 +692,10 @@
dirInApex = "lib64"
}
dirInApex = filepath.Join(dirInApex, cc.RelativeInstallPath())
- if cc.Target().NativeBridge == android.NativeBridgeEnabled || !cc.Arch().Native {
+ if !cc.Arch().Native {
dirInApex = filepath.Join(dirInApex, cc.Arch().ArchType.String())
+ } else if cc.Target().NativeBridge == android.NativeBridgeEnabled {
+ dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
}
if handleSpecialLibs {
switch cc.Name() {
@@ -681,8 +719,10 @@
func getCopyManifestForExecutable(cc *cc.Module) (fileToCopy android.Path, dirInApex string) {
dirInApex = filepath.Join("bin", cc.RelativeInstallPath())
- if cc.Target().NativeBridge == android.NativeBridgeEnabled || !cc.Arch().Native {
+ if !cc.Arch().Native {
dirInApex = filepath.Join(dirInApex, cc.Arch().ArchType.String())
+ } else if cc.Target().NativeBridge == android.NativeBridgeEnabled {
+ dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
}
fileToCopy = cc.OutputFile().Path()
return
@@ -716,6 +756,18 @@
return
}
+func getCopyManifestForPrebuiltJavaLibrary(java *java.Import) (fileToCopy android.Path, dirInApex string) {
+ dirInApex = "javalib"
+ // The output is only one, but for some reason, ImplementationJars returns Paths, not Path
+ implJars := java.ImplementationJars()
+ if len(implJars) != 1 {
+ panic(fmt.Errorf("java.ImplementationJars() must return single Path, but got: %s",
+ strings.Join(implJars.Strings(), ", ")))
+ }
+ fileToCopy = implJars[0]
+ return
+}
+
func getCopyManifestForPrebuiltEtc(prebuilt *android.PrebuiltEtc) (fileToCopy android.Path, dirInApex string) {
dirInApex = filepath.Join("etc", prebuilt.SubDir())
fileToCopy = prebuilt.OutputFile()
@@ -743,6 +795,10 @@
handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
+ // native lib dependencies
+ var provideNativeLibs []string
+ var requireNativeLibs []string
+
// Check if "uses" requirements are met with dependent apexBundles
var providedNativeSharedLibs []string
useVendor := proptools.Bool(a.properties.Use_vendor)
@@ -768,13 +824,16 @@
})
ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
+ depTag := ctx.OtherModuleDependencyTag(child)
+ depName := ctx.OtherModuleName(child)
if _, ok := parent.(*apexBundle); ok {
// direct dependencies
- depTag := ctx.OtherModuleDependencyTag(child)
- depName := ctx.OtherModuleName(child)
switch depTag {
case sharedLibTag:
if cc, ok := child.(*cc.Module); ok {
+ if cc.HasStubsVariants() {
+ provideNativeLibs = append(provideNativeLibs, cc.OutputFile().Path().Base())
+ }
fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc, handleSpecialLibs)
filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeSharedLib, cc, nil})
return true
@@ -802,16 +861,24 @@
ctx.PropertyErrorf("binaries", "%q is neither cc_binary, (embedded) py_binary, (host) blueprint_go_binary, (host) bootstrap_go_binary, nor sh_binary", depName)
}
case javaLibTag:
- if java, ok := child.(*java.Library); ok {
- fileToCopy, dirInApex := getCopyManifestForJavaLibrary(java)
+ if javaLib, ok := child.(*java.Library); ok {
+ fileToCopy, dirInApex := getCopyManifestForJavaLibrary(javaLib)
if fileToCopy == nil {
ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
} else {
- filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, javaSharedLib, java, nil})
+ filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, javaSharedLib, javaLib, nil})
+ }
+ return true
+ } else if javaLib, ok := child.(*java.Import); ok {
+ fileToCopy, dirInApex := getCopyManifestForPrebuiltJavaLibrary(javaLib)
+ if fileToCopy == nil {
+ ctx.PropertyErrorf("java_libs", "%q does not have a jar output", depName)
+ } else {
+ filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, javaSharedLib, javaLib, nil})
}
return true
} else {
- ctx.PropertyErrorf("java_libs", "%q is not a java_library module", depName)
+ ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
}
case prebuiltTag:
if prebuilt, ok := child.(*android.PrebuiltEtc); ok {
@@ -822,9 +889,20 @@
ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
}
case testTag:
- if cc, ok := child.(*cc.Module); ok {
- fileToCopy, dirInApex := getCopyManifestForExecutable(cc)
- filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeTest, cc, nil})
+ if ccTest, ok := child.(*cc.Module); ok {
+ if ccTest.IsTestPerSrcAllTestsVariation() {
+ // Multiple-output test module (where `test_per_src: true`).
+ //
+ // `ccTest` is the "" ("all tests") variation of a `test_per_src` module.
+ // We do not add this variation to `filesInfo`, as it has no output;
+ // however, we do add the other variations of this module as indirect
+ // dependencies (see below).
+ return true
+ } else {
+ // Single-output test module (where `test_per_src: false`).
+ fileToCopy, dirInApex := getCopyManifestForExecutable(ccTest)
+ filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeTest, ccTest, nil})
+ }
return true
} else {
ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
@@ -845,34 +923,57 @@
} else {
ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
}
+ case android.PrebuiltDepTag:
+ // If the prebuilt is force disabled, remember to delete the prebuilt file
+ // that might have been installed in the previous builds
+ if prebuilt, ok := child.(*Prebuilt); ok && prebuilt.isForceDisabled() {
+ a.prebuiltFileToDelete = prebuilt.InstallFilename()
+ }
}
} else {
// indirect dependencies
if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() && am.IsInstallableToApex() {
- if cc, ok := child.(*cc.Module); ok {
- if android.InList(cc.Name(), providedNativeSharedLibs) {
- // If we're using a shared library which is provided from other APEX,
- // don't include it in this APEX
- return false
- }
- if !a.Host() && (cc.IsStubs() || cc.HasStubsVariants()) {
- // If the dependency is a stubs lib, don't include it in this APEX,
- // but make sure that the lib is installed on the device.
- // In case no APEX is having the lib, the lib is installed to the system
- // partition.
- //
- // Always include if we are a host-apex however since those won't have any
- // system libraries.
- if !android.DirectlyInAnyApex(ctx, cc.Name()) && !android.InList(cc.Name(), a.externalDeps) {
- a.externalDeps = append(a.externalDeps, cc.Name())
+ // We cannot use a switch statement on `depTag` here as the checked
+ // tags used below are private (e.g. `cc.sharedDepTag`).
+ if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
+ if cc, ok := child.(*cc.Module); ok {
+ if android.InList(cc.Name(), providedNativeSharedLibs) {
+ // If we're using a shared library which is provided from other APEX,
+ // don't include it in this APEX
+ return false
}
- // Don't track further
- return false
+ if !a.Host() && (cc.IsStubs() || cc.HasStubsVariants()) {
+ // If the dependency is a stubs lib, don't include it in this APEX,
+ // but make sure that the lib is installed on the device.
+ // In case no APEX is having the lib, the lib is installed to the system
+ // partition.
+ //
+ // Always include if we are a host-apex however since those won't have any
+ // system libraries.
+ if !android.DirectlyInAnyApex(ctx, cc.Name()) && !android.InList(cc.Name(), a.externalDeps) {
+ a.externalDeps = append(a.externalDeps, cc.Name())
+ }
+ requireNativeLibs = append(requireNativeLibs, cc.OutputFile().Path().Base())
+ // Don't track further
+ return false
+ }
+ fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc, handleSpecialLibs)
+ filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeSharedLib, cc, nil})
+ return true
}
- depName := ctx.OtherModuleName(child)
- fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc, handleSpecialLibs)
- filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeSharedLib, cc, nil})
- return true
+ } else if cc.IsTestPerSrcDepTag(depTag) {
+ if cc, ok := child.(*cc.Module); ok {
+ fileToCopy, dirInApex := getCopyManifestForExecutable(cc)
+ // Handle modules created as `test_per_src` variations of a single test module:
+ // use the name of the generated test binary (`fileToCopy`) instead of the name
+ // of the original test module (`depName`, shared by all `test_per_src`
+ // variations of that module).
+ moduleName := filepath.Base(fileToCopy.String())
+ filesInfo = append(filesInfo, apexFile{fileToCopy, moduleName, dirInApex, nativeTest, cc, nil})
+ return true
+ }
+ } else {
+ ctx.ModuleErrorf("unexpected tag %q for indirect dependency %q", depTag, depName)
}
}
}
@@ -913,12 +1014,27 @@
a.installDir = android.PathForModuleInstall(ctx, "apex")
a.filesInfo = filesInfo
+ a.manifestOut = android.PathForModuleOut(ctx, "apex_manifest.json")
+ // put dependency({provide|require}NativeLibs) in apex_manifest.json
+ manifestSrc := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
+ provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs)
+ requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs))
+ ctx.Build(pctx, android.BuildParams{
+ Rule: injectApexDependency,
+ Input: manifestSrc,
+ Output: a.manifestOut,
+ Args: map[string]string{
+ "provideNativeLibs": strings.Join(provideNativeLibs, " "),
+ "requireNativeLibs": strings.Join(requireNativeLibs, " "),
+ },
+ })
+
if a.apexTypes.zip() {
a.buildUnflattenedApex(ctx, zipApex)
}
if a.apexTypes.image() {
// Build rule for unflattened APEX is created even when ctx.Config().FlattenApex()
- // is true. This is to support referencing APEX via ":<module_name" syntax
+ // is true. This is to support referencing APEX via ":<module_name>" syntax
// in other modules. It is in AndroidMk where the selection of flattened
// or unflattened APEX is made.
a.buildUnflattenedApex(ctx, imageApex)
@@ -960,8 +1076,6 @@
a.container_private_key_file = key
}
- manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
-
var abis []string
for _, target := range ctx.MultiTargets() {
if len(target.Arch.Abi) > 0 {
@@ -991,7 +1105,7 @@
}
}
implicitInputs := append(android.Paths(nil), filesToCopy...)
- implicitInputs = append(implicitInputs, manifest)
+ implicitInputs = append(implicitInputs, a.manifestOut)
outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String()
prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
@@ -1005,7 +1119,7 @@
if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") {
executablePaths = append(executablePaths, pathInApex)
for _, s := range f.symlinks {
- executablePaths = append(executablePaths, filepath.Join("bin", s))
+ executablePaths = append(executablePaths, filepath.Join(f.installDir, s))
}
} else {
readOnlyPaths = append(readOnlyPaths, pathInApex)
@@ -1086,7 +1200,7 @@
"tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir,
"image_dir": android.PathForModuleOut(ctx, "image"+suffix).String(),
"copy_commands": strings.Join(copyCommands, " && "),
- "manifest": manifest.String(),
+ "manifest": a.manifestOut.String(),
"file_contexts": fileContexts.String(),
"canned_fs_config": cannedFsConfig.String(),
"key": a.private_key_file.String(),
@@ -1124,7 +1238,7 @@
"tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir,
"image_dir": android.PathForModuleOut(ctx, "image"+suffix).String(),
"copy_commands": strings.Join(copyCommands, " && "),
- "manifest": manifest.String(),
+ "manifest": a.manifestOut.String(),
},
})
}
@@ -1155,16 +1269,7 @@
if a.installable() {
// For flattened APEX, do nothing but make sure that apex_manifest.json and apex_pubkey are also copied along
// with other ordinary files.
- manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
-
- // rename to apex_manifest.json
- copiedManifest := android.PathForModuleOut(ctx, "apex_manifest.json")
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cp,
- Input: manifest,
- Output: copiedManifest,
- })
- a.filesInfo = append(a.filesInfo, apexFile{copiedManifest, ctx.ModuleName() + ".apex_manifest.json", ".", etc, nil, nil})
+ a.filesInfo = append(a.filesInfo, apexFile{a.manifestOut, ctx.ModuleName() + ".apex_manifest.json", ".", etc, nil, nil})
// rename to apex_pubkey
copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
@@ -1188,6 +1293,11 @@
}
func (a *apexBundle) AndroidMk() android.AndroidMkData {
+ if a.properties.HideFromMake {
+ return android.AndroidMkData{
+ Disabled: true,
+ }
+ }
writers := []android.AndroidMkData{}
if a.apexTypes.image() {
writers = append(writers, a.androidMkForType(imageApex))
@@ -1282,6 +1392,9 @@
fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", cc.UnstrippedOutputFile().String())
}
cc.AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w)
+ if cc.CoverageOutputFile().Valid() {
+ fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", cc.CoverageOutputFile().String())
+ }
}
fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
} else {
@@ -1329,6 +1442,10 @@
if len(a.externalDeps) > 0 {
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.externalDeps, " "))
}
+ if a.prebuiltFileToDelete != "" {
+ fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", "rm -rf "+
+ filepath.Join("$(OUT_DIR)", a.installDir.RelPathString(), a.prebuiltFileToDelete))
+ }
fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
if apexType == imageApex {
@@ -1403,7 +1520,8 @@
type PrebuiltProperties struct {
// the path to the prebuilt .apex file to import.
- Source string `blueprint:"mutated"`
+ Source string `blueprint:"mutated"`
+ ForceDisable bool `blueprint:"mutated"`
Src *string
Arch struct {
@@ -1425,6 +1543,13 @@
// Optional name for the installed apex. If unspecified, name of the
// module is used as the file name
Filename *string
+
+ // Names of modules to be overridden. Listed modules can only be other binaries
+ // (in Make or Soong).
+ // This does not completely prevent installation of the overridden binaries, but if both
+ // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
+ // from PRODUCT_PACKAGES.
+ Overrides []string
}
func (p *Prebuilt) installable() bool {
@@ -1432,6 +1557,27 @@
}
func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // If the device is configured to use flattened APEX, force disable the prebuilt because
+ // the prebuilt is a non-flattened one.
+ forceDisable := ctx.Config().FlattenApex()
+
+ // Force disable the prebuilts when we are doing unbundled build. We do unbundled build
+ // to build the prebuilts themselves.
+ forceDisable = forceDisable || ctx.Config().UnbundledBuild()
+
+ // Force disable the prebuilts when coverage is enabled.
+ forceDisable = forceDisable || ctx.DeviceConfig().NativeCoverageEnabled()
+ forceDisable = forceDisable || ctx.Config().IsEnvTrue("EMMA_INSTRUMENT")
+
+ // b/137216042 don't use prebuilts when address sanitizer is on
+ forceDisable = forceDisable || android.InList("address", ctx.Config().SanitizeDevice()) ||
+ android.InList("hwaddress", ctx.Config().SanitizeDevice())
+
+ if forceDisable && p.prebuilt.SourceExists() {
+ p.properties.ForceDisable = true
+ return
+ }
+
// This is called before prebuilt_select and prebuilt_postdeps mutators
// The mutators requires that src to be set correctly for each arch so that
// arch variants are disabled when src is not provided for the arch.
@@ -1459,6 +1605,10 @@
p.properties.Source = src
}
+func (p *Prebuilt) isForceDisabled() bool {
+ return p.properties.ForceDisable
+}
+
func (p *Prebuilt) OutputFiles(tag string) (android.Paths, error) {
switch tag {
case "":
@@ -1473,6 +1623,10 @@
}
func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ if p.properties.ForceDisable {
+ return
+ }
+
// TODO(jungjw): Check the key validity.
p.inputApex = p.Prebuilt().SingleSourcePath(ctx)
p.installDir = android.PathForModuleInstall(ctx, "apex")
@@ -1499,17 +1653,16 @@
return p.prebuilt.Name(p.ModuleBase.Name())
}
-func (p *Prebuilt) AndroidMk() android.AndroidMkData {
- return android.AndroidMkData{
+func (p *Prebuilt) AndroidMkEntries() android.AndroidMkEntries {
+ return android.AndroidMkEntries{
Class: "ETC",
OutputFile: android.OptionalPathForPath(p.inputApex),
Include: "$(BUILD_PREBUILT)",
- Extra: []android.AndroidMkExtraFunc{
- func(w io.Writer, outputFile android.Path) {
- fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", p.installDir.RelPathString()))
- fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", p.installFilename)
- fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !p.installable())
- },
+ AddCustomEntries: func(name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", filepath.Join("$(OUT_DIR)", p.installDir.RelPathString()))
+ entries.SetString("LOCAL_MODULE_STEM", p.installFilename)
+ entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
+ entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", p.properties.Overrides...)
},
}
}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 94cf19d..e06c193 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -17,6 +17,7 @@
import (
"io/ioutil"
"os"
+ "reflect"
"strings"
"testing"
@@ -29,6 +30,16 @@
var buildDir string
+// names returns name list from white space separated string
+func names(s string) (ns []string) {
+ for _, n := range strings.Split(s, " ") {
+ if len(n) > 0 {
+ ns = append(ns, n)
+ }
+ }
+ return
+}
+
func testApexError(t *testing.T, pattern, bp string) {
ctx, config := testApexContext(t, bp)
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
@@ -45,13 +56,13 @@
t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
}
-func testApex(t *testing.T, bp string) *android.TestContext {
+func testApex(t *testing.T, bp string) (*android.TestContext, android.Config) {
ctx, config := testApexContext(t, bp)
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
android.FailIfErrored(t, errs)
- return ctx
+ return ctx, config
}
func testApexContext(t *testing.T, bp string) (*android.TestContext, android.Config) {
@@ -90,6 +101,9 @@
ctx.RegisterModuleType("sh_binary", android.ModuleFactoryAdaptor(android.ShBinaryFactory))
ctx.RegisterModuleType("android_app_certificate", android.ModuleFactoryAdaptor(java.AndroidAppCertificateFactory))
ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
+ ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(java.LibraryFactory))
+ ctx.RegisterModuleType("java_import", android.ModuleFactoryAdaptor(java.ImportFactory))
+
ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
ctx.BottomUp("prebuilts", android.PrebuiltMutator).Parallel()
})
@@ -97,6 +111,7 @@
ctx.BottomUp("image", cc.ImageMutator).Parallel()
ctx.BottomUp("link", cc.LinkageMutator).Parallel()
ctx.BottomUp("vndk", cc.VndkMutator).Parallel()
+ ctx.BottomUp("test_per_src", cc.TestPerSrcMutator).Parallel()
ctx.BottomUp("version", cc.VersionMutator).Parallel()
ctx.BottomUp("begin", cc.BeginMutator).Parallel()
})
@@ -196,10 +211,15 @@
"system/sepolicy/apex/otherapex-file_contexts": nil,
"system/sepolicy/apex/commonapex-file_contexts": nil,
"mylib.cpp": nil,
- "mytest.cpp": nil,
"mylib_common.cpp": nil,
+ "mytest.cpp": nil,
+ "mytest1.cpp": nil,
+ "mytest2.cpp": nil,
+ "mytest3.cpp": nil,
"myprebuilt": nil,
"my_include": nil,
+ "foo/bar/MyClass.java": nil,
+ "prebuilt.jar": nil,
"vendor/foo/devkeys/test.x509.pem": nil,
"vendor/foo/devkeys/test.pk8": nil,
"testkey.x509.pem": nil,
@@ -262,9 +282,16 @@
}
}
+func ensureListEmpty(t *testing.T, result []string) {
+ t.Helper()
+ if len(result) > 0 {
+ t.Errorf("%q is expected to be empty", result)
+ }
+}
+
// Minimal test
func TestBasicApex(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex_defaults {
name: "myapex-defaults",
manifest: ":myapex.manifest",
@@ -275,7 +302,8 @@
both: {
binaries: ["foo",],
}
- }
+ },
+ java_libs: ["myjar", "myprebuiltjar"],
}
apex {
@@ -333,6 +361,29 @@
stl: "none",
notice: "custom_notice",
}
+
+ java_library {
+ name: "myjar",
+ srcs: ["foo/bar/MyClass.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ compile_dex: true,
+ static_libs: ["myotherjar"],
+ }
+
+ java_library {
+ name: "myotherjar",
+ srcs: ["foo/bar/MyClass.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ compile_dex: true,
+ }
+
+ java_import {
+ name: "myprebuiltjar",
+ jars: ["prebuilt.jar"],
+ installable: true,
+ }
`)
apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
@@ -349,17 +400,27 @@
// Ensure that apex variant is created for the direct dep
ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared_myapex")
+ ensureListContains(t, ctx.ModuleVariantsForTests("myjar"), "android_common_myapex")
+ ensureListContains(t, ctx.ModuleVariantsForTests("myprebuiltjar"), "android_common_myapex")
// Ensure that apex variant is created for the indirect dep
ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_core_shared_myapex")
+ ensureListContains(t, ctx.ModuleVariantsForTests("myotherjar"), "android_common_myapex")
// Ensure that both direct and indirect deps are copied into apex
ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
ensureContains(t, copyCmds, "image.apex/lib64/mylib2.so")
+ ensureContains(t, copyCmds, "image.apex/javalib/myjar.jar")
+ ensureContains(t, copyCmds, "image.apex/javalib/myprebuiltjar.jar")
+ // .. but not for java libs
+ ensureNotContains(t, copyCmds, "image.apex/javalib/myotherjar.jar")
- // Ensure that the platform variant ends with _core_shared
+ // Ensure that the platform variant ends with _core_shared or _common
ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared")
ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_core_shared")
+ ensureListContains(t, ctx.ModuleVariantsForTests("myjar"), "android_common")
+ ensureListContains(t, ctx.ModuleVariantsForTests("myotherjar"), "android_common")
+ ensureListContains(t, ctx.ModuleVariantsForTests("myprebuiltjar"), "android_common")
// Ensure that all symlinks are present.
found_foo_link_64 := false
@@ -388,7 +449,7 @@
}
func TestBasicZipApex(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -436,7 +497,7 @@
}
func TestApexWithStubs(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -520,7 +581,7 @@
}
func TestApexWithExplicitStubsDependency(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -586,8 +647,75 @@
ensureNotContains(t, libFooStubsLdFlags, "libbar.so")
}
+func TestApexWithRuntimeLibsDependency(t *testing.T) {
+ /*
+ myapex
+ |
+ v (runtime_libs)
+ mylib ------+------> libfoo [provides stub]
+ |
+ `------> libbar
+ */
+ ctx, _ := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ runtime_libs: ["libfoo", "libbar"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libfoo",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["10", "20", "30"],
+ },
+ }
+
+ cc_library {
+ name: "libbar",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ `)
+
+ apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+
+ // Ensure that direct non-stubs dep is always included
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+
+ // Ensure that indirect stubs dep is not included
+ ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.so")
+
+ // Ensure that runtime_libs dep in included
+ ensureContains(t, copyCmds, "image.apex/lib64/libbar.so")
+
+ injectRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("injectApexDependency")
+ ensureListEmpty(t, names(injectRule.Args["provideNativeLibs"]))
+ ensureListContains(t, names(injectRule.Args["requireNativeLibs"]), "libfoo.so")
+
+}
+
func TestApexWithSystemLibsStubs(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -708,7 +836,7 @@
}
func TestFilesInSubDir(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -768,7 +896,7 @@
}
func TestUseVendor(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -842,7 +970,7 @@
}
func TestStaticLinking(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -882,7 +1010,7 @@
}
func TestKeys(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex_keytest",
key: "myapex.key",
@@ -936,7 +1064,7 @@
}
func TestMacro(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -980,7 +1108,7 @@
}
func TestHeaderLibsDependency(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -1027,8 +1155,102 @@
ensureContains(t, cFlags, "-Imy_include")
}
+func TestDependenciesInApexManifest(t *testing.T) {
+ ctx, _ := testApex(t, `
+ apex {
+ name: "myapex_nodep",
+ key: "myapex.key",
+ native_shared_libs: ["lib_nodep"],
+ compile_multilib: "both",
+ file_contexts: "myapex",
+ }
+
+ apex {
+ name: "myapex_dep",
+ key: "myapex.key",
+ native_shared_libs: ["lib_dep"],
+ compile_multilib: "both",
+ file_contexts: "myapex",
+ }
+
+ apex {
+ name: "myapex_provider",
+ key: "myapex.key",
+ native_shared_libs: ["libfoo"],
+ compile_multilib: "both",
+ file_contexts: "myapex",
+ }
+
+ apex {
+ name: "myapex_selfcontained",
+ key: "myapex.key",
+ native_shared_libs: ["lib_dep", "libfoo"],
+ compile_multilib: "both",
+ file_contexts: "myapex",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "lib_nodep",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "lib_dep",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["libfoo"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libfoo",
+ srcs: ["mytest.cpp"],
+ stubs: {
+ versions: ["1"],
+ },
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `)
+
+ var injectRule android.TestingBuildParams
+ var provideNativeLibs, requireNativeLibs []string
+
+ injectRule = ctx.ModuleForTests("myapex_nodep", "android_common_myapex_nodep").Rule("injectApexDependency")
+ provideNativeLibs = names(injectRule.Args["provideNativeLibs"])
+ requireNativeLibs = names(injectRule.Args["requireNativeLibs"])
+ ensureListEmpty(t, provideNativeLibs)
+ ensureListEmpty(t, requireNativeLibs)
+
+ injectRule = ctx.ModuleForTests("myapex_dep", "android_common_myapex_dep").Rule("injectApexDependency")
+ provideNativeLibs = names(injectRule.Args["provideNativeLibs"])
+ requireNativeLibs = names(injectRule.Args["requireNativeLibs"])
+ ensureListEmpty(t, provideNativeLibs)
+ ensureListContains(t, requireNativeLibs, "libfoo.so")
+
+ injectRule = ctx.ModuleForTests("myapex_provider", "android_common_myapex_provider").Rule("injectApexDependency")
+ provideNativeLibs = names(injectRule.Args["provideNativeLibs"])
+ requireNativeLibs = names(injectRule.Args["requireNativeLibs"])
+ ensureListContains(t, provideNativeLibs, "libfoo.so")
+ ensureListEmpty(t, requireNativeLibs)
+
+ injectRule = ctx.ModuleForTests("myapex_selfcontained", "android_common_myapex_selfcontained").Rule("injectApexDependency")
+ provideNativeLibs = names(injectRule.Args["provideNativeLibs"])
+ requireNativeLibs = names(injectRule.Args["requireNativeLibs"])
+ ensureListContains(t, provideNativeLibs, "libfoo.so")
+ ensureListEmpty(t, requireNativeLibs)
+}
+
func TestNonTestApex(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -1079,7 +1301,7 @@
if android.InAnyApex("mylib_common_test") {
t.Fatal("mylib_common_test must not be used in any other tests since this checks that global state is not updated in an illegal way!")
}
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex_test {
name: "myapex",
key: "myapex.key",
@@ -1127,7 +1349,7 @@
}
func TestApexWithTarget(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -1207,7 +1429,7 @@
}
func TestApexWithShBinary(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -1235,7 +1457,7 @@
}
func TestApexInProductPartition(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -1267,7 +1489,7 @@
}
func TestApexKeyFromOtherModule(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex_key {
name: "myapex.key",
public_key: ":my.avbpubkey",
@@ -1300,7 +1522,7 @@
}
func TestPrebuilt(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
prebuilt_apex {
name: "myapex",
arch: {
@@ -1323,7 +1545,7 @@
}
func TestPrebuiltFilenameOverride(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
prebuilt_apex {
name: "myapex",
src: "myapex-arm.apex",
@@ -1339,13 +1561,34 @@
}
}
+func TestPrebuiltOverrides(t *testing.T) {
+ ctx, config := testApex(t, `
+ prebuilt_apex {
+ name: "myapex.prebuilt",
+ src: "myapex-arm.apex",
+ overrides: [
+ "myapex",
+ ],
+ }
+ `)
+
+ p := ctx.ModuleForTests("myapex.prebuilt", "android_common").Module().(*Prebuilt)
+
+ expected := []string{"myapex"}
+ actual := android.AndroidMkEntriesForTest(t, config, "", p).EntryMap["LOCAL_OVERRIDES_PACKAGES"]
+ if !reflect.DeepEqual(actual, expected) {
+ t.Errorf("Incorrect LOCAL_OVERRIDES_PACKAGES value '%s', expected '%s'", actual, expected)
+ }
+}
+
func TestApexWithTests(t *testing.T) {
- ctx := testApex(t, `
+ ctx, config := testApex(t, `
apex_test {
name: "myapex",
key: "myapex.key",
tests: [
"mytest",
+ "mytests",
],
}
@@ -1364,6 +1607,21 @@
static_executable: true,
stl: "none",
}
+
+ cc_test {
+ name: "mytests",
+ gtest: false,
+ srcs: [
+ "mytest1.cpp",
+ "mytest2.cpp",
+ "mytest3.cpp",
+ ],
+ test_per_src: true,
+ relative_install_path: "test",
+ system_shared_libs: [],
+ static_executable: true,
+ stl: "none",
+ }
`)
apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
@@ -1371,10 +1629,31 @@
// Ensure that test dep is copied into apex.
ensureContains(t, copyCmds, "image.apex/bin/test/mytest")
+
+ // Ensure that test deps built with `test_per_src` are copied into apex.
+ ensureContains(t, copyCmds, "image.apex/bin/test/mytest1")
+ ensureContains(t, copyCmds, "image.apex/bin/test/mytest2")
+ ensureContains(t, copyCmds, "image.apex/bin/test/mytest3")
+
+ // Ensure the module is correctly translated.
+ apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
+ data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+ name := apexBundle.BaseModuleName()
+ prefix := "TARGET_"
+ var builder strings.Builder
+ data.Custom(&builder, name, prefix, "", data)
+ androidMk := builder.String()
+ ensureContains(t, androidMk, "LOCAL_MODULE := myapex.mytest\n")
+ ensureContains(t, androidMk, "LOCAL_MODULE := myapex.mytest1\n")
+ ensureContains(t, androidMk, "LOCAL_MODULE := myapex.mytest2\n")
+ ensureContains(t, androidMk, "LOCAL_MODULE := myapex.mytest3\n")
+ ensureContains(t, androidMk, "LOCAL_MODULE := myapex.apex_manifest.json\n")
+ ensureContains(t, androidMk, "LOCAL_MODULE := myapex.apex_pubkey\n")
+ ensureContains(t, androidMk, "LOCAL_MODULE := myapex\n")
}
func TestApexUsesOtherApex(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
diff --git a/build_kzip.bash b/build_kzip.bash
new file mode 100755
index 0000000..5364e7f
--- /dev/null
+++ b/build_kzip.bash
@@ -0,0 +1,25 @@
+# /bin/bash -uv
+#
+# Build kzip files (source files for the indexing pipeline) for the given configuration,
+# merge them and place the resulting all.kzip into $DIST_DIR.
+# It is assumed that the current directory is the top of the source tree.
+# The following enviromnet variables affect the result:
+# TARGET_PRODUCT target device name, e.g., `aosp_blueline`
+# TARGET_BUILD_VARIANT variant, e.g., `userdebug`
+# OUT_DIR where the build is happening (./out if not specified)
+# DIST_DIR where the resulting all.kzip will be placed
+# XREF_CORPUS source code repository URI, e.g.,
+# `android.googlesource.com/platform/superproject`
+
+# The extraction might fail for some source files, so run with -k
+build/soong/soong_ui.bash --build-mode --all-modules --dir=$PWD -k merge_zips xref_cxx xref_java
+
+# We build with -k, so check that we have generated at least 100K files
+# (the actual number is 180K+)
+declare -r kzip_count=$(find $OUT_DIR -name '*.kzip' | wc -l)
+(($kzip_count>100000)) || { printf "Too few kzip files were generated: %d\n" $kzip_count; exit 1; }
+
+# Pack
+# TODO(asmundak): this should be done by soong.
+declare -r allkzip=all.kzip
+"${OUT_DIR:-out}/soong/host/linux-x86/bin/merge_zips" "$DIST_DIR/$allkzip" @<(find $OUT_DIR -name '*.kzip')
diff --git a/cc/binary.go b/cc/binary.go
index 149a92e..fd00060 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -425,6 +425,10 @@
return true
}
+func (binary *binaryDecorator) coverageOutputFilePath() android.OptionalPath {
+ return binary.coverageOutputFile
+}
+
// /system/bin/linker -> /apex/com.android.runtime/bin/linker
func (binary *binaryDecorator) installSymlinkToRuntimeApex(ctx ModuleContext, file android.Path) {
dir := binary.baseInstaller.installDir(ctx)
diff --git a/cc/builder.go b/cc/builder.go
index ee40736..3d89770 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -78,7 +78,7 @@
blueprint.RuleParams{
// Without -no-pie, clang 7.0 adds -pie to link Android files,
// but -r and -pie cannot be used together.
- Command: "$ldCmd -nostdlib -no-pie -Wl,-r ${in} -o ${out} ${ldFlags}",
+ Command: "$ldCmd -fuse-ld=lld -nostdlib -no-pie -Wl,-r ${in} -o ${out} ${ldFlags}",
CommandDeps: []string{"$ldCmd"},
},
"ldCmd", "ldFlags")
@@ -221,6 +221,17 @@
Rspfile: "$out.rsp",
RspfileContent: "$in",
})
+
+ _ = pctx.SourcePathVariable("cxxExtractor",
+ "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/cxx_extractor")
+ _ = pctx.VariableFunc("kytheCorpus",
+ func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
+ kytheExtract = pctx.StaticRule("kythe",
+ blueprint.RuleParams{
+ Command: "rm -f $out && KYTHE_CORPUS=${kytheCorpus} KYTHE_OUTPUT_FILE=$out $cxxExtractor $cFlags $in ",
+ CommandDeps: []string{"$cxxExtractor"},
+ },
+ "cFlags")
)
func init() {
@@ -257,6 +268,7 @@
tidy bool
coverage bool
sAbiDump bool
+ emitXrefs bool
systemIncludeFlags string
@@ -281,6 +293,7 @@
tidyFiles android.Paths
coverageFiles android.Paths
sAbiDumpFiles android.Paths
+ kytheFiles android.Paths
}
func (a Objects) Copy() Objects {
@@ -289,6 +302,7 @@
tidyFiles: append(android.Paths{}, a.tidyFiles...),
coverageFiles: append(android.Paths{}, a.coverageFiles...),
sAbiDumpFiles: append(android.Paths{}, a.sAbiDumpFiles...),
+ kytheFiles: append(android.Paths{}, a.kytheFiles...),
}
}
@@ -298,6 +312,7 @@
tidyFiles: append(a.tidyFiles, b.tidyFiles...),
coverageFiles: append(a.coverageFiles, b.coverageFiles...),
sAbiDumpFiles: append(a.sAbiDumpFiles, b.sAbiDumpFiles...),
+ kytheFiles: append(a.kytheFiles, b.kytheFiles...),
}
}
@@ -314,6 +329,10 @@
if flags.coverage {
coverageFiles = make(android.Paths, 0, len(srcFiles))
}
+ var kytheFiles android.Paths
+ if flags.emitXrefs {
+ kytheFiles = make(android.Paths, 0, len(srcFiles))
+ }
commonFlags := strings.Join([]string{
flags.globalFlags,
@@ -401,6 +420,7 @@
coverage := flags.coverage
dump := flags.sAbiDump
rule := cc
+ emitXref := flags.emitXrefs
switch srcFile.Ext() {
case ".s":
@@ -412,6 +432,7 @@
tidy = false
coverage = false
dump = false
+ emitXref = false
case ".c":
ccCmd = "clang"
moduleCflags = cflags
@@ -450,6 +471,22 @@
},
})
+ if emitXref {
+ kytheFile := android.ObjPathWithExt(ctx, subdir, srcFile, "kzip")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: kytheExtract,
+ Description: "Xref C++ extractor " + srcFile.Rel(),
+ Output: kytheFile,
+ Input: srcFile,
+ Implicits: cFlagsDeps,
+ OrderOnly: pathDeps,
+ Args: map[string]string{
+ "cFlags": moduleCflags,
+ },
+ })
+ kytheFiles = append(kytheFiles, kytheFile)
+ }
+
if tidy {
tidyFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy")
tidyFiles = append(tidyFiles, tidyFile)
@@ -461,7 +498,9 @@
Input: srcFile,
// We must depend on objFile, since clang-tidy doesn't
// support exporting dependencies.
- Implicit: objFile,
+ Implicit: objFile,
+ Implicits: cFlagsDeps,
+ OrderOnly: pathDeps,
Args: map[string]string{
"cFlags": moduleToolingCflags,
"tidyFlags": flags.tidyFlags,
@@ -479,6 +518,8 @@
Output: sAbiDumpFile,
Input: srcFile,
Implicit: objFile,
+ Implicits: cFlagsDeps,
+ OrderOnly: pathDeps,
Args: map[string]string{
"cFlags": moduleToolingCflags,
"exportDirs": flags.sAbiFlags,
@@ -493,6 +534,7 @@
tidyFiles: tidyFiles,
coverageFiles: coverageFiles,
sAbiDumpFiles: sAbiDumpFiles,
+ kytheFiles: kytheFiles,
}
}
@@ -601,9 +643,6 @@
excludedSymbolVersions, excludedSymbolTags []string) android.OptionalPath {
outputFile := android.PathForModuleOut(ctx, baseName+".lsdump")
- sabiLock.Lock()
- lsdumpPaths = append(lsdumpPaths, outputFile.String())
- sabiLock.Unlock()
implicits := android.Paths{soFile}
symbolFilterStr := "-so " + soFile.String()
diff --git a/cc/cc.go b/cc/cc.go
index a0ab255..c853e67 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -40,7 +40,7 @@
ctx.BottomUp("link", LinkageMutator).Parallel()
ctx.BottomUp("vndk", VndkMutator).Parallel()
ctx.BottomUp("ndk_api", ndkApiMutator).Parallel()
- ctx.BottomUp("test_per_src", testPerSrcMutator).Parallel()
+ ctx.BottomUp("test_per_src", TestPerSrcMutator).Parallel()
ctx.BottomUp("version", VersionMutator).Parallel()
ctx.BottomUp("begin", BeginMutator).Parallel()
ctx.BottomUp("sysprop", SyspropMutator).Parallel()
@@ -56,6 +56,8 @@
ctx.TopDown("fuzzer_deps", sanitizerDepsMutator(fuzzer))
ctx.BottomUp("fuzzer", sanitizerMutator(fuzzer)).Parallel()
+ // cfi mutator shouldn't run before sanitizers that return true for
+ // incompatibleWithCfi()
ctx.TopDown("cfi_deps", sanitizerDepsMutator(cfi))
ctx.BottomUp("cfi", sanitizerMutator(cfi)).Parallel()
@@ -77,6 +79,7 @@
ctx.TopDown("double_loadable", checkDoubleLoadableLibraries).Parallel()
})
+ android.RegisterSingletonType("kythe_extract_all", kytheExtractAllFactory)
pctx.Import("android/soong/cc/config")
}
@@ -162,6 +165,7 @@
Tidy bool
Coverage bool
SAbiDump bool
+ EmitXrefs bool // If true, generate Ninja rules to generate emitXrefs input files for Kythe
RequiredInstructionSet string
DynamicLinker string
@@ -179,6 +183,9 @@
}
type ObjectLinkerProperties struct {
+ // list of modules that should only provide headers for this module.
+ Header_libs []string `android:"arch_variant,variant_prepend"`
+
// names of other cc_object modules to link into this module using partial linking
Objs []string `android:"arch_variant"`
@@ -250,6 +257,7 @@
type ModuleContextIntf interface {
static() bool
staticBinary() bool
+ header() bool
toolchain() config.Toolchain
useSdk() bool
sdkVersion() string
@@ -262,7 +270,7 @@
isVndkSp() bool
isVndkExt() bool
inRecovery() bool
- shouldCreateVndkSourceAbiDump(config android.Config) bool
+ shouldCreateSourceAbiDump() bool
selectedStl() string
baseModuleName() string
getVndkExtendsModuleName() string
@@ -322,6 +330,7 @@
unstrippedOutputFilePath() android.Path
nativeCoverage() bool
+ coverageOutputFilePath() android.OptionalPath
}
type installer interface {
@@ -337,17 +346,22 @@
blueprint.BaseDependencyTag
name string
library bool
+ shared bool
reexportFlags bool
explicitlyVersioned bool
}
+type xref interface {
+ XrefCcFiles() android.Paths
+}
+
var (
- sharedDepTag = dependencyTag{name: "shared", library: true}
- sharedExportDepTag = dependencyTag{name: "shared", library: true, reexportFlags: true}
- earlySharedDepTag = dependencyTag{name: "early_shared", library: true}
- lateSharedDepTag = dependencyTag{name: "late shared", library: true}
+ sharedDepTag = dependencyTag{name: "shared", library: true, shared: true}
+ sharedExportDepTag = dependencyTag{name: "shared", library: true, shared: true, reexportFlags: true}
+ earlySharedDepTag = dependencyTag{name: "early_shared", library: true, shared: true}
+ lateSharedDepTag = dependencyTag{name: "late shared", library: true, shared: true}
staticDepTag = dependencyTag{name: "static", library: true}
staticExportDepTag = dependencyTag{name: "static", library: true, reexportFlags: true}
lateStaticDepTag = dependencyTag{name: "late static", library: true}
@@ -369,8 +383,24 @@
vndkExtDepTag = dependencyTag{name: "vndk extends", library: true}
runtimeDepTag = dependencyTag{name: "runtime lib"}
coverageDepTag = dependencyTag{name: "coverage"}
+ testPerSrcDepTag = dependencyTag{name: "test_per_src"}
)
+func IsSharedDepTag(depTag blueprint.DependencyTag) bool {
+ ccDepTag, ok := depTag.(dependencyTag)
+ return ok && ccDepTag.shared
+}
+
+func IsRuntimeDepTag(depTag blueprint.DependencyTag) bool {
+ ccDepTag, ok := depTag.(dependencyTag)
+ return ok && ccDepTag == runtimeDepTag
+}
+
+func IsTestPerSrcDepTag(depTag blueprint.DependencyTag) bool {
+ ccDepTag, ok := depTag.(dependencyTag)
+ return ok && ccDepTag == testPerSrcDepTag
+}
+
// Module contains the properties and members used by all C/C++ module types, and implements
// the blueprint.Module interface. It delegates to compiler, linker, and installer interfaces
// to construct the output file. Behavior can be customized with a Customizer interface
@@ -420,6 +450,8 @@
staticVariant *Module
makeLinkType string
+ // Kythe (source file indexer) paths for this compilation module
+ kytheFiles android.Paths
}
func (c *Module) OutputFile() android.OptionalPath {
@@ -433,6 +465,13 @@
return nil
}
+func (c *Module) CoverageOutputFile() android.OptionalPath {
+ if c.linker != nil {
+ return c.linker.coverageOutputFilePath()
+ }
+ return android.OptionalPath{}
+}
+
func (c *Module) RelativeInstallPath() string {
if c.installer != nil {
return c.installer.relativeInstallPath()
@@ -646,6 +685,10 @@
return isBionic(name)
}
+func (c *Module) XrefCcFiles() android.Paths {
+ return c.kytheFiles
+}
+
type baseModuleContext struct {
android.BaseModuleContext
moduleContextImpl
@@ -683,6 +726,10 @@
return ctx.mod.staticBinary()
}
+func (ctx *moduleContextImpl) header() bool {
+ return ctx.mod.header()
+}
+
func (ctx *moduleContextImpl) useSdk() bool {
if ctx.ctx.Device() && !ctx.useVndk() && !ctx.inRecovery() && !ctx.ctx.Fuchsia() {
return String(ctx.mod.Properties.Sdk_version) != ""
@@ -757,7 +804,7 @@
}
// Check whether ABI dumps should be created for this module.
-func (ctx *moduleContextImpl) shouldCreateVndkSourceAbiDump(config android.Config) bool {
+func (ctx *moduleContextImpl) shouldCreateSourceAbiDump() bool {
if ctx.ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
return false
}
@@ -783,18 +830,7 @@
// Stubs do not need ABI dumps.
return false
}
- if ctx.isNdk() {
- return true
- }
- if ctx.isLlndkPublic(config) {
- return true
- }
- if ctx.useVndk() && ctx.isVndk() && !ctx.isVndkPrivate(config) {
- // Return true if this is VNDK-core, VNDK-SP, or VNDK-Ext and this is not
- // VNDK-private.
- return true
- }
- return false
+ return true
}
func (ctx *moduleContextImpl) selectedStl() string {
@@ -939,7 +975,22 @@
return results
}
+func (c *Module) IsTestPerSrcAllTestsVariation() bool {
+ test, ok := c.linker.(testPerSrc)
+ return ok && test.isAllTestsVariation()
+}
+
func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
+ // Handle the case of a test module split by `test_per_src` mutator.
+ //
+ // The `test_per_src` mutator adds an extra variation named "", depending on all the other
+ // `test_per_src` variations of the test module. Set `outputFile` to an empty path for this
+ // module and return early, as this module does not produce an output file per se.
+ if c.IsTestPerSrcAllTestsVariation() {
+ c.outputFile = android.OptionalPath{}
+ return
+ }
+
c.makeLinkType = c.getMakeLinkType(actx)
ctx := &moduleContext{
@@ -961,6 +1012,7 @@
flags := Flags{
Toolchain: c.toolchain(ctx),
+ EmitXrefs: ctx.Config().EmitXrefRules(),
}
if c.compiler != nil {
flags = c.compiler.compilerFlags(ctx, flags, deps)
@@ -1026,6 +1078,7 @@
if ctx.Failed() {
return
}
+ c.kytheFiles = objs.kytheFiles
}
if c.linker != nil {
@@ -1849,7 +1902,7 @@
isVendorPublicLib := inList(libName, *vendorPublicLibraries)
bothVendorAndCoreVariantsExist := ccDep.hasVendorVariant() || isLLndk
- if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.isVndk() && !ccDep.mustUseVendorVariant() {
+ if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.isVndk() && !ccDep.mustUseVendorVariant() && !c.inRecovery() {
// The vendor module is a no-vendor-variant VNDK library. Depend on the
// core module instead.
return libName
@@ -1985,6 +2038,15 @@
return false
}
+func (c *Module) header() bool {
+ if h, ok := c.linker.(interface {
+ header() bool
+ }); ok {
+ return h.header()
+ }
+ return false
+}
+
func (c *Module) getMakeLinkType(actx android.ModuleContext) string {
name := actx.ModuleName()
if c.useVndk() {
@@ -2016,12 +2078,14 @@
}
// Overrides ApexModule.IsInstallabeToApex()
-// Only shared libraries are installable to APEX.
+// Only shared/runtime libraries and "test_per_src" tests are installable to APEX.
func (c *Module) IsInstallableToApex() bool {
if shared, ok := c.linker.(interface {
shared() bool
}); ok {
return shared.shared()
+ } else if _, ok := c.linker.(testPerSrc); ok {
+ return true
}
return false
}
@@ -2083,6 +2147,7 @@
&VendorProperties{},
&BaseCompilerProperties{},
&BaseLinkerProperties{},
+ &ObjectLinkerProperties{},
&LibraryProperties{},
&FlagExporterProperties{},
&BinaryLinkerProperties{},
@@ -2331,6 +2396,31 @@
return ctx.Config().PlatformSdkVersion()
}
+func kytheExtractAllFactory() android.Singleton {
+ return &kytheExtractAllSingleton{}
+}
+
+type kytheExtractAllSingleton struct {
+}
+
+func (ks *kytheExtractAllSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ var xrefTargets android.Paths
+ ctx.VisitAllModules(func(module android.Module) {
+ if ccModule, ok := module.(xref); ok {
+ xrefTargets = append(xrefTargets, ccModule.XrefCcFiles()...)
+ }
+ })
+ // TODO(asmundak): Perhaps emit a rule to output a warning if there were no xrefTargets
+ if len(xrefTargets) > 0 {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: blueprint.Phony,
+ Output: android.PathForPhony(ctx, "xref_cxx"),
+ Inputs: xrefTargets,
+ //Default: true,
+ })
+ }
+}
+
var Bool = proptools.Bool
var BoolDefault = proptools.BoolDefault
var BoolPtr = proptools.BoolPtr
diff --git a/cc/cc_test.go b/cc/cc_test.go
index ca34185..c4799e9 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -1941,13 +1941,13 @@
// Check the shared version of lib2.
variant := "android_arm64_armv8-a_core_shared"
module := ctx.ModuleForTests("lib2", variant).Module().(*Module)
- checkStaticLibs(t, []string{"lib1", "libclang_rt.builtins-aarch64-android", "libatomic", "libgcc_stripped"}, module)
+ checkStaticLibs(t, []string{"lib1", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic", "libgcc_stripped"}, module)
// Check the static version of lib2.
variant = "android_arm64_armv8-a_core_static"
module = ctx.ModuleForTests("lib2", variant).Module().(*Module)
// libc++_static is linked additionally.
- checkStaticLibs(t, []string{"lib1", "libc++_static", "libclang_rt.builtins-aarch64-android", "libatomic", "libgcc_stripped"}, module)
+ checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic", "libgcc_stripped"}, module)
}
var compilerFlagsTestCases = []struct {
@@ -2264,6 +2264,22 @@
}
}
+// Simple smoke test for the cc_fuzz target that ensures the rule compiles
+// correctly.
+func TestFuzzTarget(t *testing.T) {
+ ctx := testCc(t, `
+ cc_fuzz {
+ name: "fuzz_smoke_test",
+ srcs: ["foo.c"],
+ }`)
+
+ variant := "android_arm64_armv8-a_core"
+ ctx.ModuleForTests("fuzz_smoke_test", variant).Rule("cc")
+}
+
+func TestAidl(t *testing.T) {
+}
+
func assertString(t *testing.T, got, expected string) {
t.Helper()
if got != expected {
diff --git a/cc/compiler.go b/cc/compiler.go
index fd6184b..85ff400 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -17,6 +17,7 @@
import (
"fmt"
"path/filepath"
+ "strconv"
"strings"
"github.com/google/blueprint/proptools"
@@ -308,27 +309,11 @@
flags.SystemIncludeFlags = append(flags.SystemIncludeFlags,
"-isystem "+getCurrentIncludePath(ctx).String(),
"-isystem "+getCurrentIncludePath(ctx).Join(ctx, config.NDKTriple(tc)).String())
-
- // TODO: Migrate to API suffixed triple?
- // Traditionally this has come from android/api-level.h, but with the
- // libc headers unified it must be set by the build system since we
- // don't have per-API level copies of that header now.
- version := ctx.sdkVersion()
- if version == "current" {
- version = "__ANDROID_API_FUTURE__"
- }
- flags.GlobalFlags = append(flags.GlobalFlags,
- "-D__ANDROID_API__="+version)
+ flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_NDK__")
}
if ctx.useVndk() {
- // sdkVersion() returns VNDK version for vendor modules.
- version := ctx.sdkVersion()
- if version == "current" {
- version = "__ANDROID_API_FUTURE__"
- }
- flags.GlobalFlags = append(flags.GlobalFlags,
- "-D__ANDROID_API__="+version, "-D__ANDROID_VNDK__")
+ flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_VNDK__")
}
if ctx.inRecovery() {
@@ -364,6 +349,15 @@
flags.LdFlags = config.ClangFilterUnknownCflags(flags.LdFlags)
target := "-target " + tc.ClangTriple()
+ if ctx.Os().Class == android.Device {
+ version := ctx.sdkVersion()
+ if version == "" || version == "current" {
+ target += strconv.Itoa(android.FutureApiLevel)
+ } else {
+ target += version
+ }
+ }
+
gccPrefix := "-B" + config.ToolPath(tc)
flags.CFlags = append(flags.CFlags, target, gccPrefix)
@@ -485,7 +479,7 @@
"-I"+android.PathForModuleGen(ctx, "aidl").String())
}
- if compiler.hasSrcExt(".rs") || compiler.hasSrcExt(".fs") {
+ if compiler.hasSrcExt(".rscript") || compiler.hasSrcExt(".fs") {
flags = rsFlags(ctx, flags, &compiler.Properties)
}
diff --git a/cc/config/global.go b/cc/config/global.go
index a27246e..9ce6896 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -150,8 +150,22 @@
pctx.StaticVariable("HostGlobalLdflags", strings.Join(hostGlobalLdflags, " "))
pctx.StaticVariable("HostGlobalLldflags", strings.Join(hostGlobalLldflags, " "))
- pctx.StaticVariable("CommonClangGlobalCflags",
- strings.Join(append(ClangFilterUnknownCflags(commonGlobalCflags), "${ClangExtraCflags}"), " "))
+ pctx.VariableFunc("CommonClangGlobalCflags", func(ctx android.PackageVarContext) string {
+ flags := ClangFilterUnknownCflags(commonGlobalCflags)
+ flags = append(flags, "${ClangExtraCflags}")
+
+ // http://b/131390872
+ // Automatically initialize any uninitialized stack variables.
+ // Prefer zero-init if both options are set.
+ if ctx.Config().IsEnvTrue("AUTO_ZERO_INITIALIZE") {
+ flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang")
+ } else if ctx.Config().IsEnvTrue("AUTO_PATTERN_INITIALIZE") {
+ flags = append(flags, "-ftrivial-auto-var-init=pattern")
+ }
+
+ return strings.Join(flags, " ")
+ })
+
pctx.VariableFunc("DeviceClangGlobalCflags", func(ctx android.PackageVarContext) string {
if ctx.Config().Fuchsia() {
return strings.Join(ClangFilterUnknownCflags(deviceGlobalCflags), " ")
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index d5e9d01..db9092d 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -181,6 +181,9 @@
if arch == "" {
return ""
}
+ if !t.Bionic() {
+ return "libclang_rt." + library + "-" + arch
+ }
return "libclang_rt." + library + "-" + arch + "-android"
}
@@ -224,6 +227,10 @@
return LibclangRuntimeLibrary(t, "scudo_minimal")
}
+func LibFuzzerRuntimeLibrary(t Toolchain) string {
+ return LibclangRuntimeLibrary(t, "fuzzer")
+}
+
func ToolPath(t Toolchain) string {
if p := t.ToolPath(); p != "" {
return p
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index d9c96df..bcfae5d 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -63,7 +63,7 @@
}
x86_64ArchFeatureCflags = map[string][]string{
- "ssse3": []string{"-DUSE_SSSE3", "-mssse3"},
+ "ssse3": []string{"-mssse3"},
"sse4": []string{"-msse4"},
"sse4_1": []string{"-msse4.1"},
"sse4_2": []string{"-msse4.2"},
diff --git a/cc/config/x86_64_fuchsia_device.go b/cc/config/x86_64_fuchsia_device.go
index 79af00c..0f2013b 100644
--- a/cc/config/x86_64_fuchsia_device.go
+++ b/cc/config/x86_64_fuchsia_device.go
@@ -92,7 +92,7 @@
}
func (t *toolchainFuchsiaX8664) ToolchainClangCflags() string {
- return "-DUSE_SSSE3 -mssse3"
+ return "-mssse3"
}
var toolchainFuchsiaSingleton Toolchain = &toolchainFuchsiaX8664{}
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 6504758..64392dc 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -82,7 +82,7 @@
}
x86ArchFeatureCflags = map[string][]string{
- "ssse3": []string{"-DUSE_SSSE3", "-mssse3"},
+ "ssse3": []string{"-mssse3"},
"sse4": []string{"-msse4"},
"sse4_1": []string{"-msse4.1"},
"sse4_2": []string{"-msse4.2"},
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index f072f34..f08a379 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -233,6 +233,14 @@
return "${config.LinuxX8664YasmFlags}"
}
+func (toolchainLinuxX86) LibclangRuntimeLibraryArch() string {
+ return "i686"
+}
+
+func (toolchainLinuxX8664) LibclangRuntimeLibraryArch() string {
+ return "x86_64"
+}
+
func (t *toolchainLinux) AvailableLibraries() []string {
return linuxAvailableLibraries
}
diff --git a/cc/coverage.go b/cc/coverage.go
index 0de0c1c..2e81a9e 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -140,7 +140,6 @@
} else {
// Check if Native_coverage is set to false. This property defaults to true.
needCoverageVariant = BoolDefault(cov.Properties.Native_coverage, true)
-
if sdk_version := ctx.sdkVersion(); ctx.useSdk() && sdk_version != "current" {
// Native coverage is not supported for SDK versions < 23
if fromApi, err := strconv.Atoi(sdk_version); err == nil && fromApi < 23 {
@@ -158,6 +157,14 @@
cov.Properties.NeedCoverageVariant = needCoverageVariant
}
+// Coverage is an interface for non-CC modules to implement to be mutated for coverage
+type Coverage interface {
+ android.Module
+ IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool
+ PreventInstall()
+ HideFromMake()
+}
+
func coverageMutator(mctx android.BottomUpMutatorContext) {
if c, ok := mctx.Module().(*Module); ok && c.coverage != nil {
needCoverageVariant := c.coverage.Properties.NeedCoverageVariant
@@ -177,5 +184,14 @@
m[1].(*Module).coverage.Properties.CoverageEnabled = needCoverageBuild
m[1].(*Module).coverage.Properties.IsCoverageVariant = true
}
+ } else if cov, ok := mctx.Module().(Coverage); ok && cov.IsNativeCoverageNeeded(mctx) {
+ // APEX modules fall here
+
+ // Note: variant "" is also created because an APEX can be depended on by another
+ // module which are split into "" and "cov" variants. e.g. when cc_test refers
+ // to an APEX via 'data' property.
+ m := mctx.CreateVariations("", "cov")
+ m[0].(Coverage).PreventInstall()
+ m[0].(Coverage).HideFromMake()
}
}
diff --git a/cc/fuzz.go b/cc/fuzz.go
new file mode 100644
index 0000000..c1754b2
--- /dev/null
+++ b/cc/fuzz.go
@@ -0,0 +1,124 @@
+// Copyright 2016 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 cc
+
+import (
+ "android/soong/android"
+ "android/soong/cc/config"
+)
+
+func init() {
+ android.RegisterModuleType("cc_fuzz", FuzzFactory)
+}
+
+// cc_fuzz creates a host/device fuzzer binary. Host binaries can be found at
+// $ANDROID_HOST_OUT/fuzz/, and device binaries can be found at /data/fuzz on
+// your device, or $ANDROID_PRODUCT_OUT/data/fuzz in your build tree.
+func FuzzFactory() android.Module {
+ module := NewFuzz(android.HostAndDeviceSupported)
+ return module.Init()
+}
+
+func NewFuzzInstaller() *baseInstaller {
+ return NewBaseInstaller("fuzz", "fuzz", InstallInData)
+}
+
+type fuzzBinary struct {
+ *binaryDecorator
+ *baseCompiler
+}
+
+func (fuzz *fuzzBinary) linkerProps() []interface{} {
+ props := fuzz.binaryDecorator.linkerProps()
+ return props
+}
+
+func (fuzz *fuzzBinary) linkerInit(ctx BaseModuleContext) {
+ // Add ../lib[64] to rpath so that out/host/linux-x86/fuzz/<fuzzer> can
+ // find out/host/linux-x86/lib[64]/library.so
+ runpaths := []string{"../lib"}
+ for _, runpath := range runpaths {
+ if ctx.toolchain().Is64Bit() {
+ runpath += "64"
+ }
+ fuzz.binaryDecorator.baseLinker.dynamicProperties.RunPaths = append(
+ fuzz.binaryDecorator.baseLinker.dynamicProperties.RunPaths, runpath)
+ }
+
+ // add "" to rpath so that fuzzer binaries can find libraries in their own fuzz directory
+ fuzz.binaryDecorator.baseLinker.dynamicProperties.RunPaths = append(
+ fuzz.binaryDecorator.baseLinker.dynamicProperties.RunPaths, "")
+
+ fuzz.binaryDecorator.linkerInit(ctx)
+}
+
+func (fuzz *fuzzBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
+ deps.StaticLibs = append(deps.StaticLibs,
+ config.LibFuzzerRuntimeLibrary(ctx.toolchain()))
+ deps = fuzz.binaryDecorator.linkerDeps(ctx, deps)
+ return deps
+}
+
+func (fuzz *fuzzBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+ flags = fuzz.binaryDecorator.linkerFlags(ctx, flags)
+ return flags
+}
+
+func (fuzz *fuzzBinary) install(ctx ModuleContext, file android.Path) {
+ fuzz.binaryDecorator.baseInstaller.dir = "fuzz"
+ fuzz.binaryDecorator.baseInstaller.dir64 = "fuzz"
+ fuzz.binaryDecorator.baseInstaller.install(ctx, file)
+}
+
+func NewFuzz(hod android.HostOrDeviceSupported) *Module {
+ module, binary := NewBinary(hod)
+
+ // TODO(mitchp): The toolchain does not currently export the x86 (32-bit)
+ // variant of libFuzzer for host. There is no way to only disable the host
+ // 32-bit variant, so we specify cc_fuzz targets as 64-bit only. This doesn't
+ // hurt anyone, as cc_fuzz is mostly for experimental targets as of this
+ // moment.
+ module.multilib = "64"
+
+ binary.baseInstaller = NewFuzzInstaller()
+ module.sanitize.SetSanitizer(fuzzer, true)
+
+ fuzz := &fuzzBinary{
+ binaryDecorator: binary,
+ baseCompiler: NewBaseCompiler(),
+ }
+ module.compiler = fuzz
+ module.linker = fuzz
+ module.installer = fuzz
+
+ // The fuzzer runtime is not present for darwin host modules, disable cc_fuzz modules when targeting darwin.
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ disableDarwinAndLinuxBionic := struct {
+ Target struct {
+ Darwin struct {
+ Enabled *bool
+ }
+ Linux_bionic struct {
+ Enabled *bool
+ }
+ }
+ }{}
+ disableDarwinAndLinuxBionic.Target.Darwin.Enabled = BoolPtr(false)
+ disableDarwinAndLinuxBionic.Target.Linux_bionic.Enabled = BoolPtr(false)
+ ctx.AppendProperties(&disableDarwinAndLinuxBionic)
+ })
+
+ return module
+}
diff --git a/cc/gen.go b/cc/gen.go
index 7516b28..42b0cbe 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -99,7 +99,7 @@
cmd.Text("BISON_PKGDATADIR=prebuilts/build-tools/common/bison").
FlagWithInput("M4=", ctx.Config().PrebuiltBuildTool(ctx, "m4")).
- Tool(ctx.Config().PrebuiltBuildTool(ctx, "bison")).
+ PrebuiltBuildTool(ctx, "bison").
Flag("-d").
Flags(flags).
FlagWithOutput("--defines=", headerFile).
@@ -120,8 +120,13 @@
headerBn := outDir.Join(ctx, aidlPackage, "Bn"+shortName+".h")
headerBp := outDir.Join(ctx, aidlPackage, "Bp"+shortName+".h")
+ baseDir := strings.TrimSuffix(aidlFile.String(), aidlFile.Rel())
+ if baseDir != "" {
+ aidlFlags += " -I" + baseDir
+ }
+
cmd := rule.Command()
- cmd.Tool(ctx.Config().HostToolPath(ctx, "aidl-cpp")).
+ cmd.BuiltTool(ctx, "aidl-cpp").
FlagWithDepFile("-d", depFile).
Flag("--ninja").
Flag(aidlFlags).
@@ -238,7 +243,7 @@
depFile := android.GenPathWithExt(ctx, "aidl", srcFile, "cpp.d")
srcFiles[i] = cppFile
deps = append(deps, genAidl(ctx, aidlRule, srcFile, cppFile, depFile, buildFlags.aidlFlags)...)
- case ".rs", ".fs":
+ case ".rscript", ".fs":
cppFile := rsGeneratedCppFile(ctx, srcFile)
rsFiles = append(rsFiles, srcFiles[i])
srcFiles[i] = cppFile
diff --git a/cc/gen_test.go b/cc/gen_test.go
index e4219d9..da3b4e8 100644
--- a/cc/gen_test.go
+++ b/cc/gen_test.go
@@ -16,6 +16,7 @@
import (
"path/filepath"
+ "strings"
"testing"
)
@@ -42,7 +43,8 @@
ctx := testCc(t, `
filegroup {
name: "fg",
- srcs: ["b.aidl"],
+ srcs: ["sub/c.aidl"],
+ path: "sub",
}
cc_library_shared {
@@ -59,6 +61,12 @@
if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.GlobalFlags) {
t.Errorf("missing aidl includes in global flags")
}
+
+ aidlCommand := aidl.RuleParams.Command
+ if !strings.Contains(aidlCommand, "-Isub") {
+ t.Errorf("aidl command for c.aidl should contain \"-Isub\", but was %q", aidlCommand)
+ }
+
})
}
diff --git a/cc/installer.go b/cc/installer.go
index cb261b7..a52ccf1 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -66,9 +66,12 @@
if ctx.toolchain().Is64Bit() && installer.dir64 != "" {
dir = installer.dir64
}
- if (!ctx.Host() && !ctx.Arch().Native) || ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+ if !ctx.Host() && !ctx.Arch().Native {
dir = filepath.Join(dir, ctx.Arch().ArchType.String())
}
+ if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+ dir = filepath.Join(dir, ctx.Target().NativeBridgeRelativePath)
+ }
if installer.location == InstallInData && ctx.useVndk() {
dir = filepath.Join(dir, "vendor")
}
diff --git a/cc/library.go b/cc/library.go
index cb31979..0869727 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -33,7 +33,7 @@
type StaticSharedLibraryProperties struct {
Srcs []string `android:"path,arch_variant"`
- Cflags []string `android:"path,arch_variant"`
+ Cflags []string `android:"arch_variant"`
Enabled *bool `android:"arch_variant"`
Whole_static_libs []string `android:"arch_variant"`
@@ -110,6 +110,9 @@
// Symbol tags that should be ignored from the symbol file
Exclude_symbol_tags []string
}
+
+ // Order symbols in .bss section by their sizes. Only useful for shared libraries.
+ Sort_bss_symbols_by_size *bool
}
type LibraryMutatedProperties struct {
@@ -429,11 +432,46 @@
return flags
}
-func (library *libraryDecorator) shouldCreateVndkSourceAbiDump(ctx ModuleContext) bool {
- if library.Properties.Header_abi_checker.Enabled != nil {
- return Bool(library.Properties.Header_abi_checker.Enabled)
+// Returns a string that represents the class of the ABI dump.
+// Returns an empty string if ABI check is disabled for this library.
+func (library *libraryDecorator) classifySourceAbiDump(ctx ModuleContext) string {
+ enabled := library.Properties.Header_abi_checker.Enabled
+ if enabled != nil && !Bool(enabled) {
+ return ""
}
- return ctx.shouldCreateVndkSourceAbiDump(ctx.Config())
+ // Return NDK if the library is both NDK and LLNDK.
+ if ctx.isNdk() {
+ return "NDK"
+ }
+ if ctx.isLlndkPublic(ctx.Config()) {
+ return "LLNDK"
+ }
+ if ctx.useVndk() && ctx.isVndk() && !ctx.isVndkPrivate(ctx.Config()) {
+ if ctx.isVndkSp() {
+ if ctx.isVndkExt() {
+ return "VNDK-SP-ext"
+ } else {
+ return "VNDK-SP"
+ }
+ } else {
+ if ctx.isVndkExt() {
+ return "VNDK-ext"
+ } else {
+ return "VNDK-core"
+ }
+ }
+ }
+ if enabled != nil && Bool(enabled) {
+ return "PLATFORM"
+ }
+ return ""
+}
+
+func (library *libraryDecorator) shouldCreateSourceAbiDump(ctx ModuleContext) bool {
+ if !ctx.shouldCreateSourceAbiDump() {
+ return false
+ }
+ return library.classifySourceAbiDump(ctx) != ""
}
func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
@@ -455,7 +493,7 @@
}
return Objects{}
}
- if library.shouldCreateVndkSourceAbiDump(ctx) || library.sabi.Properties.CreateSAbiDumps {
+ if library.shouldCreateSourceAbiDump(ctx) || library.sabi.Properties.CreateSAbiDumps {
exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
var SourceAbiFlags []string
for _, dir := range exportIncludeDirs.Strings() {
@@ -491,6 +529,7 @@
type libraryInterface interface {
getWholeStaticMissingDeps() []string
static() bool
+ shared() bool
objs() Objects
reuseObjs() (Objects, exportedFlagsProducer)
toc() android.OptionalPath
@@ -758,6 +797,18 @@
linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
linkerDeps = append(linkerDeps, objs.tidyFiles...)
+ if Bool(library.Properties.Sort_bss_symbols_by_size) {
+ unsortedOutputFile := android.PathForModuleOut(ctx, "unsorted", fileName)
+ TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
+ deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
+ linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, unsortedOutputFile, implicitOutputs)
+
+ symbolOrderingFile := android.PathForModuleOut(ctx, "unsorted", fileName+".symbol_order")
+ symbolOrderingFlag := library.baseLinker.sortBssSymbolsBySize(ctx, unsortedOutputFile, symbolOrderingFile, builderFlags)
+ builderFlags.ldFlags += " " + symbolOrderingFlag
+ linkerDeps = append(linkerDeps, symbolOrderingFile)
+ }
+
TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs)
@@ -785,11 +836,16 @@
return true
}
-func getRefAbiDumpFile(ctx ModuleContext, vndkVersion, fileName string) android.Path {
- isLlndkOrNdk := inList(ctx.baseModuleName(), *llndkLibraries(ctx.Config())) || inList(ctx.baseModuleName(), ndkMigratedLibs)
+func (library *libraryDecorator) coverageOutputFilePath() android.OptionalPath {
+ return library.coverageOutputFile
+}
- refAbiDumpTextFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isLlndkOrNdk, ctx.isVndk(), false)
- refAbiDumpGzipFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isLlndkOrNdk, ctx.isVndk(), true)
+func getRefAbiDumpFile(ctx ModuleContext, vndkVersion, fileName string) android.Path {
+ isNdk := ctx.isNdk()
+ isLlndkOrVndk := ctx.isLlndkPublic(ctx.Config()) || ctx.isVndk()
+
+ refAbiDumpTextFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isLlndkOrVndk, false)
+ refAbiDumpGzipFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isLlndkOrVndk, true)
if refAbiDumpTextFile.Valid() {
if refAbiDumpGzipFile.Valid() {
@@ -807,7 +863,7 @@
}
func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
- if library.shouldCreateVndkSourceAbiDump(ctx) {
+ if library.shouldCreateSourceAbiDump(ctx) {
vndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
if ver := ctx.DeviceConfig().VndkVersion(); ver != "" && ver != "current" {
vndkVersion = ver
@@ -827,6 +883,8 @@
library.Properties.Header_abi_checker.Exclude_symbol_versions,
library.Properties.Header_abi_checker.Exclude_symbol_tags)
+ addLsdumpPath(library.classifySourceAbiDump(ctx) + ":" + library.sAbiOutputFile.String())
+
refAbiDumpFile := getRefAbiDumpFile(ctx, vndkVersion, fileName)
if refAbiDumpFile != nil {
library.sAbiDiff = SourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
@@ -959,7 +1017,11 @@
}
library.baseInstaller.subDir = "bootstrap"
}
+ } else if android.DirectlyInAnyApex(ctx, ctx.ModuleName()) && ctx.isLlndk(ctx.Config()) && !isBionic(ctx.baseModuleName()) {
+ // Skip installing LLNDK (non-bionic) libraries moved to APEX.
+ ctx.Module().SkipInstall()
}
+
library.baseInstaller.install(ctx, file)
}
diff --git a/cc/linker.go b/cc/linker.go
index dda2fcb..563ad04 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -125,6 +125,10 @@
// variant of the C/C++ module.
Shared_libs []string
+ // list of static libs that only should be used to build the recovery
+ // variant of the C/C++ module.
+ Static_libs []string
+
// list of shared libs that should not be used to build
// the recovery variant of the C/C++ module.
Exclude_shared_libs []string
@@ -147,9 +151,6 @@
// local file name to pass to the linker as --version_script
Version_script *string `android:"path,arch_variant"`
-
- // Local file name to pass to the linker as --symbol-ordering-file
- Symbol_ordering_file *string `android:"arch_variant"`
}
func NewBaseLinker(sanitize *sanitize) *baseLinker {
@@ -214,6 +215,7 @@
deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Target.Recovery.Shared_libs...)
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 = append(deps.StaticLibs, linker.Properties.Target.Recovery.Static_libs...)
deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
deps.HeaderLibs = removeListFromList(deps.HeaderLibs, linker.Properties.Target.Recovery.Exclude_header_libs)
deps.ReexportHeaderLibHeaders = removeListFromList(deps.ReexportHeaderLibHeaders, linker.Properties.Target.Recovery.Exclude_header_libs)
@@ -381,6 +383,10 @@
flags.LdFlags = append(flags.LdFlags, "-lfdio", "-lzircon")
}
+ if ctx.toolchain().LibclangRuntimeLibraryArch() != "" {
+ flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs="+config.BuiltinsRuntimeLibrary(ctx.toolchain())+".a")
+ }
+
CheckBadLinkerFlags(ctx, "ldflags", linker.Properties.Ldflags)
flags.LdFlags = append(flags.LdFlags, proptools.NinjaAndShellEscapeList(linker.Properties.Ldflags)...)
@@ -442,16 +448,6 @@
}
}
- if !linker.dynamicProperties.BuildStubs {
- symbolOrderingFile := ctx.ExpandOptionalSource(
- linker.Properties.Symbol_ordering_file, "Symbol_ordering_file")
- if symbolOrderingFile.Valid() {
- flags.LdFlags = append(flags.LdFlags,
- "-Wl,--symbol-ordering-file,"+symbolOrderingFile.String())
- flags.LdFlagsDeps = append(flags.LdFlagsDeps, symbolOrderingFile.Path())
- }
- }
-
return flags
}
@@ -483,7 +479,33 @@
Input: in,
Output: out,
Args: map[string]string{
- "buildNumberFromFile": ctx.Config().BuildNumberFromFile(),
+ "buildNumberFromFile": proptools.NinjaEscape(ctx.Config().BuildNumberFromFile()),
},
})
}
+
+// Rule to generate .bss symbol ordering file.
+
+var (
+ _ = pctx.SourcePathVariable("genSortedBssSymbolsPath", "build/soong/scripts/gen_sorted_bss_symbols.sh")
+ gen_sorted_bss_symbols = pctx.AndroidStaticRule("gen_sorted_bss_symbols",
+ blueprint.RuleParams{
+ Command: "CROSS_COMPILE=$crossCompile $genSortedBssSymbolsPath ${in} ${out}",
+ CommandDeps: []string{"$genSortedBssSymbolsPath"},
+ },
+ "crossCompile")
+)
+
+func (linker *baseLinker) sortBssSymbolsBySize(ctx ModuleContext, in android.Path, symbolOrderingFile android.ModuleOutPath, flags builderFlags) string {
+ crossCompile := gccCmd(flags.toolchain, "")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: gen_sorted_bss_symbols,
+ Description: "generate bss symbol order " + symbolOrderingFile.Base(),
+ Output: symbolOrderingFile,
+ Input: in,
+ Args: map[string]string{
+ "crossCompile": crossCompile,
+ },
+ })
+ return "-Wl,--symbol-ordering-file," + symbolOrderingFile.String()
+}
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 8290103..4d59975 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -232,7 +232,7 @@
&library.MutatedProperties,
&library.flagExporter.Properties)
- android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
+ module.Init()
return module
}
diff --git a/cc/lto.go b/cc/lto.go
index 1084869..431d70d 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -80,6 +80,12 @@
}
func (lto *lto) flags(ctx BaseModuleContext, flags Flags) Flags {
+ // TODO(b/131771163): Disable LTO when using explicit fuzzing configurations.
+ // LTO breaks fuzzer builds.
+ if inList("-fsanitize=fuzzer-no-link", flags.CFlags) {
+ return flags
+ }
+
if lto.LTO() {
var ltoFlag string
if Bool(lto.Properties.Lto.Thin) {
diff --git a/cc/makevars.go b/cc/makevars.go
index 78a32c8..f9c58b9 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -63,6 +63,13 @@
}
}
+type notOnHostContext struct {
+}
+
+func (c *notOnHostContext) Host() bool {
+ return false
+}
+
func makeVarsProvider(ctx android.MakeVarsContext) {
vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
@@ -102,13 +109,23 @@
// Therefore, by removing the library here, we cause it to only be installed if libc
// depends on it.
installedLlndkLibraries := []string{}
+
+ // Make uses LLNDK_MOVED_TO_APEX_LIBRARIES to avoid installing libraries on /system if
+ // they been moved to an apex.
+ movedToApexLlndkLibraries := []string{}
for _, lib := range *llndkLibraries(ctx.Config()) {
if strings.HasPrefix(lib, "libclang_rt.hwasan-") {
continue
}
installedLlndkLibraries = append(installedLlndkLibraries, lib)
+
+ // Skip bionic libs, they are handled in different manner
+ if android.DirectlyInAnyApex(¬OnHostContext{}, lib) && !isBionic(lib) {
+ movedToApexLlndkLibraries = append(movedToApexLlndkLibraries, lib)
+ }
}
ctx.Strict("LLNDK_LIBRARIES", strings.Join(installedLlndkLibraries, " "))
+ ctx.Strict("LLNDK_MOVED_TO_APEX_LIBRARIES", strings.Join(movedToApexLlndkLibraries, " "))
ctx.Strict("VNDK_PRIVATE_LIBRARIES", strings.Join(*vndkPrivateLibraries(ctx.Config()), " "))
ctx.Strict("VNDK_USING_CORE_VARIANT_LIBRARIES", strings.Join(*vndkUsingCoreVariantLibraries(ctx.Config()), " "))
@@ -138,7 +155,6 @@
ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS", strings.Join(asanCflags, " "))
ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_LDFLAGS", strings.Join(asanLdflags, " "))
- ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_STATIC_LIBRARIES", strings.Join(asanLibs, " "))
ctx.Strict("HWADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS", strings.Join(hwasanCflags, " "))
ctx.Strict("HWADDRESS_SANITIZER_GLOBAL_OPTIONS", strings.Join(hwasanGlobalOptions, ","))
@@ -250,9 +266,9 @@
}
clangPrefix := secondPrefix + "CLANG_" + typePrefix
- clangExtras := "-target " + toolchain.ClangTriple()
- clangExtras += " -B" + config.ToolPath(toolchain)
+ clangExtras := "-B" + config.ToolPath(toolchain)
+ ctx.Strict(clangPrefix+"TRIPLE", toolchain.ClangTriple())
ctx.Strict(clangPrefix+"GLOBAL_CFLAGS", strings.Join([]string{
toolchain.ClangCflags(),
"${config.CommonClangGlobalCflags}",
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 969cb3f..3747b41 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -54,6 +54,7 @@
"mediandk",
"nativewindow",
"m",
+ "neuralnetworks",
"OpenMAXAL",
"OpenSLES",
"stdc++",
diff --git a/cc/object.go b/cc/object.go
index 50ecc38..15272eb 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -66,6 +66,7 @@
deps.LateSharedLibs = append(deps.LateSharedLibs, "libc")
}
+ deps.HeaderLibs = append(deps.HeaderLibs, object.Properties.Header_libs...)
deps.ObjFiles = append(deps.ObjFiles, object.Properties.Objs...)
return deps
}
@@ -118,3 +119,7 @@
func (object *objectLinker) nativeCoverage() bool {
return true
}
+
+func (object *objectLinker) coverageOutputFilePath() android.OptionalPath {
+ return android.OptionalPath{}
+}
diff --git a/cc/rs.go b/cc/rs.go
index fbc6bfb..5951edb 100644
--- a/cc/rs.go
+++ b/cc/rs.go
@@ -51,7 +51,7 @@
"depFiles", "outDir", "rsFlags", "stampFile")
)
-// Takes a path to a .rs or .fs file, and returns a path to a generated ScriptC_*.cpp file
+// Takes a path to a .rscript or .fs file, and returns a path to a generated ScriptC_*.cpp file
// This has to match the logic in llvm-rs-cc in DetermineOutputFile.
func rsGeneratedCppFile(ctx android.ModuleContext, rsFile android.Path) android.WritablePath {
fileName := strings.TrimSuffix(rsFile.Base(), rsFile.Ext())
diff --git a/cc/sabi.go b/cc/sabi.go
index ae7b31d..0999151 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -94,3 +94,9 @@
})
}
}
+
+func addLsdumpPath(lsdumpPath string) {
+ sabiLock.Lock()
+ lsdumpPaths = append(lsdumpPaths, lsdumpPath)
+ sabiLock.Unlock()
+}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 0eb9a74..4687782 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -31,9 +31,11 @@
// understand also need to be added to ClangLibToolingUnknownCflags in
// cc/config/clang.go
- asanCflags = []string{"-fno-omit-frame-pointer"}
+ asanCflags = []string{
+ "-fno-omit-frame-pointer",
+ "-fno-experimental-new-pass-manager",
+ }
asanLdflags = []string{"-Wl,-u,__asan_preinit"}
- asanLibs = []string{"libasan"}
// TODO(pcc): Stop passing -hwasan-allow-ifunc here once it has been made
// the default.
@@ -125,6 +127,10 @@
}
}
+func (t sanitizerType) incompatibleWithCfi() bool {
+ return t == asan || t == fuzzer || t == hwasan
+}
+
type SanitizeProperties struct {
// enable AddressSanitizer, ThreadSanitizer, or UndefinedBehaviorSanitizer
Sanitize struct {
@@ -391,7 +397,6 @@
if ctx.Device() {
if Bool(sanitize.Properties.Sanitize.Address) {
- deps.StaticLibs = append(deps.StaticLibs, asanLibs...)
// Compiling asan and having libc_scudo in the same
// executable will cause the executable to crash.
// Remove libc_scudo since it is only used to override
@@ -467,7 +472,9 @@
// TODO(b/131771163): LTO and Fuzzer support is mutually incompatible.
_, flags.LdFlags = removeFromList("-flto", flags.LdFlags)
+ _, flags.CFlags = removeFromList("-flto", flags.CFlags)
flags.LdFlags = append(flags.LdFlags, "-fno-lto")
+ flags.CFlags = append(flags.CFlags, "-fno-lto")
// TODO(b/133876586): Experimental PM breaks sanitizer coverage.
_, flags.CFlags = removeFromList("-fexperimental-new-pass-manager", flags.CFlags)
@@ -557,16 +564,18 @@
}
func (sanitize *sanitize) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
- // Add a suffix for CFI-enabled static libraries to allow surfacing both to make without a
- // name conflict.
- if ret.Class == "STATIC_LIBRARIES" && Bool(sanitize.Properties.Sanitize.Cfi) {
- ret.SubName += ".cfi"
- }
- if ret.Class == "STATIC_LIBRARIES" && Bool(sanitize.Properties.Sanitize.Hwaddress) {
- ret.SubName += ".hwasan"
- }
- if ret.Class == "STATIC_LIBRARIES" && Bool(sanitize.Properties.Sanitize.Scs) {
- ret.SubName += ".scs"
+ // Add a suffix for cfi/hwasan/scs-enabled static/header libraries to allow surfacing
+ // both the sanitized and non-sanitized variants to make without a name conflict.
+ if ret.Class == "STATIC_LIBRARIES" || ret.Class == "HEADER_LIBRARIES" {
+ if Bool(sanitize.Properties.Sanitize.Cfi) {
+ ret.SubName += ".cfi"
+ }
+ if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+ ret.SubName += ".hwasan"
+ }
+ if Bool(sanitize.Properties.Sanitize.Scs) {
+ ret.SubName += ".scs"
+ }
}
}
@@ -871,7 +880,7 @@
{Mutator: "image", Variation: c.imageVariation()},
{Mutator: "arch", Variation: mctx.Target().String()},
}, staticDepTag, runtimeLibrary)
- } else if !c.static() {
+ } else if !c.static() && !c.header() {
// dynamic executable and shared libs get shared runtime libs
mctx.AddFarVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "shared"},
@@ -900,108 +909,69 @@
modules := mctx.CreateVariations(t.variationName())
modules[0].(*Module).sanitize.SetSanitizer(t, true)
} else if c.sanitize.isSanitizerEnabled(t) || c.sanitize.Properties.SanitizeDep {
- // Save original sanitizer status before we assign values to variant
- // 0 as that overwrites the original.
isSanitizerEnabled := c.sanitize.isSanitizerEnabled(t)
+ if mctx.Device() && t.incompatibleWithCfi() {
+ // TODO: Make sure that cfi mutator runs "after" any of the sanitizers that
+ // are incompatible with cfi
+ c.sanitize.SetSanitizer(cfi, false)
+ }
+ if c.static() || c.header() || t == asan || t == fuzzer {
+ // Static and header libs are split into non-sanitized and sanitized variants.
+ // Shared libs are not split. However, for asan and fuzzer, we split even for shared
+ // libs because a library sanitized for asan/fuzzer can't be linked from a library
+ // that isn't sanitized for asan/fuzzer.
+ //
+ // Note for defaultVariation: since we don't split for shared libs but for static/header
+ // libs, it is possible for the sanitized variant of a static/header lib to depend
+ // on non-sanitized variant of a shared lib. Such unfulfilled variation causes an
+ // error when the module is split. defaultVariation is the name of the variation that
+ // will be used when such a dangling dependency occurs during the split of the current
+ // module. By setting it to the name of the sanitized variation, the dangling dependency
+ // is redirected to the sanitized variant of the dependent module.
+ defaultVariation := t.variationName()
+ mctx.SetDefaultDependencyVariation(&defaultVariation)
+ modules := mctx.CreateVariations("", t.variationName())
+ modules[0].(*Module).sanitize.SetSanitizer(t, false)
+ modules[1].(*Module).sanitize.SetSanitizer(t, true)
+ modules[0].(*Module).sanitize.Properties.SanitizeDep = false
+ modules[1].(*Module).sanitize.Properties.SanitizeDep = false
- modules := mctx.CreateVariations("", t.variationName())
- modules[0].(*Module).sanitize.SetSanitizer(t, false)
- modules[1].(*Module).sanitize.SetSanitizer(t, true)
+ // For cfi/scs/hwasan, we can export both sanitized and un-sanitized variants
+ // to Make, because the sanitized version has a different suffix in name.
+ // For other types of sanitizers, suppress the variation that is disabled.
+ if t != cfi && t != scs && t != hwasan {
+ if isSanitizerEnabled {
+ modules[0].(*Module).Properties.PreventInstall = true
+ modules[0].(*Module).Properties.HideFromMake = true
+ } else {
+ modules[1].(*Module).Properties.PreventInstall = true
+ modules[1].(*Module).Properties.HideFromMake = true
+ }
+ }
- modules[0].(*Module).sanitize.Properties.SanitizeDep = false
- modules[1].(*Module).sanitize.Properties.SanitizeDep = false
-
- // We don't need both variants active for anything but CFI-enabled
- // target static libraries, so suppress the appropriate variant in
- // all other cases.
- if t == cfi {
+ // Export the static lib name to make
if c.static() {
- if !mctx.Device() {
- if isSanitizerEnabled {
- modules[0].(*Module).Properties.PreventInstall = true
- modules[0].(*Module).Properties.HideFromMake = true
+ if t == cfi {
+ appendStringSync(c.Name(), cfiStaticLibs(mctx.Config()), &cfiStaticLibsMutex)
+ } else if t == hwasan {
+ if c.useVndk() {
+ appendStringSync(c.Name(), hwasanVendorStaticLibs(mctx.Config()),
+ &hwasanStaticLibsMutex)
} else {
- modules[1].(*Module).Properties.PreventInstall = true
- modules[1].(*Module).Properties.HideFromMake = true
+ appendStringSync(c.Name(), hwasanStaticLibs(mctx.Config()),
+ &hwasanStaticLibsMutex)
}
- } else {
- cfiStaticLibs := cfiStaticLibs(mctx.Config())
+ }
+ }
+ } else {
+ // Shared libs are not split. Only the sanitized variant is created.
+ modules := mctx.CreateVariations(t.variationName())
+ modules[0].(*Module).sanitize.SetSanitizer(t, true)
+ modules[0].(*Module).sanitize.Properties.SanitizeDep = false
- cfiStaticLibsMutex.Lock()
- *cfiStaticLibs = append(*cfiStaticLibs, c.Name())
- cfiStaticLibsMutex.Unlock()
- }
- } else {
- modules[0].(*Module).Properties.PreventInstall = true
- modules[0].(*Module).Properties.HideFromMake = true
- }
- } else if t == asan {
- if mctx.Device() {
- // CFI and ASAN are currently mutually exclusive so disable
- // CFI if this is an ASAN variant.
- modules[1].(*Module).sanitize.Properties.InSanitizerDir = true
- modules[1].(*Module).sanitize.SetSanitizer(cfi, false)
- }
- if isSanitizerEnabled {
- modules[0].(*Module).Properties.PreventInstall = true
- modules[0].(*Module).Properties.HideFromMake = true
- } else {
- modules[1].(*Module).Properties.PreventInstall = true
- modules[1].(*Module).Properties.HideFromMake = true
- }
- } else if t == scs {
- // We don't currently link any static libraries built with make into
- // libraries built with SCS, so we don't need logic for propagating
- // SCSness of dependencies into make.
- if !c.static() {
- if isSanitizerEnabled {
- modules[0].(*Module).Properties.PreventInstall = true
- modules[0].(*Module).Properties.HideFromMake = true
- } else {
- modules[1].(*Module).Properties.PreventInstall = true
- modules[1].(*Module).Properties.HideFromMake = true
- }
- }
- } else if t == fuzzer {
- // TODO(b/131771163): CFI and fuzzer support are mutually incompatible
- // as CFI pulls in LTO.
- if mctx.Device() {
- modules[1].(*Module).sanitize.SetSanitizer(cfi, false)
- }
- if isSanitizerEnabled {
- modules[0].(*Module).Properties.PreventInstall = true
- modules[0].(*Module).Properties.HideFromMake = true
- } else {
- modules[1].(*Module).Properties.PreventInstall = true
- modules[1].(*Module).Properties.HideFromMake = true
- }
- } else if t == hwasan {
- if mctx.Device() {
- // CFI and HWASAN are currently mutually exclusive so disable
- // CFI if this is an HWASAN variant.
- modules[1].(*Module).sanitize.SetSanitizer(cfi, false)
- }
-
- if c.static() {
- if c.useVndk() {
- hwasanVendorStaticLibs := hwasanVendorStaticLibs(mctx.Config())
- hwasanStaticLibsMutex.Lock()
- *hwasanVendorStaticLibs = append(*hwasanVendorStaticLibs, c.Name())
- hwasanStaticLibsMutex.Unlock()
- } else {
- hwasanStaticLibs := hwasanStaticLibs(mctx.Config())
- hwasanStaticLibsMutex.Lock()
- *hwasanStaticLibs = append(*hwasanStaticLibs, c.Name())
- hwasanStaticLibsMutex.Unlock()
- }
- } else {
- if isSanitizerEnabled {
- modules[0].(*Module).Properties.PreventInstall = true
- modules[0].(*Module).Properties.HideFromMake = true
- } else {
- modules[1].(*Module).Properties.PreventInstall = true
- modules[1].(*Module).Properties.HideFromMake = true
- }
+ // locate the asan libraries under /data/asan
+ if mctx.Device() && t == asan && isSanitizerEnabled {
+ modules[0].(*Module).sanitize.Properties.InSanitizerDir = true
}
}
}
@@ -1037,6 +1007,12 @@
}).(*[]string)
}
+func appendStringSync(item string, list *[]string, mutex *sync.Mutex) {
+ mutex.Lock()
+ *list = append(*list, item)
+ mutex.Unlock()
+}
+
func enableMinimalRuntime(sanitize *sanitize) bool {
if !Bool(sanitize.Properties.Sanitize.Address) &&
!Bool(sanitize.Properties.Sanitize.Hwaddress) &&
diff --git a/cc/stl.go b/cc/stl.go
index 1a5dd79..d7feb6f 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -161,6 +161,15 @@
} else {
deps.StaticLibs = append(deps.StaticLibs, stl.Properties.SelectedStl)
}
+ if ctx.Device() && !ctx.useSdk() {
+ // __cxa_demangle is not a part of libc++.so on the device since
+ // it's large and most processes don't need it. Statically link
+ // libc++demangle into every process so that users still have it if
+ // needed, but the linker won't include this unless it is actually
+ // called.
+ // http://b/138245375
+ deps.StaticLibs = append(deps.StaticLibs, "libc++demangle")
+ }
if ctx.toolchain().Bionic() {
if ctx.Arch().ArchType == android.Arm {
deps.StaticLibs = append(deps.StaticLibs, "libunwind_llvm")
diff --git a/cc/test.go b/cc/test.go
index c735fd9..aff8ba5 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -121,7 +121,9 @@
type testPerSrc interface {
testPerSrc() bool
srcs() []string
+ isAllTestsVariation() bool
setSrc(string, string)
+ unsetSrc()
}
func (test *testBinary) testPerSrc() bool {
@@ -132,28 +134,55 @@
return test.baseCompiler.Properties.Srcs
}
+func (test *testBinary) isAllTestsVariation() bool {
+ stem := test.binaryDecorator.Properties.Stem
+ return stem != nil && *stem == ""
+}
+
func (test *testBinary) setSrc(name, src string) {
test.baseCompiler.Properties.Srcs = []string{src}
test.binaryDecorator.Properties.Stem = StringPtr(name)
}
+func (test *testBinary) unsetSrc() {
+ test.baseCompiler.Properties.Srcs = nil
+ test.binaryDecorator.Properties.Stem = StringPtr("")
+}
+
var _ testPerSrc = (*testBinary)(nil)
-func testPerSrcMutator(mctx android.BottomUpMutatorContext) {
+func TestPerSrcMutator(mctx android.BottomUpMutatorContext) {
if m, ok := mctx.Module().(*Module); ok {
if test, ok := m.linker.(testPerSrc); ok {
- if test.testPerSrc() && len(test.srcs()) > 0 {
+ numTests := len(test.srcs())
+ if test.testPerSrc() && numTests > 0 {
if duplicate, found := checkDuplicate(test.srcs()); found {
mctx.PropertyErrorf("srcs", "found a duplicate entry %q", duplicate)
return
}
- testNames := make([]string, len(test.srcs()))
+ testNames := make([]string, numTests)
for i, src := range test.srcs() {
testNames[i] = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src))
}
+ // In addition to creating one variation per test source file,
+ // create an additional "all tests" variation named "", and have it
+ // depends on all other test_per_src variations. This is useful to
+ // create subsequent dependencies of a given module on all
+ // test_per_src variations created above: by depending on
+ // variation "", that module will transitively depend on all the
+ // other test_per_src variations without the need to know their
+ // name or even their number.
+ testNames = append(testNames, "")
tests := mctx.CreateLocalVariations(testNames...)
+ all_tests := tests[numTests]
+ all_tests.(*Module).linker.(testPerSrc).unsetSrc()
+ // Prevent the "all tests" variation from being installable nor
+ // exporting to Make, as it won't create any output file.
+ all_tests.(*Module).Properties.PreventInstall = true
+ all_tests.(*Module).Properties.HideFromMake = true
for i, src := range test.srcs() {
tests[i].(*Module).linker.(testPerSrc).setSrc(testNames[i], src)
+ mctx.AddInterVariantDependency(testPerSrcDepTag, all_tests, tests[i])
}
}
}
@@ -210,6 +239,11 @@
deps.StaticLibs = append(deps.StaticLibs, "libgtest_main_ndk_c++", "libgtest_ndk_c++")
} else if BoolDefault(test.Properties.Isolated, false) {
deps.StaticLibs = append(deps.StaticLibs, "libgtest_isolated_main")
+ // The isolated library requires liblog, but adding it
+ // as a static library means unit tests cannot override
+ // liblog functions. Instead make it a shared library
+ // dependency.
+ deps.SharedLibs = append(deps.SharedLibs, "liblog")
} else {
deps.StaticLibs = append(deps.StaticLibs, "libgtest_main", "libgtest")
}
diff --git a/cc/testing.go b/cc/testing.go
index df7cb78..e69b774 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -63,6 +63,41 @@
}
toolchain_library {
+ name: "libclang_rt.fuzzer-arm-android",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libclang_rt.fuzzer-aarch64-android",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libclang_rt.fuzzer-i686-android",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libclang_rt.fuzzer-x86_64-android",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libclang_rt.fuzzer-x86_64",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ toolchain_library {
name: "libgcc",
vendor_available: true,
recovery_available: true,
@@ -132,6 +167,16 @@
},
}
cc_library {
+ name: "libc++demangle",
+ no_libcrt: true,
+ nocrt: true,
+ system_shared_libs: [],
+ stl: "none",
+ host_supported: false,
+ vendor_available: true,
+ recovery_available: true,
+ }
+ cc_library {
name: "libunwind_llvm",
no_libcrt: true,
nocrt: true,
@@ -196,6 +241,7 @@
ctx := android.NewTestArchContext()
ctx.RegisterModuleType("cc_binary", android.ModuleFactoryAdaptor(BinaryFactory))
ctx.RegisterModuleType("cc_binary_host", android.ModuleFactoryAdaptor(binaryHostFactory))
+ ctx.RegisterModuleType("cc_fuzz", android.ModuleFactoryAdaptor(FuzzFactory))
ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(LibraryFactory))
ctx.RegisterModuleType("cc_library_shared", android.ModuleFactoryAdaptor(LibrarySharedFactory))
ctx.RegisterModuleType("cc_library_static", android.ModuleFactoryAdaptor(LibraryStaticFactory))
@@ -229,6 +275,7 @@
"bar.c": nil,
"a.proto": nil,
"b.aidl": nil,
+ "sub/c.aidl": nil,
"my_include": nil,
"foo.map.txt": nil,
"liba.so": nil,
diff --git a/cc/util.go b/cc/util.go
index 2e1bb25..0d1b2f0 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -74,6 +74,7 @@
coverage: in.Coverage,
tidy: in.Tidy,
sAbiDump: in.SAbiDump,
+ emitXrefs: in.EmitXrefs,
systemIncludeFlags: strings.Join(in.SystemIncludeFlags, " "),
diff --git a/cc/vndk.go b/cc/vndk.go
index f9f3764..2a86f5b 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -15,8 +15,8 @@
package cc
import (
+ "encoding/json"
"errors"
- "fmt"
"path/filepath"
"sort"
"strings"
@@ -31,7 +31,7 @@
// declared as a VNDK or VNDK-SP module. The vendor variant
// will be installed in /system instead of /vendor partition.
//
- // `vendor_vailable` must be explicitly set to either true or
+ // `vendor_available` must be explicitly set to either true or
// false together with `vndk: {enabled: true}`.
Enabled *bool
@@ -206,16 +206,9 @@
modulePathsKey = android.NewOnceKey("modulePaths")
vndkSnapshotOutputsKey = android.NewOnceKey("vndkSnapshotOutputs")
vndkLibrariesLock sync.Mutex
-)
-type vndkSnapshotOutputPaths struct {
- configs android.Paths
- notices android.Paths
- vndkCoreLibs android.Paths
- vndkCoreLibs2nd android.Paths
- vndkSpLibs android.Paths
- vndkSpLibs2nd android.Paths
-}
+ headerExts = []string{".h", ".hh", ".hpp", ".hxx", ".h++", ".inl", ".inc", ".ipp", ".h.generic"}
+)
func vndkCoreLibraries(config android.Config) *[]string {
return config.Once(vndkCoreLibrariesKey, func() interface{} {
@@ -253,10 +246,10 @@
}).(map[string]string)
}
-func vndkSnapshotOutputs(config android.Config) *vndkSnapshotOutputPaths {
+func vndkSnapshotOutputs(config android.Config) *android.RuleBuilderInstalls {
return config.Once(vndkSnapshotOutputsKey, func() interface{} {
- return &vndkSnapshotOutputPaths{}
- }).(*vndkSnapshotOutputPaths)
+ return &android.RuleBuilderInstalls{}
+ }).(*android.RuleBuilderInstalls)
}
func processLlndkLibrary(mctx android.BottomUpMutatorContext, m *Module) {
@@ -357,13 +350,7 @@
android.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
android.RegisterMakeVarsProvider(pctx, func(ctx android.MakeVarsContext) {
outputs := vndkSnapshotOutputs(ctx.Config())
-
- ctx.Strict("SOONG_VNDK_SNAPSHOT_CONFIGS", strings.Join(outputs.configs.Strings(), " "))
- ctx.Strict("SOONG_VNDK_SNAPSHOT_NOTICES", strings.Join(outputs.notices.Strings(), " "))
- ctx.Strict("SOONG_VNDK_SNAPSHOT_CORE_LIBS", strings.Join(outputs.vndkCoreLibs.Strings(), " "))
- ctx.Strict("SOONG_VNDK_SNAPSHOT_SP_LIBS", strings.Join(outputs.vndkSpLibs.Strings(), " "))
- ctx.Strict("SOONG_VNDK_SNAPSHOT_CORE_LIBS_2ND", strings.Join(outputs.vndkCoreLibs2nd.Strings(), " "))
- ctx.Strict("SOONG_VNDK_SNAPSHOT_SP_LIBS_2ND", strings.Join(outputs.vndkSpLibs2nd.Strings(), " "))
+ ctx.Strict("SOONG_VNDK_SNAPSHOT_FILES", outputs.String())
})
}
@@ -373,26 +360,6 @@
type vndkSnapshotSingleton struct{}
-func installVndkSnapshotLib(ctx android.SingletonContext, name string, module *Module, dir string) android.Path {
- if !module.outputFile.Valid() {
- panic(fmt.Errorf("module %s has no outputFile\n", name))
- }
-
- out := android.PathForOutput(ctx, dir, name+".so")
-
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cp,
- Input: module.outputFile.Path(),
- Output: out,
- Description: "vndk snapshot " + dir + "/" + name + ".so",
- Args: map[string]string{
- "cpFlags": "-f -L",
- },
- })
-
- return out
-}
-
func (c *vndkSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
// BOARD_VNDK_VERSION must be set to 'current' in order to generate a VNDK snapshot.
if ctx.DeviceConfig().VndkVersion() != "current" {
@@ -411,30 +378,58 @@
snapshotDir := "vndk-snapshot"
- var vndkLibPath, vndkLib2ndPath string
+ vndkLibDir := make(map[android.ArchType]string)
- snapshotVariantPath := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
- if ctx.DeviceConfig().BinderBitness() == "32" {
- vndkLibPath = filepath.Join(snapshotVariantPath, "binder32", fmt.Sprintf(
- "arch-%s-%s", ctx.DeviceConfig().DeviceArch(), ctx.DeviceConfig().DeviceArchVariant()))
- vndkLib2ndPath = filepath.Join(snapshotVariantPath, "binder32", fmt.Sprintf(
- "arch-%s-%s", ctx.DeviceConfig().DeviceSecondaryArch(), ctx.DeviceConfig().DeviceSecondaryArchVariant()))
- } else {
- vndkLibPath = filepath.Join(snapshotVariantPath, fmt.Sprintf(
- "arch-%s-%s", ctx.DeviceConfig().DeviceArch(), ctx.DeviceConfig().DeviceArchVariant()))
- vndkLib2ndPath = filepath.Join(snapshotVariantPath, fmt.Sprintf(
- "arch-%s-%s", ctx.DeviceConfig().DeviceSecondaryArch(), ctx.DeviceConfig().DeviceSecondaryArchVariant()))
+ snapshotVariantDir := ctx.DeviceConfig().DeviceArch()
+ for _, target := range ctx.Config().Targets[android.Android] {
+ dir := snapshotVariantDir
+ if ctx.DeviceConfig().BinderBitness() == "32" {
+ dir = filepath.Join(dir, "binder32")
+ }
+ arch := "arch-" + target.Arch.ArchType.String()
+ if target.Arch.ArchVariant != "" {
+ arch += "-" + target.Arch.ArchVariant
+ }
+ dir = filepath.Join(dir, arch)
+ vndkLibDir[target.Arch.ArchType] = dir
}
-
- vndkCoreLibPath := filepath.Join(vndkLibPath, "shared", "vndk-core")
- vndkSpLibPath := filepath.Join(vndkLibPath, "shared", "vndk-sp")
- vndkCoreLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "vndk-core")
- vndkSpLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "vndk-sp")
- noticePath := filepath.Join(snapshotVariantPath, "NOTICE_FILES")
+ configsDir := filepath.Join(snapshotVariantDir, "configs")
+ noticeDir := filepath.Join(snapshotVariantDir, "NOTICE_FILES")
+ includeDir := filepath.Join(snapshotVariantDir, "include")
noticeBuilt := make(map[string]bool)
+ installSnapshotFileFromPath := func(path android.Path, out string) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: path,
+ Output: android.PathForOutput(ctx, snapshotDir, out),
+ Description: "vndk snapshot " + out,
+ Args: map[string]string{
+ "cpFlags": "-f -L",
+ },
+ })
+ *outputs = append(*outputs, android.RuleBuilderInstall{
+ From: android.PathForOutput(ctx, snapshotDir, out),
+ To: out,
+ })
+ }
+ installSnapshotFileFromContent := func(content, out string) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.WriteFile,
+ Output: android.PathForOutput(ctx, snapshotDir, out),
+ Description: "vndk snapshot " + out,
+ Args: map[string]string{
+ "content": content,
+ },
+ })
+ *outputs = append(*outputs, android.RuleBuilderInstall{
+ From: android.PathForOutput(ctx, snapshotDir, out),
+ To: out,
+ })
+ }
+
tryBuildNotice := func(m *Module) {
- name := ctx.ModuleName(m)
+ name := ctx.ModuleName(m) + ".so.txt"
if _, ok := noticeBuilt[name]; ok {
return
@@ -443,17 +438,7 @@
noticeBuilt[name] = true
if m.NoticeFile().Valid() {
- out := android.PathForOutput(ctx, noticePath, name+".so.txt")
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cp,
- Input: m.NoticeFile().Path(),
- Output: out,
- Description: "vndk snapshot notice " + name + ".so.txt",
- Args: map[string]string{
- "cpFlags": "-f -L",
- },
- })
- outputs.notices = append(outputs.notices, out)
+ installSnapshotFileFromPath(m.NoticeFile().Path(), filepath.Join(noticeDir, name))
}
}
@@ -461,84 +446,160 @@
vndkSpLibraries := vndkSpLibraries(ctx.Config())
vndkPrivateLibraries := vndkPrivateLibraries(ctx.Config())
+ var generatedHeaders android.Paths
+ includeDirs := make(map[string]bool)
+
+ type vndkSnapshotLibraryInterface interface {
+ exportedFlagsProducer
+ libraryInterface
+ }
+
+ var _ vndkSnapshotLibraryInterface = (*prebuiltLibraryLinker)(nil)
+ var _ vndkSnapshotLibraryInterface = (*libraryDecorator)(nil)
+
+ installVndkSnapshotLib := func(m *Module, l vndkSnapshotLibraryInterface, dir string) bool {
+ name := ctx.ModuleName(m)
+ libOut := filepath.Join(dir, name+".so")
+
+ installSnapshotFileFromPath(m.outputFile.Path(), libOut)
+ tryBuildNotice(m)
+
+ if ctx.Config().VndkSnapshotBuildArtifacts() {
+ prop := struct {
+ ExportedDirs []string `json:",omitempty"`
+ ExportedSystemDirs []string `json:",omitempty"`
+ ExportedFlags []string `json:",omitempty"`
+ RelativeInstallPath string `json:",omitempty"`
+ }{}
+ prop.ExportedFlags = l.exportedFlags()
+ prop.ExportedDirs = l.exportedDirs()
+ prop.ExportedSystemDirs = l.exportedSystemDirs()
+ prop.RelativeInstallPath = m.RelativeInstallPath()
+
+ propOut := libOut + ".json"
+
+ j, err := json.Marshal(prop)
+ if err != nil {
+ ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
+ return false
+ }
+
+ installSnapshotFileFromContent(string(j), propOut)
+ }
+ return true
+ }
+
+ isVndkSnapshotLibrary := func(m *Module) (i vndkSnapshotLibraryInterface, libDir string, isVndkSnapshotLib bool) {
+ if m.Target().NativeBridge == android.NativeBridgeEnabled {
+ return nil, "", false
+ }
+ if !m.useVndk() || !m.IsForPlatform() || !m.installable() {
+ return nil, "", false
+ }
+ l, ok := m.linker.(vndkSnapshotLibraryInterface)
+ if !ok || !l.shared() {
+ return nil, "", false
+ }
+ name := ctx.ModuleName(m)
+ if inList(name, *vndkCoreLibraries) {
+ return l, filepath.Join("shared", "vndk-core"), true
+ } else if inList(name, *vndkSpLibraries) {
+ return l, filepath.Join("shared", "vndk-sp"), true
+ } else {
+ return nil, "", false
+ }
+ }
+
ctx.VisitAllModules(func(module android.Module) {
m, ok := module.(*Module)
- if !ok || !m.Enabled() || !m.useVndk() || !m.installable() {
+ if !ok || !m.Enabled() {
return
}
- if m.Target().NativeBridge == android.NativeBridgeEnabled {
+ baseDir, ok := vndkLibDir[m.Target().Arch.ArchType]
+ if !ok {
return
}
- lib, is_lib := m.linker.(*libraryDecorator)
- prebuilt_lib, is_prebuilt_lib := m.linker.(*prebuiltLibraryLinker)
-
- if !(is_lib && lib.shared()) && !(is_prebuilt_lib && prebuilt_lib.shared()) {
+ l, libDir, ok := isVndkSnapshotLibrary(m)
+ if !ok {
return
}
- is_2nd := m.Target().Arch.ArchType != ctx.Config().DevicePrimaryArchType()
+ if !installVndkSnapshotLib(m, l, filepath.Join(baseDir, libDir)) {
+ return
+ }
- name := ctx.ModuleName(module)
+ generatedHeaders = append(generatedHeaders, l.exportedDeps()...)
+ for _, dir := range append(l.exportedDirs(), l.exportedSystemDirs()...) {
+ includeDirs[dir] = true
+ }
+ })
- if inList(name, *vndkCoreLibraries) {
- if is_2nd {
- out := installVndkSnapshotLib(ctx, name, m, vndkCoreLib2ndPath)
- outputs.vndkCoreLibs2nd = append(outputs.vndkCoreLibs2nd, out)
- } else {
- out := installVndkSnapshotLib(ctx, name, m, vndkCoreLibPath)
- outputs.vndkCoreLibs = append(outputs.vndkCoreLibs, out)
+ if ctx.Config().VndkSnapshotBuildArtifacts() {
+ headers := make(map[string]bool)
+
+ for _, dir := range android.SortedStringKeys(includeDirs) {
+ // workaround to determine if dir is under output directory
+ if strings.HasPrefix(dir, android.PathForOutput(ctx).String()) {
+ continue
}
- tryBuildNotice(m)
- } else if inList(name, *vndkSpLibraries) {
- if is_2nd {
- out := installVndkSnapshotLib(ctx, name, m, vndkSpLib2ndPath)
- outputs.vndkSpLibs2nd = append(outputs.vndkSpLibs2nd, out)
- } else {
- out := installVndkSnapshotLib(ctx, name, m, vndkSpLibPath)
- outputs.vndkSpLibs = append(outputs.vndkSpLibs, out)
+ exts := headerExts
+ // Glob all files under this special directory, because of C++ headers.
+ if strings.HasPrefix(dir, "external/libcxx/include") {
+ exts = []string{""}
}
- tryBuildNotice(m)
+ for _, ext := range exts {
+ glob, err := ctx.GlobWithDeps(dir+"/**/*"+ext, nil)
+ if err != nil {
+ ctx.Errorf("%#v\n", err)
+ return
+ }
+ for _, header := range glob {
+ if strings.HasSuffix(header, "/") {
+ continue
+ }
+ headers[header] = true
+ }
+ }
}
- })
- configsPath := filepath.Join(snapshotVariantPath, "configs")
- vndkCoreTxt := android.PathForOutput(ctx, configsPath, "vndkcore.libraries.txt")
- vndkPrivateTxt := android.PathForOutput(ctx, configsPath, "vndkprivate.libraries.txt")
- modulePathTxt := android.PathForOutput(ctx, configsPath, "module_paths.txt")
+ for _, header := range android.SortedStringKeys(headers) {
+ installSnapshotFileFromPath(android.PathForSource(ctx, header),
+ filepath.Join(includeDir, header))
+ }
- ctx.Build(pctx, android.BuildParams{
- Rule: android.WriteFile,
- Output: vndkCoreTxt,
- Description: "vndk snapshot vndkcore.libraries.txt",
- Args: map[string]string{
- "content": android.JoinWithSuffix(*vndkCoreLibraries, ".so", "\\n"),
- },
- })
- outputs.configs = append(outputs.configs, vndkCoreTxt)
+ isHeader := func(path string) bool {
+ for _, ext := range headerExts {
+ if strings.HasSuffix(path, ext) {
+ return true
+ }
+ }
+ return false
+ }
- ctx.Build(pctx, android.BuildParams{
- Rule: android.WriteFile,
- Output: vndkPrivateTxt,
- Description: "vndk snapshot vndkprivate.libraries.txt",
- Args: map[string]string{
- "content": android.JoinWithSuffix(*vndkPrivateLibraries, ".so", "\\n"),
- },
- })
- outputs.configs = append(outputs.configs, vndkPrivateTxt)
+ for _, path := range android.PathsToDirectorySortedPaths(android.FirstUniquePaths(generatedHeaders)) {
+ header := path.String()
+
+ if !isHeader(header) {
+ continue
+ }
+
+ installSnapshotFileFromPath(path, filepath.Join(includeDir, header))
+ }
+ }
+
+ installSnapshotFileFromContent(android.JoinWithSuffix(*vndkCoreLibraries, ".so", "\\n"),
+ filepath.Join(configsDir, "vndkcore.libraries.txt"))
+ installSnapshotFileFromContent(android.JoinWithSuffix(*vndkPrivateLibraries, ".so", "\\n"),
+ filepath.Join(configsDir, "vndkprivate.libraries.txt"))
var modulePathTxtBuilder strings.Builder
modulePaths := modulePaths(ctx.Config())
- var libs []string
- for lib := range modulePaths {
- libs = append(libs, lib)
- }
- sort.Strings(libs)
first := true
- for _, lib := range libs {
+ for _, lib := range android.SortedStringKeys(modulePaths) {
if first {
first = false
} else {
@@ -549,13 +610,6 @@
modulePathTxtBuilder.WriteString(modulePaths[lib])
}
- ctx.Build(pctx, android.BuildParams{
- Rule: android.WriteFile,
- Output: modulePathTxt,
- Description: "vndk snapshot module_paths.txt",
- Args: map[string]string{
- "content": modulePathTxtBuilder.String(),
- },
- })
- outputs.configs = append(outputs.configs, modulePathTxt)
+ installSnapshotFileFromContent(modulePathTxtBuilder.String(),
+ filepath.Join(configsDir, "module_paths.txt"))
}
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index 0ecf566..c8ff87f 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -61,6 +61,13 @@
// Prebuilt files for each arch.
Srcs []string `android:"arch_variant"`
+ // list of directories relative to the Blueprints file that will be added to the include
+ // path (using -isystem) for any module that links against this module.
+ Export_system_include_dirs []string `android:"arch_variant"`
+
+ // list of flags that will be used for any module that links against this module.
+ Export_flags []string `android:"arch_variant"`
+
// Check the prebuilt ELF files (e.g. DT_SONAME, DT_NEEDED, resolution of undefined symbols,
// etc).
Check_elf_files *bool
@@ -123,6 +130,9 @@
func (p *vndkPrebuiltLibraryDecorator) link(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
if len(p.properties.Srcs) > 0 && p.shared() {
+ p.libraryDecorator.exportIncludes(ctx)
+ p.libraryDecorator.reexportSystemDirs(p.properties.Export_system_include_dirs...)
+ p.libraryDecorator.reexportFlags(p.properties.Export_flags...)
// current VNDK prebuilts are only shared libs.
return p.singleSourcePath(ctx)
}
diff --git a/cmd/merge_zips/Android.bp b/cmd/merge_zips/Android.bp
index ab658fd..f70c86e 100644
--- a/cmd/merge_zips/Android.bp
+++ b/cmd/merge_zips/Android.bp
@@ -18,6 +18,7 @@
"android-archive-zip",
"blueprint-pathtools",
"soong-jar",
+ "soong-zip",
],
srcs: [
"merge_zips.go",
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index 68fe259..27179cb 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -30,8 +30,566 @@
"android/soong/jar"
"android/soong/third_party/zip"
+ soongZip "android/soong/zip"
)
+// Input zip: we can open it, close it, and obtain an array of entries
+type InputZip interface {
+ Name() string
+ Open() error
+ Close() error
+ Entries() []*zip.File
+ IsOpen() bool
+}
+
+// An entry that can be written to the output zip
+type ZipEntryContents interface {
+ String() string
+ IsDir() bool
+ CRC32() uint32
+ Size() uint64
+ WriteToZip(dest string, zw *zip.Writer) error
+}
+
+// a ZipEntryFromZip is a ZipEntryContents that pulls its content from another zip
+// identified by the input zip and the index of the entry in its entries array
+type ZipEntryFromZip struct {
+ inputZip InputZip
+ index int
+ name string
+ isDir bool
+ crc32 uint32
+ size uint64
+}
+
+func NewZipEntryFromZip(inputZip InputZip, entryIndex int) *ZipEntryFromZip {
+ fi := inputZip.Entries()[entryIndex]
+ newEntry := ZipEntryFromZip{inputZip: inputZip,
+ index: entryIndex,
+ name: fi.Name,
+ isDir: fi.FileInfo().IsDir(),
+ crc32: fi.CRC32,
+ size: fi.UncompressedSize64,
+ }
+ return &newEntry
+}
+
+func (ze ZipEntryFromZip) String() string {
+ return fmt.Sprintf("%s!%s", ze.inputZip.Name(), ze.name)
+}
+
+func (ze ZipEntryFromZip) IsDir() bool {
+ return ze.isDir
+}
+
+func (ze ZipEntryFromZip) CRC32() uint32 {
+ return ze.crc32
+}
+
+func (ze ZipEntryFromZip) Size() uint64 {
+ return ze.size
+}
+
+func (ze ZipEntryFromZip) WriteToZip(dest string, zw *zip.Writer) error {
+ if err := ze.inputZip.Open(); err != nil {
+ return err
+ }
+ return zw.CopyFrom(ze.inputZip.Entries()[ze.index], dest)
+}
+
+// a ZipEntryFromBuffer is a ZipEntryContents that pulls its content from a []byte
+type ZipEntryFromBuffer struct {
+ fh *zip.FileHeader
+ content []byte
+}
+
+func (be ZipEntryFromBuffer) String() string {
+ return "internal buffer"
+}
+
+func (be ZipEntryFromBuffer) IsDir() bool {
+ return be.fh.FileInfo().IsDir()
+}
+
+func (be ZipEntryFromBuffer) CRC32() uint32 {
+ return crc32.ChecksumIEEE(be.content)
+}
+
+func (be ZipEntryFromBuffer) Size() uint64 {
+ return uint64(len(be.content))
+}
+
+func (be ZipEntryFromBuffer) WriteToZip(dest string, zw *zip.Writer) error {
+ w, err := zw.CreateHeader(be.fh)
+ if err != nil {
+ return err
+ }
+
+ if !be.IsDir() {
+ _, err = w.Write(be.content)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// Processing state.
+type OutputZip struct {
+ outputWriter *zip.Writer
+ stripDirEntries bool
+ emulateJar bool
+ sortEntries bool
+ ignoreDuplicates bool
+ excludeDirs []string
+ excludeFiles []string
+ sourceByDest map[string]ZipEntryContents
+}
+
+func NewOutputZip(outputWriter *zip.Writer, sortEntries, emulateJar, stripDirEntries, ignoreDuplicates bool) *OutputZip {
+ return &OutputZip{
+ outputWriter: outputWriter,
+ stripDirEntries: stripDirEntries,
+ emulateJar: emulateJar,
+ sortEntries: sortEntries,
+ sourceByDest: make(map[string]ZipEntryContents, 0),
+ ignoreDuplicates: ignoreDuplicates,
+ }
+}
+
+func (oz *OutputZip) setExcludeDirs(excludeDirs []string) {
+ oz.excludeDirs = make([]string, len(excludeDirs))
+ for i, dir := range excludeDirs {
+ oz.excludeDirs[i] = filepath.Clean(dir)
+ }
+}
+
+func (oz *OutputZip) setExcludeFiles(excludeFiles []string) {
+ oz.excludeFiles = excludeFiles
+}
+
+// Adds an entry with given name whose source is given ZipEntryContents. Returns old ZipEntryContents
+// if entry with given name already exists.
+func (oz *OutputZip) addZipEntry(name string, source ZipEntryContents) (ZipEntryContents, error) {
+ if existingSource, exists := oz.sourceByDest[name]; exists {
+ return existingSource, nil
+ }
+ oz.sourceByDest[name] = source
+ // Delay writing an entry if entries need to be rearranged.
+ if oz.emulateJar || oz.sortEntries {
+ return nil, nil
+ }
+ return nil, source.WriteToZip(name, oz.outputWriter)
+}
+
+// Adds an entry for the manifest (META-INF/MANIFEST.MF from the given file
+func (oz *OutputZip) addManifest(manifestPath string) error {
+ if !oz.stripDirEntries {
+ if _, err := oz.addZipEntry(jar.MetaDir, ZipEntryFromBuffer{jar.MetaDirFileHeader(), nil}); err != nil {
+ return err
+ }
+ }
+ contents, err := ioutil.ReadFile(manifestPath)
+ if err == nil {
+ fh, buf, err := jar.ManifestFileContents(contents)
+ if err == nil {
+ _, err = oz.addZipEntry(jar.ManifestFile, ZipEntryFromBuffer{fh, buf})
+ }
+ }
+ return err
+}
+
+// Adds an entry with given name and contents read from given file
+func (oz *OutputZip) addZipEntryFromFile(name string, path string) error {
+ buf, err := ioutil.ReadFile(path)
+ if err == nil {
+ fh := &zip.FileHeader{
+ Name: name,
+ Method: zip.Store,
+ UncompressedSize64: uint64(len(buf)),
+ }
+ fh.SetMode(0700)
+ fh.SetModTime(jar.DefaultTime)
+ _, err = oz.addZipEntry(name, ZipEntryFromBuffer{fh, buf})
+ }
+ return err
+}
+
+func (oz *OutputZip) addEmptyEntry(entry string) error {
+ var emptyBuf []byte
+ fh := &zip.FileHeader{
+ Name: entry,
+ Method: zip.Store,
+ UncompressedSize64: uint64(len(emptyBuf)),
+ }
+ fh.SetMode(0700)
+ fh.SetModTime(jar.DefaultTime)
+ _, err := oz.addZipEntry(entry, ZipEntryFromBuffer{fh, emptyBuf})
+ return err
+}
+
+// Returns true if given entry is to be excluded
+func (oz *OutputZip) isEntryExcluded(name string) bool {
+ for _, dir := range oz.excludeDirs {
+ dir = filepath.Clean(dir)
+ patterns := []string{
+ dir + "/", // the directory itself
+ dir + "/**/*", // files recursively in the directory
+ dir + "/**/*/", // directories recursively in the directory
+ }
+
+ for _, pattern := range patterns {
+ match, err := pathtools.Match(pattern, name)
+ if err != nil {
+ panic(fmt.Errorf("%s: %s", err.Error(), pattern))
+ }
+ if match {
+ if oz.emulateJar {
+ // When merging jar files, don't strip META-INF/MANIFEST.MF even if stripping META-INF is
+ // requested.
+ // TODO(ccross): which files does this affect?
+ if name != jar.MetaDir && name != jar.ManifestFile {
+ return true
+ }
+ }
+ return true
+ }
+ }
+ }
+
+ for _, pattern := range oz.excludeFiles {
+ match, err := pathtools.Match(pattern, name)
+ if err != nil {
+ panic(fmt.Errorf("%s: %s", err.Error(), pattern))
+ }
+ if match {
+ return true
+ }
+ }
+ return false
+}
+
+// Creates a zip entry whose contents is an entry from the given input zip.
+func (oz *OutputZip) copyEntry(inputZip InputZip, index int) error {
+ entry := NewZipEntryFromZip(inputZip, index)
+ if oz.stripDirEntries && entry.IsDir() {
+ return nil
+ }
+ existingEntry, err := oz.addZipEntry(entry.name, entry)
+ if err != nil {
+ return err
+ }
+ if existingEntry == nil {
+ return nil
+ }
+
+ // File types should match
+ if existingEntry.IsDir() != entry.IsDir() {
+ return fmt.Errorf("Directory/file mismatch at %v from %v and %v\n",
+ entry.name, existingEntry, entry)
+ }
+
+ if oz.ignoreDuplicates ||
+ // Skip manifest and module info files that are not from the first input file
+ (oz.emulateJar && entry.name == jar.ManifestFile || entry.name == jar.ModuleInfoClass) ||
+ // Identical entries
+ (existingEntry.CRC32() == entry.CRC32() && existingEntry.Size() == entry.Size()) ||
+ // Directory entries
+ entry.IsDir() {
+ return nil
+ }
+
+ return fmt.Errorf("Duplicate path %v found in %v and %v\n", entry.name, existingEntry, inputZip.Name())
+}
+
+func (oz *OutputZip) entriesArray() []string {
+ entries := make([]string, len(oz.sourceByDest))
+ i := 0
+ for entry := range oz.sourceByDest {
+ entries[i] = entry
+ i++
+ }
+ return entries
+}
+
+func (oz *OutputZip) jarSorted() []string {
+ entries := oz.entriesArray()
+ sort.SliceStable(entries, func(i, j int) bool { return jar.EntryNamesLess(entries[i], entries[j]) })
+ return entries
+}
+
+func (oz *OutputZip) alphanumericSorted() []string {
+ entries := oz.entriesArray()
+ sort.Strings(entries)
+ return entries
+}
+
+func (oz *OutputZip) writeEntries(entries []string) error {
+ for _, entry := range entries {
+ source, _ := oz.sourceByDest[entry]
+ if err := source.WriteToZip(entry, oz.outputWriter); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (oz *OutputZip) getUninitializedPythonPackages(inputZips []InputZip) ([]string, error) {
+ // the runfiles packages needs to be populated with "__init__.py".
+ // the runfiles dirs have been treated as packages.
+ allPackages := make(map[string]bool)
+ initedPackages := make(map[string]bool)
+ getPackage := func(path string) string {
+ ret := filepath.Dir(path)
+ // filepath.Dir("abc") -> "." and filepath.Dir("/abc") -> "/".
+ if ret == "." || ret == "/" {
+ return ""
+ }
+ return ret
+ }
+
+ // put existing __init__.py files to a set first. This set is used for preventing
+ // generated __init__.py files from overwriting existing ones.
+ for _, inputZip := range inputZips {
+ if err := inputZip.Open(); err != nil {
+ return nil, err
+ }
+ for _, file := range inputZip.Entries() {
+ pyPkg := getPackage(file.Name)
+ if filepath.Base(file.Name) == "__init__.py" {
+ if _, found := initedPackages[pyPkg]; found {
+ panic(fmt.Errorf("found __init__.py path duplicates during pars merging: %q", file.Name))
+ }
+ initedPackages[pyPkg] = true
+ }
+ for pyPkg != "" {
+ if _, found := allPackages[pyPkg]; found {
+ break
+ }
+ allPackages[pyPkg] = true
+ pyPkg = getPackage(pyPkg)
+ }
+ }
+ }
+ noInitPackages := make([]string, 0)
+ for pyPkg := range allPackages {
+ if _, found := initedPackages[pyPkg]; !found {
+ noInitPackages = append(noInitPackages, pyPkg)
+ }
+ }
+ return noInitPackages, nil
+}
+
+// An InputZip owned by the InputZipsManager. Opened ManagedInputZip's are chained in the open order.
+type ManagedInputZip struct {
+ owner *InputZipsManager
+ realInputZip InputZip
+ older *ManagedInputZip
+ newer *ManagedInputZip
+}
+
+// Maintains the array of ManagedInputZips, keeping track of open input ones. When an InputZip is opened,
+// may close some other InputZip to limit the number of open ones.
+type InputZipsManager struct {
+ inputZips []*ManagedInputZip
+ nOpenZips int
+ maxOpenZips int
+ openInputZips *ManagedInputZip
+}
+
+func (miz *ManagedInputZip) unlink() {
+ olderMiz := miz.older
+ newerMiz := miz.newer
+ if newerMiz.older != miz || olderMiz.newer != miz {
+ panic(fmt.Errorf("removing %p:%#v: broken list between %p:%#v and %p:%#v",
+ miz, miz, newerMiz, newerMiz, olderMiz, olderMiz))
+ }
+ olderMiz.newer = newerMiz
+ newerMiz.older = olderMiz
+ miz.newer = nil
+ miz.older = nil
+}
+
+func (miz *ManagedInputZip) link(olderMiz *ManagedInputZip) {
+ if olderMiz.newer != nil || olderMiz.older != nil {
+ panic(fmt.Errorf("inputZip is already open"))
+ }
+ oldOlderMiz := miz.older
+ if oldOlderMiz.newer != miz {
+ panic(fmt.Errorf("broken list between %p:%#v and %p:%#v", miz, oldOlderMiz))
+ }
+ miz.older = olderMiz
+ olderMiz.older = oldOlderMiz
+ oldOlderMiz.newer = olderMiz
+ olderMiz.newer = miz
+}
+
+func NewInputZipsManager(nInputZips, maxOpenZips int) *InputZipsManager {
+ if maxOpenZips < 3 {
+ panic(fmt.Errorf("open zips limit should be above 3"))
+ }
+ // In the dummy element .older points to the most recently opened InputZip, and .newer points to the oldest.
+ head := new(ManagedInputZip)
+ head.older = head
+ head.newer = head
+ return &InputZipsManager{
+ inputZips: make([]*ManagedInputZip, 0, nInputZips),
+ maxOpenZips: maxOpenZips,
+ openInputZips: head,
+ }
+}
+
+// InputZip factory
+func (izm *InputZipsManager) Manage(inz InputZip) InputZip {
+ iz := &ManagedInputZip{owner: izm, realInputZip: inz}
+ izm.inputZips = append(izm.inputZips, iz)
+ return iz
+}
+
+// Opens or reopens ManagedInputZip.
+func (izm *InputZipsManager) reopen(miz *ManagedInputZip) error {
+ if miz.realInputZip.IsOpen() {
+ if miz != izm.openInputZips {
+ miz.unlink()
+ izm.openInputZips.link(miz)
+ }
+ return nil
+ }
+ if izm.nOpenZips >= izm.maxOpenZips {
+ if err := izm.close(izm.openInputZips.older); err != nil {
+ return err
+ }
+ }
+ if err := miz.realInputZip.Open(); err != nil {
+ return err
+ }
+ izm.openInputZips.link(miz)
+ izm.nOpenZips++
+ return nil
+}
+
+func (izm *InputZipsManager) close(miz *ManagedInputZip) error {
+ if miz.IsOpen() {
+ err := miz.realInputZip.Close()
+ izm.nOpenZips--
+ miz.unlink()
+ return err
+ }
+ return nil
+}
+
+// Checks that openInputZips deque is valid
+func (izm *InputZipsManager) checkOpenZipsDeque() {
+ nReallyOpen := 0
+ el := izm.openInputZips
+ for {
+ elNext := el.older
+ if elNext.newer != el {
+ panic(fmt.Errorf("Element:\n %p: %v\nNext:\n %p %v", el, el, elNext, elNext))
+ }
+ if elNext == izm.openInputZips {
+ break
+ }
+ el = elNext
+ if !el.IsOpen() {
+ panic(fmt.Errorf("Found unopened element"))
+ }
+ nReallyOpen++
+ if nReallyOpen > izm.nOpenZips {
+ panic(fmt.Errorf("found %d open zips, should be %d", nReallyOpen, izm.nOpenZips))
+ }
+ }
+ if nReallyOpen > izm.nOpenZips {
+ panic(fmt.Errorf("found %d open zips, should be %d", nReallyOpen, izm.nOpenZips))
+ }
+}
+
+func (miz *ManagedInputZip) Name() string {
+ return miz.realInputZip.Name()
+}
+
+func (miz *ManagedInputZip) Open() error {
+ return miz.owner.reopen(miz)
+}
+
+func (miz *ManagedInputZip) Close() error {
+ return miz.owner.close(miz)
+}
+
+func (miz *ManagedInputZip) IsOpen() bool {
+ return miz.realInputZip.IsOpen()
+}
+
+func (miz *ManagedInputZip) Entries() []*zip.File {
+ if !miz.IsOpen() {
+ panic(fmt.Errorf("%s: is not open", miz.Name()))
+ }
+ return miz.realInputZip.Entries()
+}
+
+// Actual processing.
+func mergeZips(inputZips []InputZip, writer *zip.Writer, manifest, pyMain string,
+ sortEntries, emulateJar, emulatePar, stripDirEntries, ignoreDuplicates bool,
+ excludeFiles, excludeDirs []string, zipsToNotStrip map[string]bool) error {
+
+ out := NewOutputZip(writer, sortEntries, emulateJar, stripDirEntries, ignoreDuplicates)
+ out.setExcludeFiles(excludeFiles)
+ out.setExcludeDirs(excludeDirs)
+ if manifest != "" {
+ if err := out.addManifest(manifest); err != nil {
+ return err
+ }
+ }
+ if pyMain != "" {
+ if err := out.addZipEntryFromFile("__main__.py", pyMain); err != nil {
+ return err
+ }
+ }
+
+ if emulatePar {
+ noInitPackages, err := out.getUninitializedPythonPackages(inputZips)
+ if err != nil {
+ return err
+ }
+ for _, uninitializedPyPackage := range noInitPackages {
+ if err = out.addEmptyEntry(filepath.Join(uninitializedPyPackage, "__init__.py")); err != nil {
+ return err
+ }
+ }
+ }
+
+ // Finally, add entries from all the input zips.
+ for _, inputZip := range inputZips {
+ _, copyFully := zipsToNotStrip[inputZip.Name()]
+ if err := inputZip.Open(); err != nil {
+ return err
+ }
+
+ for i, entry := range inputZip.Entries() {
+ if copyFully || !out.isEntryExcluded(entry.Name) {
+ if err := out.copyEntry(inputZip, i); err != nil {
+ return err
+ }
+ }
+ }
+ // Unless we need to rearrange the entries, the input zip can now be closed.
+ if !(emulateJar || sortEntries) {
+ if err := inputZip.Close(); err != nil {
+ return err
+ }
+ }
+ }
+
+ if emulateJar {
+ return out.writeEntries(out.jarSorted())
+ } else if sortEntries {
+ return out.writeEntries(out.alphanumericSorted())
+ }
+ return nil
+}
+
+// Process command line
type fileList []string
func (f *fileList) String() string {
@@ -50,9 +608,8 @@
return `""`
}
-func (s zipsToNotStripSet) Set(zip_path string) error {
- s[zip_path] = true
-
+func (s zipsToNotStripSet) Set(path string) error {
+ s[path] = true
return nil
}
@@ -60,8 +617,8 @@
sortEntries = flag.Bool("s", false, "sort entries (defaults to the order from the input zip files)")
emulateJar = flag.Bool("j", false, "sort zip entries using jar ordering (META-INF first)")
emulatePar = flag.Bool("p", false, "merge zip entries based on par format")
- stripDirs fileList
- stripFiles fileList
+ excludeDirs fileList
+ excludeFiles fileList
zipsToNotStrip = make(zipsToNotStripSet)
stripDirEntries = flag.Bool("D", false, "strip directory entries from the output zip file")
manifest = flag.String("m", "", "manifest file to insert in jar")
@@ -71,14 +628,52 @@
)
func init() {
- flag.Var(&stripDirs, "stripDir", "directories to be excluded from the output zip, accepts wildcards")
- flag.Var(&stripFiles, "stripFile", "files to be excluded from the output zip, accepts wildcards")
+ flag.Var(&excludeDirs, "stripDir", "directories to be excluded from the output zip, accepts wildcards")
+ flag.Var(&excludeFiles, "stripFile", "files to be excluded from the output zip, accepts wildcards")
flag.Var(&zipsToNotStrip, "zipToNotStrip", "the input zip file which is not applicable for stripping")
}
+type FileInputZip struct {
+ name string
+ reader *zip.ReadCloser
+}
+
+func (fiz *FileInputZip) Name() string {
+ return fiz.name
+}
+
+func (fiz *FileInputZip) Close() error {
+ if fiz.IsOpen() {
+ reader := fiz.reader
+ fiz.reader = nil
+ return reader.Close()
+ }
+ return nil
+}
+
+func (fiz *FileInputZip) Entries() []*zip.File {
+ if !fiz.IsOpen() {
+ panic(fmt.Errorf("%s: is not open", fiz.Name()))
+ }
+ return fiz.reader.File
+}
+
+func (fiz *FileInputZip) IsOpen() bool {
+ return fiz.reader != nil
+}
+
+func (fiz *FileInputZip) Open() error {
+ if fiz.IsOpen() {
+ return nil
+ }
+ var err error
+ fiz.reader, err = zip.OpenReader(fiz.Name())
+ return err
+}
+
func main() {
flag.Usage = func() {
- fmt.Fprintln(os.Stderr, "usage: merge_zips [-jpsD] [-m manifest] [--prefix script] [-pm __main__.py] output [inputs...]")
+ fmt.Fprintln(os.Stderr, "usage: merge_zips [-jpsD] [-m manifest] [--prefix script] [-pm __main__.py] OutputZip [inputs...]")
flag.PrintDefaults()
}
@@ -90,16 +685,28 @@
os.Exit(1)
}
outputPath := args[0]
- inputs := args[1:]
+ inputs := make([]string, 0)
+ for _, input := range args[1:] {
+ if input[0] == '@' {
+ bytes, err := ioutil.ReadFile(input[1:])
+ if err != nil {
+ log.Fatal(err)
+ }
+ inputs = append(inputs, soongZip.ReadRespFile(bytes)...)
+ continue
+ }
+ inputs = append(inputs, input)
+ continue
+ }
log.SetFlags(log.Lshortfile)
// make writer
- output, err := os.Create(outputPath)
+ outputZip, err := os.Create(outputPath)
if err != nil {
log.Fatal(err)
}
- defer output.Close()
+ defer outputZip.Close()
var offset int64
if *prefix != "" {
@@ -107,13 +714,13 @@
if err != nil {
log.Fatal(err)
}
- offset, err = io.Copy(output, prefixFile)
+ offset, err = io.Copy(outputZip, prefixFile)
if err != nil {
log.Fatal(err)
}
}
- writer := zip.NewWriter(output)
+ writer := zip.NewWriter(outputZip)
defer func() {
err := writer.Close()
if err != nil {
@@ -122,18 +729,6 @@
}()
writer.SetOffset(offset)
- // make readers
- readers := []namedZipReader{}
- for _, input := range inputs {
- reader, err := zip.OpenReader(input)
- if err != nil {
- log.Fatal(err)
- }
- defer reader.Close()
- namedReader := namedZipReader{path: input, reader: &reader.Reader}
- readers = append(readers, namedReader)
- }
-
if *manifest != "" && !*emulateJar {
log.Fatal(errors.New("must specify -j when specifying a manifest via -m"))
}
@@ -143,344 +738,15 @@
}
// do merge
- err = mergeZips(readers, writer, *manifest, *pyMain, *sortEntries, *emulateJar, *emulatePar,
- *stripDirEntries, *ignoreDuplicates, []string(stripFiles), []string(stripDirs), map[string]bool(zipsToNotStrip))
+ inputZipsManager := NewInputZipsManager(len(inputs), 1000)
+ inputZips := make([]InputZip, len(inputs))
+ for i, input := range inputs {
+ inputZips[i] = inputZipsManager.Manage(&FileInputZip{name: input})
+ }
+ err = mergeZips(inputZips, writer, *manifest, *pyMain, *sortEntries, *emulateJar, *emulatePar,
+ *stripDirEntries, *ignoreDuplicates, []string(excludeFiles), []string(excludeDirs),
+ map[string]bool(zipsToNotStrip))
if err != nil {
log.Fatal(err)
}
}
-
-// a namedZipReader reads a .zip file and can say which file it's reading
-type namedZipReader struct {
- path string
- reader *zip.Reader
-}
-
-// a zipEntryPath refers to a file contained in a zip
-type zipEntryPath struct {
- zipName string
- entryName string
-}
-
-func (p zipEntryPath) String() string {
- return p.zipName + "/" + p.entryName
-}
-
-// a zipEntry is a zipSource that pulls its content from another zip
-type zipEntry struct {
- path zipEntryPath
- content *zip.File
-}
-
-func (ze zipEntry) String() string {
- return ze.path.String()
-}
-
-func (ze zipEntry) IsDir() bool {
- return ze.content.FileInfo().IsDir()
-}
-
-func (ze zipEntry) CRC32() uint32 {
- return ze.content.FileHeader.CRC32
-}
-
-func (ze zipEntry) Size() uint64 {
- return ze.content.FileHeader.UncompressedSize64
-}
-
-func (ze zipEntry) WriteToZip(dest string, zw *zip.Writer) error {
- return zw.CopyFrom(ze.content, dest)
-}
-
-// a bufferEntry is a zipSource that pulls its content from a []byte
-type bufferEntry struct {
- fh *zip.FileHeader
- content []byte
-}
-
-func (be bufferEntry) String() string {
- return "internal buffer"
-}
-
-func (be bufferEntry) IsDir() bool {
- return be.fh.FileInfo().IsDir()
-}
-
-func (be bufferEntry) CRC32() uint32 {
- return crc32.ChecksumIEEE(be.content)
-}
-
-func (be bufferEntry) Size() uint64 {
- return uint64(len(be.content))
-}
-
-func (be bufferEntry) WriteToZip(dest string, zw *zip.Writer) error {
- w, err := zw.CreateHeader(be.fh)
- if err != nil {
- return err
- }
-
- if !be.IsDir() {
- _, err = w.Write(be.content)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
-type zipSource interface {
- String() string
- IsDir() bool
- CRC32() uint32
- Size() uint64
- WriteToZip(dest string, zw *zip.Writer) error
-}
-
-// a fileMapping specifies to copy a zip entry from one place to another
-type fileMapping struct {
- dest string
- source zipSource
-}
-
-func mergeZips(readers []namedZipReader, writer *zip.Writer, manifest, pyMain string,
- sortEntries, emulateJar, emulatePar, stripDirEntries, ignoreDuplicates bool,
- stripFiles, stripDirs []string, zipsToNotStrip map[string]bool) error {
-
- sourceByDest := make(map[string]zipSource, 0)
- orderedMappings := []fileMapping{}
-
- // if dest already exists returns a non-null zipSource for the existing source
- addMapping := func(dest string, source zipSource) zipSource {
- mapKey := filepath.Clean(dest)
- if existingSource, exists := sourceByDest[mapKey]; exists {
- return existingSource
- }
-
- sourceByDest[mapKey] = source
- orderedMappings = append(orderedMappings, fileMapping{source: source, dest: dest})
- return nil
- }
-
- if manifest != "" {
- if !stripDirEntries {
- dirHeader := jar.MetaDirFileHeader()
- dirSource := bufferEntry{dirHeader, nil}
- addMapping(jar.MetaDir, dirSource)
- }
-
- contents, err := ioutil.ReadFile(manifest)
- if err != nil {
- return err
- }
-
- fh, buf, err := jar.ManifestFileContents(contents)
- if err != nil {
- return err
- }
-
- fileSource := bufferEntry{fh, buf}
- addMapping(jar.ManifestFile, fileSource)
- }
-
- if pyMain != "" {
- buf, err := ioutil.ReadFile(pyMain)
- if err != nil {
- return err
- }
- fh := &zip.FileHeader{
- Name: "__main__.py",
- Method: zip.Store,
- UncompressedSize64: uint64(len(buf)),
- }
- fh.SetMode(0700)
- fh.SetModTime(jar.DefaultTime)
- fileSource := bufferEntry{fh, buf}
- addMapping("__main__.py", fileSource)
- }
-
- if emulatePar {
- // the runfiles packages needs to be populated with "__init__.py".
- newPyPkgs := []string{}
- // the runfiles dirs have been treated as packages.
- existingPyPkgSet := make(map[string]bool)
- // put existing __init__.py files to a set first. This set is used for preventing
- // generated __init__.py files from overwriting existing ones.
- for _, namedReader := range readers {
- for _, file := range namedReader.reader.File {
- if filepath.Base(file.Name) != "__init__.py" {
- continue
- }
- pyPkg := pathBeforeLastSlash(file.Name)
- if _, found := existingPyPkgSet[pyPkg]; found {
- panic(fmt.Errorf("found __init__.py path duplicates during pars merging: %q.", file.Name))
- } else {
- existingPyPkgSet[pyPkg] = true
- }
- }
- }
- for _, namedReader := range readers {
- for _, file := range namedReader.reader.File {
- var parentPath string /* the path after trimming last "/" */
- if filepath.Base(file.Name) == "__init__.py" {
- // for existing __init__.py files, we should trim last "/" for twice.
- // eg. a/b/c/__init__.py ---> a/b
- parentPath = pathBeforeLastSlash(pathBeforeLastSlash(file.Name))
- } else {
- parentPath = pathBeforeLastSlash(file.Name)
- }
- populateNewPyPkgs(parentPath, existingPyPkgSet, &newPyPkgs)
- }
- }
- for _, pkg := range newPyPkgs {
- var emptyBuf []byte
- fh := &zip.FileHeader{
- Name: filepath.Join(pkg, "__init__.py"),
- Method: zip.Store,
- UncompressedSize64: uint64(len(emptyBuf)),
- }
- fh.SetMode(0700)
- fh.SetModTime(jar.DefaultTime)
- fileSource := bufferEntry{fh, emptyBuf}
- addMapping(filepath.Join(pkg, "__init__.py"), fileSource)
- }
- }
- for _, namedReader := range readers {
- _, skipStripThisZip := zipsToNotStrip[namedReader.path]
- for _, file := range namedReader.reader.File {
- if !skipStripThisZip {
- if skip, err := shouldStripEntry(emulateJar, stripFiles, stripDirs, file.Name); err != nil {
- return err
- } else if skip {
- continue
- }
- }
-
- if stripDirEntries && file.FileInfo().IsDir() {
- continue
- }
-
- // check for other files or directories destined for the same path
- dest := file.Name
-
- // make a new entry to add
- source := zipEntry{path: zipEntryPath{zipName: namedReader.path, entryName: file.Name}, content: file}
-
- if existingSource := addMapping(dest, source); existingSource != nil {
- // handle duplicates
- if existingSource.IsDir() != source.IsDir() {
- return fmt.Errorf("Directory/file mismatch at %v from %v and %v\n",
- dest, existingSource, source)
- }
-
- if ignoreDuplicates {
- continue
- }
-
- if emulateJar &&
- file.Name == jar.ManifestFile || file.Name == jar.ModuleInfoClass {
- // Skip manifest and module info files that are not from the first input file
- continue
- }
-
- if source.IsDir() {
- continue
- }
-
- if existingSource.CRC32() == source.CRC32() && existingSource.Size() == source.Size() {
- continue
- }
-
- return fmt.Errorf("Duplicate path %v found in %v and %v\n",
- dest, existingSource, source)
- }
- }
- }
-
- if emulateJar {
- jarSort(orderedMappings)
- } else if sortEntries {
- alphanumericSort(orderedMappings)
- }
-
- for _, entry := range orderedMappings {
- if err := entry.source.WriteToZip(entry.dest, writer); err != nil {
- return err
- }
- }
-
- return nil
-}
-
-// Sets the given directory and all its ancestor directories as Python packages.
-func populateNewPyPkgs(pkgPath string, existingPyPkgSet map[string]bool, newPyPkgs *[]string) {
- for pkgPath != "" {
- if _, found := existingPyPkgSet[pkgPath]; !found {
- existingPyPkgSet[pkgPath] = true
- *newPyPkgs = append(*newPyPkgs, pkgPath)
- // Gets its ancestor directory by trimming last slash.
- pkgPath = pathBeforeLastSlash(pkgPath)
- } else {
- break
- }
- }
-}
-
-func pathBeforeLastSlash(path string) string {
- ret := filepath.Dir(path)
- // filepath.Dir("abc") -> "." and filepath.Dir("/abc") -> "/".
- if ret == "." || ret == "/" {
- return ""
- }
- return ret
-}
-
-func shouldStripEntry(emulateJar bool, stripFiles, stripDirs []string, name string) (bool, error) {
- for _, dir := range stripDirs {
- dir = filepath.Clean(dir)
- patterns := []string{
- dir + "/", // the directory itself
- dir + "/**/*", // files recursively in the directory
- dir + "/**/*/", // directories recursively in the directory
- }
-
- for _, pattern := range patterns {
- match, err := pathtools.Match(pattern, name)
- if err != nil {
- return false, fmt.Errorf("%s: %s", err.Error(), pattern)
- } else if match {
- if emulateJar {
- // When merging jar files, don't strip META-INF/MANIFEST.MF even if stripping META-INF is
- // requested.
- // TODO(ccross): which files does this affect?
- if name != jar.MetaDir && name != jar.ManifestFile {
- return true, nil
- }
- }
- return true, nil
- }
- }
- }
-
- for _, pattern := range stripFiles {
- if match, err := pathtools.Match(pattern, name); err != nil {
- return false, fmt.Errorf("%s: %s", err.Error(), pattern)
- } else if match {
- return true, nil
- }
- }
- return false, nil
-}
-
-func jarSort(files []fileMapping) {
- sort.SliceStable(files, func(i, j int) bool {
- return jar.EntryNamesLess(files[i].dest, files[j].dest)
- })
-}
-
-func alphanumericSort(files []fileMapping) {
- sort.SliceStable(files, func(i, j int) bool {
- return files[i].dest < files[j].dest
- })
-}
diff --git a/cmd/merge_zips/merge_zips_test.go b/cmd/merge_zips/merge_zips_test.go
index dbde270..cb58436 100644
--- a/cmd/merge_zips/merge_zips_test.go
+++ b/cmd/merge_zips/merge_zips_test.go
@@ -51,6 +51,39 @@
moduleInfoFile = testZipEntry{jar.ModuleInfoClass, 0755, []byte("module-info")}
)
+type testInputZip struct {
+ name string
+ entries []testZipEntry
+ reader *zip.Reader
+}
+
+func (tiz *testInputZip) Name() string {
+ return tiz.name
+}
+
+func (tiz *testInputZip) Open() error {
+ if tiz.reader == nil {
+ tiz.reader = testZipEntriesToZipReader(tiz.entries)
+ }
+ return nil
+}
+
+func (tiz *testInputZip) Close() error {
+ tiz.reader = nil
+ return nil
+}
+
+func (tiz *testInputZip) Entries() []*zip.File {
+ if tiz.reader == nil {
+ panic(fmt.Errorf("%s: should be open to get entries", tiz.Name()))
+ }
+ return tiz.reader.File
+}
+
+func (tiz *testInputZip) IsOpen() bool {
+ return tiz.reader != nil
+}
+
func TestMergeZips(t *testing.T) {
testCases := []struct {
name string
@@ -207,13 +240,9 @@
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
- var readers []namedZipReader
+ inputZips := make([]InputZip, len(test.in))
for i, in := range test.in {
- r := testZipEntriesToZipReader(in)
- readers = append(readers, namedZipReader{
- path: "in" + strconv.Itoa(i),
- reader: r,
- })
+ inputZips[i] = &testInputZip{name: "in" + strconv.Itoa(i), entries: in}
}
want := testZipEntriesToBuf(test.out)
@@ -221,7 +250,7 @@
out := &bytes.Buffer{}
writer := zip.NewWriter(out)
- err := mergeZips(readers, writer, "", "",
+ err := mergeZips(inputZips, writer, "", "",
test.sort, test.jar, false, test.stripDirEntries, test.ignoreDuplicates,
test.stripFiles, test.stripDirs, test.zipsToNotStrip)
@@ -304,3 +333,60 @@
return ret
}
+
+type DummyInpuZip struct {
+ isOpen bool
+}
+
+func (diz *DummyInpuZip) Name() string {
+ return "dummy"
+}
+
+func (diz *DummyInpuZip) Open() error {
+ diz.isOpen = true
+ return nil
+}
+
+func (diz *DummyInpuZip) Close() error {
+ diz.isOpen = false
+ return nil
+}
+
+func (DummyInpuZip) Entries() []*zip.File {
+ panic("implement me")
+}
+
+func (diz *DummyInpuZip) IsOpen() bool {
+ return diz.isOpen
+}
+
+func TestInputZipsManager(t *testing.T) {
+ const nInputZips = 20
+ const nMaxOpenZips = 10
+ izm := NewInputZipsManager(20, 10)
+ managedZips := make([]InputZip, nInputZips)
+ for i := 0; i < nInputZips; i++ {
+ managedZips[i] = izm.Manage(&DummyInpuZip{})
+ }
+
+ t.Run("InputZipsManager", func(t *testing.T) {
+ for i, iz := range managedZips {
+ if err := iz.Open(); err != nil {
+ t.Fatalf("Step %d: open failed: %s", i, err)
+ return
+ }
+ if izm.nOpenZips > nMaxOpenZips {
+ t.Errorf("Step %d: should be <=%d open zips", i, nMaxOpenZips)
+ }
+ }
+ if !managedZips[nInputZips-1].IsOpen() {
+ t.Error("The last input should stay open")
+ }
+ for _, iz := range managedZips {
+ iz.Close()
+ }
+ if izm.nOpenZips > 0 {
+ t.Error("Some input zips are still open")
+ }
+ })
+}
diff --git a/cmd/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go
index c858c40..191b919 100644
--- a/cmd/pom2bp/pom2bp.go
+++ b/cmd/pom2bp/pom2bp.go
@@ -124,6 +124,25 @@
var hostModuleNames = HostModuleNames{}
+type HostAndDeviceModuleNames map[string]bool
+
+func (n HostAndDeviceModuleNames) IsHostAndDeviceModule(groupId string, artifactId string) bool {
+ _, found := n[groupId+":"+artifactId]
+
+ return found
+}
+
+func (n HostAndDeviceModuleNames) String() string {
+ return ""
+}
+
+func (n HostAndDeviceModuleNames) Set(v string) error {
+ n[v] = true
+ return nil
+}
+
+var hostAndDeviceModuleNames = HostAndDeviceModuleNames{}
+
var sdkVersion string
var useVersion string
var staticDeps bool
@@ -190,10 +209,14 @@
return !p.IsHostModule()
}
+func (p Pom) IsHostAndDeviceModule() bool {
+ return hostAndDeviceModuleNames.IsHostAndDeviceModule(p.GroupId, p.ArtifactId)
+}
+
func (p Pom) ModuleType() string {
if p.IsAar() {
return "android_library"
- } else if p.IsHostModule() {
+ } else if p.IsHostModule() && !p.IsHostAndDeviceModule() {
return "java_library_host"
} else {
return "java_library_static"
@@ -203,7 +226,7 @@
func (p Pom) ImportModuleType() string {
if p.IsAar() {
return "android_library_import"
- } else if p.IsHostModule() {
+ } else if p.IsHostModule() && !p.IsHostAndDeviceModule() {
return "java_import_host"
} else {
return "java_import"
@@ -340,6 +363,9 @@
{{- if .Jetifier}}
jetifier: true,
{{- end}}
+ {{- if .IsHostAndDeviceModule}}
+ host_supported: true,
+ {{- end}}
{{- if .IsAar}}
min_sdk_version: "{{.MinSdkVersion}}",
static_libs: [
@@ -372,6 +398,9 @@
{{- if .Jetifier}}
jetifier: true,
{{- end}}
+ {{- if .IsHostAndDeviceModule}}
+ host_supported: true,
+ {{- end}}
{{- if .IsAar}}
min_sdk_version: "{{.MinSdkVersion}}",
static_libs: [
@@ -399,6 +428,9 @@
name: "{{.BpName}}",
{{- if .IsDeviceModule}}
sdk_version: "{{.SdkVersion}}",
+ {{- if .IsHostAndDeviceModule}}
+ host_supported: true,
+ {{- end}}
{{- if .IsAar}}
min_sdk_version: "{{.MinSdkVersion}}",
manifest: "manifests/{{.BpName}}/AndroidManifest.xml",
@@ -564,6 +596,7 @@
flag.Var(&extraLibs, "extra-libs", "Extra runtime dependencies needed when depending on a module")
flag.Var(&rewriteNames, "rewrite", "Regex(es) to rewrite artifact names")
flag.Var(&hostModuleNames, "host", "Specifies that the corresponding module (specified in the form 'module.group:module.artifact') is a host module")
+ flag.Var(&hostAndDeviceModuleNames, "host-and-device", "Specifies that the corresponding module (specified in the form 'module.group:module.artifact') is both a host and device module.")
flag.StringVar(&sdkVersion, "sdk-version", "", "What to write to sdk_version")
flag.StringVar(&useVersion, "use-version", "", "Only read artifacts of a specific version")
flag.BoolVar(&staticDeps, "static-deps", false, "Statically include direct dependencies")
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 4d59a39..39303bf 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -331,36 +331,36 @@
}
buildActionFlags := []struct {
- name string
- description string
- action build.BuildAction
- buildDependencies bool
- set bool
+ name string
+ description string
+ action build.BuildAction
+ set bool
}{{
- name: "all-modules",
- description: "Build action: build from the top of the source tree.",
- action: build.BUILD_MODULES,
- buildDependencies: true,
+ name: "all-modules",
+ description: "Build action: build from the top of the source tree.",
+ action: build.BUILD_MODULES,
}, {
- name: "modules-in-a-dir-no-deps",
- description: "Build action: builds all of the modules in the current directory without their dependencies.",
- action: build.BUILD_MODULES_IN_A_DIRECTORY,
- buildDependencies: false,
+ // This is redirecting to mma build command behaviour. Once it has soaked for a
+ // while, the build command is deleted from here once it has been removed from the
+ // envsetup.sh.
+ name: "modules-in-a-dir-no-deps",
+ description: "Build action: builds all of the modules in the current directory without their dependencies.",
+ action: build.BUILD_MODULES_IN_A_DIRECTORY,
}, {
- name: "modules-in-dirs-no-deps",
- description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
- action: build.BUILD_MODULES_IN_DIRECTORIES,
- buildDependencies: false,
+ // This is redirecting to mmma build command behaviour. Once it has soaked for a
+ // while, the build command is deleted from here once it has been removed from the
+ // envsetup.sh.
+ name: "modules-in-dirs-no-deps",
+ description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
+ action: build.BUILD_MODULES_IN_DIRECTORIES,
}, {
- name: "modules-in-a-dir",
- description: "Build action: builds all of the modules in the current directory and their dependencies.",
- action: build.BUILD_MODULES_IN_A_DIRECTORY,
- buildDependencies: true,
+ name: "modules-in-a-dir",
+ description: "Build action: builds all of the modules in the current directory and their dependencies.",
+ action: build.BUILD_MODULES_IN_A_DIRECTORY,
}, {
- name: "modules-in-dirs",
- description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
- action: build.BUILD_MODULES_IN_DIRECTORIES,
- buildDependencies: true,
+ name: "modules-in-dirs",
+ description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
+ action: build.BUILD_MODULES_IN_DIRECTORIES,
}}
for i, flag := range buildActionFlags {
flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
@@ -380,12 +380,10 @@
// is specified.
buildActionCount := 0
var buildAction build.BuildAction
- buildDependency := false
for _, flag := range buildActionFlags {
if flag.set {
buildActionCount++
buildAction = flag.action
- buildDependency = flag.buildDependencies
}
}
if buildActionCount != 1 {
@@ -397,7 +395,7 @@
// Remove the build action flags from the args as they are not recognized by the config.
args = args[numBuildActionFlags:]
- return build.NewBuildActionConfig(buildAction, *dir, buildDependency, ctx, args...)
+ return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
}
func make(ctx build.Context, config build.Config, _ []string, logsDir string) {
@@ -410,7 +408,22 @@
fmt.Fprintln(writer, "!")
fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
fmt.Fprintln(writer, "")
- time.Sleep(5 * time.Second)
+ select {
+ case <-time.After(5 * time.Second):
+ case <-ctx.Done():
+ return
+ }
+ }
+
+ if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
+ writer := ctx.Writer
+ fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
+ fmt.Fprintln(writer, "!")
+ fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
+ fmt.Fprintln(writer, "!")
+ fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
+ fmt.Fprintln(writer, "")
+ ctx.Fatal("done")
}
toBuild := build.BuildAll
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 3e32958..51f5519 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -113,6 +113,7 @@
ProfileClassListing android.OptionalPath
ProfileIsTextListing bool
+ ProfileBootListing android.OptionalPath
EnforceUsesLibraries bool
PresentOptionalUsesLibraries []string
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index e02e60f..c378f09 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -108,11 +108,15 @@
rule = android.NewRuleBuilder()
generateProfile := module.ProfileClassListing.Valid() && !global.DisableGenerateProfile
+ generateBootProfile := module.ProfileBootListing.Valid() && !global.DisableGenerateProfile
var profile android.WritablePath
if generateProfile {
profile = profileCommand(ctx, global, module, rule)
}
+ if generateBootProfile {
+ bootProfileCommand(ctx, global, module, rule)
+ }
if !dexpreoptDisabled(global, module) {
// Don't preopt individual boot jars, they will be preopted together.
@@ -190,6 +194,38 @@
return profilePath
}
+func bootProfileCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig,
+ rule *android.RuleBuilder) android.WritablePath {
+
+ profilePath := module.BuildPath.InSameDir(ctx, "profile.bprof")
+ profileInstalledPath := module.DexLocation + ".bprof"
+
+ if !module.ProfileIsTextListing {
+ rule.Command().FlagWithOutput("touch ", profilePath)
+ }
+
+ cmd := rule.Command().
+ Text(`ANDROID_LOG_TAGS="*:e"`).
+ Tool(global.Tools.Profman)
+
+ // The profile is a test listing of methods.
+ // We need to generate the actual binary profile.
+ cmd.FlagWithInput("--create-profile-from=", module.ProfileBootListing.Path())
+
+ cmd.
+ Flag("--generate-boot-profile").
+ FlagWithInput("--apk=", module.DexPath).
+ Flag("--dex-location="+module.DexLocation).
+ FlagWithOutput("--reference-profile-file=", profilePath)
+
+ if !module.ProfileIsTextListing {
+ cmd.Text(fmt.Sprintf(`|| echo "Profile out of date for %s"`, module.DexPath))
+ }
+ rule.Install(profilePath, profileInstalledPath)
+
+ return profilePath
+}
+
func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig, rule *android.RuleBuilder,
arch android.ArchType, profile, bootImage android.Path, bootImageDeps android.Paths, appImage, generateDM bool) {
diff --git a/genrule/genrule.go b/genrule/genrule.go
index b0657ff..cf0b484 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -40,6 +40,7 @@
)
func init() {
+ pctx.Import("android/soong/android")
pctx.HostBinToolVariable("sboxCmd", "sbox")
}
@@ -284,12 +285,12 @@
referencedDepfile := false
- rawCommand, err := android.Expand(task.cmd, func(name string) (string, error) {
+ rawCommand, err := android.ExpandNinjaEscaped(task.cmd, func(name string) (string, bool, error) {
// report the error directly without returning an error to android.Expand to catch multiple errors in a
// single run
- reportError := func(fmt string, args ...interface{}) (string, error) {
+ reportError := func(fmt string, args ...interface{}) (string, bool, error) {
ctx.PropertyErrorf("cmd", fmt, args...)
- return "SOONG_ERROR", nil
+ return "SOONG_ERROR", false, nil
}
switch name {
@@ -304,19 +305,19 @@
return reportError("default label %q has multiple files, use $(locations %s) to reference it",
firstLabel, firstLabel)
}
- return locationLabels[firstLabel][0], nil
+ return locationLabels[firstLabel][0], false, nil
case "in":
- return "${in}", nil
+ return "${in}", true, nil
case "out":
- return "__SBOX_OUT_FILES__", nil
+ return "__SBOX_OUT_FILES__", false, nil
case "depfile":
referencedDepfile = true
if !Bool(g.properties.Depfile) {
return reportError("$(depfile) used without depfile property")
}
- return "__SBOX_DEPFILE__", nil
+ return "__SBOX_DEPFILE__", false, nil
case "genDir":
- return "__SBOX_OUT_DIR__", nil
+ return "__SBOX_OUT_DIR__", false, nil
default:
if strings.HasPrefix(name, "location ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
@@ -327,7 +328,7 @@
return reportError("label %q has multiple files, use $(locations %s) to reference it",
label, label)
}
- return paths[0], nil
+ return paths[0], false, nil
} else {
return reportError("unknown location label %q", label)
}
@@ -337,7 +338,7 @@
if len(paths) == 0 {
return reportError("label %q has no files", label)
}
- return strings.Join(paths, " "), nil
+ return strings.Join(paths, " "), false, nil
} else {
return reportError("unknown locations label %q", label)
}
@@ -425,7 +426,24 @@
for _, outputFile := range task.out {
g.outputFiles = append(g.outputFiles, outputFile)
}
- g.outputDeps = append(g.outputDeps, task.out[0])
+
+ // For <= 6 outputs, just embed those directly in the users. Right now, that covers >90% of
+ // the genrules on AOSP. That will make things simpler to look at the graph in the common
+ // case. For larger sets of outputs, inject a phony target in between to limit ninja file
+ // growth.
+ if len(task.out) <= 6 {
+ g.outputDeps = g.outputFiles
+ } else {
+ phonyFile := android.PathForModuleGen(ctx, "genrule-phony")
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Phony,
+ Output: phonyFile,
+ Inputs: g.outputFiles,
+ })
+
+ g.outputDeps = android.Paths{phonyFile}
+ }
}
// Collect information for opening IDE project files in java/jdeps.go.
diff --git a/java/aar.go b/java/aar.go
index ce3d126..a4e6f91 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -69,6 +69,9 @@
// path to AndroidManifest.xml. If unset, defaults to "AndroidManifest.xml".
Manifest *string `android:"path"`
+
+ // paths to additional manifest files to merge with main manifest.
+ Additional_manifests []string `android:"path"`
}
type aapt struct {
@@ -220,7 +223,11 @@
a.transitiveManifestPaths = append(android.Paths{manifestPath}, transitiveStaticLibManifests...)
if len(transitiveStaticLibManifests) > 0 {
- a.mergedManifestFile = manifestMerger(ctx, manifestPath, transitiveStaticLibManifests, a.isLibrary)
+ // Merge additional manifest files with app manifest.
+ additionalManifests := android.PathsForModuleSrc(ctx, a.aaptProperties.Additional_manifests)
+ additionalManifests = append(additionalManifests, transitiveStaticLibManifests...)
+
+ a.mergedManifestFile = manifestMerger(ctx, manifestPath, additionalManifests, a.isLibrary)
if !a.isLibrary {
// Only use the merged manifest for applications. For libraries, the transitive closure of manifests
// will be propagated to the final application and merged there. The merged manifest for libraries is
diff --git a/java/androidmk.go b/java/androidmk.go
index 90fdd0f..c00e070 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -55,6 +55,11 @@
}
func (library *Library) AndroidMk() android.AndroidMkData {
+ if !library.IsForPlatform() {
+ return android.AndroidMkData{
+ Disabled: true,
+ }
+ }
return android.AndroidMkData{
Class: "JAVA_LIBRARIES",
OutputFile: android.OptionalPathForPath(library.outputFile),
@@ -141,6 +146,11 @@
}
func (prebuilt *Import) AndroidMk() android.AndroidMkData {
+ if !prebuilt.IsForPlatform() {
+ return android.AndroidMkData{
+ Disabled: true,
+ }
+ }
return android.AndroidMkData{
Class: "JAVA_LIBRARIES",
OutputFile: android.OptionalPathForPath(prebuilt.combinedClasspathFile),
@@ -157,6 +167,11 @@
}
func (prebuilt *DexImport) AndroidMk() android.AndroidMkData {
+ if !prebuilt.IsForPlatform() {
+ return android.AndroidMkData{
+ Disabled: true,
+ }
+ }
return android.AndroidMkData{
Class: "JAVA_LIBRARIES",
OutputFile: android.OptionalPathForPath(prebuilt.maybeStrippedDexJarFile),
@@ -594,28 +609,23 @@
}
}
-func (app *AndroidAppImport) AndroidMk() android.AndroidMkData {
- return android.AndroidMkData{
+func (a *AndroidAppImport) AndroidMkEntries() android.AndroidMkEntries {
+ return android.AndroidMkEntries{
Class: "APPS",
- OutputFile: android.OptionalPathForPath(app.outputFile),
+ OutputFile: android.OptionalPathForPath(a.outputFile),
Include: "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
- Extra: []android.AndroidMkExtraFunc{
- func(w io.Writer, outputFile android.Path) {
- if Bool(app.properties.Privileged) {
- fmt.Fprintln(w, "LOCAL_PRIVILEGED_MODULE := true")
- }
- if app.certificate != nil {
- fmt.Fprintln(w, "LOCAL_CERTIFICATE :=", app.certificate.Pem.String())
- } else {
- fmt.Fprintln(w, "LOCAL_CERTIFICATE := PRESIGNED")
- }
- if len(app.properties.Overrides) > 0 {
- fmt.Fprintln(w, "LOCAL_OVERRIDES_PACKAGES :=", strings.Join(app.properties.Overrides, " "))
- }
- if len(app.dexpreopter.builtInstalled) > 0 {
- fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED :=", app.dexpreopter.builtInstalled)
- }
- },
+ AddCustomEntries: func(name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+ entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", Bool(a.properties.Privileged))
+ if a.certificate != nil {
+ entries.SetString("LOCAL_CERTIFICATE", a.certificate.Pem.String())
+ } else {
+ entries.SetString("LOCAL_CERTIFICATE", "PRESIGNED")
+ }
+ entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", a.properties.Overrides...)
+ if len(a.dexpreopter.builtInstalled) > 0 {
+ entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", a.dexpreopter.builtInstalled)
+ }
+ entries.AddStrings("LOCAL_INSTALLED_MODULE_STEM", a.installPath.Rel())
},
}
}
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
new file mode 100644
index 0000000..fbf2baa
--- /dev/null
+++ b/java/androidmk_test.go
@@ -0,0 +1,178 @@
+// Copyright 2019 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 (
+ "bytes"
+ "io"
+ "io/ioutil"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+type testAndroidMk struct {
+ *testing.T
+ body []byte
+}
+
+type testAndroidMkModule struct {
+ *testing.T
+ props map[string]string
+}
+
+func newTestAndroidMk(t *testing.T, r io.Reader) *testAndroidMk {
+ t.Helper()
+ buf, err := ioutil.ReadAll(r)
+ if err != nil {
+ t.Fatal("failed to open read Android.mk.", err)
+ }
+ return &testAndroidMk{
+ T: t,
+ body: buf,
+ }
+}
+
+func parseAndroidMkProps(lines []string) map[string]string {
+ props := make(map[string]string)
+ for _, line := range lines {
+ line = strings.TrimLeft(line, " ")
+ if line == "" || strings.HasPrefix(line, "#") {
+ continue
+ }
+ tokens := strings.Split(line, " ")
+ if tokens[1] == "+=" {
+ props[tokens[0]] += " " + strings.Join(tokens[2:], " ")
+ } else {
+ props[tokens[0]] = strings.Join(tokens[2:], " ")
+ }
+ }
+ return props
+}
+
+func (t *testAndroidMk) moduleFor(moduleName string) *testAndroidMkModule {
+ t.Helper()
+ lines := strings.Split(string(t.body), "\n")
+ index := android.IndexList("LOCAL_MODULE := "+moduleName, lines)
+ if index == -1 {
+ t.Fatalf("%q is not found.", moduleName)
+ }
+ lines = lines[index:]
+ includeIndex := android.IndexListPred(func(line string) bool {
+ return strings.HasPrefix(line, "include")
+ }, lines)
+ if includeIndex == -1 {
+ t.Fatalf("%q is not properly defined. (\"include\" not found).", moduleName)
+ }
+ props := parseAndroidMkProps(lines[:includeIndex])
+ return &testAndroidMkModule{
+ T: t.T,
+ props: props,
+ }
+}
+
+func (t *testAndroidMkModule) hasRequired(dep string) {
+ t.Helper()
+ required, ok := t.props["LOCAL_REQUIRED_MODULES"]
+ if !ok {
+ t.Error("LOCAL_REQUIRED_MODULES is not found.")
+ return
+ }
+ if !android.InList(dep, strings.Split(required, " ")) {
+ t.Errorf("%q is expected in LOCAL_REQUIRED_MODULES, but not found in %q.", dep, required)
+ }
+}
+
+func (t *testAndroidMkModule) hasNoRequired(dep string) {
+ t.Helper()
+ required, ok := t.props["LOCAL_REQUIRED_MODULES"]
+ if !ok {
+ return
+ }
+ if android.InList(dep, strings.Split(required, " ")) {
+ t.Errorf("%q is not expected in LOCAL_REQUIRED_MODULES, but found.", dep)
+ }
+}
+
+func getAndroidMk(t *testing.T, ctx *android.TestContext, config android.Config, name string) *testAndroidMk {
+ t.Helper()
+ lib, _ := ctx.ModuleForTests(name, "android_common").Module().(*Library)
+ data := android.AndroidMkDataForTest(t, config, "", lib)
+ w := &bytes.Buffer{}
+ data.Custom(w, name, "", "", data)
+ return newTestAndroidMk(t, w)
+}
+
+func TestRequired(t *testing.T) {
+ ctx, config := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ required: ["libfoo"],
+ }
+ `)
+
+ mk := getAndroidMk(t, ctx, config, "foo")
+ mk.moduleFor("foo").hasRequired("libfoo")
+}
+
+func TestHostdex(t *testing.T) {
+ ctx, config := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ hostdex: true,
+ }
+ `)
+
+ mk := getAndroidMk(t, ctx, config, "foo")
+ mk.moduleFor("foo")
+ mk.moduleFor("foo-hostdex")
+}
+
+func TestHostdexRequired(t *testing.T) {
+ ctx, config := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ hostdex: true,
+ required: ["libfoo"],
+ }
+ `)
+
+ mk := getAndroidMk(t, ctx, config, "foo")
+ mk.moduleFor("foo").hasRequired("libfoo")
+ mk.moduleFor("foo-hostdex").hasRequired("libfoo")
+}
+
+func TestHostdexSpecificRequired(t *testing.T) {
+ ctx, config := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ hostdex: true,
+ target: {
+ hostdex: {
+ required: ["libfoo"],
+ },
+ },
+ }
+ `)
+
+ mk := getAndroidMk(t, ctx, config, "foo")
+ mk.moduleFor("foo").hasNoRequired("libfoo")
+ mk.moduleFor("foo-hostdex").hasRequired("libfoo")
+}
diff --git a/java/app.go b/java/app.go
index 3442658..31f07d3 100644
--- a/java/app.go
+++ b/java/app.go
@@ -39,6 +39,8 @@
android.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
android.RegisterModuleType("override_android_app", OverrideAndroidAppModuleFactory)
android.RegisterModuleType("android_app_import", AndroidAppImportFactory)
+
+ initAndroidAppImportVariantGroupTypes()
}
// AndroidManifest.xml merging
@@ -191,9 +193,12 @@
}
}
+func (a *AndroidTestHelperApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ a.generateAndroidBuildActions(ctx)
+}
+
func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- a.aapt.useEmbeddedNativeLibs = a.useEmbeddedNativeLibs(ctx)
- a.aapt.useEmbeddedDex = Bool(a.appProperties.Use_embedded_dex)
+ a.checkPlatformAPI(ctx)
a.generateAndroidBuildActions(ctx)
}
@@ -422,6 +427,9 @@
func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
var apkDeps android.Paths
+ a.aapt.useEmbeddedNativeLibs = a.useEmbeddedNativeLibs(ctx)
+ a.aapt.useEmbeddedDex = Bool(a.appProperties.Use_embedded_dex)
+
// Check if the install APK name needs to be overridden.
a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Name())
@@ -584,8 +592,6 @@
a.additionalAaptFlags = append(a.additionalAaptFlags, "--rename-instrumentation-target-package "+manifestPackageName)
}
}
- a.aapt.useEmbeddedNativeLibs = a.useEmbeddedNativeLibs(ctx)
- a.aapt.useEmbeddedDex = Bool(a.appProperties.Use_embedded_dex)
a.generateAndroidBuildActions(ctx)
a.testConfig = tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config, a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites)
@@ -727,8 +733,9 @@
android.DefaultableModuleBase
prebuilt android.Prebuilt
- properties AndroidAppImportProperties
- dpiVariants interface{}
+ properties AndroidAppImportProperties
+ dpiVariants interface{}
+ archVariants interface{}
outputFile android.Path
certificate *Certificate
@@ -736,6 +743,8 @@
dexpreopter
usesLibrary usesLibrary
+
+ installPath android.OutputPath
}
type AndroidAppImportProperties struct {
@@ -761,10 +770,13 @@
// binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
// from PRODUCT_PACKAGES.
Overrides []string
+
+ // Optional name for the installed app. If unspecified, it is derived from the module name.
+ Filename *string
}
-// Chooses a source APK path to use based on the module and product specs.
-func (a *AndroidAppImport) updateSrcApkPath(ctx android.LoadHookContext) {
+// Updates properties with variant-specific values.
+func (a *AndroidAppImport) processVariants(ctx android.LoadHookContext) {
config := ctx.Config()
dpiProps := reflect.ValueOf(a.dpiVariants).Elem().FieldByName("Dpi_variants")
@@ -772,24 +784,22 @@
// overwrites everything else.
// TODO(jungjw): Can we optimize this by making it priority order?
for i := len(config.ProductAAPTPrebuiltDPI()) - 1; i >= 0; i-- {
- dpi := config.ProductAAPTPrebuiltDPI()[i]
- if inList(dpi, supportedDpis) {
- MergePropertiesFromVariant(ctx, &a.properties, dpiProps, dpi, "dpi_variants")
- }
+ MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPrebuiltDPI()[i])
}
if config.ProductAAPTPreferredConfig() != "" {
- dpi := config.ProductAAPTPreferredConfig()
- if inList(dpi, supportedDpis) {
- MergePropertiesFromVariant(ctx, &a.properties, dpiProps, dpi, "dpi_variants")
- }
+ MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPreferredConfig())
}
+
+ archProps := reflect.ValueOf(a.archVariants).Elem().FieldByName("Arch")
+ archType := ctx.Config().Targets[android.Android][0].Arch.ArchType
+ MergePropertiesFromVariant(ctx, &a.properties, archProps, archType.Name)
}
func MergePropertiesFromVariant(ctx android.BaseModuleContext,
- dst interface{}, variantGroup reflect.Value, variant, variantGroupPath string) {
+ dst interface{}, variantGroup reflect.Value, variant string) {
src := variantGroup.FieldByName(proptools.FieldNameForProperty(variant))
if !src.IsValid() {
- ctx.ModuleErrorf("field %q does not exist", variantGroupPath+"."+variant)
+ return
}
err := proptools.ExtendMatchingProperties([]interface{}{dst}, src.Interface(), nil, proptools.OrderAppend)
@@ -816,7 +826,7 @@
rule := android.NewRuleBuilder()
rule.Command().
Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
- Tool(ctx.Config().HostToolPath(ctx, "zip2zip")).
+ BuiltTool(ctx, "zip2zip").
FlagWithInput("-i ", inputPath).
FlagWithOutput("-o ", outputPath).
FlagWithArg("-0 ", "'lib/**/*.so'").
@@ -843,7 +853,7 @@
rule := android.NewRuleBuilder()
rule.Command().
Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
- Tool(ctx.Config().HostToolPath(ctx, "zip2zip")).
+ BuiltTool(ctx, "zip2zip").
FlagWithInput("-i ", inputPath).
FlagWithOutput("-o ", outputPath).
FlagWithArg("-0 ", "'classes*.dex'").
@@ -913,7 +923,8 @@
// TODO: Optionally compress the output apk.
- ctx.InstallFile(installDir, a.BaseModuleName()+".apk", a.outputFile)
+ a.installPath = ctx.InstallFile(installDir,
+ proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".apk"), a.outputFile)
// TODO: androidmk converter jni libs
}
@@ -926,26 +937,46 @@
return a.prebuilt.Name(a.ModuleBase.Name())
}
-// Populates dpi_variants property and its fields at creation time.
-func (a *AndroidAppImport) addDpiVariants() {
- // TODO(jungjw): Do we want to do some filtering here?
- props := reflect.ValueOf(&a.properties).Type()
+var dpiVariantGroupType reflect.Type
+var archVariantGroupType reflect.Type
- dpiFields := make([]reflect.StructField, len(supportedDpis))
- for i, dpi := range supportedDpis {
- dpiFields[i] = reflect.StructField{
- Name: proptools.FieldNameForProperty(dpi),
+func initAndroidAppImportVariantGroupTypes() {
+ dpiVariantGroupType = createVariantGroupType(supportedDpis, "Dpi_variants")
+
+ archNames := make([]string, len(android.ArchTypeList()))
+ for i, archType := range android.ArchTypeList() {
+ archNames[i] = archType.Name
+ }
+ archVariantGroupType = createVariantGroupType(archNames, "Arch")
+}
+
+// Populates all variant struct properties at creation time.
+func (a *AndroidAppImport) populateAllVariantStructs() {
+ a.dpiVariants = reflect.New(dpiVariantGroupType).Interface()
+ a.AddProperties(a.dpiVariants)
+
+ a.archVariants = reflect.New(archVariantGroupType).Interface()
+ a.AddProperties(a.archVariants)
+}
+
+func createVariantGroupType(variants []string, variantGroupName string) reflect.Type {
+ props := reflect.TypeOf((*AndroidAppImportProperties)(nil))
+
+ variantFields := make([]reflect.StructField, len(variants))
+ for i, variant := range variants {
+ variantFields[i] = reflect.StructField{
+ Name: proptools.FieldNameForProperty(variant),
Type: props,
}
}
- dpiStruct := reflect.StructOf(dpiFields)
- a.dpiVariants = reflect.New(reflect.StructOf([]reflect.StructField{
+
+ variantGroupStruct := reflect.StructOf(variantFields)
+ return reflect.StructOf([]reflect.StructField{
{
- Name: "Dpi_variants",
- Type: dpiStruct,
+ Name: variantGroupName,
+ Type: variantGroupStruct,
},
- })).Interface()
- a.AddProperties(a.dpiVariants)
+ })
}
// android_app_import imports a prebuilt apk with additional processing specified in the module.
@@ -969,9 +1000,9 @@
module.AddProperties(&module.properties)
module.AddProperties(&module.dexpreoptProperties)
module.AddProperties(&module.usesLibrary.usesLibraryProperties)
- module.addDpiVariants()
+ module.populateAllVariantStructs()
android.AddLoadHook(module, func(ctx android.LoadHookContext) {
- module.updateSrcApkPath(ctx)
+ module.processVariants(ctx)
})
InitJavaModule(module, android.DeviceSupported)
@@ -1067,7 +1098,7 @@
outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml")
rule := android.NewRuleBuilder()
- cmd := rule.Command().Tool(ctx.Config().HostToolPath(ctx, "manifest_check")).
+ cmd := rule.Command().BuiltTool(ctx, "manifest_check").
Flag("--enforce-uses-libraries").
Input(manifest).
FlagWithOutput("-o ", outputFile)
diff --git a/java/app_test.go b/java/app_test.go
index f08969d..564211c 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -43,7 +43,7 @@
}
)
-func testAppContext(config android.Config, bp string, fs map[string][]byte) *android.TestContext {
+func testAppContext(bp string, fs map[string][]byte) *android.TestContext {
appFS := map[string][]byte{}
for k, v := range fs {
appFS[k] = v
@@ -53,13 +53,13 @@
appFS[file] = nil
}
- return testContext(config, bp, appFS)
+ return testContext(bp, appFS)
}
func testApp(t *testing.T, bp string) *android.TestContext {
config := testConfig(nil)
- ctx := testAppContext(config, bp, nil)
+ ctx := testAppContext(bp, nil)
run(t, ctx, config)
@@ -72,6 +72,7 @@
ctx := testApp(t, moduleType+` {
name: "foo",
srcs: ["a.java"],
+ sdk_version: "current"
}
`)
@@ -117,6 +118,7 @@
name: "foo",
srcs: ["a.java"],
package_splits: ["v4", "v7,hdpi"],
+ sdk_version: "current"
}`)
foo := ctx.ModuleForTests("foo", "android_common")
@@ -139,6 +141,40 @@
}
}
+func TestPlatformAPIs(t *testing.T) {
+ testJava(t, `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ platform_apis: true,
+ }
+ `)
+
+ testJava(t, `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ sdk_version: "current",
+ }
+ `)
+
+ testJavaError(t, "platform_apis must be true when sdk_version is empty.", `
+ android_app {
+ name: "bar",
+ srcs: ["b.java"],
+ }
+ `)
+
+ testJavaError(t, "platform_apis must be false when sdk_version is not empty.", `
+ android_app {
+ name: "bar",
+ srcs: ["b.java"],
+ sdk_version: "system_current",
+ platform_apis: true,
+ }
+ `)
+}
+
func TestResourceDirs(t *testing.T) {
testCases := []struct {
name string
@@ -169,6 +205,7 @@
bp := `
android_app {
name: "foo",
+ sdk_version: "current",
%s
}
`
@@ -176,7 +213,7 @@
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
config := testConfig(nil)
- ctx := testContext(config, fmt.Sprintf(bp, testCase.prop), fs)
+ ctx := testContext(fmt.Sprintf(bp, testCase.prop), fs)
run(t, ctx, config)
module := ctx.ModuleForTests("foo", "android_common")
@@ -349,12 +386,14 @@
bp := `
android_app {
name: "foo",
+ sdk_version: "current",
resource_dirs: ["foo/res"],
static_libs: ["lib", "lib3"],
}
android_app {
name: "bar",
+ sdk_version: "current",
resource_dirs: ["bar/res"],
}
@@ -388,7 +427,7 @@
config.TestProductVariables.EnforceRROExcludedOverlays = testCase.enforceRROExcludedOverlays
}
- ctx := testAppContext(config, bp, fs)
+ ctx := testAppContext(bp, fs)
run(t, ctx, config)
resourceListToFiles := func(module android.TestingModule, list []string) (files []string) {
@@ -461,6 +500,7 @@
platformSdkCodename string
platformSdkFinal bool
expectedMinSdkVersion string
+ platformApis bool
}{
{
name: "current final SDK",
@@ -481,6 +521,7 @@
{
name: "default final SDK",
sdkVersion: "",
+ platformApis: true,
platformSdkInt: 27,
platformSdkCodename: "REL",
platformSdkFinal: true,
@@ -489,6 +530,7 @@
{
name: "default non-final SDK",
sdkVersion: "",
+ platformApis: true,
platformSdkInt: 27,
platformSdkCodename: "OMR1",
platformSdkFinal: false,
@@ -504,18 +546,23 @@
for _, moduleType := range []string{"android_app", "android_library"} {
for _, test := range testCases {
t.Run(moduleType+" "+test.name, func(t *testing.T) {
+ platformApiProp := ""
+ if test.platformApis {
+ platformApiProp = "platform_apis: true,"
+ }
bp := fmt.Sprintf(`%s {
name: "foo",
srcs: ["a.java"],
sdk_version: "%s",
- }`, moduleType, test.sdkVersion)
+ %s
+ }`, moduleType, test.sdkVersion, platformApiProp)
config := testConfig(nil)
config.TestProductVariables.Platform_sdk_version = &test.platformSdkInt
config.TestProductVariables.Platform_sdk_codename = &test.platformSdkCodename
config.TestProductVariables.Platform_sdk_final = &test.platformSdkFinal
- ctx := testAppContext(config, bp, nil)
+ ctx := testAppContext(bp, nil)
run(t, ctx, config)
@@ -547,7 +594,7 @@
}
func TestJNIABI(t *testing.T) {
- ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+ ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
cc_library {
name: "libjni",
system_shared_libs: [],
@@ -620,7 +667,7 @@
}
func TestJNIPackaging(t *testing.T) {
- ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+ ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
cc_library {
name: "libjni",
system_shared_libs: [],
@@ -630,18 +677,21 @@
android_app {
name: "app",
jni_libs: ["libjni"],
+ sdk_version: "current",
}
android_app {
name: "app_noembed",
jni_libs: ["libjni"],
use_embedded_native_libs: false,
+ sdk_version: "current",
}
android_app {
name: "app_embed",
jni_libs: ["libjni"],
use_embedded_native_libs: true,
+ sdk_version: "current",
}
android_test {
@@ -715,6 +765,7 @@
android_app {
name: "foo",
srcs: ["a.java"],
+ sdk_version: "current",
}
`,
certificateOverride: "",
@@ -726,7 +777,8 @@
android_app {
name: "foo",
srcs: ["a.java"],
- certificate: ":new_certificate"
+ certificate: ":new_certificate",
+ sdk_version: "current",
}
android_app_certificate {
@@ -743,7 +795,8 @@
android_app {
name: "foo",
srcs: ["a.java"],
- certificate: "expiredkey"
+ certificate: "expiredkey",
+ sdk_version: "current",
}
`,
certificateOverride: "",
@@ -755,7 +808,8 @@
android_app {
name: "foo",
srcs: ["a.java"],
- certificate: "expiredkey"
+ certificate: "expiredkey",
+ sdk_version: "current",
}
android_app_certificate {
@@ -774,7 +828,7 @@
if test.certificateOverride != "" {
config.TestProductVariables.CertificateOverrides = []string{test.certificateOverride}
}
- ctx := testAppContext(config, test.bp, nil)
+ ctx := testAppContext(test.bp, nil)
run(t, ctx, config)
foo := ctx.ModuleForTests("foo", "android_common")
@@ -801,6 +855,7 @@
android_app {
name: "foo",
srcs: ["a.java"],
+ sdk_version: "current",
}
`,
packageNameOverride: "",
@@ -815,6 +870,7 @@
android_app {
name: "foo",
srcs: ["a.java"],
+ sdk_version: "current",
}
`,
packageNameOverride: "foo:bar",
@@ -832,7 +888,7 @@
if test.packageNameOverride != "" {
config.TestProductVariables.PackageNameOverrides = []string{test.packageNameOverride}
}
- ctx := testAppContext(config, test.bp, nil)
+ ctx := testAppContext(test.bp, nil)
run(t, ctx, config)
foo := ctx.ModuleForTests("foo", "android_common")
@@ -856,16 +912,18 @@
android_app {
name: "foo",
srcs: ["a.java"],
+ sdk_version: "current",
}
android_test {
name: "bar",
instrumentation_for: "foo",
+ sdk_version: "current",
}
`
config := testConfig(nil)
config.TestProductVariables.ManifestPackageNameOverrides = []string{"foo:org.dandroid.bp"}
- ctx := testAppContext(config, bp, nil)
+ ctx := testAppContext(bp, nil)
run(t, ctx, config)
@@ -879,12 +937,13 @@
}
func TestOverrideAndroidApp(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
android_app {
name: "foo",
srcs: ["a.java"],
certificate: "expiredkey",
overrides: ["qux"],
+ sdk_version: "current",
}
override_android_app {
@@ -980,10 +1039,11 @@
}
func TestOverrideAndroidAppDependency(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
android_app {
name: "foo",
srcs: ["a.java"],
+ sdk_version: "current",
}
override_android_app {
@@ -1021,7 +1081,7 @@
}
func TestAndroidAppImport(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
android_app_import {
name: "foo",
apk: "prebuilts/apk/app.apk",
@@ -1050,7 +1110,7 @@
}
func TestAndroidAppImport_NoDexPreopt(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
android_app_import {
name: "foo",
apk: "prebuilts/apk/app.apk",
@@ -1071,7 +1131,7 @@
}
func TestAndroidAppImport_Presigned(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
android_app_import {
name: "foo",
apk: "prebuilts/apk/app.apk",
@@ -1166,7 +1226,7 @@
config := testConfig(nil)
config.TestProductVariables.AAPTPreferredConfig = test.aaptPreferredConfig
config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
- ctx := testAppContext(config, bp, nil)
+ ctx := testAppContext(bp, nil)
run(t, ctx, config)
@@ -1182,8 +1242,118 @@
}
}
+func TestAndroidAppImport_Filename(t *testing.T) {
+ ctx, config := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ presigned: true,
+ }
+
+ android_app_import {
+ name: "bar",
+ apk: "prebuilts/apk/app.apk",
+ presigned: true,
+ filename: "bar_sample.apk"
+ }
+ `)
+
+ testCases := []struct {
+ name string
+ expected string
+ }{
+ {
+ name: "foo",
+ expected: "foo.apk",
+ },
+ {
+ name: "bar",
+ expected: "bar_sample.apk",
+ },
+ }
+
+ for _, test := range testCases {
+ variant := ctx.ModuleForTests(test.name, "android_common")
+ if variant.MaybeOutput(test.expected).Rule == nil {
+ t.Errorf("can't find output named %q - all outputs: %v", test.expected, variant.AllOutputs())
+ }
+
+ a := variant.Module().(*AndroidAppImport)
+ expectedValues := []string{test.expected}
+ actualValues := android.AndroidMkEntriesForTest(
+ t, config, "", a).EntryMap["LOCAL_INSTALLED_MODULE_STEM"]
+ if !reflect.DeepEqual(actualValues, expectedValues) {
+ t.Errorf("Incorrect LOCAL_INSTALLED_MODULE_STEM value '%s', expected '%s'",
+ actualValues, expectedValues)
+ }
+ }
+}
+
+func TestAndroidAppImport_ArchVariants(t *testing.T) {
+ // The test config's target arch is ARM64.
+ testCases := []struct {
+ name string
+ bp string
+ expected string
+ }{
+ {
+ name: "matching arch",
+ bp: `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ arch: {
+ arm64: {
+ apk: "prebuilts/apk/app_arm64.apk",
+ },
+ },
+ certificate: "PRESIGNED",
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `,
+ expected: "prebuilts/apk/app_arm64.apk",
+ },
+ {
+ name: "no matching arch",
+ bp: `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ arch: {
+ arm: {
+ apk: "prebuilts/apk/app_arm.apk",
+ },
+ },
+ certificate: "PRESIGNED",
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `,
+ expected: "prebuilts/apk/app.apk",
+ },
+ }
+
+ jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
+ for _, test := range testCases {
+ ctx, _ := testJava(t, test.bp)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+ jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
+ matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
+ if len(matches) != 2 {
+ t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
+ }
+ if test.expected != matches[1] {
+ t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
+ }
+ }
+}
+
func TestStl(t *testing.T) {
- ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+ ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
cc_library {
name: "libjni",
}
@@ -1253,18 +1423,21 @@
name: "foo",
srcs: ["a.java"],
api_packages: ["foo"],
+ sdk_version: "current",
}
java_sdk_library {
name: "bar",
srcs: ["a.java"],
api_packages: ["bar"],
+ sdk_version: "current",
}
android_app {
name: "app",
srcs: ["a.java"],
uses_libs: ["foo"],
+ sdk_version: "current",
optional_uses_libs: [
"bar",
"baz",
@@ -1286,7 +1459,7 @@
config := testConfig(nil)
config.TestProductVariables.MissingUsesLibraries = []string{"baz"}
- ctx := testAppContext(config, bp, nil)
+ ctx := testAppContext(bp, nil)
run(t, ctx, config)
@@ -1339,6 +1512,7 @@
android_app {
name: "foo",
srcs: ["a.java"],
+ sdk_version: "current",
}
`,
noCode: false,
@@ -1348,6 +1522,7 @@
bp: `
android_app {
name: "foo",
+ sdk_version: "current",
}
`,
noCode: true,
@@ -1358,11 +1533,13 @@
android_app {
name: "foo",
static_libs: ["lib"],
+ sdk_version: "current",
}
java_library {
name: "lib",
srcs: ["a.java"],
+ sdk_version: "current",
}
`,
noCode: false,
@@ -1373,10 +1550,12 @@
android_app {
name: "foo",
static_libs: ["lib"],
+ sdk_version: "current",
}
java_library {
name: "lib",
+ sdk_version: "current",
}
`,
// TODO(jungjw): this should probably be true
@@ -1398,7 +1577,7 @@
}
func TestEmbedNotice(t *testing.T) {
- ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+ ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
android_app {
name: "foo",
srcs: ["a.java"],
@@ -1406,6 +1585,7 @@
jni_libs: ["libjni"],
notice: "APP_NOTICE",
embed_notices: true,
+ sdk_version: "current",
}
// No embed_notice flag
@@ -1414,6 +1594,7 @@
srcs: ["a.java"],
jni_libs: ["libjni"],
notice: "APP_NOTICE",
+ sdk_version: "current",
}
// No NOTICE files
@@ -1421,6 +1602,7 @@
name: "baz",
srcs: ["a.java"],
embed_notices: true,
+ sdk_version: "current",
}
cc_library {
@@ -1435,6 +1617,7 @@
srcs: [
":gen",
],
+ sdk_version: "current",
}
genrule {
@@ -1510,6 +1693,7 @@
android_app {
name: "foo",
srcs: ["a.java"],
+ sdk_version: "current",
}
`,
uncompressedPlatform: true,
@@ -1522,6 +1706,7 @@
name: "foo",
use_embedded_dex: true,
srcs: ["a.java"],
+ sdk_version: "current",
}
`,
uncompressedPlatform: true,
@@ -1534,6 +1719,7 @@
name: "foo",
privileged: true,
srcs: ["a.java"],
+ sdk_version: "current",
}
`,
uncompressedPlatform: true,
@@ -1549,7 +1735,7 @@
config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
}
- ctx := testAppContext(config, bp, nil)
+ ctx := testAppContext(bp, nil)
run(t, ctx, config)
diff --git a/java/builder.go b/java/builder.go
index a48e8b1..f174cf0 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -62,6 +62,37 @@
"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
"outDir", "annoDir", "javaVersion")
+ _ = pctx.VariableFunc("kytheCorpus",
+ func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
+ // Run it with -add-opens=java.base/java.nio=ALL-UNNAMED to avoid JDK9's warning about
+ // "Illegal reflective access by com.google.protobuf.Utf8$UnsafeProcessor ...
+ // to field java.nio.Buffer.address"
+ kytheExtract = pctx.AndroidStaticRule("kythe",
+ blueprint.RuleParams{
+ Command: `${config.ZipSyncCmd} -d $srcJarDir ` +
+ `-l $srcJarDir/list -f "*.java" $srcJars && ` +
+ `( [ ! -s $srcJarDir/list -a ! -s $out.rsp ] || ` +
+ `KYTHE_ROOT_DIRECTORY=. KYTHE_OUTPUT_FILE=$out ` +
+ `KYTHE_CORPUS=${kytheCorpus} ` +
+ `${config.SoongJavacWrapper} ${config.JavaCmd} ` +
+ `--add-opens=java.base/java.nio=ALL-UNNAMED ` +
+ `-jar ${config.JavaKytheExtractorJar} ` +
+ `${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
+ `$processorpath $processor $javacFlags $bootClasspath $classpath ` +
+ `-source $javaVersion -target $javaVersion ` +
+ `-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list)`,
+ CommandDeps: []string{
+ "${config.JavaCmd}",
+ "${config.JavaKytheExtractorJar}",
+ "${config.ZipSyncCmd}",
+ },
+ CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
+ Rspfile: "$out.rsp",
+ RspfileContent: "$in",
+ },
+ "javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
+ "outDir", "annoDir", "javaVersion")
+
turbine = pctx.AndroidStaticRule("turbine",
blueprint.RuleParams{
Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
@@ -148,16 +179,15 @@
}
type javaBuilderFlags struct {
- javacFlags string
- bootClasspath classpath
- classpath classpath
- processorPath classpath
- processor string
- systemModules classpath
- systemModulesDeps android.Paths
- aidlFlags string
- aidlDeps android.Paths
- javaVersion string
+ javacFlags string
+ bootClasspath classpath
+ classpath classpath
+ processorPath classpath
+ processor string
+ systemModules *systemModules
+ aidlFlags string
+ aidlDeps android.Paths
+ javaVersion string
errorProneExtraJavacFlags string
errorProneProcessorPath classpath
@@ -197,6 +227,61 @@
"errorprone", "errorprone")
}
+// Emits the rule to generate Xref input file (.kzip file) for the given set of source files and source jars
+// to compile with given set of builder flags, etc.
+func emitXrefRule(ctx android.ModuleContext, xrefFile android.WritablePath,
+ srcFiles, srcJars android.Paths,
+ flags javaBuilderFlags, deps android.Paths,
+ intermediatesDir string) {
+
+ deps = append(deps, srcJars...)
+
+ var bootClasspath string
+ if flags.javaVersion == "1.9" {
+ var systemModuleDeps android.Paths
+ bootClasspath, systemModuleDeps = flags.systemModules.FormJavaSystemModulesPath(ctx.Device())
+ deps = append(deps, systemModuleDeps...)
+ } else {
+ deps = append(deps, flags.bootClasspath...)
+ if len(flags.bootClasspath) == 0 && ctx.Device() {
+ // explicitly specify -bootclasspath "" if the bootclasspath is empty to
+ // ensure java does not fall back to the default bootclasspath.
+ bootClasspath = `-bootclasspath ""`
+ } else {
+ bootClasspath = flags.bootClasspath.FormJavaClassPath("-bootclasspath")
+ }
+ }
+
+ deps = append(deps, flags.classpath...)
+ deps = append(deps, flags.processorPath...)
+
+ processor := "-proc:none"
+ if flags.processor != "" {
+ processor = "-processor " + flags.processor
+ }
+
+ ctx.Build(pctx,
+ android.BuildParams{
+ Rule: kytheExtract,
+ Description: "Xref Java extractor",
+ Output: xrefFile,
+ Inputs: srcFiles,
+ Implicits: deps,
+ Args: map[string]string{
+ "annoDir": android.PathForModuleOut(ctx, intermediatesDir, "anno").String(),
+ "bootClasspath": bootClasspath,
+ "classpath": flags.classpath.FormJavaClassPath("-classpath"),
+ "javacFlags": flags.javacFlags,
+ "javaVersion": flags.javaVersion,
+ "outDir": android.PathForModuleOut(ctx, "javac", "classes.xref").String(),
+ "processorpath": flags.processorPath.FormJavaClassPath("-processorpath"),
+ "processor": processor,
+ "srcJarDir": android.PathForModuleOut(ctx, intermediatesDir, "srcjars.xref").String(),
+ "srcJars": strings.Join(srcJars.Strings(), " "),
+ },
+ })
+}
+
func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath,
srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
@@ -249,8 +334,9 @@
var bootClasspath string
if flags.javaVersion == "1.9" {
- deps = append(deps, flags.systemModulesDeps...)
- bootClasspath = flags.systemModules.FormJavaSystemModulesPath("--system=", ctx.Device())
+ var systemModuleDeps android.Paths
+ bootClasspath, systemModuleDeps = flags.systemModules.FormJavaSystemModulesPath(ctx.Device())
+ deps = append(deps, systemModuleDeps...)
} else {
deps = append(deps, flags.bootClasspath...)
if len(flags.bootClasspath) == 0 && ctx.Device() {
@@ -411,7 +497,7 @@
})
}
-type classpath []android.Path
+type classpath android.Paths
func (x *classpath) FormJavaClassPath(optName string) string {
if optName != "" && !strings.HasSuffix(optName, "=") && !strings.HasSuffix(optName, " ") {
@@ -424,21 +510,6 @@
}
}
-// Returns a --system argument in the form javac expects with -source 1.9. If forceEmpty is true,
-// returns --system=none if the list is empty to ensure javac does not fall back to the default
-// system modules.
-func (x *classpath) FormJavaSystemModulesPath(optName string, forceEmpty bool) string {
- if len(*x) > 1 {
- panic("more than one system module")
- } else if len(*x) == 1 {
- return optName + (*x)[0].String()
- } else if forceEmpty {
- return optName + "none"
- } else {
- return ""
- }
-}
-
func (x *classpath) FormTurbineClasspath(optName string) []string {
if x == nil || *x == nil {
return nil
@@ -466,3 +537,21 @@
}
return ret
}
+
+type systemModules struct {
+ dir android.Path
+ deps android.Paths
+}
+
+// Returns a --system argument in the form javac expects with -source 1.9. If forceEmpty is true,
+// returns --system=none if the list is empty to ensure javac does not fall back to the default
+// system modules.
+func (x *systemModules) FormJavaSystemModulesPath(forceEmpty bool) (string, android.Paths) {
+ if x != nil {
+ return "--system=" + x.dir.String(), x.deps
+ } else if forceEmpty {
+ return "--system=none", nil
+ } else {
+ return "", nil
+ }
+}
diff --git a/java/config/config.go b/java/config/config.go
index d017ae6..b371cbf 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -42,11 +42,17 @@
"android.car",
"android.car7",
"conscrypt",
+ "core-icu4j",
"core-oj",
"core-libart",
}
)
+const (
+ JavaVmFlags = `-XX:OnError="cat hs_err_pid%p.log" -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads`
+ JavacVmFlags = `-J-XX:OnError="cat hs_err_pid%p.log" -J-XX:CICompilerCount=6 -J-XX:+UseDynamicNumberOfGCThreads`
+)
+
func init() {
pctx.Import("github.com/google/blueprint/bootstrap")
@@ -69,8 +75,9 @@
// b/65004097: prevent using java.lang.invoke.StringConcatFactory when using -target 1.9
`-XDstringConcat=inline`,
}, " "))
- pctx.StaticVariable("JavaVmFlags", "-XX:OnError=\"cat hs_err_pid%p.log\" -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads")
- pctx.StaticVariable("JavacVmFlags", "-J-XX:OnError=\"cat hs_err_pid%p.log\" -J-XX:CICompilerCount=6 -J-XX:+UseDynamicNumberOfGCThreads")
+
+ pctx.StaticVariable("JavaVmFlags", JavaVmFlags)
+ pctx.StaticVariable("JavacVmFlags", JavacVmFlags)
pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS)
@@ -88,6 +95,7 @@
pctx.SourcePathVariable("JlinkCmd", "${JavaToolchain}/jlink")
pctx.SourcePathVariable("JmodCmd", "${JavaToolchain}/jmod")
pctx.SourcePathVariable("JrtFsJar", "${JavaHome}/lib/jrt-fs.jar")
+ pctx.SourcePathVariable("JavaKytheExtractorJar", "prebuilts/build-tools/common/framework/javac_extractor.jar")
pctx.SourcePathVariable("Ziptime", "prebuilts/build-tools/${hostPrebuiltTag}/bin/ziptime")
pctx.SourcePathVariable("GenKotlinBuildFileCmd", "build/soong/scripts/gen-kotlin-build-file.sh")
@@ -154,3 +162,41 @@
pctx.HostBinToolVariable("Class2Greylist", "class2greylist")
pctx.HostBinToolVariable("HiddenAPI", "hiddenapi")
}
+
+// JavaCmd returns a SourcePath object with the path to the java command.
+func JavaCmd(ctx android.PathContext) android.SourcePath {
+ return javaTool(ctx, "java")
+}
+
+// JavadocCmd returns a SourcePath object with the path to the java command.
+func JavadocCmd(ctx android.PathContext) android.SourcePath {
+ return javaTool(ctx, "javadoc")
+}
+
+func javaTool(ctx android.PathContext, tool string) android.SourcePath {
+ type javaToolKey string
+
+ key := android.NewCustomOnceKey(javaToolKey(tool))
+
+ return ctx.Config().OnceSourcePath(key, func() android.SourcePath {
+ return javaToolchain(ctx).Join(ctx, tool)
+ })
+
+}
+
+var javaToolchainKey = android.NewOnceKey("javaToolchain")
+
+func javaToolchain(ctx android.PathContext) android.SourcePath {
+ return ctx.Config().OnceSourcePath(javaToolchainKey, func() android.SourcePath {
+ return javaHome(ctx).Join(ctx, "bin")
+ })
+}
+
+var javaHomeKey = android.NewOnceKey("javaHome")
+
+func javaHome(ctx android.PathContext) android.SourcePath {
+ return ctx.Config().OnceSourcePath(javaHomeKey, func() android.SourcePath {
+ // This is set up and guaranteed by soong_ui
+ return android.PathForSource(ctx, ctx.Config().Getenv("ANDROID_JAVA_HOME"))
+ })
+}
diff --git a/java/device_host_converter_test.go b/java/device_host_converter_test.go
index 9b9d0d8..44aae9b 100644
--- a/java/device_host_converter_test.go
+++ b/java/device_host_converter_test.go
@@ -50,9 +50,7 @@
}
`
- config := testConfig(nil)
- ctx := testContext(config, bp, nil)
- run(t, ctx, config)
+ ctx, config := testJava(t, bp)
deviceModule := ctx.ModuleForTests("device_module", "android_common")
deviceTurbineCombined := deviceModule.Output("turbine-combined/device_module.jar")
@@ -133,9 +131,7 @@
}
`
- config := testConfig(nil)
- ctx := testContext(config, bp, nil)
- run(t, ctx, config)
+ ctx, config := testJava(t, bp)
hostModule := ctx.ModuleForTests("host_module", config.BuildOsCommonVariant)
hostJavac := hostModule.Output("javac/host_module.jar")
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index ed12fe6..6214dac 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -143,6 +143,7 @@
strippedDexJarFile := android.PathForModuleOut(ctx, "dexpreopt", dexJarFile.Base())
var profileClassListing android.OptionalPath
+ var profileBootListing android.OptionalPath
profileIsTextListing := false
if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) {
// If dex_preopt.profile_guided is not set, default it based on the existence of the
@@ -150,6 +151,8 @@
if String(d.dexpreoptProperties.Dex_preopt.Profile) != "" {
profileClassListing = android.OptionalPathForPath(
android.PathForModuleSrc(ctx, String(d.dexpreoptProperties.Dex_preopt.Profile)))
+ profileBootListing = android.ExistentPathForSource(ctx,
+ ctx.ModuleDir(), String(d.dexpreoptProperties.Dex_preopt.Profile)+"-boot")
profileIsTextListing = true
} else {
profileClassListing = android.ExistentPathForSource(ctx,
@@ -169,6 +172,7 @@
ProfileClassListing: profileClassListing,
ProfileIsTextListing: profileIsTextListing,
+ ProfileBootListing: profileBootListing,
EnforceUsesLibraries: d.enforceUsesLibs,
PresentOptionalUsesLibraries: d.optionalUsesLibs,
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index fe468a9..2a142ba 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -216,6 +216,7 @@
}
profile := bootImageProfileRule(ctx, image, missingDeps)
+ bootFrameworkProfileRule(ctx, image, missingDeps)
var allFiles android.Paths
@@ -229,7 +230,7 @@
if image.zip != nil {
rule := android.NewRuleBuilder()
rule.Command().
- Tool(ctx.Config().HostToolPath(ctx, "soong_zip")).
+ BuiltTool(ctx, "soong_zip").
FlagWithOutput("-o ", image.zip).
FlagWithArg("-C ", image.dir.String()).
FlagWithInputList("-f ", allFiles, " -f ")
@@ -297,6 +298,7 @@
FlagForEachArg("--dex-location=", image.dexLocations).
Flag("--generate-debug-info").
Flag("--generate-build-id").
+ Flag("--image-format=lz4hc").
FlagWithOutput("--oat-symbols=", symbolsFile).
Flag("--strip").
FlagWithOutput("--oat-file=", outputPath.ReplaceExtension(ctx, "oat")).
@@ -424,6 +426,52 @@
var bootImageProfileRuleKey = android.NewOnceKey("bootImageProfileRule")
+func bootFrameworkProfileRule(ctx android.SingletonContext, image *bootImage, missingDeps []string) android.WritablePath {
+ global := dexpreoptGlobalConfig(ctx)
+
+ if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
+ return nil
+ }
+ return ctx.Config().Once(bootFrameworkProfileRuleKey, func() interface{} {
+ tools := global.Tools
+
+ rule := android.NewRuleBuilder()
+ rule.MissingDeps(missingDeps)
+
+ // Some branches like master-art-host don't have frameworks/base, so manually
+ // handle the case that the default is missing. Those branches won't attempt to build the profile rule,
+ // and if they do they'll get a missing deps error.
+ defaultProfile := "frameworks/base/config/boot-profile.txt"
+ path := android.ExistentPathForSource(ctx, defaultProfile)
+ var bootFrameworkProfile android.Path
+ if path.Valid() {
+ bootFrameworkProfile = path.Path()
+ } else {
+ missingDeps = append(missingDeps, defaultProfile)
+ bootFrameworkProfile = android.PathForOutput(ctx, "missing")
+ }
+
+ profile := image.dir.Join(ctx, "boot.bprof")
+
+ rule.Command().
+ Text(`ANDROID_LOG_TAGS="*:e"`).
+ Tool(tools.Profman).
+ Flag("--generate-boot-profile").
+ FlagWithInput("--create-profile-from=", bootFrameworkProfile).
+ FlagForEachInput("--apk=", image.dexPaths.Paths()).
+ FlagForEachArg("--dex-location=", image.dexLocations).
+ FlagWithOutput("--reference-profile-file=", profile)
+
+ rule.Install(profile, "/system/etc/boot-image.bprof")
+ rule.Build(pctx, ctx, "bootFrameworkProfile", "profile boot framework jars")
+ image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
+
+ return profile
+ }).(android.WritablePath)
+}
+
+var bootFrameworkProfileRuleKey = android.NewOnceKey("bootFrameworkProfileRule")
+
func dumpOatRules(ctx android.SingletonContext, image *bootImage) {
var archs []android.ArchType
for arch := range image.images {
@@ -438,7 +486,7 @@
rule := android.NewRuleBuilder()
rule.Command().
// TODO: for now, use the debug version for better error reporting
- Tool(ctx.Config().HostToolPath(ctx, "oatdumpd")).
+ BuiltTool(ctx, "oatdumpd").
FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPaths.Paths(), ":").
FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocations, ":").
FlagWithArg("--image=", dexpreopt.PathToLocation(image.images[arch], arch)).Implicit(image.images[arch]).
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index f91ff69..4c38399 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -51,7 +51,7 @@
dexpreoptConfig.RuntimeApexJars = []string{"foo", "bar", "baz"}
setDexpreoptTestGlobalConfig(config, dexpreoptConfig)
- ctx := testContext(config, bp, nil)
+ ctx := testContext(bp, nil)
ctx.RegisterSingletonType("dex_bootjars", android.SingletonFactoryAdaptor(dexpreoptBootJarsFactory))
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 7d0109f..5550a4c 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -30,6 +30,7 @@
android_app {
name: "foo",
srcs: ["a.java"],
+ sdk_version: "current",
}`,
enabled: true,
},
@@ -57,6 +58,7 @@
bp: `
android_app {
name: "foo",
+ sdk_version: "current",
}`,
enabled: false,
},
@@ -66,11 +68,13 @@
android_app {
name: "foo",
static_libs: ["lib"],
+ sdk_version: "current",
}
java_library {
name: "lib",
srcs: ["a.java"],
+ sdk_version: "current",
}`,
enabled: true,
},
@@ -142,7 +146,7 @@
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
- ctx := testJava(t, test.bp)
+ ctx, _ := testJava(t, test.bp)
dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeDescription("dexpreopt")
enabled := dexpreopt.Rule != nil
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 25101de..9956270 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -15,126 +15,15 @@
package java
import (
- "android/soong/android"
- "android/soong/java/config"
"fmt"
"path/filepath"
"runtime"
"strings"
- "github.com/google/blueprint"
"github.com/google/blueprint/proptools"
-)
-var (
- javadoc = pctx.AndroidStaticRule("javadoc",
- blueprint.RuleParams{
- Command: `rm -rf "$outDir" "$srcJarDir" "$stubsDir" && mkdir -p "$outDir" "$srcJarDir" "$stubsDir" && ` +
- `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
- `${config.SoongJavacWrapper} ${config.JavadocCmd} -encoding UTF-8 @$out.rsp @$srcJarDir/list ` +
- `$opts $bootclasspathArgs $classpathArgs $sourcepathArgs ` +
- `-d $outDir -quiet && ` +
- `${config.SoongZipCmd} -write_if_changed -d -o $docZip -C $outDir -D $outDir && ` +
- `${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir $postDoclavaCmds && ` +
- `rm -rf "$srcJarDir"`,
-
- CommandDeps: []string{
- "${config.ZipSyncCmd}",
- "${config.JavadocCmd}",
- "${config.SoongZipCmd}",
- },
- CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
- Rspfile: "$out.rsp",
- RspfileContent: "$in",
- Restat: true,
- },
- "outDir", "srcJarDir", "stubsDir", "srcJars", "opts",
- "bootclasspathArgs", "classpathArgs", "sourcepathArgs", "docZip", "postDoclavaCmds")
-
- 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 $srcApiFile $destApiFile && cp -f $srcRemovedApiFile $destRemovedApiFile ) ` +
- `&& touch $out ) || (echo failed to update public API ; exit 38)`,
- },
- "srcApiFile", "destApiFile", "srcRemovedApiFile", "destRemovedApiFile")
-
- metalava = pctx.AndroidStaticRule("metalava",
- blueprint.RuleParams{
- Command: `rm -rf "$outDir" "$srcJarDir" "$stubsDir" && ` +
- `mkdir -p "$outDir" "$srcJarDir" "$stubsDir" && ` +
- `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
- `${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.MetalavaJar} -encoding UTF-8 -source $javaVersion @$out.rsp @$srcJarDir/list ` +
- `$bootclasspathArgs $classpathArgs $sourcepathArgs --no-banner --color --quiet --format=v2 ` +
- `$opts && ` +
- `${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir && ` +
- `rm -rf "$srcJarDir"`,
- CommandDeps: []string{
- "${config.ZipSyncCmd}",
- "${config.JavaCmd}",
- "${config.MetalavaJar}",
- "${config.SoongZipCmd}",
- },
- Rspfile: "$out.rsp",
- RspfileContent: "$in",
- Restat: true,
- },
- "outDir", "srcJarDir", "stubsDir", "srcJars", "javaVersion", "bootclasspathArgs",
- "classpathArgs", "sourcepathArgs", "opts")
-
- metalavaApiCheck = pctx.AndroidStaticRule("metalavaApiCheck",
- blueprint.RuleParams{
- Command: `( rm -rf "$srcJarDir" && mkdir -p "$srcJarDir" && ` +
- `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
- `${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.MetalavaJar} -encoding UTF-8 -source $javaVersion @$out.rsp @$srcJarDir/list ` +
- `$bootclasspathArgs $classpathArgs $sourcepathArgs --no-banner --color --quiet --format=v2 ` +
- `$opts && touch $out && rm -rf "$srcJarDir") || ` +
- `( echo -e "$msg" ; exit 38 )`,
- CommandDeps: []string{
- "${config.ZipSyncCmd}",
- "${config.JavaCmd}",
- "${config.MetalavaJar}",
- },
- Rspfile: "$out.rsp",
- RspfileContent: "$in",
- },
- "srcJarDir", "srcJars", "javaVersion", "bootclasspathArgs", "classpathArgs", "sourcepathArgs", "opts", "msg")
-
- nullabilityWarningsCheck = pctx.AndroidStaticRule("nullabilityWarningsCheck",
- blueprint.RuleParams{
- Command: `( diff $expected $actual && touch $out ) || ( echo -e "$msg" ; exit 38 )`,
- },
- "expected", "actual", "msg")
-
- dokka = pctx.AndroidStaticRule("dokka",
- blueprint.RuleParams{
- Command: `rm -rf "$outDir" "$srcJarDir" "$stubsDir" && ` +
- `mkdir -p "$outDir" "$srcJarDir" "$stubsDir" && ` +
- `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
- `${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.DokkaJar} $srcJarDir ` +
- `$classpathArgs -format dac -dacRoot /reference/kotlin -output $outDir $opts && ` +
- `${config.SoongZipCmd} -write_if_changed -d -o $docZip -C $outDir -D $outDir && ` +
- `${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir && ` +
- `rm -rf "$srcJarDir"`,
- CommandDeps: []string{
- "${config.ZipSyncCmd}",
- "${config.DokkaJar}",
- "${config.MetalavaJar}",
- "${config.SoongZipCmd}",
- },
- Restat: true,
- },
- "outDir", "srcJarDir", "stubsDir", "srcJars", "classpathArgs", "opts", "docZip")
+ "android/soong/android"
+ "android/soong/java/config"
)
func init() {
@@ -222,6 +111,9 @@
// :module syntax).
Removed_api_file *string `android:"path"`
+ // If not blank, path to the baseline txt file for approved API check violations.
+ Baseline_file *string `android:"path"`
+
// Arguments to the apicheck tool.
Args *string
}
@@ -238,7 +130,7 @@
// proofread file contains all of the text content of the javadocs concatenated into one file,
// suitable for spell-checking and other goodness.
- Proofread_file *string `android:"path"`
+ Proofread_file *string
// a todo file lists the program elements that are missing documentation.
// At some point, this might be improved to show more warnings.
@@ -404,14 +296,6 @@
doclavaStubsFlags string
doclavaDocsFlags string
postDoclavaCmds string
-
- metalavaStubsFlags string
- metalavaAnnotationsFlags string
- metalavaMergeAnnoDirFlags string
- metalavaInclusionAnnotationsFlags string
- metalavaApiLevelsAnnotationsFlags string
-
- metalavaApiToXmlFlags string
}
func InitDroiddocModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
@@ -454,23 +338,6 @@
ApiFilePath() android.Path
}
-func transformUpdateApi(ctx android.ModuleContext, destApiFile, destRemovedApiFile,
- srcApiFile, srcRemovedApiFile android.Path, output android.WritablePath) {
- ctx.Build(pctx, android.BuildParams{
- Rule: updateApi,
- Description: "Update API",
- Output: output,
- Implicits: append(android.Paths{}, srcApiFile, srcRemovedApiFile,
- destApiFile, destRemovedApiFile),
- Args: map[string]string{
- "destApiFile": destApiFile.String(),
- "srcApiFile": srcApiFile.String(),
- "destRemovedApiFile": destRemovedApiFile.String(),
- "srcRemovedApiFile": srcRemovedApiFile.String(),
- },
- })
-}
-
//
// Javadoc
//
@@ -500,6 +367,7 @@
}
}
+// javadoc converts .java source files to documentation using javadoc.
func JavadocFactory() android.Module {
module := &Javadoc{}
@@ -509,6 +377,7 @@
return module
}
+// javadoc_host converts .java source files to documentation using javadoc.
func JavadocHostFactory() android.Module {
module := &Javadoc{}
@@ -653,6 +522,7 @@
deps.classpath = append(deps.classpath, dep.SdkImplementationJars(ctx, j.sdkVersion())...)
case Dependency:
deps.classpath = append(deps.classpath, dep.HeaderJars()...)
+ deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
case android.SourceFileProducer:
checkProducesJars(ctx, dep)
deps.classpath = append(deps.classpath, dep.Srcs()...)
@@ -689,8 +559,7 @@
if sm.outputDir == nil && len(sm.outputDeps) == 0 {
panic("Missing directory for system module dependency")
}
- deps.systemModules = sm.outputDir
- deps.systemModulesDeps = sm.outputDeps
+ deps.systemModules = &systemModules{sm.outputDir, sm.outputDeps}
}
})
// do not pass exclude_srcs directly when expanding srcFiles since exclude_srcs
@@ -706,9 +575,6 @@
j.srcFiles = srcFiles.FilterOutByExt(".srcjar")
j.srcFiles = append(j.srcFiles, deps.srcs...)
- j.docZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"docs.zip")
- j.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
-
if j.properties.Local_sourcepaths == nil && len(j.srcFiles) > 0 {
j.properties.Local_sourcepaths = append(j.properties.Local_sourcepaths, ".")
}
@@ -759,52 +625,43 @@
func (j *Javadoc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
deps := j.collectDeps(ctx)
- var implicits android.Paths
- implicits = append(implicits, deps.bootClasspath...)
- implicits = append(implicits, deps.classpath...)
+ j.docZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"docs.zip")
- var bootClasspathArgs, classpathArgs, sourcepathArgs string
+ outDir := android.PathForModuleOut(ctx, "out")
+ srcJarDir := android.PathForModuleOut(ctx, "srcjars")
+
+ j.stubsSrcJar = nil
+
+ rule := android.NewRuleBuilder()
+
+ rule.Command().Text("rm -rf").Text(outDir.String())
+ rule.Command().Text("mkdir -p").Text(outDir.String())
+
+ srcJarList := zipSyncCmd(ctx, rule, srcJarDir, j.srcJars)
javaVersion := getJavaVersion(ctx, String(j.properties.Java_version), sdkContext(j))
- if len(deps.bootClasspath) > 0 {
- var systemModules classpath
- if deps.systemModules != nil {
- systemModules = append(systemModules, deps.systemModules)
- }
- implicits = append(implicits, deps.systemModulesDeps...)
- bootClasspathArgs = systemModules.FormJavaSystemModulesPath("--system ", ctx.Device())
- bootClasspathArgs = bootClasspathArgs + " --patch-module java.base=."
- }
- if len(deps.classpath.Strings()) > 0 {
- classpathArgs = "-classpath " + strings.Join(deps.classpath.Strings(), ":")
- }
- implicits = append(implicits, j.srcJars...)
- implicits = append(implicits, j.argFiles...)
+ cmd := javadocSystemModulesCmd(ctx, rule, j.srcFiles, outDir, srcJarDir, srcJarList,
+ deps.systemModules, deps.classpath, j.sourcepaths)
- opts := "-source " + javaVersion + " -J-Xmx1024m -XDignore.symbol.file -Xdoclint:none"
+ cmd.FlagWithArg("-source ", javaVersion).
+ Flag("-J-Xmx1024m").
+ Flag("-XDignore.symbol.file").
+ Flag("-Xdoclint:none")
- sourcepathArgs = "-sourcepath " + strings.Join(j.sourcepaths.Strings(), ":")
+ rule.Command().
+ BuiltTool(ctx, "soong_zip").
+ Flag("-write_if_changed").
+ Flag("-d").
+ FlagWithOutput("-o ", j.docZip).
+ FlagWithArg("-C ", outDir.String()).
+ FlagWithArg("-D ", outDir.String())
- ctx.Build(pctx, android.BuildParams{
- Rule: javadoc,
- Description: "Javadoc",
- Output: j.stubsSrcJar,
- ImplicitOutput: j.docZip,
- Inputs: j.srcFiles,
- Implicits: implicits,
- Args: map[string]string{
- "outDir": android.PathForModuleOut(ctx, "out").String(),
- "srcJarDir": android.PathForModuleOut(ctx, "srcjars").String(),
- "stubsDir": android.PathForModuleOut(ctx, "stubsDir").String(),
- "srcJars": strings.Join(j.srcJars.Strings(), " "),
- "opts": opts,
- "bootclasspathArgs": bootClasspathArgs,
- "classpathArgs": classpathArgs,
- "sourcepathArgs": sourcepathArgs,
- "docZip": j.docZip.String(),
- },
- })
+ rule.Restat()
+
+ zipSyncCleanupCmd(rule, srcJarDir)
+
+ rule.Build(pctx, ctx, "javadoc", "javadoc")
}
//
@@ -831,6 +688,7 @@
apiFilePath android.Path
}
+// droiddoc converts .java source files to documentation using doclava or dokka.
func DroiddocFactory() android.Module {
module := &Droiddoc{}
@@ -841,6 +699,7 @@
return module
}
+// droiddoc_host converts .java source files to documentation using doclava or dokka.
func DroiddocHostFactory() android.Module {
module := &Droiddoc{}
@@ -867,58 +726,25 @@
}
}
-func (d *Droiddoc) initBuilderFlags(ctx android.ModuleContext, implicits *android.Paths,
- deps deps) (droiddocBuilderFlags, error) {
- var flags droiddocBuilderFlags
-
- *implicits = append(*implicits, deps.bootClasspath...)
- *implicits = append(*implicits, deps.classpath...)
-
- if len(deps.bootClasspath.Strings()) > 0 {
- // For OpenJDK 8 we can use -bootclasspath to define the core libraries code.
- flags.bootClasspathArgs = deps.bootClasspath.FormJavaClassPath("-bootclasspath")
- }
- flags.classpathArgs = deps.classpath.FormJavaClassPath("-classpath")
- // Dokka doesn't support bootClasspath, so combine these two classpath vars for Dokka.
- dokkaClasspath := classpath{}
- dokkaClasspath = append(dokkaClasspath, deps.bootClasspath...)
- dokkaClasspath = append(dokkaClasspath, deps.classpath...)
- flags.dokkaClasspathArgs = dokkaClasspath.FormJavaClassPath("-classpath")
-
- // TODO(nanzhang): Remove this if- statement once we finish migration for all Doclava
- // based stubs generation.
- // In the future, all the docs generation depends on Metalava stubs (droidstubs) srcjar
- // dir. We need add the srcjar dir to -sourcepath arg, so that Javadoc can figure out
- // the correct package name base path.
- if len(d.Javadoc.properties.Local_sourcepaths) > 0 {
- flags.sourcepathArgs = "-sourcepath " + strings.Join(d.Javadoc.sourcepaths.Strings(), ":")
- } else {
- flags.sourcepathArgs = "-sourcepath " + android.PathForModuleOut(ctx, "srcjars").String()
- }
-
- return flags, nil
-}
-
-func (d *Droiddoc) collectDoclavaDocsFlags(ctx android.ModuleContext, implicits *android.Paths,
- jsilver, doclava android.Path) string {
-
- *implicits = append(*implicits, jsilver)
- *implicits = append(*implicits, doclava)
-
+func (d *Droiddoc) doclavaDocsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, docletPath classpath) {
var date string
if runtime.GOOS == "darwin" {
date = `date -r`
} else {
- date = `date -d`
+ date = `date -d @`
}
// Droiddoc always gets "-source 1.8" because it doesn't support 1.9 sources. For modules with 1.9
// sources, droiddoc will get sources produced by metalava which will have already stripped out the
// 1.9 language features.
- args := " -source 1.8 -J-Xmx1600m -J-XX:-OmitStackTraceInFastThrow -XDignore.symbol.file " +
- "-doclet com.google.doclava.Doclava -docletpath " + jsilver.String() + ":" + doclava.String() + " " +
- "-hdf page.build " + ctx.Config().BuildId() + "-" + ctx.Config().BuildNumberFromFile() + " " +
- `-hdf page.now "$$(` + date + ` @$$(cat ` + ctx.Config().Getenv("BUILD_DATETIME_FILE") + `) "+%d %b %Y %k:%M")" `
+ cmd.FlagWithArg("-source ", "1.8").
+ Flag("-J-Xmx1600m").
+ Flag("-J-XX:-OmitStackTraceInFastThrow").
+ Flag("-XDignore.symbol.file").
+ FlagWithArg("-doclet ", "com.google.doclava.Doclava").
+ FlagWithInputList("-docletpath ", docletPath.Paths(), ":").
+ FlagWithArg("-hdf page.build ", ctx.Config().BuildId()+"-"+ctx.Config().BuildNumberFromFile()).
+ FlagWithArg("-hdf page.now ", `"$(`+date+`$(cat `+ctx.Config().Getenv("BUILD_DATETIME_FILE")+`) "+%d %b %Y %k:%M")" `)
if String(d.properties.Custom_template) == "" {
// TODO: This is almost always droiddoc-templates-sdk
@@ -927,23 +753,22 @@
ctx.VisitDirectDepsWithTag(droiddocTemplateTag, func(m android.Module) {
if t, ok := m.(*ExportedDroiddocDir); ok {
- *implicits = append(*implicits, t.deps...)
- args = args + " -templatedir " + t.dir.String()
+ cmd.FlagWithArg("-templatedir ", t.dir.String()).Implicits(t.deps)
} else {
ctx.PropertyErrorf("custom_template", "module %q is not a droiddoc_template", ctx.OtherModuleName(m))
}
})
if len(d.properties.Html_dirs) > 0 {
- htmlDir := d.properties.Html_dirs[0]
- *implicits = append(*implicits, android.PathsForModuleSrc(ctx, []string{filepath.Join(d.properties.Html_dirs[0], "**/*")})...)
- args = args + " -htmldir " + htmlDir
+ htmlDir := android.PathForModuleSrc(ctx, d.properties.Html_dirs[0])
+ cmd.FlagWithArg("-htmldir ", htmlDir.String()).
+ Implicits(android.PathsForModuleSrc(ctx, []string{filepath.Join(d.properties.Html_dirs[0], "**/*")}))
}
if len(d.properties.Html_dirs) > 1 {
- htmlDir2 := d.properties.Html_dirs[1]
- *implicits = append(*implicits, android.PathsForModuleSrc(ctx, []string{filepath.Join(htmlDir2, "**/*")})...)
- args = args + " -htmldir2 " + htmlDir2
+ htmlDir2 := android.PathForModuleSrc(ctx, d.properties.Html_dirs[1])
+ cmd.FlagWithArg("-htmldir2 ", htmlDir2.String()).
+ Implicits(android.PathsForModuleSrc(ctx, []string{filepath.Join(d.properties.Html_dirs[1], "**/*")}))
}
if len(d.properties.Html_dirs) > 2 {
@@ -951,51 +776,43 @@
}
knownTags := android.PathsForModuleSrc(ctx, d.properties.Knowntags)
- *implicits = append(*implicits, knownTags...)
+ cmd.FlagForEachInput("-knowntags ", knownTags)
- for _, kt := range knownTags {
- args = args + " -knowntags " + kt.String()
- }
-
- for _, hdf := range d.properties.Hdf {
- args = args + " -hdf " + hdf
- }
+ cmd.FlagForEachArg("-hdf ", d.properties.Hdf)
if String(d.properties.Proofread_file) != "" {
proofreadFile := android.PathForModuleOut(ctx, String(d.properties.Proofread_file))
- args = args + " -proofread " + proofreadFile.String()
+ cmd.FlagWithOutput("-proofread ", proofreadFile)
}
if String(d.properties.Todo_file) != "" {
// tricky part:
// we should not compute full path for todo_file through PathForModuleOut().
// the non-standard doclet will get the full path relative to "-o".
- args = args + " -todo " + String(d.properties.Todo_file)
+ cmd.FlagWithArg("-todo ", String(d.properties.Todo_file)).
+ ImplicitOutput(android.PathForModuleOut(ctx, String(d.properties.Todo_file)))
}
if String(d.properties.Resourcesdir) != "" {
// TODO: should we add files under resourcesDir to the implicits? It seems that
// resourcesDir is one sub dir of htmlDir
resourcesDir := android.PathForModuleSrc(ctx, String(d.properties.Resourcesdir))
- args = args + " -resourcesdir " + resourcesDir.String()
+ cmd.FlagWithArg("-resourcesdir ", resourcesDir.String())
}
if String(d.properties.Resourcesoutdir) != "" {
// TODO: it seems -resourceoutdir reference/android/images/ didn't get generated anywhere.
- args = args + " -resourcesoutdir " + String(d.properties.Resourcesoutdir)
+ cmd.FlagWithArg("-resourcesoutdir ", String(d.properties.Resourcesoutdir))
}
- return args
}
-func (d *Droiddoc) collectStubsFlags(ctx android.ModuleContext,
- implicitOutputs *android.WritablePaths) string {
- var doclavaFlags string
+func (d *Droiddoc) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.WritablePath) {
if apiCheckEnabled(d.properties.Check_api.Current, "current") ||
apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") ||
String(d.properties.Api_filename) != "" {
+
d.apiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.txt")
- doclavaFlags += " -api " + d.apiFile.String()
- *implicitOutputs = append(*implicitOutputs, d.apiFile)
+ cmd.FlagWithOutput("-api ", d.apiFile)
d.apiFilePath = d.apiFile
}
@@ -1003,223 +820,324 @@
apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") ||
String(d.properties.Removed_api_filename) != "" {
d.removedApiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_removed.txt")
- doclavaFlags += " -removedApi " + d.removedApiFile.String()
- *implicitOutputs = append(*implicitOutputs, d.removedApiFile)
+ cmd.FlagWithOutput("-removedApi ", d.removedApiFile)
}
if String(d.properties.Private_api_filename) != "" {
d.privateApiFile = android.PathForModuleOut(ctx, String(d.properties.Private_api_filename))
- doclavaFlags += " -privateApi " + d.privateApiFile.String()
- *implicitOutputs = append(*implicitOutputs, d.privateApiFile)
+ cmd.FlagWithOutput("-privateApi ", d.privateApiFile)
}
if String(d.properties.Dex_api_filename) != "" {
d.dexApiFile = android.PathForModuleOut(ctx, String(d.properties.Dex_api_filename))
- doclavaFlags += " -dexApi " + d.dexApiFile.String()
- *implicitOutputs = append(*implicitOutputs, d.dexApiFile)
+ cmd.FlagWithOutput("-dexApi ", d.dexApiFile)
}
if String(d.properties.Private_dex_api_filename) != "" {
d.privateDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Private_dex_api_filename))
- doclavaFlags += " -privateDexApi " + d.privateDexApiFile.String()
- *implicitOutputs = append(*implicitOutputs, d.privateDexApiFile)
+ cmd.FlagWithOutput("-privateDexApi ", d.privateDexApiFile)
}
if String(d.properties.Removed_dex_api_filename) != "" {
d.removedDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Removed_dex_api_filename))
- doclavaFlags += " -removedDexApi " + d.removedDexApiFile.String()
- *implicitOutputs = append(*implicitOutputs, d.removedDexApiFile)
+ cmd.FlagWithOutput("-removedDexApi ", d.removedDexApiFile)
}
if String(d.properties.Exact_api_filename) != "" {
d.exactApiFile = android.PathForModuleOut(ctx, String(d.properties.Exact_api_filename))
- doclavaFlags += " -exactApi " + d.exactApiFile.String()
- *implicitOutputs = append(*implicitOutputs, d.exactApiFile)
+ cmd.FlagWithOutput("-exactApi ", d.exactApiFile)
}
if String(d.properties.Dex_mapping_filename) != "" {
d.apiMappingFile = android.PathForModuleOut(ctx, String(d.properties.Dex_mapping_filename))
- doclavaFlags += " -apiMapping " + d.apiMappingFile.String()
- *implicitOutputs = append(*implicitOutputs, d.apiMappingFile)
+ cmd.FlagWithOutput("-apiMapping ", d.apiMappingFile)
}
if String(d.properties.Proguard_filename) != "" {
d.proguardFile = android.PathForModuleOut(ctx, String(d.properties.Proguard_filename))
- doclavaFlags += " -proguard " + d.proguardFile.String()
- *implicitOutputs = append(*implicitOutputs, d.proguardFile)
+ cmd.FlagWithOutput("-proguard ", d.proguardFile)
}
if BoolDefault(d.properties.Create_stubs, true) {
- doclavaFlags += " -stubs " + android.PathForModuleOut(ctx, "stubsDir").String()
+ cmd.FlagWithArg("-stubs ", stubsDir.String())
}
if Bool(d.properties.Write_sdk_values) {
- doclavaFlags += " -sdkvalues " + android.PathForModuleOut(ctx, "out").String()
+ cmd.FlagWithArg("-sdkvalues ", android.PathForModuleOut(ctx, "out").String())
}
-
- return doclavaFlags
}
-func (d *Droiddoc) getPostDoclavaCmds(ctx android.ModuleContext, implicits *android.Paths) string {
- var cmds string
+func (d *Droiddoc) postDoclavaCmds(ctx android.ModuleContext, rule *android.RuleBuilder) {
if String(d.properties.Static_doc_index_redirect) != "" {
- static_doc_index_redirect := ctx.ExpandSource(String(d.properties.Static_doc_index_redirect),
- "static_doc_index_redirect")
- *implicits = append(*implicits, static_doc_index_redirect)
- cmds = cmds + " && cp " + static_doc_index_redirect.String() + " " +
- android.PathForModuleOut(ctx, "out", "index.html").String()
+ staticDocIndexRedirect := android.PathForModuleSrc(ctx, String(d.properties.Static_doc_index_redirect))
+ rule.Command().Text("cp").
+ Input(staticDocIndexRedirect).
+ Output(android.PathForModuleOut(ctx, "out", "index.html"))
}
if String(d.properties.Static_doc_properties) != "" {
- static_doc_properties := ctx.ExpandSource(String(d.properties.Static_doc_properties),
- "static_doc_properties")
- *implicits = append(*implicits, static_doc_properties)
- cmds = cmds + " && cp " + static_doc_properties.String() + " " +
- android.PathForModuleOut(ctx, "out", "source.properties").String()
+ staticDocProperties := android.PathForModuleSrc(ctx, String(d.properties.Static_doc_properties))
+ rule.Command().Text("cp").
+ Input(staticDocProperties).
+ Output(android.PathForModuleOut(ctx, "out", "source.properties"))
}
- return cmds
}
-func (d *Droiddoc) transformDoclava(ctx android.ModuleContext, implicits android.Paths,
- implicitOutputs android.WritablePaths,
- bootclasspathArgs, classpathArgs, sourcepathArgs, opts, postDoclavaCmds string) {
- ctx.Build(pctx, android.BuildParams{
- Rule: javadoc,
- Description: "Doclava",
- Output: d.Javadoc.stubsSrcJar,
- Inputs: d.Javadoc.srcFiles,
- Implicits: implicits,
- ImplicitOutputs: implicitOutputs,
- Args: map[string]string{
- "outDir": android.PathForModuleOut(ctx, "out").String(),
- "srcJarDir": android.PathForModuleOut(ctx, "srcjars").String(),
- "stubsDir": android.PathForModuleOut(ctx, "stubsDir").String(),
- "srcJars": strings.Join(d.Javadoc.srcJars.Strings(), " "),
- "opts": opts,
- "bootclasspathArgs": bootclasspathArgs,
- "classpathArgs": classpathArgs,
- "sourcepathArgs": sourcepathArgs,
- "docZip": d.Javadoc.docZip.String(),
- "postDoclavaCmds": postDoclavaCmds,
- },
- })
+func javadocCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
+ outDir, srcJarDir, srcJarList android.Path, sourcepaths android.Paths) *android.RuleBuilderCommand {
+
+ cmd := rule.Command().
+ BuiltTool(ctx, "soong_javac_wrapper").Tool(config.JavadocCmd(ctx)).
+ Flag(config.JavacVmFlags).
+ FlagWithArg("-encoding ", "UTF-8").
+ FlagWithRspFileInputList("@", srcs).
+ FlagWithInput("@", srcJarList)
+
+ // TODO(ccross): Remove this if- statement once we finish migration for all Doclava
+ // based stubs generation.
+ // In the future, all the docs generation depends on Metalava stubs (droidstubs) srcjar
+ // dir. We need add the srcjar dir to -sourcepath arg, so that Javadoc can figure out
+ // the correct package name base path.
+ if len(sourcepaths) > 0 {
+ cmd.FlagWithList("-sourcepath ", sourcepaths.Strings(), ":")
+ } else {
+ cmd.FlagWithArg("-sourcepath ", srcJarDir.String())
+ }
+
+ cmd.FlagWithArg("-d ", outDir.String()).
+ Flag("-quiet")
+
+ return cmd
}
-func (d *Droiddoc) transformCheckApi(ctx android.ModuleContext, apiFile, removedApiFile android.Path,
- checkApiClasspath classpath, msg, opts string, output android.WritablePath) {
- ctx.Build(pctx, android.BuildParams{
- Rule: apiCheck,
- Description: "Doclava Check API",
- Output: output,
- Inputs: nil,
- Implicits: append(android.Paths{apiFile, removedApiFile, d.apiFile, d.removedApiFile},
- checkApiClasspath...),
- Args: map[string]string{
- "msg": msg,
- "classpath": checkApiClasspath.FormJavaClassPath(""),
- "opts": opts,
- "apiFile": apiFile.String(),
- "apiFileToCheck": d.apiFile.String(),
- "removedApiFile": removedApiFile.String(),
- "removedApiFileToCheck": d.removedApiFile.String(),
- },
- })
+func javadocSystemModulesCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
+ outDir, srcJarDir, srcJarList android.Path, systemModules *systemModules,
+ classpath classpath, sourcepaths android.Paths) *android.RuleBuilderCommand {
+
+ cmd := javadocCmd(ctx, rule, srcs, outDir, srcJarDir, srcJarList, sourcepaths)
+
+ flag, deps := systemModules.FormJavaSystemModulesPath(ctx.Device())
+ cmd.Flag(flag).Implicits(deps)
+
+ cmd.FlagWithArg("--patch-module ", "java.base=.")
+
+ if len(classpath) > 0 {
+ cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
+ }
+
+ return cmd
}
-func (d *Droiddoc) transformDokka(ctx android.ModuleContext, implicits android.Paths,
- classpathArgs, opts string) {
- ctx.Build(pctx, android.BuildParams{
- Rule: dokka,
- Description: "Dokka",
- Output: d.Javadoc.stubsSrcJar,
- Inputs: d.Javadoc.srcFiles,
- Implicits: implicits,
- Args: map[string]string{
- "outDir": android.PathForModuleOut(ctx, "dokka-out").String(),
- "srcJarDir": android.PathForModuleOut(ctx, "dokka-srcjars").String(),
- "stubsDir": android.PathForModuleOut(ctx, "dokka-stubsDir").String(),
- "srcJars": strings.Join(d.Javadoc.srcJars.Strings(), " "),
- "classpathArgs": classpathArgs,
- "opts": opts,
- "docZip": d.Javadoc.docZip.String(),
- },
- })
+func javadocBootclasspathCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
+ outDir, srcJarDir, srcJarList android.Path, bootclasspath, classpath classpath,
+ sourcepaths android.Paths) *android.RuleBuilderCommand {
+
+ cmd := javadocCmd(ctx, rule, srcs, outDir, srcJarDir, srcJarList, sourcepaths)
+
+ if len(bootclasspath) == 0 && ctx.Device() {
+ // explicitly specify -bootclasspath "" if the bootclasspath is empty to
+ // ensure java does not fall back to the default bootclasspath.
+ cmd.FlagWithArg("-bootclasspath ", `""`)
+ } else if len(bootclasspath) > 0 {
+ cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":")
+ }
+
+ if len(classpath) > 0 {
+ cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
+ }
+
+ return cmd
+}
+
+func dokkaCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
+ outDir, srcJarDir android.Path, bootclasspath, classpath classpath) *android.RuleBuilderCommand {
+
+ // Dokka doesn't support bootClasspath, so combine these two classpath vars for Dokka.
+ dokkaClasspath := append(bootclasspath.Paths(), classpath.Paths()...)
+
+ return rule.Command().
+ BuiltTool(ctx, "dokka").
+ Flag(config.JavacVmFlags).
+ Flag(srcJarDir.String()).
+ FlagWithInputList("-classpath ", dokkaClasspath, ":").
+ FlagWithArg("-format ", "dac").
+ FlagWithArg("-dacRoot ", "/reference/kotlin").
+ FlagWithArg("-output ", outDir.String())
}
func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
deps := d.Javadoc.collectDeps(ctx)
+ d.Javadoc.docZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"docs.zip")
+ d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
+
jsilver := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "jsilver.jar")
doclava := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "doclava.jar")
java8Home := ctx.Config().Getenv("ANDROID_JAVA8_HOME")
checkApiClasspath := classpath{jsilver, doclava, android.PathForSource(ctx, java8Home, "lib/tools.jar")}
- var implicits android.Paths
- implicits = append(implicits, d.Javadoc.srcJars...)
- implicits = append(implicits, d.Javadoc.argFiles...)
+ outDir := android.PathForModuleOut(ctx, "out")
+ srcJarDir := android.PathForModuleOut(ctx, "srcjars")
+ stubsDir := android.PathForModuleOut(ctx, "stubsDir")
- var implicitOutputs android.WritablePaths
- implicitOutputs = append(implicitOutputs, d.Javadoc.docZip)
- for _, o := range d.Javadoc.properties.Out {
- implicitOutputs = append(implicitOutputs, android.PathForModuleGen(ctx, o))
- }
+ rule := android.NewRuleBuilder()
- flags, err := d.initBuilderFlags(ctx, &implicits, deps)
- if err != nil {
- return
- }
+ rule.Command().Text("rm -rf").Text(outDir.String()).Text(stubsDir.String())
+ rule.Command().Text("mkdir -p").Text(outDir.String()).Text(stubsDir.String())
- flags.doclavaStubsFlags = d.collectStubsFlags(ctx, &implicitOutputs)
+ srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
+
+ var cmd *android.RuleBuilderCommand
if Bool(d.properties.Dokka_enabled) {
- d.transformDokka(ctx, implicits, flags.classpathArgs, d.Javadoc.args)
+ cmd = dokkaCmd(ctx, rule, outDir, srcJarDir, deps.bootClasspath, deps.classpath)
} else {
- flags.doclavaDocsFlags = d.collectDoclavaDocsFlags(ctx, &implicits, jsilver, doclava)
- flags.postDoclavaCmds = d.getPostDoclavaCmds(ctx, &implicits)
- d.transformDoclava(ctx, implicits, implicitOutputs, flags.bootClasspathArgs, flags.classpathArgs,
- flags.sourcepathArgs, flags.doclavaDocsFlags+flags.doclavaStubsFlags+" "+d.Javadoc.args,
- flags.postDoclavaCmds)
+ cmd = javadocBootclasspathCmd(ctx, rule, d.Javadoc.srcFiles, outDir, srcJarDir, srcJarList,
+ deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
}
+ d.stubsFlags(ctx, cmd, stubsDir)
+
+ cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles)
+
+ var desc string
+ if Bool(d.properties.Dokka_enabled) {
+ desc = "dokka"
+ } else {
+ d.doclavaDocsFlags(ctx, cmd, classpath{jsilver, doclava})
+
+ for _, o := range d.Javadoc.properties.Out {
+ cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
+ }
+
+ d.postDoclavaCmds(ctx, rule)
+ desc = "doclava"
+ }
+
+ rule.Command().
+ BuiltTool(ctx, "soong_zip").
+ Flag("-write_if_changed").
+ Flag("-d").
+ FlagWithOutput("-o ", d.docZip).
+ FlagWithArg("-C ", outDir.String()).
+ FlagWithArg("-D ", outDir.String())
+
+ rule.Command().
+ BuiltTool(ctx, "soong_zip").
+ Flag("-write_if_changed").
+ Flag("-jar").
+ FlagWithOutput("-o ", d.stubsSrcJar).
+ FlagWithArg("-C ", stubsDir.String()).
+ FlagWithArg("-D ", stubsDir.String())
+
+ rule.Restat()
+
+ zipSyncCleanupCmd(rule, srcJarDir)
+
+ rule.Build(pctx, ctx, "javadoc", desc)
+
if apiCheckEnabled(d.properties.Check_api.Current, "current") &&
!ctx.Config().IsPdkBuild() {
- 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")
+
+ apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
+ removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp")
- d.transformCheckApi(ctx, apiFile, removedApiFile, checkApiClasspath,
- 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()), String(d.properties.Check_api.Current.Args),
- d.checkCurrentApiTimestamp)
+
+ rule := android.NewRuleBuilder()
+
+ rule.Command().Text("( true")
+
+ rule.Command().
+ BuiltTool(ctx, "apicheck").
+ Flag("-JXmx1024m").
+ FlagWithInputList("-Jclasspath\\ ", checkApiClasspath.Paths(), ":").
+ OptionalFlag(d.properties.Check_api.Current.Args).
+ Input(apiFile).
+ Input(d.apiFile).
+ Input(removedApiFile).
+ Input(d.removedApiFile)
+
+ 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())
+
+ rule.Command().
+ Text("touch").Output(d.checkCurrentApiTimestamp).
+ Text(") || (").
+ Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+ Text("; exit 38").
+ Text(")")
+
+ rule.Build(pctx, ctx, "doclavaCurrentApiCheck", "check current API")
d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "update_current_api.timestamp")
- transformUpdateApi(ctx, apiFile, removedApiFile, d.apiFile, d.removedApiFile,
- d.updateCurrentApiTimestamp)
+
+ // update API rule
+ rule = android.NewRuleBuilder()
+
+ rule.Command().Text("( true")
+
+ rule.Command().
+ Text("cp").Flag("-f").
+ Input(d.apiFile).Flag(apiFile.String())
+
+ rule.Command().
+ Text("cp").Flag("-f").
+ Input(d.removedApiFile).Flag(removedApiFile.String())
+
+ msg = "failed to update public API"
+
+ rule.Command().
+ Text("touch").Output(d.updateCurrentApiTimestamp).
+ Text(") || (").
+ Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+ Text("; exit 38").
+ Text(")")
+
+ rule.Build(pctx, ctx, "doclavaCurrentApiUpdate", "update current API")
}
if apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") &&
!ctx.Config().IsPdkBuild() {
- 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")
+
+ apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
+ removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "check_last_released_api.timestamp")
- d.transformCheckApi(ctx, apiFile, removedApiFile, checkApiClasspath,
- `\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`, String(d.properties.Check_api.Last_released.Args),
- d.checkLastReleasedApiTimestamp)
+
+ rule := android.NewRuleBuilder()
+
+ rule.Command().
+ Text("(").
+ BuiltTool(ctx, "apicheck").
+ Flag("-JXmx1024m").
+ FlagWithInputList("-Jclasspath\\ ", checkApiClasspath.Paths(), ":").
+ OptionalFlag(d.properties.Check_api.Last_released.Args).
+ Input(apiFile).
+ Input(d.apiFile).
+ Input(removedApiFile).
+ Input(d.removedApiFile)
+
+ 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`
+
+ rule.Command().
+ Text("touch").Output(d.checkLastReleasedApiTimestamp).
+ Text(") || (").
+ Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+ Text("; exit 38").
+ Text(")")
+
+ rule.Build(pctx, ctx, "doclavaLastApiCheck", "check last API")
}
}
@@ -1258,6 +1176,9 @@
jdiffStubsSrcJar android.WritablePath
}
+// droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be
+// documented, filtering out hidden classes and methods. The resulting .java files are intended to be passed to
+// a droiddoc module to generate documentation.
func DroidstubsFactory() android.Module {
module := &Droidstubs{}
@@ -1268,6 +1189,10 @@
return module
}
+// droidstubs_host passes sources files through Metalava to generate stub .java files that only contain the API
+// to be documented, filtering out hidden classes and methods. The resulting .java files are intended to be
+// passed to a droiddoc_host module to generate documentation. Use a droidstubs_host instead of a droidstubs
+// module when symbols needed by the source files are provided by java_library_host modules.
func DroidstubsHostFactory() android.Module {
module := &Droidstubs{}
@@ -1308,34 +1233,12 @@
}
}
-func (d *Droidstubs) initBuilderFlags(ctx android.ModuleContext, implicits *android.Paths,
- deps deps) (droiddocBuilderFlags, error) {
- var flags droiddocBuilderFlags
-
- *implicits = append(*implicits, deps.bootClasspath...)
- *implicits = append(*implicits, deps.classpath...)
-
- // continue to use -bootclasspath even if Metalava under -source 1.9 is enabled
- // since it doesn't support system modules yet.
- if len(deps.bootClasspath.Strings()) > 0 {
- // For OpenJDK 8 we can use -bootclasspath to define the core libraries code.
- flags.bootClasspathArgs = deps.bootClasspath.FormJavaClassPath("-bootclasspath")
- }
- flags.classpathArgs = deps.classpath.FormJavaClassPath("-classpath")
-
- flags.sourcepathArgs = "-sourcepath \"" + strings.Join(d.Javadoc.sourcepaths.Strings(), ":") + "\""
- return flags, nil
-}
-
-func (d *Droidstubs) collectStubsFlags(ctx android.ModuleContext,
- implicitOutputs *android.WritablePaths) string {
- var metalavaFlags string
+func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.WritablePath) {
if apiCheckEnabled(d.properties.Check_api.Current, "current") ||
apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") ||
String(d.properties.Api_filename) != "" {
d.apiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.txt")
- metalavaFlags = metalavaFlags + " --api " + d.apiFile.String()
- *implicitOutputs = append(*implicitOutputs, d.apiFile)
+ cmd.FlagWithOutput("--api ", d.apiFile)
d.apiFilePath = d.apiFile
}
@@ -1343,159 +1246,144 @@
apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") ||
String(d.properties.Removed_api_filename) != "" {
d.removedApiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_removed.txt")
- metalavaFlags = metalavaFlags + " --removed-api " + d.removedApiFile.String()
- *implicitOutputs = append(*implicitOutputs, d.removedApiFile)
+ cmd.FlagWithOutput("--removed-api ", d.removedApiFile)
}
if String(d.properties.Private_api_filename) != "" {
d.privateApiFile = android.PathForModuleOut(ctx, String(d.properties.Private_api_filename))
- metalavaFlags = metalavaFlags + " --private-api " + d.privateApiFile.String()
- *implicitOutputs = append(*implicitOutputs, d.privateApiFile)
+ cmd.FlagWithOutput("--private-api ", d.privateApiFile)
}
if String(d.properties.Dex_api_filename) != "" {
d.dexApiFile = android.PathForModuleOut(ctx, String(d.properties.Dex_api_filename))
- metalavaFlags += " --dex-api " + d.dexApiFile.String()
- *implicitOutputs = append(*implicitOutputs, d.dexApiFile)
+ cmd.FlagWithOutput("--dex-api ", d.dexApiFile)
}
if String(d.properties.Private_dex_api_filename) != "" {
d.privateDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Private_dex_api_filename))
- metalavaFlags = metalavaFlags + " --private-dex-api " + d.privateDexApiFile.String()
- *implicitOutputs = append(*implicitOutputs, d.privateDexApiFile)
+ cmd.FlagWithOutput("--private-dex-api ", d.privateDexApiFile)
}
if String(d.properties.Removed_dex_api_filename) != "" {
d.removedDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Removed_dex_api_filename))
- metalavaFlags = metalavaFlags + " --removed-dex-api " + d.removedDexApiFile.String()
- *implicitOutputs = append(*implicitOutputs, d.removedDexApiFile)
+ cmd.FlagWithOutput("--removed-dex-api ", d.removedDexApiFile)
}
if String(d.properties.Exact_api_filename) != "" {
d.exactApiFile = android.PathForModuleOut(ctx, String(d.properties.Exact_api_filename))
- metalavaFlags = metalavaFlags + " --exact-api " + d.exactApiFile.String()
- *implicitOutputs = append(*implicitOutputs, d.exactApiFile)
+ cmd.FlagWithOutput("--exact-api ", d.exactApiFile)
}
if String(d.properties.Dex_mapping_filename) != "" {
d.apiMappingFile = android.PathForModuleOut(ctx, String(d.properties.Dex_mapping_filename))
- metalavaFlags = metalavaFlags + " --dex-api-mapping " + d.apiMappingFile.String()
- *implicitOutputs = append(*implicitOutputs, d.apiMappingFile)
+ cmd.FlagWithOutput("--dex-api-mapping ", d.apiMappingFile)
}
if String(d.properties.Proguard_filename) != "" {
d.proguardFile = android.PathForModuleOut(ctx, String(d.properties.Proguard_filename))
- metalavaFlags += " --proguard " + d.proguardFile.String()
- *implicitOutputs = append(*implicitOutputs, d.proguardFile)
+ cmd.FlagWithOutput("--proguard ", d.proguardFile)
}
if Bool(d.properties.Write_sdk_values) {
- metalavaFlags = metalavaFlags + " --sdk-values " + android.PathForModuleOut(ctx, "out").String()
+ cmd.FlagWithArg("--sdk-values ", android.PathForModuleOut(ctx, "out").String())
}
if Bool(d.properties.Create_doc_stubs) {
- metalavaFlags += " --doc-stubs " + android.PathForModuleOut(ctx, "stubsDir").String()
+ cmd.FlagWithArg("--doc-stubs ", stubsDir.String())
} else {
- metalavaFlags += " --stubs " + android.PathForModuleOut(ctx, "stubsDir").String()
+ cmd.FlagWithArg("--stubs ", stubsDir.String())
}
- return metalavaFlags
}
-func (d *Droidstubs) collectAnnotationsFlags(ctx android.ModuleContext,
- implicits *android.Paths, implicitOutputs *android.WritablePaths) (string, string) {
- var flags, mergeAnnoDirFlags string
+func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
if Bool(d.properties.Annotations_enabled) {
- flags += " --include-annotations"
+ cmd.Flag("--include-annotations")
+
validatingNullability :=
strings.Contains(d.Javadoc.args, "--validate-nullability-from-merged-stubs") ||
String(d.properties.Validate_nullability_from_list) != ""
migratingNullability := String(d.properties.Previous_api) != ""
+
if !(migratingNullability || validatingNullability) {
ctx.PropertyErrorf("previous_api",
"has to be non-empty if annotations was enabled (unless validating nullability)")
}
+
if migratingNullability {
previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api))
- *implicits = append(*implicits, previousApi)
- flags += " --migrate-nullness " + previousApi.String()
+ cmd.FlagWithInput("--migrate-nullness ", previousApi)
}
+
if s := String(d.properties.Validate_nullability_from_list); s != "" {
- flags += " --validate-nullability-from-list " + android.PathForModuleSrc(ctx, s).String()
+ cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s))
}
+
if validatingNullability {
d.nullabilityWarningsFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_nullability_warnings.txt")
- *implicitOutputs = append(*implicitOutputs, d.nullabilityWarningsFile)
- flags += " --nullability-warnings-txt " + d.nullabilityWarningsFile.String()
+ cmd.FlagWithOutput("--nullability-warnings-txt ", d.nullabilityWarningsFile)
}
d.annotationsZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"_annotations.zip")
- *implicitOutputs = append(*implicitOutputs, d.annotationsZip)
-
- flags += " --extract-annotations " + d.annotationsZip.String()
+ cmd.FlagWithOutput("--extract-annotations ", d.annotationsZip)
if len(d.properties.Merge_annotations_dirs) == 0 {
ctx.PropertyErrorf("merge_annotations_dirs",
"has to be non-empty if annotations was enabled!")
}
- ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
- if t, ok := m.(*ExportedDroiddocDir); ok {
- *implicits = append(*implicits, t.deps...)
- mergeAnnoDirFlags += " --merge-qualifier-annotations " + t.dir.String()
- } else {
- ctx.PropertyErrorf("merge_annotations_dirs",
- "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
- }
- })
- flags += mergeAnnoDirFlags
- // TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
- flags += " --hide HiddenTypedefConstant --hide SuperfluousPrefix --hide AnnotationExtraction"
- }
- return flags, mergeAnnoDirFlags
+ d.mergeAnnoDirFlags(ctx, cmd)
+
+ // TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
+ cmd.FlagWithArg("--hide ", "HiddenTypedefConstant").
+ FlagWithArg("--hide ", "SuperfluousPrefix").
+ FlagWithArg("--hide ", "AnnotationExtraction")
+ }
}
-func (d *Droidstubs) collectInclusionAnnotationsFlags(ctx android.ModuleContext,
- implicits *android.Paths, implicitOutputs *android.WritablePaths) string {
- var flags string
+func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
+ ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
+ if t, ok := m.(*ExportedDroiddocDir); ok {
+ cmd.FlagWithArg("--merge-qualifier-annotations ", t.dir.String()).Implicits(t.deps)
+ } else {
+ ctx.PropertyErrorf("merge_annotations_dirs",
+ "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
+ }
+ })
+}
+
+func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
ctx.VisitDirectDepsWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.Module) {
if t, ok := m.(*ExportedDroiddocDir); ok {
- *implicits = append(*implicits, t.deps...)
- flags += " --merge-inclusion-annotations " + t.dir.String()
+ cmd.FlagWithArg("--merge-inclusion-annotations ", t.dir.String()).Implicits(t.deps)
} else {
ctx.PropertyErrorf("merge_inclusion_annotations_dirs",
"module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
}
})
-
- return flags
}
-func (d *Droidstubs) collectAPILevelsAnnotationsFlags(ctx android.ModuleContext,
- implicits *android.Paths, implicitOutputs *android.WritablePaths) string {
- var flags string
+func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
if Bool(d.properties.Api_levels_annotations_enabled) {
d.apiVersionsXml = android.PathForModuleOut(ctx, "api-versions.xml")
- *implicitOutputs = append(*implicitOutputs, d.apiVersionsXml)
if len(d.properties.Api_levels_annotations_dirs) == 0 {
ctx.PropertyErrorf("api_levels_annotations_dirs",
"has to be non-empty if api levels annotations was enabled!")
}
- flags = " --generate-api-levels " + d.apiVersionsXml.String() + " --apply-api-levels " +
- d.apiVersionsXml.String() + " --current-version " + ctx.Config().PlatformSdkVersion() +
- " --current-codename " + ctx.Config().PlatformSdkCodename() + " "
+ cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml)
+ cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml)
+ cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion())
+ cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
if t, ok := m.(*ExportedDroiddocDir); ok {
- var androidJars android.Paths
for _, dep := range t.deps {
if strings.HasSuffix(dep.String(), "android.jar") {
- androidJars = append(androidJars, dep)
+ cmd.Implicit(dep)
}
}
- *implicits = append(*implicits, androidJars...)
- flags += " --android-jar-pattern " + t.dir.String() + "/%/public/android.jar "
+ cmd.FlagWithArg("--android-jar-pattern ", t.dir.String()+"/%/public/android.jar")
} else {
ctx.PropertyErrorf("api_levels_annotations_dirs",
"module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
@@ -1503,112 +1391,57 @@
})
}
-
- return flags
}
-func (d *Droidstubs) collectApiToXmlFlags(ctx android.ModuleContext, implicits *android.Paths,
- implicitOutputs *android.WritablePaths) string {
- var flags string
+func (d *Droidstubs) apiToXmlFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
if Bool(d.properties.Jdiff_enabled) && !ctx.Config().IsPdkBuild() {
if d.apiFile.String() == "" {
ctx.ModuleErrorf("API signature file has to be specified in Metalava when jdiff is enabled.")
}
d.apiXmlFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.xml")
- *implicitOutputs = append(*implicitOutputs, d.apiXmlFile)
-
- flags = " --api-xml " + d.apiXmlFile.String()
+ cmd.FlagWithOutput("--api-xml ", d.apiXmlFile)
if String(d.properties.Check_api.Last_released.Api_file) == "" {
ctx.PropertyErrorf("check_api.last_released.api_file",
"has to be non-empty if jdiff was enabled!")
}
- lastReleasedApi := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Api_file),
- "check_api.last_released.api_file")
- *implicits = append(*implicits, lastReleasedApi)
+ lastReleasedApi := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
d.lastReleasedApiXmlFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_last_released_api.xml")
- *implicitOutputs = append(*implicitOutputs, d.lastReleasedApiXmlFile)
+ cmd.FlagWithInput("--convert-to-jdiff ", lastReleasedApi).Output(d.lastReleasedApiXmlFile)
+ }
+}
- flags += " --convert-to-jdiff " + lastReleasedApi.String() + " " +
- d.lastReleasedApiXmlFile.String()
+func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion string, srcs android.Paths,
+ srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths) *android.RuleBuilderCommand {
+ cmd := rule.Command().BuiltTool(ctx, "metalava").
+ Flag(config.JavacVmFlags).
+ FlagWithArg("-encoding ", "UTF-8").
+ FlagWithArg("-source ", javaVersion).
+ FlagWithRspFileInputList("@", srcs).
+ FlagWithInput("@", srcJarList)
+
+ if len(bootclasspath) > 0 {
+ cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":")
}
- return flags
-}
+ if len(classpath) > 0 {
+ cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
+ }
-func (d *Droidstubs) transformMetalava(ctx android.ModuleContext, implicits android.Paths,
- implicitOutputs android.WritablePaths, javaVersion,
- bootclasspathArgs, classpathArgs, sourcepathArgs, opts string) {
+ if len(sourcepaths) > 0 {
+ cmd.FlagWithList("-sourcepath ", sourcepaths.Strings(), ":")
+ } else {
+ cmd.FlagWithArg("-sourcepath ", `""`)
+ }
- ctx.Build(pctx, android.BuildParams{
- Rule: metalava,
- Description: "Metalava",
- Output: d.Javadoc.stubsSrcJar,
- Inputs: d.Javadoc.srcFiles,
- Implicits: implicits,
- ImplicitOutputs: implicitOutputs,
- Args: map[string]string{
- "outDir": android.PathForModuleOut(ctx, "out").String(),
- "srcJarDir": android.PathForModuleOut(ctx, "srcjars").String(),
- "stubsDir": android.PathForModuleOut(ctx, "stubsDir").String(),
- "srcJars": strings.Join(d.Javadoc.srcJars.Strings(), " "),
- "javaVersion": javaVersion,
- "bootclasspathArgs": bootclasspathArgs,
- "classpathArgs": classpathArgs,
- "sourcepathArgs": sourcepathArgs,
- "opts": opts,
- },
- })
-}
+ cmd.Flag("--no-banner").
+ Flag("--color").
+ Flag("--quiet").
+ Flag("--format=v2")
-func (d *Droidstubs) transformCheckApi(ctx android.ModuleContext,
- apiFile, removedApiFile android.Path, implicits android.Paths,
- javaVersion, bootclasspathArgs, classpathArgs, sourcepathArgs, opts, subdir, msg string,
- output android.WritablePath) {
- ctx.Build(pctx, android.BuildParams{
- Rule: metalavaApiCheck,
- Description: "Metalava Check API",
- Output: output,
- Inputs: d.Javadoc.srcFiles,
- Implicits: append(android.Paths{apiFile, removedApiFile, d.apiFile, d.removedApiFile},
- implicits...),
- Args: map[string]string{
- "srcJarDir": android.PathForModuleOut(ctx, subdir, "srcjars").String(),
- "srcJars": strings.Join(d.Javadoc.srcJars.Strings(), " "),
- "javaVersion": javaVersion,
- "bootclasspathArgs": bootclasspathArgs,
- "classpathArgs": classpathArgs,
- "sourcepathArgs": sourcepathArgs,
- "opts": opts,
- "msg": msg,
- },
- })
-}
-
-func (d *Droidstubs) transformJdiff(ctx android.ModuleContext, implicits android.Paths,
- implicitOutputs android.WritablePaths,
- bootclasspathArgs, classpathArgs, sourcepathArgs, opts string) {
- ctx.Build(pctx, android.BuildParams{
- Rule: javadoc,
- Description: "Jdiff",
- Output: d.jdiffStubsSrcJar,
- Inputs: d.Javadoc.srcFiles,
- Implicits: implicits,
- ImplicitOutputs: implicitOutputs,
- Args: map[string]string{
- "outDir": android.PathForModuleOut(ctx, "jdiff-out").String(),
- "srcJarDir": android.PathForModuleOut(ctx, "jdiff-srcjars").String(),
- "stubsDir": android.PathForModuleOut(ctx, "jdiff-stubsDir").String(),
- "srcJars": strings.Join(d.Javadoc.srcJars.Strings(), " "),
- "opts": opts,
- "bootclasspathArgs": bootclasspathArgs,
- "classpathArgs": classpathArgs,
- "sourcepathArgs": sourcepathArgs,
- "docZip": d.jdiffDocZip.String(),
- },
- })
+ return cmd
}
func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -1616,29 +1449,29 @@
javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), sdkContext(d))
- var implicits android.Paths
- implicits = append(implicits, d.Javadoc.srcJars...)
- implicits = append(implicits, d.Javadoc.argFiles...)
+ // Create rule for metalava
- var implicitOutputs android.WritablePaths
- for _, o := range d.Javadoc.properties.Out {
- implicitOutputs = append(implicitOutputs, android.PathForModuleGen(ctx, o))
- }
+ d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
- flags, err := d.initBuilderFlags(ctx, &implicits, deps)
- metalavaCheckApiImplicits := implicits
- jdiffImplicits := implicits
+ srcJarDir := android.PathForModuleOut(ctx, "srcjars")
+ stubsDir := android.PathForModuleOut(ctx, "stubsDir")
- if err != nil {
- return
- }
+ rule := android.NewRuleBuilder()
- flags.metalavaStubsFlags = d.collectStubsFlags(ctx, &implicitOutputs)
- flags.metalavaAnnotationsFlags, flags.metalavaMergeAnnoDirFlags =
- d.collectAnnotationsFlags(ctx, &implicits, &implicitOutputs)
- flags.metalavaInclusionAnnotationsFlags = d.collectInclusionAnnotationsFlags(ctx, &implicits, &implicitOutputs)
- flags.metalavaApiLevelsAnnotationsFlags = d.collectAPILevelsAnnotationsFlags(ctx, &implicits, &implicitOutputs)
- flags.metalavaApiToXmlFlags = d.collectApiToXmlFlags(ctx, &implicits, &implicitOutputs)
+ rule.Command().Text("rm -rf").Text(stubsDir.String())
+ rule.Command().Text("mkdir -p").Text(stubsDir.String())
+
+ srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
+
+ cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
+ deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
+
+ d.stubsFlags(ctx, cmd, stubsDir)
+
+ d.annotationsFlags(ctx, cmd)
+ d.inclusionAnnotationsFlags(ctx, cmd)
+ d.apiLevelsAnnotationsFlags(ctx, cmd)
+ d.apiToXmlFlags(ctx, cmd)
if strings.Contains(d.Javadoc.args, "--generate-documentation") {
// Currently Metalava have the ability to invoke Javadoc in a seperate process.
@@ -1646,61 +1479,164 @@
// "--generate-documentation" arg. This is not needed when Metalava removes this feature.
d.Javadoc.args = d.Javadoc.args + " -nodocs "
}
- d.transformMetalava(ctx, implicits, implicitOutputs, javaVersion,
- flags.bootClasspathArgs, flags.classpathArgs, flags.sourcepathArgs,
- flags.metalavaStubsFlags+flags.metalavaAnnotationsFlags+flags.metalavaInclusionAnnotationsFlags+
- flags.metalavaApiLevelsAnnotationsFlags+flags.metalavaApiToXmlFlags+" "+d.Javadoc.args)
+
+ cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles)
+ for _, o := range d.Javadoc.properties.Out {
+ cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
+ }
+
+ rule.Command().
+ BuiltTool(ctx, "soong_zip").
+ Flag("-write_if_changed").
+ Flag("-jar").
+ FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
+ FlagWithArg("-C ", stubsDir.String()).
+ FlagWithArg("-D ", stubsDir.String())
+ rule.Restat()
+
+ zipSyncCleanupCmd(rule, srcJarDir)
+
+ rule.Build(pctx, ctx, "metalava", "metalava")
+
+ // Create rule for apicheck
if apiCheckEnabled(d.properties.Check_api.Current, "current") &&
!ctx.Config().IsPdkBuild() {
- 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")
+
+ if len(d.Javadoc.properties.Out) > 0 {
+ ctx.PropertyErrorf("out", "out property may not be combined with check_api")
+ }
+
+ apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
+ removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
+ baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
+ updatedBaselineOutput := android.PathForModuleOut(ctx, "current_baseline.txt")
d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp")
- opts := " " + d.Javadoc.args + " --check-compatibility:api:current " + apiFile.String() +
- " --check-compatibility:removed:current " + removedApiFile.String() +
- flags.metalavaInclusionAnnotationsFlags + flags.metalavaMergeAnnoDirFlags + " "
- d.transformCheckApi(ctx, apiFile, removedApiFile, metalavaCheckApiImplicits,
- javaVersion, flags.bootClasspathArgs, flags.classpathArgs, flags.sourcepathArgs, opts, "current-apicheck",
- 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.checkCurrentApiTimestamp)
+ rule := android.NewRuleBuilder()
+
+ rule.Command().Text("( true")
+
+ srcJarDir := android.PathForModuleOut(ctx, "current-apicheck", "srcjars")
+ srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
+
+ cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
+ deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
+
+ cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles).
+ FlagWithInput("--check-compatibility:api:current ", apiFile).
+ FlagWithInput("--check-compatibility:removed:current ", removedApiFile)
+
+ d.inclusionAnnotationsFlags(ctx, cmd)
+ d.mergeAnnoDirFlags(ctx, cmd)
+
+ if baselineFile.Valid() {
+ cmd.FlagWithInput("--baseline ", baselineFile.Path())
+ cmd.FlagWithOutput("--update-baseline ", updatedBaselineOutput)
+ }
+
+ zipSyncCleanupCmd(rule, srcJarDir)
+
+ 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())
+
+ rule.Command().
+ Text("touch").Output(d.checkCurrentApiTimestamp).
+ Text(") || (").
+ Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+ Text("; exit 38").
+ Text(")")
+
+ rule.Build(pctx, ctx, "metalavaCurrentApiCheck", "metalava check current API")
d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "update_current_api.timestamp")
- transformUpdateApi(ctx, apiFile, removedApiFile, d.apiFile, d.removedApiFile,
- d.updateCurrentApiTimestamp)
+
+ // update API rule
+ rule = android.NewRuleBuilder()
+
+ rule.Command().Text("( true")
+
+ rule.Command().
+ Text("cp").Flag("-f").
+ Input(d.apiFile).Flag(apiFile.String())
+
+ rule.Command().
+ Text("cp").Flag("-f").
+ Input(d.removedApiFile).Flag(removedApiFile.String())
+
+ msg = "failed to update public API"
+
+ rule.Command().
+ Text("touch").Output(d.updateCurrentApiTimestamp).
+ Text(") || (").
+ Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+ Text("; exit 38").
+ Text(")")
+
+ rule.Build(pctx, ctx, "metalavaCurrentApiUpdate", "update current API")
}
if apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") &&
!ctx.Config().IsPdkBuild() {
- 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")
+
+ if len(d.Javadoc.properties.Out) > 0 {
+ ctx.PropertyErrorf("out", "out property may not be combined with check_api")
+ }
+
+ apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
+ removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
+ baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
+ updatedBaselineOutput := android.PathForModuleOut(ctx, "last_released_baseline.txt")
d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "check_last_released_api.timestamp")
- opts := " " + d.Javadoc.args + " --check-compatibility:api:released " + apiFile.String() +
- flags.metalavaInclusionAnnotationsFlags + " --check-compatibility:removed:released " +
- removedApiFile.String() + flags.metalavaMergeAnnoDirFlags + " "
- d.transformCheckApi(ctx, apiFile, removedApiFile, metalavaCheckApiImplicits,
- javaVersion, flags.bootClasspathArgs, flags.classpathArgs, flags.sourcepathArgs, opts, "last-apicheck",
- `\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`,
- d.checkLastReleasedApiTimestamp)
+ rule := android.NewRuleBuilder()
+
+ rule.Command().Text("( true")
+
+ srcJarDir := android.PathForModuleOut(ctx, "last-apicheck", "srcjars")
+ srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
+
+ cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
+ deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
+
+ cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles).
+ FlagWithInput("--check-compatibility:api:released ", apiFile)
+
+ d.inclusionAnnotationsFlags(ctx, cmd)
+
+ cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile)
+
+ d.mergeAnnoDirFlags(ctx, cmd)
+
+ if baselineFile.Valid() {
+ cmd.FlagWithInput("--baseline ", baselineFile.Path())
+ cmd.FlagWithOutput("--update-baseline ", updatedBaselineOutput)
+ }
+
+ zipSyncCleanupCmd(rule, srcJarDir)
+
+ 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`
+ rule.Command().
+ Text("touch").Output(d.checkLastReleasedApiTimestamp).
+ Text(") || (").
+ Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+ Text("; exit 38").
+ Text(")")
+
+ rule.Build(pctx, ctx, "metalavaLastApiCheck", "metalava check last API")
}
if String(d.properties.Check_nullability_warnings) != "" {
@@ -1708,9 +1644,11 @@
ctx.PropertyErrorf("check_nullability_warnings",
"Cannot specify check_nullability_warnings unless validating nullability")
}
- checkNullabilityWarnings := ctx.ExpandSource(String(d.properties.Check_nullability_warnings),
- "check_nullability_warnings")
+
+ checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings))
+
d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, "check_nullability_warnings.timestamp")
+
msg := fmt.Sprintf(`\n******************************\n`+
`The warnings encountered during nullability annotation validation did\n`+
`not match the checked in file of expected warnings. The diffs are shown\n`+
@@ -1720,20 +1658,32 @@
` cp %s %s\n`+
` and submitting the updated file as part of your change.`,
d.nullabilityWarningsFile, checkNullabilityWarnings)
- ctx.Build(pctx, android.BuildParams{
- Rule: nullabilityWarningsCheck,
- Description: "Nullability Warnings Check",
- Output: d.checkNullabilityWarningsTimestamp,
- Implicits: android.Paths{checkNullabilityWarnings, d.nullabilityWarningsFile},
- Args: map[string]string{
- "expected": checkNullabilityWarnings.String(),
- "actual": d.nullabilityWarningsFile.String(),
- "msg": msg,
- },
- })
+
+ rule := android.NewRuleBuilder()
+
+ rule.Command().
+ Text("(").
+ Text("diff").Input(checkNullabilityWarnings).Input(d.nullabilityWarningsFile).
+ Text("&&").
+ Text("touch").Output(d.checkNullabilityWarningsTimestamp).
+ Text(") || (").
+ Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+ Text("; exit 38").
+ Text(")")
+
+ rule.Build(pctx, ctx, "nullabilityWarningsCheck", "nullability warnings check")
}
if Bool(d.properties.Jdiff_enabled) && !ctx.Config().IsPdkBuild() {
+ if len(d.Javadoc.properties.Out) > 0 {
+ ctx.PropertyErrorf("out", "out property may not be combined with jdiff")
+ }
+
+ outDir := android.PathForModuleOut(ctx, "jdiff-out")
+ srcJarDir := android.PathForModuleOut(ctx, "jdiff-srcjars")
+ stubsDir := android.PathForModuleOut(ctx, "jdiff-stubsDir")
+
+ rule := android.NewRuleBuilder()
// Please sync with android-api-council@ before making any changes for the name of jdiffDocZip below
// since there's cron job downstream that fetch this .zip file periodically.
@@ -1741,21 +1691,49 @@
d.jdiffDocZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"jdiff-docs.zip")
d.jdiffStubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"jdiff-stubs.srcjar")
- var jdiffImplicitOutputs android.WritablePaths
- jdiffImplicitOutputs = append(jdiffImplicitOutputs, d.jdiffDocZip)
-
jdiff := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "jdiff.jar")
- jdiffImplicits = append(jdiffImplicits, android.Paths{jdiff, d.apiXmlFile, d.lastReleasedApiXmlFile}...)
- opts := " -source 1.8 -J-Xmx1600m -XDignore.symbol.file " +
- "-doclet jdiff.JDiff -docletpath " + jdiff.String() + " -quiet " +
- "-newapi " + strings.TrimSuffix(d.apiXmlFile.Base(), d.apiXmlFile.Ext()) +
- " -newapidir " + filepath.Dir(d.apiXmlFile.String()) +
- " -oldapi " + strings.TrimSuffix(d.lastReleasedApiXmlFile.Base(), d.lastReleasedApiXmlFile.Ext()) +
- " -oldapidir " + filepath.Dir(d.lastReleasedApiXmlFile.String())
+ rule.Command().Text("rm -rf").Text(outDir.String()).Text(stubsDir.String())
+ rule.Command().Text("mkdir -p").Text(outDir.String()).Text(stubsDir.String())
- d.transformJdiff(ctx, jdiffImplicits, jdiffImplicitOutputs, flags.bootClasspathArgs, flags.classpathArgs,
- flags.sourcepathArgs, opts)
+ srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
+
+ cmd := javadocBootclasspathCmd(ctx, rule, d.Javadoc.srcFiles, outDir, srcJarDir, srcJarList,
+ deps.bootClasspath, deps.classpath, d.sourcepaths)
+
+ cmd.Flag("-J-Xmx1600m").
+ Flag("-XDignore.symbol.file").
+ FlagWithArg("-doclet ", "jdiff.JDiff").
+ FlagWithInput("-docletpath ", jdiff).
+ Flag("-quiet").
+ FlagWithArg("-newapi ", strings.TrimSuffix(d.apiXmlFile.Base(), d.apiXmlFile.Ext())).
+ FlagWithArg("-newapidir ", filepath.Dir(d.apiXmlFile.String())).
+ Implicit(d.apiXmlFile).
+ FlagWithArg("-oldapi ", strings.TrimSuffix(d.lastReleasedApiXmlFile.Base(), d.lastReleasedApiXmlFile.Ext())).
+ FlagWithArg("-oldapidir ", filepath.Dir(d.lastReleasedApiXmlFile.String())).
+ Implicit(d.lastReleasedApiXmlFile)
+
+ rule.Command().
+ BuiltTool(ctx, "soong_zip").
+ Flag("-write_if_changed").
+ Flag("-d").
+ FlagWithOutput("-o ", d.jdiffDocZip).
+ FlagWithArg("-C ", outDir.String()).
+ FlagWithArg("-D ", outDir.String())
+
+ rule.Command().
+ BuiltTool(ctx, "soong_zip").
+ Flag("-write_if_changed").
+ Flag("-jar").
+ FlagWithOutput("-o ", d.jdiffStubsSrcJar).
+ FlagWithArg("-C ", stubsDir.String()).
+ FlagWithArg("-D ", stubsDir.String())
+
+ rule.Restat()
+
+ zipSyncCleanupCmd(rule, srcJarDir)
+
+ rule.Build(pctx, ctx, "jdiff", "jdiff")
}
}
@@ -1781,6 +1759,7 @@
dir android.Path
}
+// droiddoc_exported_dir exports a directory of html templates or nullability annotations for use by doclava.
func ExportedDroiddocDirFactory() android.Module {
module := &ExportedDroiddocDir{}
module.AddProperties(&module.properties)
@@ -1829,3 +1808,25 @@
return module
}
+
+func zipSyncCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
+ srcJarDir android.ModuleOutPath, srcJars android.Paths) android.OutputPath {
+
+ rule.Command().Text("rm -rf").Text(srcJarDir.String())
+ rule.Command().Text("mkdir -p").Text(srcJarDir.String())
+ srcJarList := srcJarDir.Join(ctx, "list")
+
+ rule.Temporary(srcJarList)
+
+ rule.Command().BuiltTool(ctx, "zipsync").
+ FlagWithArg("-d ", srcJarDir.String()).
+ FlagWithOutput("-l ", srcJarList).
+ FlagWithArg("-f ", `"*.java"`).
+ Inputs(srcJars)
+
+ return srcJarList
+}
+
+func zipSyncCleanupCmd(rule *android.RuleBuilder, srcJarDir android.ModuleOutPath) {
+ rule.Command().Text("rm -rf").Text(srcJarDir.String())
+}
diff --git a/java/gen.go b/java/gen.go
index b1c028d..69965ec 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -15,6 +15,8 @@
package java
import (
+ "strings"
+
"github.com/google/blueprint"
"android/soong/android"
@@ -63,6 +65,10 @@
func genAidl(ctx android.ModuleContext, aidlFile android.Path, aidlFlags string, deps android.Paths) android.Path {
javaFile := android.GenPathWithExt(ctx, "aidl", aidlFile, "java")
depFile := javaFile.String() + ".d"
+ baseDir := strings.TrimSuffix(aidlFile.String(), aidlFile.Rel())
+ if baseDir != "" {
+ aidlFlags += " -I" + baseDir
+ }
ctx.Build(pctx, android.BuildParams{
Rule: aidl,
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index cf9f492..8379f53 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -64,7 +64,7 @@
stubFlagsRule(ctx)
// These rules depend on files located in frameworks/base, skip them if running in a tree that doesn't have them.
- if ctx.Config().FrameworksBaseDirExists(ctx) && !ctx.Config().UnbundledBuild() {
+ if ctx.Config().FrameworksBaseDirExists(ctx) {
h.flags = flagsRule(ctx)
h.metadata = metadataRule(ctx)
} else {
@@ -97,7 +97,7 @@
// Add the android.test.base to the set of stubs only if the android.test.base module is on
// the boot jars list as the runtime will only enforce hiddenapi access against modules on
// that list.
- if inList("android.test.base", ctx.Config().BootJars()) {
+ if inList("android.test.base", ctx.Config().BootJars()) && !ctx.Config().UnbundledBuildUsePrebuiltSdks() {
publicStubModules = append(publicStubModules, "android.test.base.stubs")
}
@@ -152,6 +152,14 @@
// Collect dex jar paths for modules that had hiddenapi encode called on them.
if h, ok := module.(hiddenAPIIntf); ok {
if jar := h.bootDexJar(); jar != nil {
+ // For a java lib included in an APEX, only take the one built for
+ // the platform variant, and skip the variants for APEXes.
+ // Otherwise, the hiddenapi tool will complain about duplicated classes
+ if a, ok := module.(android.ApexModule); ok {
+ if android.InAnyApex(module.Name()) && !a.IsForPlatform() {
+ return
+ }
+ }
bootDexJars = append(bootDexJars, jar)
}
}
diff --git a/java/java.go b/java/java.go
index 7c84e76..afb1218 100644
--- a/java/java.go
+++ b/java/java.go
@@ -50,6 +50,21 @@
android.RegisterModuleType("dex_import", DexImportFactory)
android.RegisterSingletonType("logtags", LogtagsSingleton)
+ android.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory)
+}
+
+func (j *Module) checkPlatformAPI(ctx android.ModuleContext) {
+ if sc, ok := ctx.Module().(sdkContext); ok {
+ usePlatformAPI := proptools.Bool(j.deviceProperties.Platform_apis)
+ if usePlatformAPI != (sc.sdkVersion() == "") {
+ if usePlatformAPI {
+ ctx.PropertyErrorf("platform_apis", "platform_apis must be false when sdk_version is not empty.")
+ } else {
+ ctx.PropertyErrorf("platform_apis", "platform_apis must be true when sdk_version is empty.")
+ }
+ }
+
+ }
}
// TODO:
@@ -178,8 +193,8 @@
// list of module-specific flags that will be used for dex compiles
Dxflags []string `android:"arch_variant"`
- // if not blank, set to the version of the sdk to compile against. Defaults to compiling against the current
- // sdk if platform_apis is not set.
+ // if not blank, set to the version of the sdk to compile against.
+ // Defaults to compiling against the current platform.
Sdk_version *string
// if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
@@ -190,7 +205,9 @@
// Defaults to sdk_version if not set.
Target_sdk_version *string
- // if true, compile against the platform APIs instead of an SDK.
+ // Whether to compile against the platform APIs instead of an SDK.
+ // If true, then sdk_version must be empty. The value of this field
+ // is ignored when module's type isn't android_app.
Platform_apis *bool
Aidl struct {
@@ -267,6 +284,7 @@
type Module struct {
android.ModuleBase
android.DefaultableModuleBase
+ android.ApexModuleBase
properties CompilerProperties
protoProperties android.ProtoProperties
@@ -342,6 +360,9 @@
hiddenAPI
dexpreopter
+
+ // list of the xref extraction files
+ kytheFiles android.Paths
}
func (j *Module) OutputFiles(tag string) (android.Paths, error) {
@@ -350,6 +371,8 @@
return append(android.Paths{j.outputFile}, j.extraOutputFiles...), nil
case ".jar":
return android.Paths{j.implementationAndResourcesJar}, nil
+ case ".proguard_map":
+ return android.Paths{j.proguardDictionary}, nil
default:
return nil, fmt.Errorf("unsupported module reference tag %q", tag)
}
@@ -382,6 +405,10 @@
CompiledSrcJars() android.Paths
}
+type xref interface {
+ XrefJavaFiles() android.Paths
+}
+
func (j *Module) CompiledSrcs() android.Paths {
return j.compiledJavaSrcs
}
@@ -390,6 +417,10 @@
return j.compiledSrcJars
}
+func (j *Module) XrefJavaFiles() android.Paths {
+ return j.kytheFiles
+}
+
var _ SrcDependency = (*Module)(nil)
func InitJavaModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
@@ -632,8 +663,7 @@
aidlIncludeDirs android.Paths
srcs android.Paths
srcJars android.Paths
- systemModules android.Path
- systemModulesDeps android.Paths
+ systemModules *systemModules
aidlPreprocess android.OptionalPath
kotlinStdlib android.Paths
kotlinAnnotations android.Paths
@@ -844,8 +874,7 @@
if sm.outputDir == nil || len(sm.outputDeps) == 0 {
panic("Missing directory for system module dependency")
}
- deps.systemModules = sm.outputDir
- deps.systemModulesDeps = sm.outputDeps
+ deps.systemModules = &systemModules{sm.outputDir, sm.outputDeps}
}
}
})
@@ -973,10 +1002,7 @@
}
// systemModules
- if deps.systemModules != nil {
- flags.systemModules = append(flags.systemModules, deps.systemModules)
- flags.systemModulesDeps = append(flags.systemModulesDeps, deps.systemModulesDeps...)
- }
+ flags.systemModules = deps.systemModules
// aidl flags.
flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
@@ -1143,6 +1169,12 @@
TransformJavaToClasses(ctx, classes, -1, uniqueSrcFiles, srcJars, flags, extraJarDeps)
jars = append(jars, classes)
}
+ if ctx.Config().EmitXrefRules() {
+ extractionFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".kzip")
+ emitXrefRule(ctx, extractionFile, uniqueSrcFiles, srcJars, flags, extraJarDeps, "xref")
+ j.kytheFiles = append(j.kytheFiles, extractionFile)
+
+ }
if ctx.Failed() {
return
}
@@ -1322,11 +1354,9 @@
return
}
- if !ctx.Config().UnbundledBuild() {
- // Hidden API CSV generation and dex encoding
- dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, dexOutputFile, j.implementationJarFile,
- j.deviceProperties.UncompressDex)
- }
+ // Hidden API CSV generation and dex encoding
+ dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, dexOutputFile, j.implementationJarFile,
+ j.deviceProperties.UncompressDex)
// merge dex jar with resources if necessary
if j.resourceJar != nil {
@@ -1557,7 +1587,8 @@
j.deviceProperties.UncompressDex = j.dexpreopter.uncompressedDex
j.compile(ctx, nil)
- if (Bool(j.properties.Installable) || ctx.Host()) && !android.DirectlyInAnyApex(ctx, ctx.ModuleName()) {
+ exclusivelyForApex := android.InAnyApex(ctx.ModuleName()) && !j.IsForPlatform()
+ if (Bool(j.properties.Installable) || ctx.Host()) && !exclusivelyForApex {
j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
ctx.ModuleName()+".jar", j.outputFile)
}
@@ -1588,6 +1619,7 @@
&module.Module.protoProperties)
InitJavaModule(module, android.HostAndDeviceSupported)
+ android.InitApexModule(module)
return module
}
@@ -1610,6 +1642,7 @@
module.Module.properties.Installable = proptools.BoolPtr(true)
InitJavaModule(module, android.HostSupported)
+ android.InitApexModule(module)
return module
}
@@ -1865,6 +1898,7 @@
type Import struct {
android.ModuleBase
android.DefaultableModuleBase
+ android.ApexModuleBase
prebuilt android.Prebuilt
properties ImportProperties
@@ -2021,6 +2055,7 @@
android.InitPrebuiltModule(module, &module.properties.Jars)
InitJavaModule(module, android.HostAndDeviceSupported)
+ android.InitApexModule(module)
return module
}
@@ -2036,18 +2071,20 @@
android.InitPrebuiltModule(module, &module.properties.Jars)
InitJavaModule(module, android.HostSupported)
+ android.InitApexModule(module)
return module
}
// dex_import module
type DexImportProperties struct {
- Jars []string
+ Jars []string `android:"path"`
}
type DexImport struct {
android.ModuleBase
android.DefaultableModuleBase
+ android.ApexModuleBase
prebuilt android.Prebuilt
properties DexImportProperties
@@ -2070,10 +2107,6 @@
return j.prebuilt.Name(j.ModuleBase.Name())
}
-func (j *DexImport) DepsMutator(ctx android.BottomUpMutatorContext) {
- android.ExtractSourcesDeps(ctx, j.properties.Jars)
-}
-
func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
if len(j.properties.Jars) != 1 {
ctx.PropertyErrorf("jars", "exactly one jar must be provided")
@@ -2094,14 +2127,14 @@
// use zip2zip to uncompress classes*.dex files
rule.Command().
- Tool(ctx.Config().HostToolPath(ctx, "zip2zip")).
+ BuiltTool(ctx, "zip2zip").
FlagWithInput("-i ", inputJar).
FlagWithOutput("-o ", temporary).
FlagWithArg("-0 ", "'classes*.dex'")
// use zipalign to align uncompressed classes*.dex files
rule.Command().
- Tool(ctx.Config().HostToolPath(ctx, "zipalign")).
+ BuiltTool(ctx, "zipalign").
Flag("-f").
Text("4").
Input(temporary).
@@ -2143,6 +2176,7 @@
android.InitPrebuiltModule(module, &module.properties.Jars)
InitJavaModule(module, android.DeviceSupported)
+ android.InitApexModule(module)
return module
}
@@ -2152,6 +2186,7 @@
type Defaults struct {
android.ModuleBase
android.DefaultsModuleBase
+ android.ApexModuleBase
}
// java_defaults provides a set of properties that can be inherited by other java or android modules.
@@ -2210,10 +2245,34 @@
)
android.InitDefaultsModule(module)
-
+ android.InitApexModule(module)
return module
}
+func kytheExtractJavaFactory() android.Singleton {
+ return &kytheExtractJavaSingleton{}
+}
+
+type kytheExtractJavaSingleton struct {
+}
+
+func (ks *kytheExtractJavaSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ var xrefTargets android.Paths
+ ctx.VisitAllModules(func(module android.Module) {
+ if javaModule, ok := module.(xref); ok {
+ xrefTargets = append(xrefTargets, javaModule.XrefJavaFiles()...)
+ }
+ })
+ // TODO(asmundak): perhaps emit a rule to output a warning if there were no xrefTargets
+ if len(xrefTargets) > 0 {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: blueprint.Phony,
+ Output: android.PathForPhony(ctx, "xref_java"),
+ Inputs: xrefTargets,
+ })
+ }
+}
+
var Bool = proptools.Bool
var BoolDefault = proptools.BoolDefault
var String = proptools.String
diff --git a/java/java_test.go b/java/java_test.go
index 677174d..5fcdf96 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -57,8 +57,7 @@
return TestConfig(buildDir, env)
}
-func testContext(config android.Config, bp string,
- fs map[string][]byte) *android.TestContext {
+func testContext(bp string, fs map[string][]byte) *android.TestContext {
ctx := android.NewTestArchContext()
ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(AndroidAppFactory))
@@ -172,6 +171,8 @@
"prebuilts/sdk/Android.bp": []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "current"],}`),
"prebuilts/apk/app.apk": nil,
+ "prebuilts/apk/app_arm.apk": nil,
+ "prebuilts/apk/app_arm64.apk": nil,
"prebuilts/apk/app_xhdpi.apk": nil,
"prebuilts/apk/app_xxhdpi.apk": nil,
@@ -193,6 +194,7 @@
"bar-doc/a.java": nil,
"bar-doc/b.java": nil,
"bar-doc/IFoo.aidl": nil,
+ "bar-doc/IBar.aidl": nil,
"bar-doc/known_oj_tags.txt": nil,
"external/doclava/templates-sdk": nil,
@@ -222,13 +224,36 @@
android.FailIfErrored(t, errs)
}
-func testJava(t *testing.T, bp string) *android.TestContext {
+func testJavaError(t *testing.T, pattern string, bp string) {
t.Helper()
config := testConfig(nil)
- ctx := testContext(config, bp, nil)
+ ctx := testContext(bp, nil)
+
+ pathCtx := android.PathContextForTesting(config, nil)
+ setDexpreoptTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
+
+ ctx.Register()
+ _, errs := ctx.ParseBlueprintsFiles("Android.bp")
+ if len(errs) > 0 {
+ android.FailIfNoMatchingErrors(t, pattern, errs)
+ return
+ }
+ _, errs = ctx.PrepareBuildActions(config)
+ if len(errs) > 0 {
+ android.FailIfNoMatchingErrors(t, pattern, errs)
+ return
+ }
+
+ t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
+}
+
+func testJava(t *testing.T, bp string) (*android.TestContext, android.Config) {
+ t.Helper()
+ config := testConfig(nil)
+ ctx := testContext(bp, nil)
run(t, ctx, config)
- return ctx
+ return ctx, config
}
func moduleToPath(name string) string {
@@ -243,7 +268,7 @@
}
func TestSimple(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -287,7 +312,7 @@
}
func TestSdkVersion(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -313,7 +338,7 @@
}
func TestArchSpecific(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -332,7 +357,7 @@
}
func TestBinary(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library_host {
name: "foo",
srcs: ["a.java"],
@@ -361,7 +386,7 @@
}
func TestPrebuilts(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -412,7 +437,7 @@
}
func TestDefaults(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_defaults {
name: "defaults",
srcs: ["a.java"],
@@ -558,7 +583,7 @@
for _, test := range table {
t.Run(test.name, func(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: [
@@ -587,7 +612,7 @@
}
func TestIncludeSrcs(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: [
@@ -650,7 +675,7 @@
}
func TestGeneratedSources(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: [
@@ -683,7 +708,7 @@
}
func TestTurbine(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -732,7 +757,7 @@
}
func TestSharding(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "bar",
srcs: ["a.java","b.java","c.java"],
@@ -750,16 +775,22 @@
}
func TestDroiddoc(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
droiddoc_template {
name: "droiddoc-templates-sdk",
path: ".",
}
+ filegroup {
+ name: "bar-doc-aidl-srcs",
+ srcs: ["bar-doc/IBar.aidl"],
+ path: "bar-doc",
+ }
droiddoc {
name: "bar-doc",
srcs: [
"bar-doc/*.java",
"bar-doc/IFoo.aidl",
+ ":bar-doc-aidl-srcs",
],
exclude_srcs: [
"bar-doc/b.java"
@@ -777,23 +808,24 @@
}
`)
- stubsJar := filepath.Join(buildDir, ".intermediates", "bar-doc", "android_common", "bar-doc-stubs.srcjar")
- barDoc := ctx.ModuleForTests("bar-doc", "android_common").Output("bar-doc-stubs.srcjar")
- if stubsJar != barDoc.Output.String() {
- t.Errorf("expected stubs Jar [%q], got %q", stubsJar, barDoc.Output.String())
- }
inputs := ctx.ModuleForTests("bar-doc", "android_common").Rule("javadoc").Inputs
var javaSrcs []string
for _, i := range inputs {
javaSrcs = append(javaSrcs, i.Base())
}
- if len(javaSrcs) != 2 || javaSrcs[0] != "a.java" || javaSrcs[1] != "IFoo.java" {
- t.Errorf("inputs of bar-doc must be []string{\"a.java\", \"IFoo.java\", but was %#v.", javaSrcs)
+ if len(javaSrcs) != 3 || javaSrcs[0] != "a.java" || javaSrcs[1] != "IFoo.java" || javaSrcs[2] != "IBar.java" {
+ t.Errorf("inputs of bar-doc must be []string{\"a.java\", \"IFoo.java\", \"IBar.java\", but was %#v.", javaSrcs)
+ }
+
+ aidlRule := ctx.ModuleForTests("bar-doc", "android_common").Output(inputs[2].String())
+ aidlFlags := aidlRule.Args["aidlFlags"]
+ if !strings.Contains(aidlFlags, "-Ibar-doc") {
+ t.Errorf("aidl flags for IBar.aidl should contain \"-Ibar-doc\", but was %q", aidlFlags)
}
}
func TestJarGenrules(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -847,7 +879,7 @@
}
func TestExcludeFileGroupInSrcs(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java", ":foo-srcs"],
@@ -874,7 +906,7 @@
func TestJavaLibrary(t *testing.T) {
config := testConfig(nil)
- ctx := testContext(config, "", map[string][]byte{
+ ctx := testContext("", map[string][]byte{
"libcore/Android.bp": []byte(`
java_library {
name: "core",
@@ -886,7 +918,7 @@
}
func TestJavaSdkLibrary(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
droiddoc_template {
name: "droiddoc-templates-sdk",
path: ".",
@@ -1057,7 +1089,7 @@
t.Run("Java language level 8", func(t *testing.T) {
// Test default javac -source 1.8 -target 1.8
- ctx := testJava(t, bp)
+ ctx, _ := testJava(t, bp)
checkPatchModuleFlag(t, ctx, "foo", "")
checkPatchModuleFlag(t, ctx, "bar", "")
@@ -1067,7 +1099,7 @@
t.Run("Java language level 9", func(t *testing.T) {
// Test again with javac -source 9 -target 9
config := testConfig(map[string]string{"EXPERIMENTAL_JAVA_LANGUAGE_LEVEL_9": "true"})
- ctx := testContext(config, bp, nil)
+ ctx := testContext(bp, nil)
run(t, ctx, config)
checkPatchModuleFlag(t, ctx, "foo", "")
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index e0eb0c0..5c6d45f 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -22,7 +22,7 @@
)
func TestKotlin(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java", "b.kt"],
@@ -84,7 +84,7 @@
}
func TestKapt(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java", "b.kt"],
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
new file mode 100644
index 0000000..792edf3
--- /dev/null
+++ b/java/platform_compat_config.go
@@ -0,0 +1,92 @@
+// Copyright 2019 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"
+)
+
+func init() {
+ android.RegisterModuleType("platform_compat_config", platformCompatConfigFactory)
+}
+
+type platformCompatConfigProperties struct {
+ Src *string `android:"path"`
+ Prefix *string
+}
+
+type platformCompatConfig struct {
+ android.ModuleBase
+
+ properties platformCompatConfigProperties
+ installDirPath android.OutputPath
+ configFile android.OutputPath
+}
+
+func (p *platformCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ rule := android.NewRuleBuilder()
+
+ configFileName := String(p.properties.Prefix) + "_platform_compat_config.xml"
+ p.configFile = android.PathForModuleOut(ctx, configFileName).OutputPath
+ path := android.PathForModuleSrc(ctx, String(p.properties.Src))
+
+ // Use the empty config if the compat config file idoesn't exist (can happen if @ChangeId
+ // annotation is not used).
+ emptyConfig := `<?xml version="1.0" encoding="UTF-8" standalone="no"?><config/>`
+ configPath := `compat/compat_config.xml`
+
+ rule.Command().
+ Text(`unzip`).
+ Flag(`-l`).
+ Input(path).
+ Text(`| grep`).
+ Flag(`-q`).
+ Text(configPath).
+ Text(`; if [ "$?" = "0" ] ; then`).
+ Text(`unzip`).
+ Flag(`-qp`).
+ Input(path).
+ Text(configPath).
+ Text(`>`).
+ Output(p.configFile).
+ Text(`; else echo '`).
+ Text(emptyConfig).
+ Text(`' >`).
+ Output(p.configFile).
+ Text(`; fi`)
+
+ p.installDirPath = android.PathForModuleInstall(ctx, "etc", "sysconfig")
+ rule.Build(pctx, ctx, configFileName, "Extract compat/compat_config.xml and install it")
+
+}
+
+func (p *platformCompatConfig) AndroidMkEntries() android.AndroidMkEntries {
+ return android.AndroidMkEntries{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(p.configFile),
+ Include: "$(BUILD_PREBUILT)",
+ AddCustomEntries: func(name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", "$(OUT_DIR)/"+p.installDirPath.RelPathString())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base())
+ },
+ }
+}
+
+func platformCompatConfigFactory() android.Module {
+ module := &platformCompatConfig{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ return module
+}
diff --git a/java/plugin_test.go b/java/plugin_test.go
index d1aef2c..c7913d3 100644
--- a/java/plugin_test.go
+++ b/java/plugin_test.go
@@ -20,7 +20,7 @@
)
func TestNoPlugin(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -44,7 +44,7 @@
}
func TestPlugin(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -83,7 +83,7 @@
}
func TestPluginGeneratesApi(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
diff --git a/java/proto.go b/java/proto.go
index 37de1d2..22a3eed 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -34,7 +34,7 @@
// Proto generated java files have an unknown package name in the path, so package the entire output directory
// into a srcjar.
rule.Command().
- Tool(ctx.Config().HostToolPath(ctx, "soong_zip")).
+ BuiltTool(ctx, "soong_zip").
Flag("-jar").
FlagWithOutput("-o ", srcJarFile).
FlagWithArg("-C ", outDir.String()).
@@ -75,9 +75,11 @@
flags.proto = android.GetProtoFlags(ctx, p)
if String(p.Proto.Plugin) == "" {
+ var typeToPlugin string
switch String(p.Proto.Type) {
case "micro":
flags.proto.OutTypeFlag = "--javamicro_out"
+ typeToPlugin = "javamicro"
case "nano":
flags.proto.OutTypeFlag = "--javanano_out"
case "lite":
@@ -89,6 +91,12 @@
ctx.PropertyErrorf("proto.type", "unknown proto type %q",
String(p.Proto.Type))
}
+
+ if typeToPlugin != "" {
+ hostTool := ctx.Config().HostToolPath(ctx, "protoc-gen-"+typeToPlugin)
+ flags.proto.Deps = append(flags.proto.Deps, hostTool)
+ flags.proto.Flags = append(flags.proto.Flags, "--plugin=protoc-gen-"+typeToPlugin+"="+hostTool.String())
+ }
}
flags.proto.OutParams = append(flags.proto.OutParams, j.Proto.Output_params...)
diff --git a/java/sdk.go b/java/sdk.go
index 7b79a49..d1e2ae4 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -296,7 +296,7 @@
rule.Command().
Text("rm -f").Output(aidl)
rule.Command().
- Tool(ctx.Config().HostToolPath(ctx, "sdkparcelables")).
+ BuiltTool(ctx, "sdkparcelables").
Input(jar).
Output(aidl)
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 1182b96..d38088d 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -149,16 +149,21 @@
var _ SdkLibraryDependency = (*SdkLibrary)(nil)
func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
+ useBuiltStubs := !ctx.Config().UnbundledBuildUsePrebuiltSdks()
// Add dependencies to the stubs library
- ctx.AddVariationDependencies(nil, publicApiStubsTag, module.stubsName(apiScopePublic))
+ if useBuiltStubs {
+ ctx.AddVariationDependencies(nil, publicApiStubsTag, module.stubsName(apiScopePublic))
+ }
ctx.AddVariationDependencies(nil, publicApiFileTag, module.docsName(apiScopePublic))
sdkDep := decodeSdkDep(ctx, sdkContext(&module.Library))
if sdkDep.hasStandardLibs() {
- ctx.AddVariationDependencies(nil, systemApiStubsTag, module.stubsName(apiScopeSystem))
+ if useBuiltStubs {
+ ctx.AddVariationDependencies(nil, systemApiStubsTag, module.stubsName(apiScopeSystem))
+ ctx.AddVariationDependencies(nil, testApiStubsTag, module.stubsName(apiScopeTest))
+ }
ctx.AddVariationDependencies(nil, systemApiFileTag, module.docsName(apiScopeSystem))
ctx.AddVariationDependencies(nil, testApiFileTag, module.docsName(apiScopeTest))
- ctx.AddVariationDependencies(nil, testApiStubsTag, module.stubsName(apiScopeTest))
}
module.Library.deps(ctx)
diff --git a/java/sdk_test.go b/java/sdk_test.go
index f82a4fb..6be17eb 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -282,7 +282,7 @@
if testcase.pdk {
config.TestProductVariables.Pdk = proptools.BoolPtr(true)
}
- ctx := testContext(config, bp, nil)
+ ctx := testContext(bp, nil)
run(t, ctx, config)
checkClasspath(t, ctx)
@@ -309,7 +309,7 @@
if testcase.pdk {
config.TestProductVariables.Pdk = proptools.BoolPtr(true)
}
- ctx := testContext(config, bp, nil)
+ ctx := testContext(bp, nil)
run(t, ctx, config)
javac := ctx.ModuleForTests("foo", variant).Rule("javac")
@@ -335,7 +335,7 @@
if testcase.pdk {
config.TestProductVariables.Pdk = proptools.BoolPtr(true)
}
- ctx := testContext(config, bp, nil)
+ ctx := testContext(bp, nil)
run(t, ctx, config)
checkClasspath(t, ctx)
diff --git a/java/support_libraries.go b/java/support_libraries.go
index 5a72f41..af7c3c2 100644
--- a/java/support_libraries.go
+++ b/java/support_libraries.go
@@ -52,8 +52,6 @@
supportAars = append(supportAars, name)
case *Library, *Import:
supportJars = append(supportJars, name)
- default:
- ctx.ModuleErrorf(module, "unknown module type %t", module)
}
})
diff --git a/python/proto.go b/python/proto.go
index 85ed1a5..b71e047 100644
--- a/python/proto.go
+++ b/python/proto.go
@@ -34,7 +34,7 @@
// Proto generated python files have an unknown package name in the path, so package the entire output directory
// into a srcszip.
zipCmd := rule.Command().
- Tool(ctx.Config().HostToolPath(ctx, "soong_zip")).
+ BuiltTool(ctx, "soong_zip").
FlagWithOutput("-o ", srcsZipFile)
if pkgPath != "" {
zipCmd.FlagWithArg("-P ", pkgPath)
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 31f5922..8c59cbc 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -69,3 +69,19 @@
},
test_suites: ["general-tests"],
}
+
+python_binary_host {
+ name: "jsonmodify",
+ main: "jsonmodify.py",
+ srcs: [
+ "jsonmodify.py",
+ ],
+ version: {
+ py2: {
+ enabled: true,
+ },
+ py3: {
+ enabled: false,
+ },
+ }
+}
diff --git a/scripts/gen_sorted_bss_symbols.sh b/scripts/gen_sorted_bss_symbols.sh
new file mode 100755
index 0000000..244ed0d
--- /dev/null
+++ b/scripts/gen_sorted_bss_symbols.sh
@@ -0,0 +1,28 @@
+#!/bin/bash -e
+
+# Copyright 2019 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.
+
+# Script to generate a symbol ordering file that sorts bss section symbols by
+# their sizes.
+# Inputs:
+# Environment:
+# CROSS_COMPILE: prefix added to nm tools
+# Arguments:
+# $1: Input ELF file
+# $2: Output symbol ordering file
+
+set -o pipefail
+
+${CROSS_COMPILE}nm --size-sort $1 | awk '{if ($2 == "b" || $2 == "B") print $3}' > $2
diff --git a/scripts/jar-wrapper.sh b/scripts/jar-wrapper.sh
index 71c1d90..b468041 100644
--- a/scripts/jar-wrapper.sh
+++ b/scripts/jar-wrapper.sh
@@ -48,11 +48,11 @@
exit 1
fi
-javaOpts=""
+declare -a javaOpts=()
while expr "x$1" : 'x-J' >/dev/null; do
- opt=`expr "$1" : '-J\(.*\)'`
- javaOpts="${javaOpts} -${opt}"
+ opt=`expr "$1" : '-J-\{0,1\}\(.*\)'`
+ javaOpts+=("-${opt}")
shift
done
-exec java ${javaOpts} -jar ${jardir}/${jarfile} "$@"
+exec java "${javaOpts[@]}" -jar ${jardir}/${jarfile} "$@"
diff --git a/scripts/jsonmodify.py b/scripts/jsonmodify.py
new file mode 100755
index 0000000..4b2c3c2
--- /dev/null
+++ b/scripts/jsonmodify.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# 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.
+
+
+import argparse
+import collections
+import json
+import sys
+
+def follow_path(obj, path):
+ cur = obj
+ last_key = None
+ for key in path.split('.'):
+ if last_key:
+ if last_key not in cur:
+ return None,None
+ cur = cur[last_key]
+ last_key = key
+ if last_key not in cur:
+ return None,None
+ return cur, last_key
+
+
+def ensure_path(obj, path):
+ cur = obj
+ last_key = None
+ for key in path.split('.'):
+ if last_key:
+ if last_key not in cur:
+ cur[last_key] = dict()
+ cur = cur[last_key]
+ last_key = key
+ return cur, last_key
+
+
+class SetValue(str):
+ def apply(self, obj, val):
+ cur, key = ensure_path(obj, self)
+ cur[key] = val
+
+
+class Replace(str):
+ def apply(self, obj, val):
+ cur, key = follow_path(obj, self)
+ if cur:
+ cur[key] = val
+
+
+class Remove(str):
+ def apply(self, obj):
+ cur, key = follow_path(obj, self)
+ if cur:
+ del cur[key]
+
+
+class AppendList(str):
+ def apply(self, obj, *args):
+ cur, key = ensure_path(obj, self)
+ if key not in cur:
+ cur[key] = list()
+ if not isinstance(cur[key], list):
+ raise ValueError(self + " should be a array.")
+ cur[key].extend(args)
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-o', '--out',
+ help='write result to a file. If omitted, print to stdout',
+ metavar='output',
+ action='store')
+ parser.add_argument('input', nargs='?', help='JSON file')
+ parser.add_argument("-v", "--value", type=SetValue,
+ help='set value of the key specified by path. If path doesn\'t exist, creates new one.',
+ metavar=('path', 'value'),
+ nargs=2, dest='patch', default=[], action='append')
+ parser.add_argument("-s", "--replace", type=Replace,
+ help='replace value of the key specified by path. If path doesn\'t exist, no op.',
+ metavar=('path', 'value'),
+ nargs=2, dest='patch', action='append')
+ parser.add_argument("-r", "--remove", type=Remove,
+ help='remove the key specified by path. If path doesn\'t exist, no op.',
+ metavar='path',
+ nargs=1, dest='patch', action='append')
+ parser.add_argument("-a", "--append_list", type=AppendList,
+ help='append values to the list specified by path. If path doesn\'t exist, creates new list for it.',
+ metavar=('path', 'value'),
+ nargs='+', dest='patch', default=[], action='append')
+ args = parser.parse_args()
+
+ if args.input:
+ with open(args.input) as f:
+ obj = json.load(f, object_pairs_hook=collections.OrderedDict)
+ else:
+ obj = json.load(sys.stdin, object_pairs_hook=collections.OrderedDict)
+
+ for p in args.patch:
+ p[0].apply(obj, *p[1:])
+
+ if args.out:
+ with open(args.out, "w") as f:
+ json.dump(obj, f, indent=2)
+ else:
+ print(json.dumps(obj, indent=2))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/strip.sh b/scripts/strip.sh
index bd62619..4a1ed3f 100755
--- a/scripts/strip.sh
+++ b/scripts/strip.sh
@@ -59,7 +59,7 @@
# ${CROSS_COMPILE}strip --strip-all does not strip .ARM.attributes,
# so we tell llvm-strip to keep it too.
if [ -z "${use_gnu_strip}" ]; then
- "${CLANG_BIN}/llvm-strip" --strip-all -keep-section=.ARM.attributes "${infile}" -o "${outfile}.tmp"
+ "${CLANG_BIN}/llvm-strip" --strip-all --keep-section=.ARM.attributes "${infile}" -o "${outfile}.tmp"
else
"${CROSS_COMPILE}strip" --strip-all "${infile}" -o "${outfile}.tmp"
fi
@@ -101,7 +101,7 @@
rm -f "${outfile}.dynsyms" "${outfile}.funcsyms" "${outfile}.keep_symbols" "${outfile}.debug" "${outfile}.mini_debuginfo" "${outfile}.mini_debuginfo.xz"
local fail=
if [ -z "${use_gnu_strip}" ]; then
- "${CLANG_BIN}/llvm-strip" --strip-all -keep-section=.ARM.attributes -remove-section=.comment "${infile}" -o "${outfile}.tmp" || fail=true
+ "${CLANG_BIN}/llvm-strip" --strip-all --keep-section=.ARM.attributes --remove-section=.comment "${infile}" -o "${outfile}.tmp" || fail=true
else
"${CROSS_COMPILE}strip" --strip-all -R .comment "${infile}" -o "${outfile}.tmp" || fail=true
fi
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 1ddaf68..6f81c17 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -52,6 +52,7 @@
"ninja.go",
"path.go",
"proc_sync.go",
+ "rbe.go",
"signal.go",
"soong.go",
"test_build.go",
diff --git a/ui/build/build.go b/ui/build/build.go
index 59d1474..6a19314 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -161,6 +161,11 @@
startGoma(ctx, config)
}
+ if config.StartRBE() {
+ // Ensure RBE proxy is started
+ startRBE(ctx, config)
+ }
+
if what&BuildProductConfig != 0 {
// Run make for product config
runMakeProductConfig(ctx, config)
diff --git a/ui/build/config.go b/ui/build/config.go
index 4e19e9e..665d2f0 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -61,6 +61,8 @@
const srcDirFileCheck = "build/soong/root.bp"
+var buildFiles = []string{"Android.mk", "Android.bp"}
+
type BuildAction uint
const (
@@ -178,6 +180,11 @@
ret.environ.Set("TMPDIR", absPath(ctx, ret.TempDir()))
+ // Always set ASAN_SYMBOLIZER_PATH so that ASAN-based tools can symbolize any crashes
+ symbolizerPath := filepath.Join("prebuilts/clang/host", ret.HostPrebuiltTag(),
+ "llvm-binutils-stable/llvm-symbolizer")
+ ret.environ.Set("ASAN_SYMBOLIZER_PATH", absPath(ctx, symbolizerPath))
+
// Precondition: the current directory is the top of the source tree
checkTopDir(ctx)
@@ -251,13 +258,13 @@
// NewBuildActionConfig returns a build configuration based on the build action. The arguments are
// processed based on the build action and extracts any arguments that belongs to the build action.
-func NewBuildActionConfig(action BuildAction, dir string, buildDependencies bool, ctx Context, args ...string) Config {
- return NewConfig(ctx, getConfigArgs(action, dir, buildDependencies, ctx, args)...)
+func NewBuildActionConfig(action BuildAction, dir string, ctx Context, args ...string) Config {
+ return NewConfig(ctx, getConfigArgs(action, dir, ctx, args)...)
}
// getConfigArgs processes the command arguments based on the build action and creates a set of new
// arguments to be accepted by Config.
-func getConfigArgs(action BuildAction, dir string, buildDependencies bool, ctx Context, args []string) []string {
+func getConfigArgs(action BuildAction, dir string, ctx Context, args []string) []string {
// The next block of code verifies that the current directory is the root directory of the source
// tree. It then finds the relative path of dir based on the root directory of the source tree
// and verify that dir is inside of the source tree.
@@ -293,7 +300,6 @@
configArgs = removeFromList("GET-INSTALL-PATH", configArgs)
}
- var buildFiles []string
var targets []string
switch action {
@@ -305,27 +311,16 @@
if topDir == dir {
break
}
- // Find the build file from the directory where the build action was triggered by traversing up
- // the source tree. If a blank build filename is returned, simply use the directory where the build
- // action was invoked.
+
buildFile := findBuildFile(ctx, relDir)
if buildFile == "" {
- buildFile = filepath.Join(relDir, "Android.mk")
+ ctx.Fatalf("Build file not found for %s directory", relDir)
}
- buildFiles = []string{buildFile}
targets = []string{convertToTarget(filepath.Dir(buildFile), targetNamePrefix)}
case BUILD_MODULES_IN_DIRECTORIES:
newConfigArgs, dirs := splitArgs(configArgs)
configArgs = newConfigArgs
- targets, buildFiles = getTargetsFromDirs(ctx, relDir, dirs, targetNamePrefix)
- }
-
- // This is to support building modules without building their dependencies. Soon, this will be
- // deprecated.
- if !buildDependencies && len(buildFiles) > 0 {
- if err := os.Setenv("ONE_SHOT_MAKEFILE", strings.Join(buildFiles, " ")); err != nil {
- ctx.Fatalf("Unable to set ONE_SHOT_MAKEFILE environment variable: %v", err)
- }
+ targets = getTargetsFromDirs(ctx, relDir, dirs, targetNamePrefix)
}
// Tidy only override all other specified targets.
@@ -344,26 +339,57 @@
return targetNamePrefix + strings.ReplaceAll(dir, "/", "-")
}
-// findBuildFile finds a build file (makefile or blueprint file) by looking at dir first. If not
-// found, go up one level and repeat again until one is found and the path of that build file
-// relative to the root directory of the source tree is returned. The returned filename of build
-// file is "Android.mk". If one was not found, a blank string is returned.
+// hasBuildFile returns true if dir contains an Android build file.
+func hasBuildFile(ctx Context, dir string) bool {
+ for _, buildFile := range buildFiles {
+ _, err := os.Stat(filepath.Join(dir, buildFile))
+ if err == nil {
+ return true
+ }
+ if !os.IsNotExist(err) {
+ ctx.Fatalf("Error retrieving the build file stats: %v", err)
+ }
+ }
+ return false
+}
+
+// findBuildFile finds a build file (makefile or blueprint file) by looking if there is a build file
+// in the current and any sub directory of dir. If a build file is not found, traverse the path
+// up by one directory and repeat again until either a build file is found or reached to the root
+// source tree. The returned filename of build file is "Android.mk". If one was not found, a blank
+// string is returned.
func findBuildFile(ctx Context, dir string) string {
- // If the string is empty, assume it is top directory of the source tree.
- if dir == "" {
+ // If the string is empty or ".", assume it is top directory of the source tree.
+ if dir == "" || dir == "." {
return ""
}
- for ; dir != "."; dir = filepath.Dir(dir) {
- for _, buildFile := range []string{"Android.bp", "Android.mk"} {
- _, err := os.Stat(filepath.Join(dir, buildFile))
- if err == nil {
- // Returning the filename Android.mk as it might be used for ONE_SHOT_MAKEFILE variable.
- return filepath.Join(dir, "Android.mk")
+ found := false
+ for buildDir := dir; buildDir != "."; buildDir = filepath.Dir(buildDir) {
+ err := filepath.Walk(buildDir, func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
}
- if !os.IsNotExist(err) {
- ctx.Fatalf("Error retrieving the build file stats: %v", err)
+ if found {
+ return filepath.SkipDir
}
+ if info.IsDir() {
+ return nil
+ }
+ for _, buildFile := range buildFiles {
+ if info.Name() == buildFile {
+ found = true
+ return filepath.SkipDir
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ ctx.Fatalf("Error finding Android build file: %v", err)
+ }
+
+ if found {
+ return filepath.Join(buildDir, "Android.mk")
}
}
@@ -404,7 +430,7 @@
// directory from the dirs list does not exist, a fatal error is raised. relDir is related to the
// source root tree where the build action command was invoked. Each directory is validated if the
// build file can be found and follows the format "dir1:target1,target2,...". Target is optional.
-func getTargetsFromDirs(ctx Context, relDir string, dirs []string, targetNamePrefix string) (targets []string, buildFiles []string) {
+func getTargetsFromDirs(ctx Context, relDir string, dirs []string, targetNamePrefix string) (targets []string) {
for _, dir := range dirs {
// The directory may have specified specific modules to build. ":" is the separator to separate
// the directory and the list of modules.
@@ -428,28 +454,25 @@
}
}
- buildFile := findBuildFile(ctx, dir)
- if buildFile == "" {
- ctx.Fatalf("Build file not found for %s directory", dir)
- }
- buildFileDir := filepath.Dir(buildFile)
-
- // If there are specified targets, find the build file in the directory. If dir does not
- // contain the build file, bail out as it is required for one shot build. If there are no
- // target specified, build all the modules in dir (or the closest one in the dir path).
+ // If there are specified targets to build in dir, an android build file must exist for the one
+ // shot build. For the non-targets case, find the appropriate build file and build all the
+ // modules in dir (or the closest one in the dir path).
if len(newTargets) > 0 {
- if buildFileDir != dir {
+ if !hasBuildFile(ctx, dir) {
ctx.Fatalf("Couldn't locate a build file from %s directory", dir)
}
} else {
- newTargets = []string{convertToTarget(buildFileDir, targetNamePrefix)}
+ buildFile := findBuildFile(ctx, dir)
+ if buildFile == "" {
+ ctx.Fatalf("Build file not found for %s directory", dir)
+ }
+ newTargets = []string{convertToTarget(filepath.Dir(buildFile), targetNamePrefix)}
}
- buildFiles = append(buildFiles, buildFile)
targets = append(targets, newTargets...)
}
- return targets, buildFiles
+ return targets
}
func (c *configImpl) parseArgs(ctx Context, args []string) {
@@ -608,7 +631,7 @@
func (c *configImpl) OutDir() string {
if outDir, ok := c.environ.Get("OUT_DIR"); ok {
- return filepath.Clean(outDir)
+ return outDir
}
return "out"
}
@@ -715,6 +738,30 @@
return true
}
+func (c *configImpl) UseRBE() bool {
+ if v, ok := c.environ.Get("USE_RBE"); ok {
+ v = strings.TrimSpace(v)
+ if v != "" && v != "false" {
+ return true
+ }
+ }
+ return false
+}
+
+func (c *configImpl) StartRBE() bool {
+ if !c.UseRBE() {
+ return false
+ }
+
+ if v, ok := c.environ.Get("NOSTART_RBE"); ok {
+ v = strings.TrimSpace(v)
+ if v != "" && v != "false" {
+ return false
+ }
+ }
+ return true
+}
+
// RemoteParallel controls how many remote jobs (i.e., commands which contain
// gomacc) are run in parallel. Note the parallelism of all other jobs is
// still limited by Parallel()
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index 7a1ee17..df618c4 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -363,19 +363,15 @@
// Expected targets from the function.
expectedTargets []string
- // Expected build from the build system.
- expectedBuildFiles []string
-
// Expecting error from running test case.
errStr string
}{{
- description: "one target dir specified",
- dirsInTrees: []string{"0/1/2/3"},
- buildFiles: []string{"0/1/2/3/Android.bp"},
- dirs: []string{"1/2/3"},
- curDir: "0",
- expectedTargets: []string{"MODULES-IN-0-1-2-3"},
- expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
+ description: "one target dir specified",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{"0/1/2/3/Android.bp"},
+ dirs: []string{"1/2/3"},
+ curDir: "0",
+ expectedTargets: []string{"MODULES-IN-0-1-2-3"},
}, {
description: "one target dir specified, build file does not exist",
dirsInTrees: []string{"0/1/2/3"},
@@ -391,21 +387,19 @@
curDir: "0",
errStr: "1/2/3:t1:t2 not in proper directory:target1,target2,... format (\":\" was specified more than once)",
}, {
- description: "one target dir specified, no targets specified but has colon",
- dirsInTrees: []string{"0/1/2/3"},
- buildFiles: []string{"0/1/2/3/Android.bp"},
- dirs: []string{"1/2/3:"},
- curDir: "0",
- expectedTargets: []string{"MODULES-IN-0-1-2-3"},
- expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
+ description: "one target dir specified, no targets specified but has colon",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{"0/1/2/3/Android.bp"},
+ dirs: []string{"1/2/3:"},
+ curDir: "0",
+ expectedTargets: []string{"MODULES-IN-0-1-2-3"},
}, {
- description: "one target dir specified, two targets specified",
- dirsInTrees: []string{"0/1/2/3"},
- buildFiles: []string{"0/1/2/3/Android.bp"},
- dirs: []string{"1/2/3:t1,t2"},
- curDir: "0",
- expectedTargets: []string{"t1", "t2"},
- expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
+ description: "one target dir specified, two targets specified",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{"0/1/2/3/Android.bp"},
+ dirs: []string{"1/2/3:t1,t2"},
+ curDir: "0",
+ expectedTargets: []string{"t1", "t2"},
}, {
description: "one target dir specified, no targets and has a comma",
dirsInTrees: []string{"0/1/2/3"},
@@ -428,20 +422,19 @@
curDir: "0",
errStr: "0/1/2/3 not in proper directory:target1,target2,... format",
}, {
- description: "one target dir specified, many targets specified",
- dirsInTrees: []string{"0/1/2/3"},
- buildFiles: []string{"0/1/2/3/Android.bp"},
- dirs: []string{"1/2/3:t1,t2,t3,t4,t5,t6,t7,t8,t9,t10"},
- curDir: "0",
- expectedTargets: []string{"t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10"},
- expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
+ description: "one target dir specified, many targets specified",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{"0/1/2/3/Android.bp"},
+ dirs: []string{"1/2/3:t1,t2,t3,t4,t5,t6,t7,t8,t9,t10"},
+ curDir: "0",
+ expectedTargets: []string{"t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10"},
}, {
description: "one target dir specified, one target specified, build file does not exist",
dirsInTrees: []string{"0/1/2/3"},
buildFiles: []string{},
dirs: []string{"1/2/3:t1"},
curDir: "0",
- errStr: "Build file not found for 0/1/2/3 directory",
+ errStr: "Couldn't locate a build file from 0/1/2/3 directory",
}, {
description: "one target dir specified, one target specified, build file not in target dir",
dirsInTrees: []string{"0/1/2/3"},
@@ -450,29 +443,26 @@
curDir: "0",
errStr: "Couldn't locate a build file from 0/1/2/3 directory",
}, {
- description: "one target dir specified, build file not in target dir",
- dirsInTrees: []string{"0/1/2/3"},
- buildFiles: []string{"0/1/2/Android.mk"},
- dirs: []string{"1/2/3"},
- curDir: "0",
- expectedTargets: []string{"MODULES-IN-0-1-2"},
- expectedBuildFiles: []string{"0/1/2/Android.mk"},
+ description: "one target dir specified, build file not in target dir",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{"0/1/2/Android.mk"},
+ dirs: []string{"1/2/3"},
+ curDir: "0",
+ expectedTargets: []string{"MODULES-IN-0-1-2"},
}, {
- description: "multiple targets dir specified, targets specified",
- dirsInTrees: []string{"0/1/2/3", "0/3/4"},
- buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
- dirs: []string{"1/2/3:t1,t2", "3/4:t3,t4,t5"},
- curDir: "0",
- expectedTargets: []string{"t1", "t2", "t3", "t4", "t5"},
- expectedBuildFiles: []string{"0/1/2/3/Android.mk", "0/3/4/Android.mk"},
+ description: "multiple targets dir specified, targets specified",
+ dirsInTrees: []string{"0/1/2/3", "0/3/4"},
+ buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
+ dirs: []string{"1/2/3:t1,t2", "3/4:t3,t4,t5"},
+ curDir: "0",
+ expectedTargets: []string{"t1", "t2", "t3", "t4", "t5"},
}, {
- description: "multiple targets dir specified, one directory has targets specified",
- dirsInTrees: []string{"0/1/2/3", "0/3/4"},
- buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
- dirs: []string{"1/2/3:t1,t2", "3/4"},
- curDir: "0",
- expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"},
- expectedBuildFiles: []string{"0/1/2/3/Android.mk", "0/3/4/Android.mk"},
+ description: "multiple targets dir specified, one directory has targets specified",
+ dirsInTrees: []string{"0/1/2/3", "0/3/4"},
+ buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
+ dirs: []string{"1/2/3:t1,t2", "3/4"},
+ curDir: "0",
+ expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"},
}, {
description: "two dirs specified, only one dir exist",
dirsInTrees: []string{"0/1/2/3"},
@@ -481,13 +471,12 @@
curDir: "0",
errStr: "couldn't find directory 0/3/4",
}, {
- description: "multiple targets dirs specified at root source tree",
- dirsInTrees: []string{"0/1/2/3", "0/3/4"},
- buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
- dirs: []string{"0/1/2/3:t1,t2", "0/3/4"},
- curDir: ".",
- expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"},
- expectedBuildFiles: []string{"0/1/2/3/Android.mk", "0/3/4/Android.mk"},
+ description: "multiple targets dirs specified at root source tree",
+ dirsInTrees: []string{"0/1/2/3", "0/3/4"},
+ buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
+ dirs: []string{"0/1/2/3:t1,t2", "0/3/4"},
+ curDir: ".",
+ expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"},
}, {
description: "no directories specified",
dirsInTrees: []string{"0/1/2/3", "0/3/4"},
@@ -518,13 +507,10 @@
r := setTop(t, topDir)
defer r()
- targets, buildFiles := getTargetsFromDirs(ctx, tt.curDir, tt.dirs, "MODULES-IN-")
+ targets := getTargetsFromDirs(ctx, tt.curDir, tt.dirs, "MODULES-IN-")
if !reflect.DeepEqual(targets, tt.expectedTargets) {
t.Errorf("expected %v, got %v for targets", tt.expectedTargets, targets)
}
- if !reflect.DeepEqual(buildFiles, tt.expectedBuildFiles) {
- t.Errorf("expected %v, got %v for build files", tt.expectedBuildFiles, buildFiles)
- }
// If the execution reached here and there was an expected error code, the unit test case failed.
if tt.errStr != "" {
@@ -545,8 +531,11 @@
// Array of build files to create in dir.
buildFiles []string
+ // Directories that exist in the source tree.
+ dirsInTrees []string
+
// ********* Action *********
- // Directory to create, also the base directory is where findBuildFile is invoked.
+ // The base directory is where findBuildFile is invoked.
dir string
// ********* Validation *********
@@ -555,38 +544,63 @@
}{{
description: "build file exists at leaf directory",
buildFiles: []string{"1/2/3/Android.bp"},
+ dirsInTrees: []string{"1/2/3"},
dir: "1/2/3",
expectedBuildFile: "1/2/3/Android.mk",
}, {
description: "build file exists in all directory paths",
buildFiles: []string{"1/Android.mk", "1/2/Android.mk", "1/2/3/Android.mk"},
+ dirsInTrees: []string{"1/2/3"},
dir: "1/2/3",
expectedBuildFile: "1/2/3/Android.mk",
}, {
description: "build file does not exist in all directory paths",
buildFiles: []string{},
+ dirsInTrees: []string{"1/2/3"},
dir: "1/2/3",
expectedBuildFile: "",
}, {
description: "build file exists only at top directory",
buildFiles: []string{"Android.bp"},
+ dirsInTrees: []string{"1/2/3"},
dir: "1/2/3",
expectedBuildFile: "",
}, {
description: "build file exist in a subdirectory",
buildFiles: []string{"1/2/Android.bp"},
+ dirsInTrees: []string{"1/2/3"},
dir: "1/2/3",
expectedBuildFile: "1/2/Android.mk",
}, {
description: "build file exists in a subdirectory",
buildFiles: []string{"1/Android.mk"},
+ dirsInTrees: []string{"1/2/3"},
dir: "1/2/3",
expectedBuildFile: "1/Android.mk",
}, {
description: "top directory",
buildFiles: []string{"Android.bp"},
+ dirsInTrees: []string{},
dir: ".",
expectedBuildFile: "",
+ }, {
+ description: "build file exists in subdirectory",
+ buildFiles: []string{"1/2/3/Android.bp", "1/2/4/Android.bp"},
+ dirsInTrees: []string{"1/2/3", "1/2/4"},
+ dir: "1/2",
+ expectedBuildFile: "1/2/Android.mk",
+ }, {
+ description: "build file exists in parent subdirectory",
+ buildFiles: []string{"1/5/Android.bp"},
+ dirsInTrees: []string{"1/2/3", "1/2/4", "1/5"},
+ dir: "1/2",
+ expectedBuildFile: "1/Android.mk",
+ }, {
+ description: "build file exists in deep parent's subdirectory.",
+ buildFiles: []string{"1/5/6/Android.bp"},
+ dirsInTrees: []string{"1/2/3", "1/2/4", "1/5/6", "1/5/7"},
+ dir: "1/2",
+ expectedBuildFile: "1/Android.mk",
}}
for _, tt := range tests {
@@ -601,10 +615,7 @@
}
defer os.RemoveAll(topDir)
- if tt.dir != "" {
- createDirectories(t, topDir, []string{tt.dir})
- }
-
+ createDirectories(t, topDir, tt.dirsInTrees)
createBuildFiles(t, topDir, tt.buildFiles)
curDir, err := os.Getwd()
@@ -707,16 +718,24 @@
// Expected arguments to be in Config instance.
expectedArgs []string
- // Expected environment variables to be set.
- expectedEnvVars []envVar
+ // Expecting error from running test case.
+ expectedErrStr string
}
-func testGetConfigArgs(t *testing.T, tt buildActionTestCase, action BuildAction, buildDependencies bool) {
+func testGetConfigArgs(t *testing.T, tt buildActionTestCase, action BuildAction) {
ctx := testContext()
+ defer logger.Recover(func(err error) {
+ if tt.expectedErrStr == "" {
+ t.Fatalf("Got unexpected error: %v", err)
+ }
+ if tt.expectedErrStr != err.Error() {
+ t.Errorf("expected %s, got %s", tt.expectedErrStr, err.Error())
+ }
+ })
+
// Environment variables to set it to blank on every test case run.
resetEnvVars := []string{
- "ONE_SHOT_MAKEFILE",
"WITH_TIDY_ONLY",
}
@@ -770,311 +789,156 @@
t.Fatalf("failed to create %s file: %v", srcDirFileCheck, err)
}
- args := getConfigArgs(action, tt.curDir, buildDependencies, ctx, tt.args)
+ args := getConfigArgs(action, tt.curDir, ctx, tt.args)
if !reflect.DeepEqual(tt.expectedArgs, args) {
t.Fatalf("expected %v, got %v for config arguments", tt.expectedArgs, args)
}
- for _, env := range tt.expectedEnvVars {
- if val := os.Getenv(env.name); val != env.value {
- t.Errorf("expecting %s, got %s for environment variable %s", env.value, val, env.name)
- }
+ // If the execution reached here and there was an expected error code, the unit test case failed.
+ if tt.expectedErrStr != "" {
+ t.Errorf("expecting error %s", tt.expectedErrStr)
}
}
func TestGetConfigArgsBuildModules(t *testing.T) {
tests := []buildActionTestCase{{
- description: "normal execution from the root source tree directory",
- dirsInTrees: []string{"0/1/2", "0/2", "0/3"},
- buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp", "0/3/Android.mk"},
- args: []string{"-j", "fake_module", "fake_module2"},
- curDir: ".",
- tidyOnly: "",
- expectedArgs: []string{"-j", "fake_module", "fake_module2"},
- expectedEnvVars: []envVar{},
+ description: "normal execution from the root source tree directory",
+ dirsInTrees: []string{"0/1/2", "0/2", "0/3"},
+ buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp", "0/3/Android.mk"},
+ args: []string{"-j", "fake_module", "fake_module2"},
+ curDir: ".",
+ tidyOnly: "",
+ expectedArgs: []string{"-j", "fake_module", "fake_module2"},
}, {
- description: "normal execution in deep directory",
- dirsInTrees: []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"},
- buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/Android.mk"},
- args: []string{"-j", "fake_module", "fake_module2", "-k"},
- curDir: "1/2/3/4/5/6/7/8/9",
- tidyOnly: "",
- expectedArgs: []string{"-j", "fake_module", "fake_module2", "-k"},
- expectedEnvVars: []envVar{},
+ description: "normal execution in deep directory",
+ dirsInTrees: []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"},
+ buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/Android.mk"},
+ args: []string{"-j", "fake_module", "fake_module2", "-k"},
+ curDir: "1/2/3/4/5/6/7/8/9",
+ tidyOnly: "",
+ expectedArgs: []string{"-j", "fake_module", "fake_module2", "-k"},
}, {
- description: "normal execution in deep directory, no targets",
- dirsInTrees: []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"},
- buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/Android.mk"},
- args: []string{"-j", "-k"},
- curDir: "1/2/3/4/5/6/7/8/9",
- tidyOnly: "",
- expectedArgs: []string{"-j", "-k"},
- expectedEnvVars: []envVar{},
+ description: "normal execution in deep directory, no targets",
+ dirsInTrees: []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"},
+ buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/Android.mk"},
+ args: []string{"-j", "-k"},
+ curDir: "1/2/3/4/5/6/7/8/9",
+ tidyOnly: "",
+ expectedArgs: []string{"-j", "-k"},
}, {
- description: "normal execution in root source tree, no args",
- dirsInTrees: []string{"0/1/2", "0/2", "0/3"},
- buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp"},
- args: []string{},
- curDir: "0/2",
- tidyOnly: "",
- expectedArgs: []string{},
- expectedEnvVars: []envVar{},
+ description: "normal execution in root source tree, no args",
+ dirsInTrees: []string{"0/1/2", "0/2", "0/3"},
+ buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp"},
+ args: []string{},
+ curDir: "0/2",
+ tidyOnly: "",
+ expectedArgs: []string{},
}, {
- description: "normal execution in symlink root source tree, no args",
- dirsInTrees: []string{"0/1/2", "0/2", "0/3"},
- buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp"},
- rootSymlink: true,
- args: []string{},
- curDir: "0/2",
- tidyOnly: "",
- expectedArgs: []string{},
- expectedEnvVars: []envVar{},
+ description: "normal execution in symlink root source tree, no args",
+ dirsInTrees: []string{"0/1/2", "0/2", "0/3"},
+ buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp"},
+ rootSymlink: true,
+ args: []string{},
+ curDir: "0/2",
+ tidyOnly: "",
+ expectedArgs: []string{},
}}
for _, tt := range tests {
t.Run("build action BUILD_MODULES with dependencies, "+tt.description, func(t *testing.T) {
- testGetConfigArgs(t, tt, BUILD_MODULES, true)
- })
- }
-}
-
-// TODO: Remove this test case once mm shell build command has been deprecated.
-func TestGetConfigArgsBuildModulesInDirecotoryNoDeps(t *testing.T) {
- tests := []buildActionTestCase{{
- description: "normal execution in a directory",
- dirsInTrees: []string{"0/1/2"},
- buildFiles: []string{"0/1/2/Android.mk"},
- args: []string{"-j", "-k", "showcommands", "fake-module"},
- curDir: "0/1/2",
- tidyOnly: "",
- expectedArgs: []string{"-j", "-k", "showcommands", "fake-module", "MODULES-IN-0-1-2"},
- expectedEnvVars: []envVar{
- envVar{
- name: "ONE_SHOT_MAKEFILE",
- value: "0/1/2/Android.mk"}},
- }, {
- description: "makefile in parent directory",
- dirsInTrees: []string{"0/1/2"},
- buildFiles: []string{"0/1/Android.mk"},
- args: []string{},
- curDir: "0/1/2",
- tidyOnly: "",
- expectedArgs: []string{"MODULES-IN-0-1"},
- expectedEnvVars: []envVar{
- envVar{
- name: "ONE_SHOT_MAKEFILE",
- value: "0/1/Android.mk"}},
- }, {
- description: "build file not found",
- dirsInTrees: []string{"0/1/2"},
- buildFiles: []string{},
- args: []string{},
- curDir: "0/1/2",
- tidyOnly: "",
- expectedArgs: []string{"MODULES-IN-0-1-2"},
- expectedEnvVars: []envVar{
- envVar{
- name: "ONE_SHOT_MAKEFILE",
- value: "0/1/2/Android.mk"}},
- }, {
- description: "build action executed at root directory",
- dirsInTrees: []string{},
- buildFiles: []string{},
- args: []string{},
- curDir: ".",
- tidyOnly: "",
- expectedArgs: []string{},
- expectedEnvVars: []envVar{
- envVar{
- name: "ONE_SHOT_MAKEFILE",
- value: ""}},
- }, {
- description: "GET-INSTALL-PATH specified,",
- dirsInTrees: []string{"0/1/2"},
- buildFiles: []string{"0/1/Android.mk"},
- args: []string{"GET-INSTALL-PATH"},
- curDir: "0/1/2",
- tidyOnly: "",
- expectedArgs: []string{"GET-INSTALL-PATH-IN-0-1"},
- expectedEnvVars: []envVar{
- envVar{
- name: "ONE_SHOT_MAKEFILE",
- value: "0/1/Android.mk"}},
- }, {
- description: "tidy only environment variable specified,",
- dirsInTrees: []string{"0/1/2"},
- buildFiles: []string{"0/1/Android.mk"},
- args: []string{"GET-INSTALL-PATH"},
- curDir: "0/1/2",
- tidyOnly: "true",
- expectedArgs: []string{"tidy_only"},
- expectedEnvVars: []envVar{
- envVar{
- name: "ONE_SHOT_MAKEFILE",
- value: "0/1/Android.mk"}},
- }}
- for _, tt := range tests {
- t.Run("build action BUILD_MODULES_IN_DIR without their dependencies, "+tt.description, func(t *testing.T) {
- testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY, false)
+ testGetConfigArgs(t, tt, BUILD_MODULES)
})
}
}
func TestGetConfigArgsBuildModulesInDirectory(t *testing.T) {
tests := []buildActionTestCase{{
- description: "normal execution in a directory",
- dirsInTrees: []string{"0/1/2"},
- buildFiles: []string{"0/1/2/Android.mk"},
- args: []string{"fake-module"},
- curDir: "0/1/2",
- tidyOnly: "",
- expectedArgs: []string{"fake-module", "MODULES-IN-0-1-2"},
- expectedEnvVars: []envVar{},
+ description: "normal execution in a directory",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{"0/1/2/Android.mk"},
+ args: []string{"fake-module"},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"fake-module", "MODULES-IN-0-1-2"},
}, {
- description: "build file in parent directory",
- dirsInTrees: []string{"0/1/2"},
- buildFiles: []string{"0/1/Android.mk"},
- args: []string{},
- curDir: "0/1/2",
- tidyOnly: "",
- expectedArgs: []string{"MODULES-IN-0-1"},
- expectedEnvVars: []envVar{},
+ description: "build file in parent directory",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{"0/1/Android.mk"},
+ args: []string{},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"MODULES-IN-0-1"},
},
{
- description: "build file in parent directory, multiple module names passed in",
- dirsInTrees: []string{"0/1/2"},
- buildFiles: []string{"0/1/Android.mk"},
- args: []string{"fake-module1", "fake-module2", "fake-module3"},
- curDir: "0/1/2",
- tidyOnly: "",
- expectedArgs: []string{"fake-module1", "fake-module2", "fake-module3", "MODULES-IN-0-1"},
- expectedEnvVars: []envVar{},
+ description: "build file in parent directory, multiple module names passed in",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{"0/1/Android.mk"},
+ args: []string{"fake-module1", "fake-module2", "fake-module3"},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"fake-module1", "fake-module2", "fake-module3", "MODULES-IN-0-1"},
}, {
- description: "build file in 2nd level parent directory",
- dirsInTrees: []string{"0/1/2"},
- buildFiles: []string{"0/Android.bp"},
- args: []string{},
- curDir: "0/1/2",
- tidyOnly: "",
- expectedArgs: []string{"MODULES-IN-0"},
- expectedEnvVars: []envVar{},
+ description: "build file in 2nd level parent directory",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{"0/Android.bp"},
+ args: []string{},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"MODULES-IN-0"},
}, {
- description: "build action executed at root directory",
- dirsInTrees: []string{},
- buildFiles: []string{},
- rootSymlink: false,
- args: []string{},
- curDir: ".",
- tidyOnly: "",
- expectedArgs: []string{},
- expectedEnvVars: []envVar{},
+ description: "build action executed at root directory",
+ dirsInTrees: []string{},
+ buildFiles: []string{},
+ rootSymlink: false,
+ args: []string{},
+ curDir: ".",
+ tidyOnly: "",
+ expectedArgs: []string{},
}, {
- description: "build action executed at root directory in symlink",
- dirsInTrees: []string{},
- buildFiles: []string{},
- rootSymlink: true,
- args: []string{},
- curDir: ".",
- tidyOnly: "",
- expectedArgs: []string{},
- expectedEnvVars: []envVar{},
+ description: "build action executed at root directory in symlink",
+ dirsInTrees: []string{},
+ buildFiles: []string{},
+ rootSymlink: true,
+ args: []string{},
+ curDir: ".",
+ tidyOnly: "",
+ expectedArgs: []string{},
}, {
- description: "build file not found - no error is expected to return",
- dirsInTrees: []string{"0/1/2"},
- buildFiles: []string{},
- args: []string{},
- curDir: "0/1/2",
- tidyOnly: "",
- expectedArgs: []string{"MODULES-IN-0-1-2"},
- expectedEnvVars: []envVar{},
+ description: "build file not found",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{},
+ args: []string{},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"MODULES-IN-0-1-2"},
+ expectedErrStr: "Build file not found for 0/1/2 directory",
}, {
- description: "GET-INSTALL-PATH specified,",
- dirsInTrees: []string{"0/1/2"},
- buildFiles: []string{"0/1/Android.mk"},
- args: []string{"GET-INSTALL-PATH", "-j", "-k", "GET-INSTALL-PATH"},
- curDir: "0/1/2",
- tidyOnly: "",
- expectedArgs: []string{"-j", "-k", "GET-INSTALL-PATH-IN-0-1"},
- expectedEnvVars: []envVar{},
+ description: "GET-INSTALL-PATH specified,",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{"0/1/Android.mk"},
+ args: []string{"GET-INSTALL-PATH", "-j", "-k", "GET-INSTALL-PATH"},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"-j", "-k", "GET-INSTALL-PATH-IN-0-1"},
}, {
- description: "tidy only environment variable specified,",
- dirsInTrees: []string{"0/1/2"},
- buildFiles: []string{"0/1/Android.mk"},
- args: []string{"GET-INSTALL-PATH"},
- curDir: "0/1/2",
- tidyOnly: "true",
- expectedArgs: []string{"tidy_only"},
- expectedEnvVars: []envVar{},
+ description: "tidy only environment variable specified,",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{"0/1/Android.mk"},
+ args: []string{"GET-INSTALL-PATH"},
+ curDir: "0/1/2",
+ tidyOnly: "true",
+ expectedArgs: []string{"tidy_only"},
}, {
- description: "normal execution in root directory with args",
- dirsInTrees: []string{},
- buildFiles: []string{},
- args: []string{"-j", "-k", "fake_module"},
- curDir: "",
- tidyOnly: "",
- expectedArgs: []string{"-j", "-k", "fake_module"},
- expectedEnvVars: []envVar{},
+ description: "normal execution in root directory with args",
+ dirsInTrees: []string{},
+ buildFiles: []string{},
+ args: []string{"-j", "-k", "fake_module"},
+ curDir: "",
+ tidyOnly: "",
+ expectedArgs: []string{"-j", "-k", "fake_module"},
}}
for _, tt := range tests {
t.Run("build action BUILD_MODULES_IN_DIR, "+tt.description, func(t *testing.T) {
- testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY, true)
- })
- }
-}
-
-// TODO: Remove this test case once mmm shell build command has been deprecated.
-func TestGetConfigArgsBuildModulesInDirectoriesNoDeps(t *testing.T) {
- tests := []buildActionTestCase{{
- description: "normal execution in a directory",
- dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
- buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
- args: []string{"3.1/:t1,t2", "3.2/:t3,t4", "3.3/:t5,t6"},
- curDir: "0/1/2",
- tidyOnly: "",
- expectedArgs: []string{"t1", "t2", "t3", "t4", "t5", "t6"},
- expectedEnvVars: []envVar{
- envVar{
- name: "ONE_SHOT_MAKEFILE",
- value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
- }, {
- description: "GET-INSTALL-PATH specified",
- dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
- buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
- args: []string{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3/:t6"},
- curDir: "0/1/2",
- tidyOnly: "",
- expectedArgs: []string{"GET-INSTALL-PATH-IN-0-1-2-3.1", "GET-INSTALL-PATH-IN-0-1-2-3.2", "t6"},
- expectedEnvVars: []envVar{
- envVar{
- name: "ONE_SHOT_MAKEFILE",
- value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
- }, {
- description: "tidy only environment variable specified",
- dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
- buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
- args: []string{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3/:t6"},
- curDir: "0/1/2",
- tidyOnly: "1",
- expectedArgs: []string{"tidy_only"},
- expectedEnvVars: []envVar{
- envVar{
- name: "ONE_SHOT_MAKEFILE",
- value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
- }, {
- description: "normal execution from top dir directory",
- dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
- buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
- args: []string{"0/1/2/3.1", "0/1/2/3.2/:t3,t4", "0/1/2/3.3/:t5,t6"},
- curDir: ".",
- tidyOnly: "",
- expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "t3", "t4", "t5", "t6"},
- expectedEnvVars: []envVar{
- envVar{
- name: "ONE_SHOT_MAKEFILE",
- value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
- }}
- for _, tt := range tests {
- t.Run("build action BUILD_MODULES_IN_DIRS_NO_DEPS, "+tt.description, func(t *testing.T) {
- testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES, false)
+ testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY)
})
}
}
@@ -1088,10 +952,6 @@
curDir: "0/1/2",
tidyOnly: "",
expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-2-3.3"},
- expectedEnvVars: []envVar{
- envVar{
- name: "ONE_SHOT_MAKEFILE",
- value: ""}},
}, {
description: "GET-INSTALL-PATH specified",
dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3"},
@@ -1100,10 +960,6 @@
curDir: "0/1",
tidyOnly: "",
expectedArgs: []string{"GET-INSTALL-PATH-IN-0-1-2-3.1", "GET-INSTALL-PATH-IN-0-1-2-3.2", "GET-INSTALL-PATH-IN-0-1"},
- expectedEnvVars: []envVar{
- envVar{
- name: "ONE_SHOT_MAKEFILE",
- value: ""}},
}, {
description: "tidy only environment variable specified",
dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
@@ -1112,10 +968,6 @@
curDir: "0/1/2",
tidyOnly: "1",
expectedArgs: []string{"tidy_only"},
- expectedEnvVars: []envVar{
- envVar{
- name: "ONE_SHOT_MAKEFILE",
- value: ""}},
}, {
description: "normal execution from top dir directory",
dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
@@ -1125,10 +977,6 @@
curDir: ".",
tidyOnly: "",
expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-3", "MODULES-IN-0-2"},
- expectedEnvVars: []envVar{
- envVar{
- name: "ONE_SHOT_MAKEFILE",
- value: ""}},
}, {
description: "normal execution from top dir directory in symlink",
dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
@@ -1138,14 +986,10 @@
curDir: ".",
tidyOnly: "",
expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-3", "MODULES-IN-0-2"},
- expectedEnvVars: []envVar{
- envVar{
- name: "ONE_SHOT_MAKEFILE",
- value: ""}},
}}
for _, tt := range tests {
t.Run("build action BUILD_MODULES_IN_DIRS, "+tt.description, func(t *testing.T) {
- testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES, true)
+ testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES)
})
}
}
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 266130f..6b9eac1 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -169,7 +169,7 @@
// Variables to export into the environment of Kati/Ninja
exportEnvVars := []string{
// So that we can use the correct TARGET_PRODUCT if it's been
- // modified by PRODUCT-*/APP-* arguments
+ // modified by a buildspec.mk
"TARGET_PRODUCT",
"TARGET_BUILD_VARIANT",
"TARGET_BUILD_APPS",
diff --git a/ui/build/goma.go b/ui/build/goma.go
index ff0b40e..ae9b784 100644
--- a/ui/build/goma.go
+++ b/ui/build/goma.go
@@ -72,6 +72,7 @@
}
cmd := Command(ctx, config, "goma_ctl.py ensure_start", gomaCtl, "ensure_start")
+ cmd.Environment.Set("DIST_DIR", config.DistDir())
if output, err := cmd.CombinedOutput(); err != nil {
ctx.Fatalf("goma_ctl.py ensure_start failed with: %v\n%s\n", err, output)
diff --git a/ui/build/kati.go b/ui/build/kati.go
index 5ad966a..a7799ea 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -42,9 +42,6 @@
if args := config.KatiArgs(); len(args) > 0 {
katiSuffix += "-" + spaceSlashReplacer.Replace(strings.Join(args, "_"))
}
- if oneShot, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
- katiSuffix += "-" + spaceSlashReplacer.Replace(oneShot)
- }
// If the suffix is too long, replace it with a md5 hash and write a
// file that contains the original suffix.
@@ -174,6 +171,7 @@
"TMPDIR",
// Tool configs
+ "ASAN_SYMBOLIZER_PATH",
"JAVA_HOME",
"PYTHONDONTWRITEBYTECODE",
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index b41ac20..66750d6 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -43,7 +43,7 @@
args = append(args, config.NinjaArgs()...)
var parallel int
- if config.UseGoma() {
+ if config.UseGoma() || config.UseRBE() {
parallel = config.RemoteParallel()
} else {
parallel = config.Parallel()
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index a4be2ac..100cc65 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -74,21 +74,16 @@
}
var Configuration = map[string]PathConfig{
- "bash": Allowed,
- "bc": Allowed,
- // We need bzip2 here even though we provide a bzip2 binary because
- // GNU tar seems to avoid calling ours.
- "bzip2": Allowed,
+ "bash": Allowed,
+ "bc": Allowed,
"dd": Allowed,
"diff": Allowed,
"dlv": Allowed,
- "egrep": Allowed,
"expr": Allowed,
"find": Allowed,
"fuser": Allowed,
"getopt": Allowed,
"git": Allowed,
- "grep": Allowed,
"gzcat": Allowed,
"gzip": Allowed,
"hexdump": Allowed,
@@ -104,7 +99,6 @@
"realpath": Allowed,
"rsync": Allowed,
"sh": Allowed,
- "tar": Allowed,
"tr": Allowed,
"unzip": Allowed,
"zip": Allowed,
@@ -136,9 +130,11 @@
"dirname": LinuxOnlyPrebuilt,
"du": LinuxOnlyPrebuilt,
"echo": LinuxOnlyPrebuilt,
+ "egrep": LinuxOnlyPrebuilt,
"env": LinuxOnlyPrebuilt,
- "head": LinuxOnlyPrebuilt,
"getconf": LinuxOnlyPrebuilt,
+ "grep": LinuxOnlyPrebuilt,
+ "head": LinuxOnlyPrebuilt,
"hostname": LinuxOnlyPrebuilt,
"id": LinuxOnlyPrebuilt,
"ln": LinuxOnlyPrebuilt,
@@ -166,6 +162,7 @@
"sort": LinuxOnlyPrebuilt,
"stat": LinuxOnlyPrebuilt,
"tail": LinuxOnlyPrebuilt,
+ "tar": LinuxOnlyPrebuilt,
"tee": LinuxOnlyPrebuilt,
"timeout": LinuxOnlyPrebuilt,
"touch": LinuxOnlyPrebuilt,
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
new file mode 100644
index 0000000..ceea4bf
--- /dev/null
+++ b/ui/build/rbe.go
@@ -0,0 +1,52 @@
+// Copyright 2019 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 build
+
+import (
+ "path/filepath"
+
+ "android/soong/ui/metrics"
+)
+
+const bootstrapCmd = "bootstrap"
+const rbeLeastNProcs = 2500
+const rbeLeastNFiles = 16000
+
+func startRBE(ctx Context, config Config) {
+ ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap")
+ defer ctx.EndTrace()
+
+ if u := ulimitOrFatal(ctx, config, "-u"); u < rbeLeastNProcs {
+ ctx.Fatalf("max user processes is insufficient: %d; want >= %d.\n", u, rbeLeastNProcs)
+ }
+ if n := ulimitOrFatal(ctx, config, "-n"); n < rbeLeastNFiles {
+ ctx.Fatalf("max open files is insufficient: %d; want >= %d.\n", n, rbeLeastNFiles)
+ }
+
+ var rbeBootstrap string
+ if rbeDir, ok := config.Environment().Get("RBE_DIR"); ok {
+ rbeBootstrap = filepath.Join(rbeDir, bootstrapCmd)
+ } else if home, ok := config.Environment().Get("HOME"); ok {
+ rbeBootstrap = filepath.Join(home, "rbe", bootstrapCmd)
+ } else {
+ ctx.Fatalln("rbe bootstrap not found")
+ }
+
+ cmd := Command(ctx, config, "boostrap", rbeBootstrap)
+
+ if output, err := cmd.CombinedOutput(); err != nil {
+ ctx.Fatalf("rbe bootstrap failed with: %v\n%s\n", err, output)
+ }
+}
diff --git a/ui/terminal/smart_status.go b/ui/terminal/smart_status.go
index 8659d4d..57f71ab 100644
--- a/ui/terminal/smart_status.go
+++ b/ui/terminal/smart_status.go
@@ -59,21 +59,24 @@
// current build status similarly to Ninja's built-in terminal
// output.
func NewSmartStatusOutput(w io.Writer, formatter formatter) status.StatusOutput {
- tableHeight, _ := strconv.Atoi(os.Getenv(tableHeightEnVar))
-
s := &smartStatusOutput{
writer: w,
formatter: formatter,
haveBlankLine: true,
- tableMode: tableHeight > 0,
- requestedTableHeight: tableHeight,
+ tableMode: true,
done: make(chan bool),
sigwinch: make(chan os.Signal),
}
+ if env, ok := os.LookupEnv(tableHeightEnVar); ok {
+ h, _ := strconv.Atoi(env)
+ s.tableMode = h > 0
+ s.requestedTableHeight = h
+ }
+
s.updateTermSize()
if s.tableMode {
@@ -297,6 +300,14 @@
if s.tableMode {
tableHeight := s.requestedTableHeight
+ if tableHeight == 0 {
+ tableHeight = s.termHeight / 4
+ if tableHeight < 1 {
+ tableHeight = 1
+ } else if tableHeight > 10 {
+ tableHeight = 10
+ }
+ }
if tableHeight > s.termHeight-1 {
tableHeight = s.termHeight - 1
}
diff --git a/xml/xml.go b/xml/xml.go
index 7c670fb..3a680ec 100644
--- a/xml/xml.go
+++ b/xml/xml.go
@@ -71,10 +71,6 @@
return android.PathForModuleOut(ctx, p.PrebuiltEtc.SourceFilePath(ctx).Base()+"-timestamp")
}
-func (p *prebuiltEtcXml) DepsMutator(ctx android.BottomUpMutatorContext) {
- p.PrebuiltEtc.DepsMutator(ctx)
-}
-
func (p *prebuiltEtcXml) GenerateAndroidBuildActions(ctx android.ModuleContext) {
p.PrebuiltEtc.GenerateAndroidBuildActions(ctx)
@@ -125,9 +121,8 @@
func PrebuiltEtcXmlFactory() android.Module {
module := &prebuiltEtcXml{}
module.AddProperties(&module.properties)
-
- android.InitPrebuiltEtcModule(&module.PrebuiltEtc)
+ android.InitPrebuiltEtcModule(&module.PrebuiltEtc, "etc")
// This module is device-only
- android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
return module
}
diff --git a/xml/xml_test.go b/xml/xml_test.go
index e8fa49c..ae3e9fe 100644
--- a/xml/xml_test.go
+++ b/xml/xml_test.go
@@ -34,6 +34,7 @@
"foo.dtd": nil,
"bar.xml": nil,
"bar.xsd": nil,
+ "baz.xml": nil,
}
ctx.MockFileSystem(mockFiles)
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
@@ -59,6 +60,13 @@
os.RemoveAll(buildDir)
}
+func assertEqual(t *testing.T, name, expected, actual string) {
+ t.Helper()
+ if expected != actual {
+ t.Errorf(name+" expected %q != got %q", expected, actual)
+ }
+}
+
// Minimal test
func TestPrebuiltEtcXml(t *testing.T) {
ctx := testXml(t, `
@@ -72,15 +80,28 @@
src: "bar.xml",
schema: "bar.xsd",
}
+ prebuilt_etc_xml {
+ name: "baz.xml",
+ src: "baz.xml",
+ }
`)
- xmllint := ctx.ModuleForTests("foo.xml", "android_common").Rule("xmllint")
- input := xmllint.Input.String()
- if input != "foo.xml" {
- t.Errorf("input expected %q != got %q", "foo.xml", input)
+ for _, tc := range []struct {
+ rule, input, schemaType, schema string
+ }{
+ {rule: "xmllint-dtd", input: "foo.xml", schemaType: "dtd", schema: "foo.dtd"},
+ {rule: "xmllint-xsd", input: "bar.xml", schemaType: "xsd", schema: "bar.xsd"},
+ {rule: "xmllint-minimal", input: "baz.xml"},
+ } {
+ t.Run(tc.schemaType, func(t *testing.T) {
+ rule := ctx.ModuleForTests(tc.input, "android_arm64_armv8-a").Rule(tc.rule)
+ assertEqual(t, "input", tc.input, rule.Input.String())
+ if tc.schemaType != "" {
+ assertEqual(t, "schema", tc.schema, rule.Args[tc.schemaType])
+ }
+ })
}
- schema := xmllint.Args["dtd"]
- if schema != "foo.dtd" {
- t.Errorf("dtd expected %q != got %q", "foo.dtdl", schema)
- }
+
+ m := ctx.ModuleForTests("foo.xml", "android_arm64_armv8-a").Module().(*prebuiltEtcXml)
+ assertEqual(t, "installDir", "target/product/test_device/system/etc", m.InstallDirPath().RelPathString())
}