Merge "Reland "Switch to toybox tar.""
diff --git a/Android.bp b/Android.bp
index fff17ef..62b0fd4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -55,8 +55,10 @@
"android/mutator.go",
"android/namespace.go",
"android/neverallow.go",
+ "android/notices.go",
"android/onceper.go",
"android/override_module.go",
+ "android/package.go",
"android/package_ctx.go",
"android/path_properties.go",
"android/paths.go",
@@ -79,6 +81,7 @@
],
testSrcs: [
"android/android_test.go",
+ "android/androidmk_test.go",
"android/arch_test.go",
"android/config_test.go",
"android/expand_test.go",
@@ -87,6 +90,7 @@
"android/namespace_test.go",
"android/neverallow_test.go",
"android/onceper_test.go",
+ "android/package_test.go",
"android/path_properties_test.go",
"android/paths_test.go",
"android/prebuilt_test.go",
@@ -174,6 +178,7 @@
"cc/linker.go",
"cc/binary.go",
+ "cc/fuzz.go",
"cc/library.go",
"cc/object.go",
"cc/test.go",
@@ -288,6 +293,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/OWNERS b/OWNERS
index 85c70df..4ae045d 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,4 +1,5 @@
-per-file * = asmundak@google.com,ccross@android.com,dwillemsen@google.com,jungjw@google.com
+per-file * = asmundak@google.com,ccross@android.com,dwillemsen@google.com,jungjw@google.com,paulduffin@google.com
+
per-file ndk_*.go, *gen_stub_libs.py = danalbert@google.com
per-file clang.go,global.go = srhines@google.com, chh@google.com, pirama@google.com, yikong@google.com
per-file tidy.go = srhines@google.com, chh@google.com
diff --git a/README.md b/README.md
index 8fdce4b..531ef4c 100644
--- a/README.md
+++ b/README.md
@@ -133,6 +133,25 @@
This is based on the Bazel package concept.
+The `package` module type allows information to be specified about a package. Only a single
+`package` module can be specified per package and in the case where there are multiple `.bp` files
+in the same package directory it is highly recommended that the `package` module (if required) is
+specified in the `Android.bp` file.
+
+Unlike most module type `package` does not have a `name` property. Instead the name is set to the
+name of the package, e.g. if the package is in `top/intermediate/package` then the package name is
+`//top/intermediate/package`.
+
+E.g. The following will set the default visibility for all the modules defined in the package and
+any subpackages that do not set their own default visibility (irrespective of whether they are in
+the same `.bp` file as the `package` module) to be visible to all the subpackages by default.
+
+```
+package {
+ default_visibility: [":__subpackages"]
+}
+```
+
### Name resolution
Soong provides the ability for modules in different directories to specify
@@ -191,13 +210,13 @@
`//independent:evil`)
* `["//project"]`: This is shorthand for `["//project:__pkg__"]`
* `[":__subpackages__"]`: This is shorthand for `["//project:__subpackages__"]`
-where `//project` is the module's package. e.g. using `[":__subpackages__"]` in
+where `//project` is the module's package, e.g. using `[":__subpackages__"]` in
`packages/apps/Settings/Android.bp` is equivalent to
`//packages/apps/Settings:__subpackages__`.
* `["//visibility:legacy_public"]`: The default visibility, behaves as
`//visibility:public` for now. It is an error if it is used in a module.
-The visibility rules of `//visibility:public` and `//visibility:private` can not
+The visibility rules of `//visibility:public` and `//visibility:private` cannot
be combined with any other visibility specifications, except
`//visibility:public` is allowed to override visibility specifications imported
through the `defaults` property.
@@ -207,13 +226,22 @@
say `vendor/google`, instead it must make itself visible to all packages within
`vendor/` using `//vendor:__subpackages__`.
-If a module does not specify the `visibility` property the module is
-`//visibility:legacy_public`. 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 modules that do not currently specify
-a `visibility` property will be updated to have
-`visibility = [//visibility:legacy_public]` added. It will then be the owner's
-responsibility to replace that with a more appropriate visibility.
+If a module does not specify the `visibility` property then it uses the
+`default_visibility` property of the `package` module in the module's package.
+
+If the `default_visibility` property is not set for the module's package then
+it will use the `default_visibility` of its closest ancestor package for which
+a `default_visibility` property is specified.
+
+If no `default_visibility` property can be found then the module uses the
+global default of `//visibility:legacy_public`.
+
+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`
+property will be updated to have
+`default_visibility = [//visibility:legacy_public]` added. It will then be the
+owner's responsibility to replace that with a more appropriate visibility.
### Formatter
@@ -309,6 +337,19 @@
This will bind mount the Soong source directories into the directory in the layout expected by
the IDE.
+### Running Soong in a debugger
+
+To run the soong_build process in a debugger, install `dlv` and then start the build with
+`SOONG_DELVE=<listen addr>` in the environment.
+For examle:
+```bash
+SOONG_DELVE=:1234 m nothing
+```
+and then in another terminal:
+```
+dlv connect :1234
+```
+
## Contact
Email android-building@googlegroups.com (external) for any questions, or see
diff --git a/android/androidmk.go b/android/androidmk.go
index 7d0aa3b..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)
}
@@ -216,7 +208,10 @@
}
a.SetBoolIfTrue("LOCAL_ODM_MODULE", Bool(amod.commonProperties.Device_specific))
a.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(amod.commonProperties.Product_specific))
- a.SetBoolIfTrue("LOCAL_PRODUCT_SERVICES_MODULE", Bool(amod.commonProperties.Product_services_specific))
+ // TODO(b/135957588) product_services_specific is matched to LOCAL_PRODUCT_MODULE
+ // as a workaround. Remove this after clearing all Android.bp
+ a.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(amod.commonProperties.Product_services_specific))
+ a.SetBoolIfTrue("LOCAL_SYSTEM_EXT_MODULE", Bool(amod.commonProperties.System_ext_specific))
if amod.commonProperties.Owner != nil {
a.SetString("LOCAL_MODULE_OWNER", *amod.commonProperties.Owner)
}
@@ -388,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 {
@@ -401,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..44c3f48 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -691,9 +691,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 +1405,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 +1416,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 +1438,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 +1458,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 +1472,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 +1488,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 +1497,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 b0d8b7f..8ced93a 100644
--- a/android/config.go
+++ b/android/config.go
@@ -216,6 +216,7 @@
AAPTPreferredConfig: stringPtr("xhdpi"),
AAPTCharacteristics: stringPtr("nosdcard"),
AAPTPrebuiltDPI: []string{"xhdpi", "xxhdpi"},
+ UncompressPrivAppDex: boolPtr(true),
},
buildDir: buildDir,
@@ -239,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
@@ -254,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, "", ""},
},
}
@@ -271,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, "", ""},
},
}
@@ -900,11 +901,11 @@
return "product"
}
-func (c *deviceConfig) ProductServicesPath() string {
- if c.config.productVariables.ProductServicesPath != nil {
- return *c.config.productVariables.ProductServicesPath
+func (c *deviceConfig) SystemExtPath() string {
+ if c.config.productVariables.SystemExtPath != nil {
+ return *c.config.productVariables.SystemExtPath
}
- return "product_services"
+ return "system_ext"
}
func (c *deviceConfig) BtConfigIncludeDir() string {
diff --git a/android/defaults.go b/android/defaults.go
index 844b4d4..ae2c820 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -63,9 +63,28 @@
type DefaultsModuleBase struct {
DefaultableModuleBase
- defaultProperties []interface{}
}
+// The common pattern for defaults modules is to register separate instances of
+// the xxxProperties structs in the AddProperties calls, rather than reusing the
+// ones inherited from Module.
+//
+// The effect is that e.g. myDefaultsModuleInstance.base().xxxProperties won't
+// contain the values that have been set for the defaults module. Rather, to
+// retrieve the values it is necessary to iterate over properties(). E.g. to get
+// the commonProperties instance that have the real values:
+//
+// d := myModule.(Defaults)
+// for _, props := range d.properties() {
+// if cp, ok := props.(*commonProperties); ok {
+// ... access property values in cp ...
+// }
+// }
+//
+// The rationale is that the properties on a defaults module apply to the
+// defaultable modules using it, not to the defaults module itself. E.g. setting
+// the "enabled" property false makes inheriting modules disabled by default,
+// rather than disabling the defaults module itself.
type Defaults interface {
Defaultable
isDefaults() bool
diff --git a/android/env.go b/android/env.go
index 469dfff..d9f2db2 100644
--- a/android/env.go
+++ b/android/env.go
@@ -16,6 +16,7 @@
import (
"os"
+ "os/exec"
"strings"
"android/soong/env"
@@ -29,8 +30,16 @@
// a manifest regeneration.
var originalEnv map[string]string
+var SoongDelveListen string
+var SoongDelvePath string
func init() {
+ // Delve support needs to read this environment variable very early, before NewConfig has created a way to
+ // access originalEnv with dependencies. Store the value where soong_build can find it, it will manually
+ // ensure the dependencies are created.
+ SoongDelveListen = os.Getenv("SOONG_DELVE")
+ SoongDelvePath, _ = exec.LookPath("dlv")
+
originalEnv = make(map[string]string)
for _, env := range os.Environ() {
idx := strings.IndexRune(env, '=')
@@ -38,6 +47,8 @@
originalEnv[env[:idx]] = env[idx+1:]
}
}
+ // Clear the environment to prevent use of os.Getenv(), which would not provide dependencies on environment
+ // variable values. The environment is available through ctx.Config().Getenv, ctx.Config().IsEnvTrue, etc.
os.Clearenv()
}
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/hooks.go b/android/hooks.go
index 2d2f797..5810996 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -129,6 +129,8 @@
func LoadHookMutator(ctx TopDownMutatorContext) {
if m, ok := ctx.Module().(Module); ok {
+ m.base().commonProperties.DebugName = ctx.ModuleName()
+
// Cast through *topDownMutatorContext because AppendProperties is implemented
// on *topDownMutatorContext but not exposed through TopDownMutatorContext
var loadHookCtx LoadHookContext = ctx.(*topDownMutatorContext)
diff --git a/android/module.go b/android/module.go
index 87e2ca7..adb9454 100644
--- a/android/module.go
+++ b/android/module.go
@@ -126,7 +126,7 @@
DeviceSpecific() bool
SocSpecific() bool
ProductSpecific() bool
- ProductServicesSpecific() bool
+ SystemExtSpecific() bool
AConfig() Config
DeviceConfig() DeviceConfig
}
@@ -202,6 +202,58 @@
BuildParamsForTests() []BuildParams
RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
VariablesForTests() map[string]string
+
+ // String returns a string that includes the module name and variants for printing during debugging.
+ String() string
+
+ // Get the qualified module id for this module.
+ qualifiedModuleId(ctx BaseModuleContext) qualifiedModuleName
+
+ // Get information about the properties that can contain visibility rules.
+ visibilityProperties() []visibilityProperty
+}
+
+// Qualified id for a module
+type qualifiedModuleName struct {
+ // The package (i.e. directory) in which the module is defined, without trailing /
+ pkg string
+
+ // The name of the module, empty string if package.
+ name string
+}
+
+func (q qualifiedModuleName) String() string {
+ if q.name == "" {
+ return "//" + q.pkg
+ }
+ return "//" + q.pkg + ":" + q.name
+}
+
+func (q qualifiedModuleName) isRootPackage() bool {
+ return q.pkg == "" && q.name == ""
+}
+
+// Get the id for the package containing this module.
+func (q qualifiedModuleName) getContainingPackageId() qualifiedModuleName {
+ pkg := q.pkg
+ if q.name == "" {
+ if pkg == "" {
+ panic(fmt.Errorf("Cannot get containing package id of root package"))
+ }
+
+ index := strings.LastIndex(pkg, "/")
+ if index == -1 {
+ pkg = ""
+ } else {
+ pkg = pkg[:index]
+ }
+ }
+ return newPackageId(pkg)
+}
+
+func newPackageId(pkg string) qualifiedModuleName {
+ // A qualified id for a package module has no name.
+ return qualifiedModuleName{pkg: pkg, name: ""}
}
type nameProperties struct {
@@ -236,6 +288,20 @@
// //packages/apps/Settings:__subpackages__.
// ["//visibility:legacy_public"]: The default visibility, behaves as //visibility:public
// for now. It is an error if it is used in a module.
+ //
+ // If a module does not specify the `visibility` property then it uses the
+ // `default_visibility` property of the `package` module in the module's package.
+ //
+ // If a module does not specify the `visibility` property then it uses the
+ // `default_visibility` property of the `package` module in the module's package.
+ //
+ // If the `default_visibility` property is not set for the module's package then
+ // it will use the `default_visibility` of its closest ancestor package for which
+ // a `default_visibility` property is specified.
+ //
+ // If no `default_visibility` property can be found then the module uses the
+ // global default of `//visibility:legacy_public`.
+ //
// See https://android.googlesource.com/platform/build/soong/+/master/README.md#visibility for
// more details.
Visibility []string
@@ -284,11 +350,15 @@
// /system/product if product partition does not exist).
Product_specific *bool
- // whether this module provides services owned by the OS provider to the core platform. When set
- // to true, it is installed into /product_services (or /system/product_services if
- // product_services partition does not exist).
+ // TODO(b/135957588) Product_services_specific will be removed once we clear all Android.bp
+ // files that have 'product_services_specific: true'. This will be converted to
+ // Product_speicific as a workaround.
Product_services_specific *bool
+ // whether this module extends system. When set to true, it is installed into /system_ext
+ // (or /system/system_ext if system_ext partition does not exist).
+ System_ext_specific *bool
+
// Whether this module is installed to recovery partition
Recovery *bool
@@ -345,6 +415,11 @@
NamespaceExportedToMake bool `blueprint:"mutated"`
MissingDeps []string `blueprint:"mutated"`
+
+ // Name and variant strings stored by mutators to enable Module.String()
+ DebugName string `blueprint:"mutated"`
+ DebugMutators []string `blueprint:"mutated"`
+ DebugVariations []string `blueprint:"mutated"`
}
type hostAndDeviceProperties struct {
@@ -398,7 +473,7 @@
deviceSpecificModule
socSpecificModule
productSpecificModule
- productServicesSpecificModule
+ systemExtSpecificModule
)
func (k moduleKind) String() string {
@@ -411,8 +486,8 @@
return "soc-specific"
case productSpecificModule:
return "product-specific"
- case productServicesSpecificModule:
- return "productservices-specific"
+ case systemExtSpecificModule:
+ return "systemext-specific"
default:
panic(fmt.Errorf("unknown module kind %d", k))
}
@@ -562,6 +637,23 @@
return String(m.nameProperties.Name)
}
+// String returns a string that includes the module name and variants for printing during debugging.
+func (m *ModuleBase) String() string {
+ sb := strings.Builder{}
+ sb.WriteString(m.commonProperties.DebugName)
+ sb.WriteString("{")
+ for i := range m.commonProperties.DebugMutators {
+ if i != 0 {
+ sb.WriteString(",")
+ }
+ sb.WriteString(m.commonProperties.DebugMutators[i])
+ sb.WriteString(":")
+ sb.WriteString(m.commonProperties.DebugVariations[i])
+ }
+ sb.WriteString("}")
+ return sb.String()
+}
+
// BaseModuleName returns the name of the module as specified in the blueprints file.
func (m *ModuleBase) BaseModuleName() string {
return String(m.nameProperties.Name)
@@ -571,6 +663,18 @@
return m
}
+func (m *ModuleBase) qualifiedModuleId(ctx BaseModuleContext) qualifiedModuleName {
+ return qualifiedModuleName{pkg: ctx.ModuleDir(), name: ctx.ModuleName()}
+}
+
+func (m *ModuleBase) visibilityProperties() []visibilityProperty {
+ return []visibilityProperty{
+ newVisibilityProperty("visibility", func() []string {
+ return m.base().commonProperties.Visibility
+ }),
+ }
+}
+
func (m *ModuleBase) SetTarget(target Target, multiTargets []Target, primary bool) {
m.commonProperties.CompileTarget = target
m.commonProperties.CompileMultiTargets = multiTargets
@@ -638,7 +742,7 @@
}
func (m *ModuleBase) Platform() bool {
- return !m.DeviceSpecific() && !m.SocSpecific() && !m.ProductSpecific() && !m.ProductServicesSpecific()
+ return !m.DeviceSpecific() && !m.SocSpecific() && !m.ProductSpecific() && !m.SystemExtSpecific()
}
func (m *ModuleBase) DeviceSpecific() bool {
@@ -653,8 +757,8 @@
return Bool(m.commonProperties.Product_specific)
}
-func (m *ModuleBase) ProductServicesSpecific() bool {
- return Bool(m.commonProperties.Product_services_specific)
+func (m *ModuleBase) SystemExtSpecific() bool {
+ return Bool(m.commonProperties.System_ext_specific)
}
func (m *ModuleBase) Enabled() bool {
@@ -775,7 +879,7 @@
var socSpecific = Bool(m.commonProperties.Vendor) || Bool(m.commonProperties.Proprietary) || Bool(m.commonProperties.Soc_specific)
var deviceSpecific = Bool(m.commonProperties.Device_specific)
var productSpecific = Bool(m.commonProperties.Product_specific)
- var productServicesSpecific = Bool(m.commonProperties.Product_services_specific)
+ var systemExtSpecific = Bool(m.commonProperties.System_ext_specific)
msg := "conflicting value set here"
if socSpecific && deviceSpecific {
@@ -791,16 +895,16 @@
}
}
- if productSpecific && productServicesSpecific {
- ctx.PropertyErrorf("product_specific", "a module cannot be specific to product and product_services at the same time.")
- ctx.PropertyErrorf("product_services_specific", msg)
+ if productSpecific && systemExtSpecific {
+ ctx.PropertyErrorf("product_specific", "a module cannot be specific to product and system_ext at the same time.")
+ ctx.PropertyErrorf("system_ext_specific", msg)
}
- if (socSpecific || deviceSpecific) && (productSpecific || productServicesSpecific) {
+ if (socSpecific || deviceSpecific) && (productSpecific || systemExtSpecific) {
if productSpecific {
ctx.PropertyErrorf("product_specific", "a module cannot be specific to SoC or device and product at the same time.")
} else {
- ctx.PropertyErrorf("product_services_specific", "a module cannot be specific to SoC or device and product_services at the same time.")
+ ctx.PropertyErrorf("system_ext_specific", "a module cannot be specific to SoC or device and system_ext at the same time.")
}
if deviceSpecific {
ctx.PropertyErrorf("device_specific", msg)
@@ -819,8 +923,8 @@
if productSpecific {
return productSpecificModule
- } else if productServicesSpecific {
- return productServicesSpecificModule
+ } else if systemExtSpecific {
+ return systemExtSpecificModule
} else if deviceSpecific {
return deviceSpecificModule
} else if socSpecific {
@@ -901,14 +1005,6 @@
}
if m.Enabled() {
- m.module.GenerateAndroidBuildActions(ctx)
- if ctx.Failed() {
- return
- }
-
- m.installFiles = append(m.installFiles, ctx.installFiles...)
- m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
-
notice := proptools.StringDefault(m.commonProperties.Notice, "NOTICE")
if module := SrcIsModule(notice); module != "" {
m.noticeFile = ctx.ExpandOptionalSource(¬ice, "notice")
@@ -916,6 +1012,14 @@
noticePath := filepath.Join(ctx.ModuleDir(), notice)
m.noticeFile = ExistentPathForSource(ctx, noticePath)
}
+
+ m.module.GenerateAndroidBuildActions(ctx)
+ if ctx.Failed() {
+ return
+ }
+
+ m.installFiles = append(m.installFiles, ctx.installFiles...)
+ m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
} else if ctx.Config().AllowMissingDependencies() {
// If the module is not enabled it will not create any build rules, nothing will call
// ctx.GetMissingDependencies(), and blueprint will consider the missing dependencies to be unhandled
@@ -1333,18 +1437,18 @@
return b.kind == productSpecificModule
}
-func (b *baseModuleContext) ProductServicesSpecific() bool {
- return b.kind == productServicesSpecificModule
+func (b *baseModuleContext) SystemExtSpecific() bool {
+ return b.kind == systemExtSpecificModule
}
// Makes this module a platform module, i.e. not specific to soc, device,
-// product, or product_services.
+// product, or system_ext.
func (m *ModuleBase) MakeAsPlatform() {
m.commonProperties.Vendor = boolPtr(false)
m.commonProperties.Proprietary = boolPtr(false)
m.commonProperties.Soc_specific = boolPtr(false)
m.commonProperties.Product_specific = boolPtr(false)
- m.commonProperties.Product_services_specific = boolPtr(false)
+ m.commonProperties.System_ext_specific = boolPtr(false)
}
func (m *ModuleBase) EnableNativeBridgeSupportByDefault() {
diff --git a/android/mutator.go b/android/mutator.go
index 081c2b2..b799432 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -75,6 +75,8 @@
var preArch = []RegisterMutatorFunc{
registerLoadHookMutator,
RegisterNamespaceMutator,
+ // Rename package module types.
+ registerPackageRenamer,
RegisterPrebuiltsPreArchMutators,
registerVisibilityRuleChecker,
RegisterDefaultsPreArchMutators,
@@ -115,6 +117,8 @@
type TopDownMutatorContext interface {
BaseModuleContext
+ MutatorName() string
+
Rename(name string)
CreateModule(blueprint.ModuleFactory, ...interface{})
@@ -130,6 +134,8 @@
type BottomUpMutatorContext interface {
BaseModuleContext
+ MutatorName() string
+
Rename(name string)
AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string)
@@ -227,16 +233,26 @@
// non-overridden method has to be forwarded. There are fewer non-overridden methods, so use the latter. The following
// methods forward to the identical blueprint versions for topDownMutatorContext and bottomUpMutatorContext.
+func (t *topDownMutatorContext) MutatorName() string {
+ return t.bp.MutatorName()
+}
+
func (t *topDownMutatorContext) Rename(name string) {
t.bp.Rename(name)
+ t.Module().base().commonProperties.DebugName = name
}
func (t *topDownMutatorContext) CreateModule(factory blueprint.ModuleFactory, props ...interface{}) {
t.bp.CreateModule(factory, props...)
}
+func (b *bottomUpMutatorContext) MutatorName() string {
+ return b.bp.MutatorName()
+}
+
func (b *bottomUpMutatorContext) Rename(name string) {
b.bp.Rename(name)
+ b.Module().base().commonProperties.DebugName = name
}
func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) {
@@ -248,11 +264,27 @@
}
func (b *bottomUpMutatorContext) CreateVariations(variations ...string) []blueprint.Module {
- return b.bp.CreateVariations(variations...)
+ modules := b.bp.CreateVariations(variations...)
+
+ for i := range variations {
+ base := modules[i].(Module).base()
+ base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, b.MutatorName())
+ base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variations[i])
+ }
+
+ return modules
}
func (b *bottomUpMutatorContext) CreateLocalVariations(variations ...string) []blueprint.Module {
- return b.bp.CreateLocalVariations(variations...)
+ modules := b.bp.CreateLocalVariations(variations...)
+
+ for i := range variations {
+ base := modules[i].(Module).base()
+ base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, b.MutatorName())
+ base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variations[i])
+ }
+
+ return modules
}
func (b *bottomUpMutatorContext) SetDependencyVariation(variation string) {
diff --git a/android/mutator_test.go b/android/mutator_test.go
index 4cef400..0b23434 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -15,8 +15,6 @@
package android
import (
- "io/ioutil"
- "os"
"reflect"
"testing"
@@ -26,6 +24,8 @@
type mutatorTestModule struct {
ModuleBase
props struct {
+ Deps_missing_deps []string
+ Mutator_missing_deps []string
}
missingDeps []string
@@ -48,20 +48,14 @@
}
func (m *mutatorTestModule) DepsMutator(ctx BottomUpMutatorContext) {
- ctx.AddDependency(ctx.Module(), nil, "regular_missing_dep")
+ ctx.AddDependency(ctx.Module(), nil, m.props.Deps_missing_deps...)
}
func addMissingDependenciesMutator(ctx TopDownMutatorContext) {
- ctx.AddMissingDependencies([]string{"added_missing_dep"})
+ ctx.AddMissingDependencies(ctx.Module().(*mutatorTestModule).props.Mutator_missing_deps)
}
func TestMutatorAddMissingDependencies(t *testing.T) {
- buildDir, err := ioutil.TempDir("", "soong_mutator_test")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(buildDir)
-
config := TestConfig(buildDir, nil)
config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
@@ -76,6 +70,8 @@
bp := `
test {
name: "foo",
+ deps_missing_deps: ["regular_missing_dep"],
+ mutator_missing_deps: ["added_missing_dep"],
}
`
@@ -97,3 +93,107 @@
t.Errorf("want foo missing deps %q, got %q", w, g)
}
}
+
+func TestModuleString(t *testing.T) {
+ ctx := NewTestContext()
+
+ var moduleStrings []string
+
+ ctx.PreArchMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("pre_arch", func(ctx BottomUpMutatorContext) {
+ moduleStrings = append(moduleStrings, ctx.Module().String())
+ ctx.CreateVariations("a", "b")
+ })
+ ctx.TopDown("rename_top_down", func(ctx TopDownMutatorContext) {
+ moduleStrings = append(moduleStrings, ctx.Module().String())
+ ctx.Rename(ctx.Module().base().Name() + "_renamed1")
+ })
+ })
+
+ ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("pre_deps", func(ctx BottomUpMutatorContext) {
+ moduleStrings = append(moduleStrings, ctx.Module().String())
+ ctx.CreateVariations("c", "d")
+ })
+ })
+
+ ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("post_deps", func(ctx BottomUpMutatorContext) {
+ moduleStrings = append(moduleStrings, ctx.Module().String())
+ ctx.CreateLocalVariations("e", "f")
+ })
+ ctx.BottomUp("rename_bottom_up", func(ctx BottomUpMutatorContext) {
+ moduleStrings = append(moduleStrings, ctx.Module().String())
+ ctx.Rename(ctx.Module().base().Name() + "_renamed2")
+ })
+ ctx.BottomUp("final", func(ctx BottomUpMutatorContext) {
+ moduleStrings = append(moduleStrings, ctx.Module().String())
+ })
+ })
+
+ ctx.RegisterModuleType("test", ModuleFactoryAdaptor(mutatorTestModuleFactory))
+
+ bp := `
+ test {
+ name: "foo",
+ }
+ `
+
+ mockFS := map[string][]byte{
+ "Android.bp": []byte(bp),
+ }
+
+ ctx.MockFileSystem(mockFS)
+
+ ctx.Register()
+
+ config := TestConfig(buildDir, nil)
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ want := []string{
+ // Initial name.
+ "foo{}",
+
+ // After pre_arch (reversed because rename_top_down is TopDown so it visits in reverse order).
+ "foo{pre_arch:b}",
+ "foo{pre_arch:a}",
+
+ // After rename_top_down.
+ "foo_renamed1{pre_arch:a}",
+ "foo_renamed1{pre_arch:b}",
+
+ // After pre_deps.
+ "foo_renamed1{pre_arch:a,pre_deps:c}",
+ "foo_renamed1{pre_arch:a,pre_deps:d}",
+ "foo_renamed1{pre_arch:b,pre_deps:c}",
+ "foo_renamed1{pre_arch:b,pre_deps:d}",
+
+ // After post_deps.
+ "foo_renamed1{pre_arch:a,pre_deps:c,post_deps:e}",
+ "foo_renamed1{pre_arch:a,pre_deps:c,post_deps:f}",
+ "foo_renamed1{pre_arch:a,pre_deps:d,post_deps:e}",
+ "foo_renamed1{pre_arch:a,pre_deps:d,post_deps:f}",
+ "foo_renamed1{pre_arch:b,pre_deps:c,post_deps:e}",
+ "foo_renamed1{pre_arch:b,pre_deps:c,post_deps:f}",
+ "foo_renamed1{pre_arch:b,pre_deps:d,post_deps:e}",
+ "foo_renamed1{pre_arch:b,pre_deps:d,post_deps:f}",
+
+ // After rename_bottom_up.
+ "foo_renamed2{pre_arch:a,pre_deps:c,post_deps:e}",
+ "foo_renamed2{pre_arch:a,pre_deps:c,post_deps:f}",
+ "foo_renamed2{pre_arch:a,pre_deps:d,post_deps:e}",
+ "foo_renamed2{pre_arch:a,pre_deps:d,post_deps:f}",
+ "foo_renamed2{pre_arch:b,pre_deps:c,post_deps:e}",
+ "foo_renamed2{pre_arch:b,pre_deps:c,post_deps:f}",
+ "foo_renamed2{pre_arch:b,pre_deps:d,post_deps:e}",
+ "foo_renamed2{pre_arch:b,pre_deps:d,post_deps:f}",
+ }
+
+ if !reflect.DeepEqual(moduleStrings, want) {
+ t.Errorf("want module String() values:\n%q\ngot:\n%q", want, moduleStrings)
+ }
+}
diff --git a/android/neverallow.go b/android/neverallow.go
index f35d1fe..23b6454 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -31,61 +31,101 @@
// work regardless of these restrictions.
//
// A module is disallowed if all of the following are true:
-// - it is in one of the "in" paths
-// - it is not in one of the "notIn" paths
-// - it has all "with" properties matched
+// - it is in one of the "In" paths
+// - it is not in one of the "NotIn" paths
+// - it has all "With" properties matched
// - - values are matched in their entirety
// - - nil is interpreted as an empty string
// - - nested properties are separated with a '.'
// - - if the property is a list, any of the values in the list being matches
// counts as a match
-// - it has none of the "without" properties matched (same rules as above)
+// - it has none of the "Without" properties matched (same rules as above)
func registerNeverallowMutator(ctx RegisterMutatorsContext) {
ctx.BottomUp("neverallow", neverallowMutator).Parallel()
}
-var neverallows = createNeverAllows()
+var neverallows = []Rule{}
-func createNeverAllows() []*rule {
- rules := []*rule{}
- rules = append(rules, createTrebleRules()...)
- rules = append(rules, createLibcoreRules()...)
- rules = append(rules, createJavaDeviceForHostRules()...)
+func init() {
+ AddNeverAllowRules(createIncludeDirsRules()...)
+ AddNeverAllowRules(createTrebleRules()...)
+ AddNeverAllowRules(createLibcoreRules()...)
+ AddNeverAllowRules(createJavaDeviceForHostRules()...)
+}
+
+// Add a NeverAllow rule to the set of rules to apply.
+func AddNeverAllowRules(rules ...Rule) {
+ 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().
- in("vendor", "device").
- with("vndk.enabled", "true").
- without("vendor", "true").
- because("the VNDK can never contain a library that is device dependent."),
- neverallow().
- with("vndk.enabled", "true").
- without("vendor", "true").
- without("owner", "").
- because("a VNDK module can never have an owner."),
+func createTrebleRules() []Rule {
+ return []Rule{
+ NeverAllow().
+ In("vendor", "device").
+ With("vndk.enabled", "true").
+ Without("vendor", "true").
+ Because("the VNDK can never contain a library that is device dependent."),
+ NeverAllow().
+ With("vndk.enabled", "true").
+ Without("vendor", "true").
+ Without("owner", "").
+ Because("a VNDK module can never have an owner."),
// TODO(b/67974785): always enforce the manifest
- neverallow().
- without("name", "libhidltransport").
- with("product_variables.enforce_vintf_manifest.cflags", "*").
- because("manifest enforcement should be independent of ."),
+ NeverAllow().
+ Without("name", "libhidltransport").
+ With("product_variables.enforce_vintf_manifest.cflags", "*").
+ Because("manifest enforcement should be independent of ."),
// TODO(b/67975799): vendor code should always use /vendor/bin/sh
- neverallow().
- without("name", "libc_bionic_ndk").
- with("product_variables.treble_linker_namespaces.cflags", "*").
- because("nothing should care if linker namespaces are enabled or not"),
+ NeverAllow().
+ Without("name", "libc_bionic_ndk").
+ With("product_variables.treble_linker_namespaces.cflags", "*").
+ Because("nothing should care if linker namespaces are enabled or not"),
// Example:
- // *neverallow().with("Srcs", "main.cpp"))
+ // *NeverAllow().with("Srcs", "main.cpp"))
}
}
-func createLibcoreRules() []*rule {
+func createLibcoreRules() []Rule {
var coreLibraryProjects = []string{
"libcore",
"external/apache-harmony",
@@ -102,27 +142,27 @@
// Core library constraints. The sdk_version: "none" can only be used in core library projects.
// Access to core library targets is restricted using visibility rules.
- rules := []*rule{
- neverallow().
- notIn(coreLibraryProjects...).
- with("sdk_version", "none"),
+ rules := []Rule{
+ NeverAllow().
+ NotIn(coreLibraryProjects...).
+ With("sdk_version", "none"),
}
return rules
}
-func createJavaDeviceForHostRules() []*rule {
+func createJavaDeviceForHostRules() []Rule {
javaDeviceForHostProjectsWhitelist := []string{
"external/guava",
"external/robolectric-shadows",
"framework/layoutlib",
}
- return []*rule{
- neverallow().
- notIn(javaDeviceForHostProjectsWhitelist...).
- moduleType("java_device_for_host", "java_host_for_device").
- because("java_device_for_host can only be used in whitelisted projects"),
+ return []Rule{
+ NeverAllow().
+ NotIn(javaDeviceForHostProjectsWhitelist...).
+ ModuleType("java_device_for_host", "java_host_for_device").
+ Because("java_device_for_host can only be used in whitelisted projects"),
}
}
@@ -135,7 +175,8 @@
dir := ctx.ModuleDir() + "/"
properties := m.GetProperties()
- for _, n := range neverallows {
+ for _, r := range neverallows {
+ n := r.(*rule)
if !n.appliesToPath(dir) {
continue
}
@@ -152,9 +193,72 @@
}
}
+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.
+type Rule interface {
+ In(path ...string) Rule
+
+ NotIn(path ...string) 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
}
type rule struct {
@@ -171,47 +275,63 @@
unlessProps []ruleProperty
}
-func neverallow() *rule {
+// Create a new NeverAllow rule.
+func NeverAllow() Rule {
return &rule{}
}
-func (r *rule) in(path ...string) *rule {
+func (r *rule) In(path ...string) Rule {
r.paths = append(r.paths, cleanPaths(path)...)
return r
}
-func (r *rule) notIn(path ...string) *rule {
+func (r *rule) NotIn(path ...string) Rule {
r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
return r
}
-func (r *rule) moduleType(types ...string) *rule {
+func (r *rule) ModuleType(types ...string) Rule {
r.moduleTypes = append(r.moduleTypes, types...)
return r
}
-func (r *rule) notModuleType(types ...string) *rule {
+func (r *rule) NotModuleType(types ...string) Rule {
r.unlessModuleTypes = append(r.unlessModuleTypes, types...)
return r
}
-func (r *rule) with(properties, value string) *rule {
+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 {
+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 (r *rule) because(reason string) *rule {
+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
}
@@ -231,10 +351,10 @@
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()
}
if len(r.reason) != 0 {
s += " which is restricted because " + r.reason
@@ -258,6 +378,10 @@
return includeProps && !excludeProps
}
+func StartsWith(prefix string) ValueMatcher {
+ return &startsWithMatcher{prefix}
+}
+
// assorted utils
func cleanPaths(paths []string) []string {
@@ -316,8 +440,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..02b4362 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -23,6 +23,29 @@
fs map[string][]byte
expectedError string
}{
+ // 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,6 +240,7 @@
}
type mockCcLibraryProperties struct {
+ Include_dirs []string
Vendor_available *bool
Vndk struct {
diff --git a/android/notices.go b/android/notices.go
new file mode 100644
index 0000000..7b61d65
--- /dev/null
+++ b/android/notices.go
@@ -0,0 +1,102 @@
+// 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 (
+ "path/filepath"
+
+ "github.com/google/blueprint"
+)
+
+func init() {
+ pctx.SourcePathVariable("merge_notices", "build/soong/scripts/mergenotice.py")
+ pctx.SourcePathVariable("generate_notice", "build/make/tools/generate-notice-files.py")
+
+ pctx.HostBinToolVariable("minigzip", "minigzip")
+}
+
+type NoticeOutputs struct {
+ Merged OptionalPath
+ TxtOutput OptionalPath
+ HtmlOutput OptionalPath
+ HtmlGzOutput OptionalPath
+}
+
+var (
+ mergeNoticesRule = pctx.AndroidStaticRule("mergeNoticesRule", blueprint.RuleParams{
+ Command: `${merge_notices} --output $out $in`,
+ CommandDeps: []string{"${merge_notices}"},
+ Description: "merge notice files into $out",
+ })
+
+ generateNoticeRule = pctx.AndroidStaticRule("generateNoticeRule", blueprint.RuleParams{
+ 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}"},
+ Description: "produce notice file $out",
+ }, "txtOut", "htmlOut", "title", "inputDir")
+)
+
+func MergeNotices(ctx ModuleContext, mergedNotice WritablePath, noticePaths []Path) {
+ ctx.Build(pctx, BuildParams{
+ Rule: mergeNoticesRule,
+ Description: "merge notices",
+ Inputs: noticePaths,
+ Output: mergedNotice,
+ })
+}
+
+func BuildNoticeOutput(ctx ModuleContext, installPath OutputPath, installFilename string,
+ noticePaths []Path) NoticeOutputs {
+ // Merge all NOTICE files into one.
+ // TODO(jungjw): We should just produce a well-formatted NOTICE.html file in a single pass.
+ //
+ // generate-notice-files.py, which processes the merged NOTICE file, has somewhat strict rules
+ // about input NOTICE file paths.
+ // 1. Their relative paths to the src root become their NOTICE index titles. We want to use
+ // on-device paths as titles, and so output the merged NOTICE file the corresponding location.
+ // 2. They must end with .txt extension. Otherwise, they're ignored.
+ noticeRelPath := InstallPathToOnDevicePath(ctx, installPath.Join(ctx, installFilename+".txt"))
+ mergedNotice := PathForModuleOut(ctx, filepath.Join("NOTICE_FILES/src", noticeRelPath))
+ MergeNotices(ctx, mergedNotice, noticePaths)
+
+ // Transform the merged NOTICE file into a gzipped HTML file.
+ txtOuptut := PathForModuleOut(ctx, "NOTICE_txt", "NOTICE.txt")
+ htmlOutput := PathForModuleOut(ctx, "NOTICE_html", "NOTICE.html")
+ htmlGzOutput := PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
+ title := "Notices for " + ctx.ModuleName()
+ ctx.Build(pctx, BuildParams{
+ Rule: generateNoticeRule,
+ Description: "generate notice output",
+ Input: mergedNotice,
+ Output: htmlGzOutput,
+ ImplicitOutputs: WritablePaths{txtOuptut, htmlOutput},
+ Args: map[string]string{
+ "txtOut": txtOuptut.String(),
+ "htmlOut": htmlOutput.String(),
+ "title": title,
+ "inputDir": PathForModuleOut(ctx, "NOTICE_FILES/src").String(),
+ },
+ })
+
+ return NoticeOutputs{
+ Merged: OptionalPathForPath(mergedNotice),
+ TxtOutput: OptionalPathForPath(txtOuptut),
+ HtmlOutput: OptionalPathForPath(htmlOutput),
+ HtmlGzOutput: OptionalPathForPath(htmlGzOutput),
+ }
+}
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/override_module.go b/android/override_module.go
index 5a57c93..22fb7de 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -95,8 +95,6 @@
// Base module struct for overridable module types
type OverridableModuleBase struct {
- ModuleBase
-
// List of OverrideModules that override this base module
overrides []OverrideModule
// Used to parallelize registerOverrideMutator executions. Note that only addOverride locks this
@@ -144,7 +142,7 @@
// Adds the base module to the overrides property, if exists, of the overriding module. See the
// comment on OverridableModuleBase.overridesProperty for details.
if b.overridesProperty != nil {
- *b.overridesProperty = append(*b.overridesProperty, b.Name())
+ *b.overridesProperty = append(*b.overridesProperty, ctx.ModuleName())
}
for _, p := range b.overridableProperties {
for _, op := range o.getOverridingProperties() {
diff --git a/android/package.go b/android/package.go
new file mode 100644
index 0000000..03f6a1e
--- /dev/null
+++ b/android/package.go
@@ -0,0 +1,194 @@
+// 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 (
+ "fmt"
+ "sync/atomic"
+
+ "github.com/google/blueprint"
+)
+
+func init() {
+ RegisterModuleType("package", PackageFactory)
+}
+
+// The information maintained about each package.
+type packageInfo struct {
+ // The module from which this information was populated. If `duplicated` = true then this is the
+ // module that has been renamed and must be used to report errors.
+ module *packageModule
+
+ // If true this indicates that there are two package statements in the same package which is not
+ // allowed and will cause the build to fail. This flag is set by packageRenamer and checked in
+ // packageErrorReporter
+ duplicated bool
+}
+
+type packageProperties struct {
+ Name string `blueprint:"mutated"`
+
+ // Specifies the default visibility for all modules defined in this package.
+ Default_visibility []string
+}
+
+type packageModule struct {
+ ModuleBase
+
+ properties packageProperties
+ packageInfo *packageInfo
+}
+
+func (p *packageModule) GenerateAndroidBuildActions(ModuleContext) {
+ // Nothing to do.
+}
+
+func (p *packageModule) GenerateBuildActions(ctx blueprint.ModuleContext) {
+ // Nothing to do.
+}
+
+func (p *packageModule) qualifiedModuleId(ctx BaseModuleContext) qualifiedModuleName {
+ // Override to create a package id.
+ 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
+}
+
+func (p *packageModule) setName(name string) {
+ p.properties.Name = name
+}
+
+// Counter to ensure package modules are created with a unique name within whatever namespace they
+// belong.
+var packageCount uint32 = 0
+
+func PackageFactory() Module {
+ module := &packageModule{}
+
+ // Get a unique if for the package. Has to be done atomically as the creation of the modules are
+ // done in parallel.
+ id := atomic.AddUint32(&packageCount, 1)
+ name := fmt.Sprintf("soong_package_%d", id)
+
+ module.properties.Name = name
+
+ module.AddProperties(&module.properties)
+ return module
+}
+
+// Registers the function that renames the packages.
+func registerPackageRenamer(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("packageRenamer", packageRenamer).Parallel()
+ ctx.BottomUp("packageErrorReporter", packageErrorReporter).Parallel()
+}
+
+// Renames the package to match the package directory.
+//
+// This also creates a PackageInfo object for each package and uses that to detect and remember
+// duplicates for later error reporting.
+func packageRenamer(ctx BottomUpMutatorContext) {
+ m, ok := ctx.Module().(*packageModule)
+ if !ok {
+ return
+ }
+
+ packageName := "//" + ctx.ModuleDir()
+
+ pi := newPackageInfo(ctx, packageName, m)
+ if pi.module != m {
+ // Remember that the package was duplicated but do not rename as that will cause an error to
+ // be logged with the generated name. Similarly, reporting the error here will use the generated
+ // name as renames are only processed after this phase.
+ pi.duplicated = true
+ } else {
+ // This is the first package module in this package so rename it to match the package name.
+ m.setName(packageName)
+ ctx.Rename(packageName)
+
+ // Store a package info reference in the module.
+ m.packageInfo = pi
+ }
+}
+
+// Logs any deferred errors.
+func packageErrorReporter(ctx BottomUpMutatorContext) {
+ m, ok := ctx.Module().(*packageModule)
+ if !ok {
+ return
+ }
+
+ packageDir := ctx.ModuleDir()
+ packageName := "//" + packageDir
+
+ // Get the PackageInfo for the package. Should have been populated in the packageRenamer phase.
+ pi := findPackageInfo(ctx, packageName)
+ if pi == nil {
+ ctx.ModuleErrorf("internal error, expected package info to be present for package '%s'",
+ packageName)
+ return
+ }
+
+ if pi.module != m {
+ // The package module has been duplicated but this is not the module that has been renamed so
+ // ignore it. An error will be logged for the renamed module which will ensure that the error
+ // message uses the correct name.
+ return
+ }
+
+ // Check to see whether there are duplicate package modules in the package.
+ if pi.duplicated {
+ ctx.ModuleErrorf("package {...} specified multiple times")
+ return
+ }
+}
+
+type defaultPackageInfoKey string
+
+func newPackageInfo(
+ ctx BaseModuleContext, packageName string, module *packageModule) *packageInfo {
+ key := NewCustomOnceKey(defaultPackageInfoKey(packageName))
+
+ return ctx.Config().Once(key, func() interface{} {
+ return &packageInfo{module: module}
+ }).(*packageInfo)
+}
+
+// Get the PackageInfo for the package name (starts with //, no trailing /), is nil if no package
+// module type was specified.
+func findPackageInfo(ctx BaseModuleContext, packageName string) *packageInfo {
+ key := NewCustomOnceKey(defaultPackageInfoKey(packageName))
+
+ pi := ctx.Config().Once(key, func() interface{} {
+ return nil
+ })
+
+ if pi == nil {
+ return nil
+ } else {
+ return pi.(*packageInfo)
+ }
+}
diff --git a/android/package_test.go b/android/package_test.go
new file mode 100644
index 0000000..e5b0556
--- /dev/null
+++ b/android/package_test.go
@@ -0,0 +1,103 @@
+package android
+
+import (
+ "testing"
+)
+
+var packageTests = []struct {
+ name string
+ fs map[string][]byte
+ expectedErrors []string
+}{
+ // Package default_visibility handling is tested in visibility_test.go
+ {
+ name: "package must not accept visibility and name properties",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ name: "package",
+ visibility: ["//visibility:private"],
+ }`),
+ },
+ expectedErrors: []string{
+ `top/Blueprints:3:10: mutated field name cannot be set in a Blueprint file`,
+ `top/Blueprints:4:16: unrecognized property "visibility"`,
+ },
+ },
+ {
+ name: "multiple packages in separate directories",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ }`),
+ "other/Blueprints": []byte(`
+ package {
+ }`),
+ "other/nested/Blueprints": []byte(`
+ package {
+ }`),
+ },
+ },
+ {
+ name: "package must not be specified more than once per package",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_visibility: ["//visibility:private"],
+ }
+
+ package {
+ }`),
+ },
+ expectedErrors: []string{
+ `module "//top": package {...} specified multiple times`,
+ },
+ },
+}
+
+func TestPackage(t *testing.T) {
+ for _, test := range packageTests {
+ t.Run(test.name, func(t *testing.T) {
+ _, errs := testPackage(test.fs)
+
+ expectedErrors := test.expectedErrors
+ if expectedErrors == nil {
+ FailIfErrored(t, errs)
+ } else {
+ for _, expectedError := range expectedErrors {
+ FailIfNoMatchingErrors(t, expectedError, errs)
+ }
+ if len(errs) > len(expectedErrors) {
+ t.Errorf("additional errors found, expected %d, found %d", len(expectedErrors), len(errs))
+ for i, expectedError := range expectedErrors {
+ t.Errorf("expectedErrors[%d] = %s", i, expectedError)
+ }
+ for i, err := range errs {
+ t.Errorf("errs[%d] = %s", i, err)
+ }
+ }
+ }
+ })
+ }
+}
+
+func testPackage(fs map[string][]byte) (*TestContext, []error) {
+
+ // Create a new config per test as visibility information is stored in the config.
+ config := TestArchConfig(buildDir, nil)
+
+ ctx := NewTestArchContext()
+ ctx.RegisterModuleType("package", ModuleFactoryAdaptor(PackageFactory))
+ ctx.PreArchMutators(registerPackageRenamer)
+ ctx.Register()
+
+ ctx.MockFileSystem(fs)
+
+ _, errs := ctx.ParseBlueprintsFiles(".")
+ if len(errs) > 0 {
+ return ctx, errs
+ }
+
+ _, errs = ctx.PrepareBuildActions(config)
+ return ctx, errs
+}
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 fa187fa..59bfa6c 100644
--- a/android/path_properties_test.go
+++ b/android/path_properties_test.go
@@ -15,8 +15,6 @@
package android
import (
- "io/ioutil"
- "os"
"reflect"
"testing"
)
@@ -30,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
}
@@ -46,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) {
@@ -85,12 +95,6 @@
},
}
- buildDir, err := ioutil.TempDir("", "soong_path_properties_test")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(buildDir)
-
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
config := TestArchConfig(buildDir, nil)
diff --git a/android/paths.go b/android/paths.go
index 20b8b82..0ea4447 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1145,8 +1145,8 @@
partition = ctx.DeviceConfig().OdmPath()
} else if ctx.ProductSpecific() {
partition = ctx.DeviceConfig().ProductPath()
- } else if ctx.ProductServicesSpecific() {
- partition = ctx.DeviceConfig().ProductServicesPath()
+ } else if ctx.SystemExtSpecific() {
+ partition = ctx.DeviceConfig().SystemExtPath()
} else {
partition = "system"
}
diff --git a/android/paths_test.go b/android/paths_test.go
index 7bcfe41..8286e9a 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -294,15 +294,15 @@
out: "target/product/test_device/product/bin/my_test",
},
{
- name: "product_services binary",
+ name: "system_ext binary",
ctx: &moduleInstallPathContextImpl{
baseModuleContext: baseModuleContext{
target: deviceTarget,
- kind: productServicesSpecificModule,
+ kind: systemExtSpecificModule,
},
},
in: []string{"bin", "my_test"},
- out: "target/product/test_device/product_services/bin/my_test",
+ out: "target/product/test_device/system_ext/bin/my_test",
},
{
@@ -354,11 +354,11 @@
},
{
- name: "product_services native test binary",
+ name: "system_ext native test binary",
ctx: &moduleInstallPathContextImpl{
baseModuleContext: baseModuleContext{
target: deviceTarget,
- kind: productServicesSpecificModule,
+ kind: systemExtSpecificModule,
},
inData: true,
},
@@ -415,16 +415,16 @@
},
{
- name: "sanitized product_services binary",
+ name: "sanitized system_ext binary",
ctx: &moduleInstallPathContextImpl{
baseModuleContext: baseModuleContext{
target: deviceTarget,
- kind: productServicesSpecificModule,
+ kind: systemExtSpecificModule,
},
inSanitizerDir: true,
},
in: []string{"bin", "my_test"},
- out: "target/product/test_device/data/asan/product_services/bin/my_test",
+ out: "target/product/test_device/data/asan/system_ext/bin/my_test",
},
{
@@ -479,11 +479,11 @@
out: "target/product/test_device/data/asan/data/nativetest/my_test",
},
{
- name: "sanitized product_services native test binary",
+ name: "sanitized system_ext native test binary",
ctx: &moduleInstallPathContextImpl{
baseModuleContext: baseModuleContext{
target: deviceTarget,
- kind: productServicesSpecificModule,
+ kind: systemExtSpecificModule,
},
inData: true,
inSanitizerDir: true,
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 5087b18..b674153 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "reflect"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -43,7 +44,10 @@
properties PrebuiltProperties
module Module
srcs *[]string
- src *string
+
+ // Metadata for single source Prebuilt modules.
+ srcProps reflect.Value
+ srcField reflect.StructField
}
func (p *Prebuilt) Name(name string) string {
@@ -71,11 +75,16 @@
// sources.
return PathForModuleSrc(ctx, (*p.srcs)[0])
} else {
- if proptools.String(p.src) == "" {
- ctx.PropertyErrorf("src", "missing prebuilt source file")
+ if !p.srcProps.IsValid() {
+ ctx.ModuleErrorf("prebuilt source was not set")
+ }
+ src := p.getSingleSourceFieldValue()
+ if src == "" {
+ ctx.PropertyErrorf(proptools.FieldNameForProperty(p.srcField.Name),
+ "missing prebuilt source file")
return nil
}
- return PathForModuleSrc(ctx, *p.src)
+ return PathForModuleSrc(ctx, src)
}
}
@@ -89,10 +98,12 @@
p.srcs = srcs
}
-func InitSingleSourcePrebuiltModule(module PrebuiltInterface, src *string) {
+func InitSingleSourcePrebuiltModule(module PrebuiltInterface, srcProps interface{}, srcField string) {
p := module.Prebuilt()
module.AddProperties(&p.properties)
- p.src = src
+ p.srcProps = reflect.ValueOf(srcProps).Elem()
+ p.srcField, _ = p.srcProps.Type().FieldByName(srcField)
+ p.checkSingleSourceProperties()
}
type PrebuiltInterface interface {
@@ -129,7 +140,7 @@
func PrebuiltSelectModuleMutator(ctx TopDownMutatorContext) {
if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
p := m.Prebuilt()
- if p.srcs == nil && p.src == nil {
+ if p.srcs == nil && !p.srcProps.IsValid() {
panic(fmt.Errorf("prebuilt module did not have InitPrebuiltModule called on it"))
}
if !p.properties.SourceExists {
@@ -172,7 +183,7 @@
return false
}
- if p.src != nil && *p.src == "" {
+ if p.srcProps.IsValid() && p.getSingleSourceFieldValue() == "" {
return false
}
@@ -183,3 +194,28 @@
return source == nil || !source.Enabled()
}
+
+func (p *Prebuilt) checkSingleSourceProperties() {
+ if !p.srcProps.IsValid() || p.srcField.Name == "" {
+ panic(fmt.Errorf("invalid single source prebuilt %+v", p))
+ }
+
+ if p.srcProps.Kind() != reflect.Struct && p.srcProps.Kind() != reflect.Interface {
+ panic(fmt.Errorf("invalid single source prebuilt %+v", p.srcProps))
+ }
+}
+
+func (p *Prebuilt) getSingleSourceFieldValue() string {
+ value := p.srcProps.FieldByIndex(p.srcField.Index)
+ if value.Kind() == reflect.Ptr {
+ value = value.Elem()
+ }
+ if value.Kind() != reflect.String {
+ panic(fmt.Errorf("prebuilt src field %q should be a string or a pointer to one", p.srcField.Name))
+ }
+ return value.String()
+}
+
+func (p *Prebuilt) SourceExists() bool {
+ return p.properties.SourceExists
+}
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 8d7e74b..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, string(c.buf))
+ 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,28 +377,38 @@
}
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()).
Flags(sboxOutputs)
- commandString = string(sboxCmd.buf)
+ commandString = sboxCmd.buf.String()
tools = append(tools, sboxCmd.tools...)
}
// 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 []byte
- 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
@@ -420,10 +459,10 @@
// Text 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) Text(text string) *RuleBuilderCommand {
- if len(c.buf) > 0 {
- c.buf = append(c.buf, ' ')
+ if c.buf.Len() > 0 {
+ c.buf.WriteByte(' ')
}
- c.buf = append(c.buf, text...)
+ c.buf.WriteString(text)
return c
}
@@ -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,9 +673,54 @@
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 string(c.buf)
+ 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 {
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/sh_binary.go b/android/sh_binary.go
index fb7446d..2855aa0 100644
--- a/android/sh_binary.go
+++ b/android/sh_binary.go
@@ -29,6 +29,7 @@
RegisterModuleType("sh_binary", ShBinaryFactory)
RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
RegisterModuleType("sh_test", ShTestFactory)
+ RegisterModuleType("sh_test_host", ShTestHostFactory)
}
type shBinaryProperties struct {
@@ -195,6 +196,7 @@
return module
}
+// sh_test defines a shell script based test module.
func ShTestFactory() Module {
module := &ShTest{}
InitShBinaryModule(&module.ShBinary)
@@ -203,3 +205,13 @@
InitAndroidArchModule(module, HostAndDeviceSupported, MultilibFirst)
return module
}
+
+// sh_test_host defines a shell script based test module that runs on a host.
+func ShTestHostFactory() Module {
+ module := &ShTest{}
+ InitShBinaryModule(&module.ShBinary)
+ module.AddProperties(&module.testProperties)
+
+ InitAndroidArchModule(module, HostSupported, MultilibFirst)
+ return module
+}
diff --git a/android/sh_binary_test.go b/android/sh_binary_test.go
index c99e18c..9df769c 100644
--- a/android/sh_binary_test.go
+++ b/android/sh_binary_test.go
@@ -10,6 +10,7 @@
ctx := NewTestArchContext()
ctx.RegisterModuleType("sh_test", ModuleFactoryAdaptor(ShTestFactory))
+ ctx.RegisterModuleType("sh_test_host", ModuleFactoryAdaptor(ShTestHostFactory))
ctx.Register()
mockFiles := map[string][]byte{
"Android.bp": []byte(bp),
@@ -48,3 +49,23 @@
t.Errorf("Unexpected test data expected: %q, actual: %q", expected, actual)
}
}
+
+func TestShTestHost(t *testing.T) {
+ ctx, _ := testShBinary(t, `
+ sh_test_host {
+ name: "foo",
+ src: "test.sh",
+ filename: "test.sh",
+ data: [
+ "testdata/data1",
+ "testdata/sub/data2",
+ ],
+ }
+ `)
+
+ buildOS := BuildOs.String()
+ mod := ctx.ModuleForTests("foo", buildOS+"_x86_64").Module().(*ShTest)
+ if !mod.Host() {
+ t.Errorf("host bit is not set for a sh_test_host module.")
+ }
+}
diff --git a/android/testing.go b/android/testing.go
index 44bee4b..12e30ec 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -382,3 +382,14 @@
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 implmement 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..97bec10 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) {
diff --git a/android/variable.go b/android/variable.go
index b4f31c6..47586a7 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -165,15 +165,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"`
@@ -232,10 +234,10 @@
EnableXOM *bool `json:",omitempty"`
XOMExcludePaths []string `json:",omitempty"`
- VendorPath *string `json:",omitempty"`
- OdmPath *string `json:",omitempty"`
- ProductPath *string `json:",omitempty"`
- ProductServicesPath *string `json:",omitempty"`
+ VendorPath *string `json:",omitempty"`
+ OdmPath *string `json:",omitempty"`
+ ProductPath *string `json:",omitempty"`
+ SystemExtPath *string `json:",omitempty"`
ClangTidy *bool `json:",omitempty"`
TidyChecks *string `json:",omitempty"`
diff --git a/android/visibility.go b/android/visibility.go
index c7ef1da..94af343 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -23,14 +23,21 @@
// Enforces visibility rules between modules.
//
-// Two stage process:
-// * First stage works bottom up to extract visibility information from the modules, parse it,
+// Multi stage process:
+// * First stage works bottom up, before defaults expansion, to check the syntax of the visibility
+// rules that have been specified.
+//
+// * Second stage works bottom up to extract the package info for each package and store them in a
+// map by package name. See package.go for functionality for this.
+//
+// * Third stage works bottom up to extract visibility information from the modules, parse it,
// create visibilityRule structures and store them in a map keyed by the module's
// qualifiedModuleName instance, i.e. //<pkg>:<name>. The map is stored in the context rather
// than a global variable for testing. Each test has its own Config so they do not share a map
-// and so can be run in parallel.
+// and so can be run in parallel. If a module has no visibility specified then it uses the
+// default package visibility if specified.
//
-// * Second stage works top down and iterates over all the deps for each module. If the dep is in
+// * Fourth stage works top down and iterates over all the deps for each module. If the dep is in
// the same package then it is automatically visible. Otherwise, for each dep it first extracts
// its visibilityRule from the config map. If one could not be found then it assumes that it is
// publicly visible. Otherwise, it calls the visibility rule to check that the module can see
@@ -48,19 +55,6 @@
var visibilityRuleRegexp = regexp.MustCompile(visibilityRulePattern)
-// Qualified id for a module
-type qualifiedModuleName struct {
- // The package (i.e. directory) in which the module is defined, without trailing /
- pkg string
-
- // The name of the module.
- name string
-}
-
-func (q qualifiedModuleName) String() string {
- return fmt.Sprintf("//%s:%s", q.pkg, q.name)
-}
-
// A visibility rule is associated with a module and determines which other modules it is visible
// to, i.e. which other modules can depend on the rule's module.
type visibilityRule interface {
@@ -71,6 +65,32 @@
String() string
}
+// Describes the properties provided by a module that contain visibility rules.
+type visibilityPropertyImpl struct {
+ name string
+ stringsGetter func() []string
+}
+
+type visibilityProperty interface {
+ getName() string
+ getStrings() []string
+}
+
+func newVisibilityProperty(name string, stringsGetter func() []string) visibilityProperty {
+ return visibilityPropertyImpl{
+ name: name,
+ stringsGetter: stringsGetter,
+ }
+}
+
+func (p visibilityPropertyImpl) getName() string {
+ return p.name
+}
+
+func (p visibilityPropertyImpl) getStrings() []string {
+ return p.stringsGetter()
+}
+
// A compositeRule is a visibility rule composed from a list of atomic visibility rules.
//
// The list corresponds to the list of strings in the visibility property after defaults expansion.
@@ -96,9 +116,9 @@
return false
}
-func (r compositeRule) String() string {
- s := make([]string, 0, len(r))
- for _, r := range r {
+func (c compositeRule) String() string {
+ s := make([]string, 0, len(c))
+ for _, r := range c {
s = append(s, r.String())
}
@@ -173,9 +193,12 @@
ctx.BottomUp("visibilityRuleChecker", visibilityRuleChecker).Parallel()
}
+// Registers the function that gathers the visibility rules for each module.
+//
// Visibility is not dependent on arch so this must be registered before the arch phase to avoid
// having to process multiple variants for each module. This goes after defaults expansion to gather
-// the complete visibility lists from flat lists.
+// the complete visibility lists from flat lists and after the package info is gathered to ensure
+// that default_visibility is available.
func registerVisibilityRuleGatherer(ctx RegisterMutatorsContext) {
ctx.BottomUp("visibilityRuleGatherer", visibilityRuleGatherer).Parallel()
}
@@ -193,33 +216,36 @@
for _, props := range d.properties() {
if cp, ok := props.(*commonProperties); ok {
if visibility := cp.Visibility; visibility != nil {
- checkRules(ctx, qualified.pkg, visibility)
+ checkRules(ctx, qualified.pkg, "visibility", visibility)
}
}
}
} else if m, ok := ctx.Module().(Module); ok {
- if visibility := m.base().commonProperties.Visibility; visibility != nil {
- checkRules(ctx, qualified.pkg, visibility)
+ visibilityProperties := m.visibilityProperties()
+ for _, p := range visibilityProperties {
+ if visibility := p.getStrings(); visibility != nil {
+ checkRules(ctx, qualified.pkg, p.getName(), visibility)
+ }
}
}
}
-func checkRules(ctx BottomUpMutatorContext, currentPkg string, visibility []string) {
+func checkRules(ctx BaseModuleContext, currentPkg, property string, visibility []string) {
ruleCount := len(visibility)
if ruleCount == 0 {
// This prohibits an empty list as its meaning is unclear, e.g. it could mean no visibility and
// it could mean public visibility. Requiring at least one rule makes the owner's intent
// clearer.
- ctx.PropertyErrorf("visibility", "must contain at least one visibility rule")
+ ctx.PropertyErrorf(property, "must contain at least one visibility rule")
return
}
for _, v := range visibility {
- ok, pkg, name := splitRule(ctx, v, currentPkg)
+ ok, pkg, name := splitRule(v, currentPkg)
if !ok {
// Visibility rule is invalid so ignore it. Keep going rather than aborting straight away to
// ensure all the rules on this module are checked.
- ctx.PropertyErrorf("visibility",
+ ctx.PropertyErrorf(property,
"invalid visibility pattern %q must match"+
" //<package>:<module>, //<package> or :<module>",
v)
@@ -230,14 +256,14 @@
switch name {
case "private", "public":
case "legacy_public":
- ctx.PropertyErrorf("visibility", "//visibility:legacy_public must not be used")
+ ctx.PropertyErrorf(property, "//visibility:legacy_public must not be used")
continue
default:
- ctx.PropertyErrorf("visibility", "unrecognized visibility rule %q", v)
+ ctx.PropertyErrorf(property, "unrecognized visibility rule %q", v)
continue
}
if ruleCount != 1 {
- ctx.PropertyErrorf("visibility", "cannot mix %q with any other visibility rules", v)
+ ctx.PropertyErrorf(property, "cannot mix %q with any other visibility rules", v)
continue
}
}
@@ -246,7 +272,7 @@
// restrictions on the rules.
if !isAncestor("vendor", currentPkg) {
if !isAllowedFromOutsideVendor(pkg, name) {
- ctx.PropertyErrorf("visibility",
+ ctx.PropertyErrorf(property,
"%q is not allowed. Packages outside //vendor cannot make themselves visible to specific"+
" targets within //vendor, they can only use //vendor:__subpackages__.", v)
continue
@@ -265,23 +291,27 @@
return
}
- qualified := createQualifiedModuleName(ctx)
+ qualifiedModuleId := m.qualifiedModuleId(ctx)
+ currentPkg := qualifiedModuleId.pkg
- visibility := m.base().commonProperties.Visibility
- if visibility != nil {
- rule := parseRules(ctx, qualified.pkg, visibility)
- if rule != nil {
- moduleToVisibilityRuleMap(ctx).Store(qualified, rule)
+ // 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)
+ }
}
}
}
-func parseRules(ctx BottomUpMutatorContext, currentPkg string, visibility []string) compositeRule {
+func parseRules(ctx BaseModuleContext, currentPkg string, visibility []string) compositeRule {
rules := make(compositeRule, 0, len(visibility))
hasPrivateRule := false
hasNonPrivateRule := false
for _, v := range visibility {
- ok, pkg, name := splitRule(ctx, v, currentPkg)
+ ok, pkg, name := splitRule(v, currentPkg)
if !ok {
continue
}
@@ -336,7 +366,7 @@
return !isAncestor("vendor", pkg)
}
-func splitRule(ctx BaseModuleContext, ruleExpression string, currentPkg string) (bool, string, string) {
+func splitRule(ruleExpression string, currentPkg string) (bool, string, string) {
// Make sure that the rule is of the correct format.
matches := visibilityRuleRegexp.FindStringSubmatch(ruleExpression)
if ruleExpression == "" || matches == nil {
@@ -378,11 +408,15 @@
return
}
- rule, ok := moduleToVisibilityRule.Load(depQualified)
+ value, ok := moduleToVisibilityRule.Load(depQualified)
+ var rule compositeRule
if ok {
- if !rule.(compositeRule).matches(qualified) {
- ctx.ModuleErrorf("depends on %s which is not visible to this module", depQualified)
- }
+ rule = value.(compositeRule)
+ } else {
+ rule = packageDefaultVisibility(ctx, depQualified)
+ }
+ if rule != nil && !rule.matches(qualified) {
+ ctx.ModuleErrorf("depends on %s which is not visible to this module", depQualified)
}
})
}
@@ -393,3 +427,20 @@
qualified := qualifiedModuleName{dir, moduleName}
return qualified
}
+
+func packageDefaultVisibility(ctx BaseModuleContext, moduleId qualifiedModuleName) compositeRule {
+ moduleToVisibilityRule := moduleToVisibilityRuleMap(ctx)
+ packageQualifiedId := moduleId.getContainingPackageId()
+ for {
+ value, ok := moduleToVisibilityRule.Load(packageQualifiedId)
+ if ok {
+ return value.(compositeRule)
+ }
+
+ if packageQualifiedId.isRootPackage() {
+ return nil
+ }
+
+ packageQualifiedId = packageQualifiedId.getContainingPackageId()
+ }
+}
diff --git a/android/visibility_test.go b/android/visibility_test.go
index 1a51495..af6acf4 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -658,6 +658,167 @@
` visible to this module`,
},
},
+ // Package default_visibility tests
+ {
+ name: "package default_visibility property is checked",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_visibility: ["//visibility:invalid"],
+ }`),
+ },
+ expectedErrors: []string{`default_visibility: unrecognized visibility rule "//visibility:invalid"`},
+ },
+ {
+ // This test relies on the default visibility being legacy_public.
+ name: "package default_visibility property used when no visibility specified",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_visibility: ["//visibility:private"],
+ }
+
+ mock_library {
+ name: "libexample",
+ }`),
+ "outsider/Blueprints": []byte(`
+ mock_library {
+ name: "liboutsider",
+ deps: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "liboutsider" variant "android_common": depends on //top:libexample which is not` +
+ ` visible to this module`,
+ },
+ },
+ {
+ name: "package default_visibility public does not override visibility private",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_visibility: ["//visibility:public"],
+ }
+
+ mock_library {
+ name: "libexample",
+ visibility: ["//visibility:private"],
+ }`),
+ "outsider/Blueprints": []byte(`
+ mock_library {
+ name: "liboutsider",
+ deps: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "liboutsider" variant "android_common": depends on //top:libexample which is not` +
+ ` visible to this module`,
+ },
+ },
+ {
+ name: "package default_visibility private does not override visibility public",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_visibility: ["//visibility:private"],
+ }
+
+ mock_library {
+ name: "libexample",
+ visibility: ["//visibility:public"],
+ }`),
+ "outsider/Blueprints": []byte(`
+ mock_library {
+ name: "liboutsider",
+ deps: ["libexample"],
+ }`),
+ },
+ },
+ {
+ name: "package default_visibility :__subpackages__",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_visibility: [":__subpackages__"],
+ }
+
+ mock_library {
+ name: "libexample",
+ }`),
+ "top/nested/Blueprints": []byte(`
+ mock_library {
+ name: "libnested",
+ deps: ["libexample"],
+ }`),
+ "outsider/Blueprints": []byte(`
+ mock_library {
+ name: "liboutsider",
+ deps: ["libexample"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "liboutsider" variant "android_common": depends on //top:libexample which is not` +
+ ` visible to this module`,
+ },
+ },
+ {
+ name: "package default_visibility inherited to subpackages",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_visibility: ["//outsider"],
+ }
+
+ mock_library {
+ name: "libexample",
+ visibility: [":__subpackages__"],
+ }`),
+ "top/nested/Blueprints": []byte(`
+ mock_library {
+ name: "libnested",
+ deps: ["libexample"],
+ }`),
+ "outsider/Blueprints": []byte(`
+ mock_library {
+ name: "liboutsider",
+ deps: ["libexample", "libnested"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "liboutsider" variant "android_common": depends on //top:libexample which is not` +
+ ` visible to this module`,
+ },
+ },
+ {
+ name: "package default_visibility inherited to subpackages",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_visibility: ["//visibility:private"],
+ }`),
+ "top/nested/Blueprints": []byte(`
+ package {
+ default_visibility: ["//outsider"],
+ }
+
+ mock_library {
+ name: "libnested",
+ }`),
+ "top/other/Blueprints": []byte(`
+ mock_library {
+ name: "libother",
+ }`),
+ "outsider/Blueprints": []byte(`
+ mock_library {
+ name: "liboutsider",
+ deps: ["libother", "libnested"],
+ }`),
+ },
+ expectedErrors: []string{
+ `module "liboutsider" variant "android_common": depends on //top/other:libother which is` +
+ ` not visible to this module`,
+ },
+ },
}
func TestVisibility(t *testing.T) {
@@ -692,8 +853,10 @@
config := TestArchConfig(buildDir, nil)
ctx := NewTestArchContext()
+ ctx.RegisterModuleType("package", ModuleFactoryAdaptor(PackageFactory))
ctx.RegisterModuleType("mock_library", ModuleFactoryAdaptor(newMockLibraryModule))
ctx.RegisterModuleType("mock_defaults", ModuleFactoryAdaptor(defaultsFactory))
+ ctx.PreArchMutators(registerPackageRenamer)
ctx.PreArchMutators(registerVisibilityRuleChecker)
ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
ctx.PreArchMutators(registerVisibilityRuleGatherer)
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index af81e43..2def179 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -192,7 +192,7 @@
"LOCAL_VENDOR_MODULE": "vendor",
"LOCAL_ODM_MODULE": "device_specific",
"LOCAL_PRODUCT_MODULE": "product_specific",
- "LOCAL_PRODUCT_SERVICES_MODULE": "product_services_specific",
+ "LOCAL_SYSTEM_EXT_MODULE": "system_ext_specific",
"LOCAL_EXPORT_PACKAGE_RESOURCES": "export_package_resources",
"LOCAL_PRIVILEGED_MODULE": "privileged",
"LOCAL_AAPT_INCLUDE_ALL_RESOURCES": "aapt_include_all_resources",
@@ -602,8 +602,8 @@
return fmt.Errorf("Cannot handle appending to LOCAL_MODULE_PATH")
}
// Analyze value in order to set the correct values for the 'device_specific',
- // 'product_specific', 'product_services_specific' 'vendor'/'soc_specific',
- // 'product_services_specific' attribute. Two cases are allowed:
+ // 'product_specific', 'system_ext_specific' 'vendor'/'soc_specific',
+ // 'system_ext_specific' attribute. Two cases are allowed:
// $(VAR)/<literal-value>
// $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR)/<literal-value>
// The last case is equivalent to $(TARGET_OUT_VENDOR)/<literal-value>
diff --git a/androidmk/cmd/androidmk/androidmk_test.go b/androidmk/cmd/androidmk/androidmk_test.go
index 4d5180e..dbb7fde 100644
--- a/androidmk/cmd/androidmk/androidmk_test.go
+++ b/androidmk/cmd/androidmk/androidmk_test.go
@@ -957,37 +957,37 @@
`,
},
{
- desc: "prebuilt_etc_TARGET_OUT_PRODUCT_SERVICES/etc",
+ desc: "prebuilt_etc_TARGET_OUT_SYSTEM_EXT/etc",
in: `
include $(CLEAR_VARS)
LOCAL_MODULE := etc.test1
LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_SERVICES)/etc/foo/bar
+LOCAL_MODULE_PATH := $(TARGET_OUT_SYSTEM_EXT)/etc/foo/bar
include $(BUILD_PREBUILT)
`,
expected: `
prebuilt_etc {
name: "etc.test1",
sub_dir: "foo/bar",
- product_services_specific: true,
+ system_ext_specific: true,
}
`,
},
{
- desc: "prebuilt_etc_TARGET_OUT_PRODUCT_SERVICES_ETC",
+ desc: "prebuilt_etc_TARGET_OUT_SYSTEM_EXT_ETC",
in: `
include $(CLEAR_VARS)
LOCAL_MODULE := etc.test1
LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_SERVICES_ETC)/foo/bar
+LOCAL_MODULE_PATH := $(TARGET_OUT_SYSTEM_EXT_ETC)/foo/bar
include $(BUILD_PREBUILT)
`,
expected: `
prebuilt_etc {
name: "etc.test1",
sub_dir: "foo/bar",
- product_services_specific: true,
+ system_ext_specific: true,
}
diff --git a/apex/apex.go b/apex/apex.go
index 84e5497..8b0f385 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -90,12 +90,6 @@
CommandDeps: []string{"${zip2zip}"},
Description: "app bundle",
}, "abi")
-
- apexMergeNoticeRule = pctx.StaticRule("apexMergeNoticeRule", blueprint.RuleParams{
- Command: `${mergenotice} --output $out $inputs`,
- CommandDeps: []string{"${mergenotice}"},
- Description: "merge notice files into $out",
- }, "inputs")
)
var imageApexSuffix = ".apex"
@@ -114,8 +108,10 @@
executableTag = dependencyTag{name: "executable"}
javaLibTag = dependencyTag{name: "javaLib"}
prebuiltTag = dependencyTag{name: "prebuilt"}
+ testTag = dependencyTag{name: "test"}
keyTag = dependencyTag{name: "key"}
certificateTag = dependencyTag{name: "certificate"}
+ usesTag = dependencyTag{name: "uses"}
)
func init() {
@@ -144,8 +140,6 @@
pctx.HostBinToolVariable("zip2zip", "zip2zip")
pctx.HostBinToolVariable("zipalign", "zipalign")
- pctx.SourcePathVariable("mergenotice", "build/soong/scripts/mergenotice.py")
-
android.RegisterModuleType("apex", apexBundleFactory)
android.RegisterModuleType("apex_test", testApexBundleFactory)
android.RegisterModuleType("apex_defaults", defaultsFactory)
@@ -153,7 +147,8 @@
android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.TopDown("apex_deps", apexDepsMutator)
- ctx.BottomUp("apex", apexMutator)
+ ctx.BottomUp("apex", apexMutator).Parallel()
+ ctx.BottomUp("apex_uses", apexUsesMutator).Parallel()
})
}
@@ -194,12 +189,19 @@
mctx.CreateVariations(apexBundleName)
}
}
+func apexUsesMutator(mctx android.BottomUpMutatorContext) {
+ if ab, ok := mctx.Module().(*apexBundle); ok {
+ mctx.AddFarVariationDependencies(nil, usesTag, ab.properties.Uses...)
+ }
+}
type apexNativeDependencies struct {
// List of native libraries
Native_shared_libs []string
// List of native executables
Binaries []string
+ // List of native tests
+ Tests []string
}
type apexMultilibProperties struct {
// Native dependencies whose compile_multilib is "first"
@@ -240,7 +242,7 @@
// List of native shared libs that are embedded inside this APEX bundle
Native_shared_libs []string
- // List of native executables that are embedded inside this APEX bundle
+ // List of executables that are embedded inside this APEX bundle
Binaries []string
// List of java libraries that are embedded inside this APEX bundle
@@ -249,6 +251,9 @@
// List of prebuilt files that are embedded inside this APEX bundle
Prebuilts []string
+ // List of tests that are embedded inside this APEX bundle
+ Tests []string
+
// Name of the apex_key module that provides the private key to sign APEX
Key *string
@@ -274,6 +279,12 @@
// List of sanitizer names that this APEX is enabled for
SanitizerNames []string `blueprint:"mutated"`
+
+ // Indicates this APEX provides C++ shared libaries to other APEXes. Default: false.
+ Provide_cpp_shared_libs *bool
+
+ // List of providing APEXes' names so that this APEX can depend on provided shared libraries.
+ Uses []string
}
type apexTargetBundleProperties struct {
@@ -307,6 +318,7 @@
pyBinary
goBinary
javaSharedLib
+ nativeTest
)
type apexPackaging int
@@ -369,6 +381,8 @@
return "EXECUTABLES"
case javaSharedLib:
return "JAVA_LIBRARIES"
+ case nativeTest:
+ return "NATIVE_TESTS"
default:
panic(fmt.Errorf("unkonwn class %d", class))
}
@@ -402,8 +416,6 @@
container_certificate_file android.Path
container_private_key_file android.Path
- mergedNoticeFile android.WritablePath
-
// list of files to be included in this apex
filesInfo []apexFile
@@ -416,7 +428,8 @@
}
func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
- native_shared_libs []string, binaries []string, arch string, imageVariation string) {
+ native_shared_libs []string, binaries []string, tests []string,
+ arch string, imageVariation string) {
// Use *FarVariation* to be able to depend on modules having
// conflicting variations with this module. This is required since
// arch variant of an APEX bundle is 'common' but it is 'arm' or 'arm64'
@@ -432,6 +445,12 @@
{Mutator: "arch", Variation: arch},
{Mutator: "image", Variation: imageVariation},
}, executableTag, binaries...)
+
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: arch},
+ {Mutator: "image", Variation: imageVariation},
+ {Mutator: "test_per_src", Variation: ""}, // "" is the all-tests variant
+ }, testTag, tests...)
}
func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) {
@@ -469,10 +488,20 @@
{Mutator: "link", Variation: "shared"},
}, sharedLibTag, a.properties.Native_shared_libs...)
+ // When multilib.* is omitted for tests, it implies
+ // multilib.both.
+ 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
addDependenciesForNativeModules(ctx,
a.properties.Multilib.Both.Native_shared_libs,
- a.properties.Multilib.Both.Binaries, target.String(),
+ a.properties.Multilib.Both.Binaries,
+ a.properties.Multilib.Both.Tests,
+ target.String(),
a.getImageVariation(config))
isPrimaryAbi := i == 0
@@ -487,7 +516,9 @@
// Add native modules targetting the first ABI
addDependenciesForNativeModules(ctx,
a.properties.Multilib.First.Native_shared_libs,
- a.properties.Multilib.First.Binaries, target.String(),
+ a.properties.Multilib.First.Binaries,
+ a.properties.Multilib.First.Tests,
+ target.String(),
a.getImageVariation(config))
// When multilib.* is omitted for prebuilts, it implies multilib.first.
@@ -501,24 +532,32 @@
// Add native modules targetting 32-bit ABI
addDependenciesForNativeModules(ctx,
a.properties.Multilib.Lib32.Native_shared_libs,
- a.properties.Multilib.Lib32.Binaries, target.String(),
+ a.properties.Multilib.Lib32.Binaries,
+ a.properties.Multilib.Lib32.Tests,
+ target.String(),
a.getImageVariation(config))
addDependenciesForNativeModules(ctx,
a.properties.Multilib.Prefer32.Native_shared_libs,
- a.properties.Multilib.Prefer32.Binaries, target.String(),
+ a.properties.Multilib.Prefer32.Binaries,
+ a.properties.Multilib.Prefer32.Tests,
+ target.String(),
a.getImageVariation(config))
case "lib64":
// Add native modules targetting 64-bit ABI
addDependenciesForNativeModules(ctx,
a.properties.Multilib.Lib64.Native_shared_libs,
- a.properties.Multilib.Lib64.Binaries, target.String(),
+ a.properties.Multilib.Lib64.Binaries,
+ a.properties.Multilib.Lib64.Tests,
+ target.String(),
a.getImageVariation(config))
if !has32BitTarget {
addDependenciesForNativeModules(ctx,
a.properties.Multilib.Prefer32.Native_shared_libs,
- a.properties.Multilib.Prefer32.Binaries, target.String(),
+ a.properties.Multilib.Prefer32.Binaries,
+ a.properties.Multilib.Prefer32.Tests,
+ target.String(),
a.getImageVariation(config))
}
@@ -527,7 +566,7 @@
if sanitizer == "hwaddress" {
addDependenciesForNativeModules(ctx,
[]string{"libclang_rt.hwasan-aarch64-android"},
- nil, target.String(), a.getImageVariation(config))
+ nil, nil, target.String(), a.getImageVariation(config))
break
}
}
@@ -621,6 +660,8 @@
dirInApex = filepath.Join(dirInApex, cc.RelativeInstallPath())
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() {
@@ -644,10 +685,21 @@
func getCopyManifestForExecutable(cc *cc.Module) (fileToCopy android.Path, dirInApex string) {
dirInApex = filepath.Join("bin", cc.RelativeInstallPath())
+ 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
}
+func getCopyManifestForTestPerSrcExecutables(cc *cc.Module) (filesToCopy []android.Path, dirInApex string) {
+ dirInApex = filepath.Join("bin", cc.RelativeInstallPath())
+ filesToCopy = cc.TestPerSrcOutputFiles()
+ return
+}
+
func getCopyManifestForPyBinary(py *python.Module) (fileToCopy android.Path, dirInApex string) {
dirInApex = "bin"
fileToCopy = py.HostToolPath().Path()
@@ -696,8 +748,37 @@
return
}
+ if len(a.properties.Tests) > 0 && !a.testApex {
+ ctx.PropertyErrorf("tests", "property not allowed in apex module type")
+ return
+ }
+
handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
+ // Check if "uses" requirements are met with dependent apexBundles
+ var providedNativeSharedLibs []string
+ useVendor := proptools.Bool(a.properties.Use_vendor)
+ ctx.VisitDirectDepsBlueprint(func(m blueprint.Module) {
+ if ctx.OtherModuleDependencyTag(m) != usesTag {
+ return
+ }
+ otherName := ctx.OtherModuleName(m)
+ other, ok := m.(*apexBundle)
+ if !ok {
+ ctx.PropertyErrorf("uses", "%q is not a provider", otherName)
+ return
+ }
+ if proptools.Bool(other.properties.Use_vendor) != useVendor {
+ ctx.PropertyErrorf("use_vendor", "%q has different value of use_vendor", otherName)
+ return
+ }
+ if !proptools.Bool(other.properties.Provide_cpp_shared_libs) {
+ ctx.PropertyErrorf("uses", "%q does not provide native_shared_libs", otherName)
+ return
+ }
+ providedNativeSharedLibs = append(providedNativeSharedLibs, other.properties.Native_shared_libs...)
+ })
+
ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
if _, ok := parent.(*apexBundle); ok {
// direct dependencies
@@ -714,11 +795,6 @@
}
case executableTag:
if cc, ok := child.(*cc.Module); ok {
- if !cc.Arch().Native {
- // There is only one 'bin' directory so we shouldn't bother copying in
- // native-bridge'd binaries and only use main ones.
- return true
- }
fileToCopy, dirInApex := getCopyManifestForExecutable(cc)
filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeExecutable, cc, cc.Symlinks()})
return true
@@ -757,6 +833,28 @@
} else {
ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
}
+ case testTag:
+ if cc, ok := child.(*cc.Module); ok {
+ if cc.TestPerSrcOutputFiles() != nil {
+ // Multiple-output test module (using `test_per_src`).
+ filesToCopy, dirInApex := getCopyManifestForTestPerSrcExecutables(cc)
+ for _, fileToCopy := range filesToCopy {
+ // Handle modules created as `test_per_src` variations of a single test module:
+ // replace the name of the original test module (`depName`, shared by all
+ // `test_per_src` variants of that module) with the name of the generated test
+ // binary.
+ moduleName := filepath.Base(fileToCopy.String())
+ filesInfo = append(filesInfo, apexFile{fileToCopy, moduleName, dirInApex, nativeTest, cc, nil})
+ }
+ } else {
+ // Single-output test module (not using `test_per_src`).
+ fileToCopy, dirInApex := getCopyManifestForExecutable(cc)
+ filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeTest, cc, nil})
+ }
+ return true
+ } else {
+ ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
+ }
case keyTag:
if key, ok := child.(*apexKey); ok {
a.private_key_file = key.private_key_file
@@ -778,6 +876,11 @@
// 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.
@@ -836,8 +939,6 @@
a.installDir = android.PathForModuleInstall(ctx, "apex")
a.filesInfo = filesInfo
- a.buildNoticeFile(ctx)
-
if a.apexTypes.zip() {
a.buildUnflattenedApex(ctx, zipApex)
}
@@ -851,35 +952,26 @@
}
}
-func (a *apexBundle) buildNoticeFile(ctx android.ModuleContext) {
+func (a *apexBundle) buildNoticeFile(ctx android.ModuleContext, apexFileName string) android.OptionalPath {
noticeFiles := []android.Path{}
- noticeFilesString := []string{}
for _, f := range a.filesInfo {
if f.module != nil {
notice := f.module.NoticeFile()
if notice.Valid() {
noticeFiles = append(noticeFiles, notice.Path())
- noticeFilesString = append(noticeFilesString, notice.Path().String())
}
}
}
// append the notice file specified in the apex module itself
if a.NoticeFile().Valid() {
noticeFiles = append(noticeFiles, a.NoticeFile().Path())
- noticeFilesString = append(noticeFilesString, a.NoticeFile().Path().String())
}
- if len(noticeFiles) > 0 {
- a.mergedNoticeFile = android.PathForModuleOut(ctx, "NOTICE")
- ctx.Build(pctx, android.BuildParams{
- Rule: apexMergeNoticeRule,
- Inputs: noticeFiles,
- Output: a.mergedNoticeFile,
- Args: map[string]string{
- "inputs": strings.Join(noticeFilesString, " "),
- },
- })
+ if len(noticeFiles) == 0 {
+ return android.OptionalPath{}
}
+
+ return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.FirstUniquePaths(noticeFiles)).HtmlGzOutput
}
func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, apexType apexPackaging) {
@@ -936,10 +1028,10 @@
var executablePaths []string // this also includes dirs
for _, f := range a.filesInfo {
pathInApex := filepath.Join(f.installDir, f.builtFile.Base())
- if f.installDir == "bin" {
+ 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)
@@ -1004,6 +1096,13 @@
}
optFlags = append(optFlags, "--target_sdk_version "+targetSdkVersion)
+ noticeFile := a.buildNoticeFile(ctx, ctx.ModuleName()+suffix)
+ if noticeFile.Valid() {
+ // If there's a NOTICE file, embed it as an asset file in the APEX.
+ implicitInputs = append(implicitInputs, noticeFile.Path())
+ optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeFile.String()))
+ }
+
ctx.Build(pctx, android.BuildParams{
Rule: apexRule,
Implicits: implicitInputs,
@@ -1250,9 +1349,6 @@
fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", a.installDir.RelPathString()))
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+apexType.suffix())
fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
- if a.installable() && a.mergedNoticeFile != nil {
- fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", a.mergedNoticeFile.String())
- }
if len(moduleNames) > 0 {
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(moduleNames, " "))
}
@@ -1269,11 +1365,11 @@
}
func testApexBundleFactory() android.Module {
- return ApexBundleFactory( /*testApex*/ true)
+ return ApexBundleFactory(true /*testApex*/)
}
func apexBundleFactory() android.Module {
- return ApexBundleFactory( /*testApex*/ false)
+ return ApexBundleFactory(false /*testApex*/)
}
func ApexBundleFactory(testApex bool) android.Module {
@@ -1333,7 +1429,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 {
@@ -1355,6 +1452,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 {
@@ -1362,6 +1466,23 @@
}
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()
+
+ // 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.
@@ -1403,6 +1524,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")
@@ -1429,17 +1554,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...)
},
}
}
@@ -1448,7 +1572,7 @@
func PrebuiltFactory() android.Module {
module := &Prebuilt{}
module.AddProperties(&module.properties)
- android.InitSingleSourcePrebuiltModule(module, &module.properties.Source)
+ android.InitSingleSourcePrebuiltModule(module, &module.properties, "Source")
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
return module
}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index f71abd7..bb0c4c5 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -17,6 +17,7 @@
import (
"io/ioutil"
"os"
+ "reflect"
"strings"
"testing"
@@ -27,9 +28,40 @@
"android/soong/java"
)
-func testApex(t *testing.T, bp string) *android.TestContext {
- config, buildDir := setup(t)
- defer teardown(buildDir)
+var buildDir string
+
+func testApexError(t *testing.T, pattern, bp string) {
+ ctx, config := testApexContext(t, bp)
+ _, errs := ctx.ParseFileList(".", []string{"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 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, config
+}
+
+func testApexContext(t *testing.T, bp string) (*android.TestContext, android.Config) {
+ config := android.TestArchConfig(buildDir, nil)
+ config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current")
+ config.TestProductVariables.DefaultAppCertificate = proptools.StringPtr("vendor/foo/devkeys/test")
+ config.TestProductVariables.CertificateOverrides = []string{"myapex_keytest:myapex.certificate.override"}
+ config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("Q")
+ config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(false)
ctx := android.NewTestArchContext()
ctx.RegisterModuleType("apex", android.ModuleFactoryAdaptor(apexBundleFactory))
@@ -42,6 +74,7 @@
ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.TopDown("apex_deps", apexDepsMutator)
ctx.BottomUp("apex", apexMutator)
+ ctx.BottomUp("apex_uses", apexUsesMutator)
ctx.TopDown("prebuilt_select", android.PrebuiltSelectModuleMutator).Parallel()
ctx.BottomUp("prebuilt_postdeps", android.PrebuiltPostDepsMutator).Parallel()
})
@@ -51,6 +84,7 @@
ctx.RegisterModuleType("cc_library_headers", android.ModuleFactoryAdaptor(cc.LibraryHeaderFactory))
ctx.RegisterModuleType("cc_binary", android.ModuleFactoryAdaptor(cc.BinaryFactory))
ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory))
+ ctx.RegisterModuleType("cc_test", android.ModuleFactoryAdaptor(cc.TestFactory))
ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(cc.LlndkLibraryFactory))
ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc.ToolchainLibraryFactory))
ctx.RegisterModuleType("prebuilt_etc", android.ModuleFactoryAdaptor(android.PrebuiltEtcFactory))
@@ -64,6 +98,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()
})
@@ -161,7 +196,13 @@
"system/sepolicy/apex/myapex-file_contexts": nil,
"system/sepolicy/apex/myapex_keytest-file_contexts": nil,
"system/sepolicy/apex/otherapex-file_contexts": nil,
+ "system/sepolicy/apex/commonapex-file_contexts": nil,
"mylib.cpp": nil,
+ "mylib_common.cpp": nil,
+ "mytest.cpp": nil,
+ "mytest1.cpp": nil,
+ "mytest2.cpp": nil,
+ "mytest3.cpp": nil,
"myprebuilt": nil,
"my_include": nil,
"vendor/foo/devkeys/test.x509.pem": nil,
@@ -180,35 +221,25 @@
"myapex-arm.apex": nil,
"frameworks/base/api/current.txt": nil,
})
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
- return ctx
+ return ctx, config
}
-func setup(t *testing.T) (config android.Config, buildDir string) {
- buildDir, err := ioutil.TempDir("", "soong_apex_test")
+func setUp() {
+ var err error
+ buildDir, err = ioutil.TempDir("", "soong_apex_test")
if err != nil {
- t.Fatal(err)
+ panic(err)
}
-
- config = android.TestArchConfig(buildDir, nil)
- config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current")
- config.TestProductVariables.DefaultAppCertificate = proptools.StringPtr("vendor/foo/devkeys/test")
- config.TestProductVariables.CertificateOverrides = []string{"myapex_keytest:myapex.certificate.override"}
- config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("Q")
- config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(false)
- return
}
-func teardown(buildDir string) {
+func tearDown() {
os.RemoveAll(buildDir)
}
// ensure that 'result' contains 'expected'
func ensureContains(t *testing.T, result string, expected string) {
+ t.Helper()
if !strings.Contains(result, expected) {
t.Errorf("%q is not found in %q", expected, result)
}
@@ -216,18 +247,21 @@
// ensures that 'result' does not contain 'notExpected'
func ensureNotContains(t *testing.T, result string, notExpected string) {
+ t.Helper()
if strings.Contains(result, notExpected) {
t.Errorf("%q is found in %q", notExpected, result)
}
}
func ensureListContains(t *testing.T, result []string, expected string) {
+ t.Helper()
if !android.InList(expected, result) {
t.Errorf("%q is not found in %v", expected, result)
}
}
func ensureListNotContains(t *testing.T, result []string, notExpected string) {
+ t.Helper()
if android.InList(notExpected, result) {
t.Errorf("%q is found in %v", notExpected, result)
}
@@ -235,7 +269,7 @@
// Minimal test
func TestBasicApex(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex_defaults {
name: "myapex-defaults",
manifest: ":myapex.manifest",
@@ -310,6 +344,8 @@
optFlags := apexRule.Args["opt_flags"]
ensureContains(t, optFlags, "--pubkey vendor/foo/devkeys/testkey.avbpubkey")
+ // Ensure that the NOTICE output is being packaged as an asset.
+ ensureContains(t, optFlags, "--assets_dir "+buildDir+"/.intermediates/myapex/android_common_myapex/NOTICE")
copyCmds := apexRule.Args["copy_commands"]
@@ -347,17 +383,17 @@
t.Errorf("Could not find all expected symlinks! foo: %t, foo_link_64: %t. Command was %s", found_foo, found_foo_link_64, copyCmds)
}
- apexMergeNoticeRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexMergeNoticeRule")
- noticeInputs := strings.Split(apexMergeNoticeRule.Args["inputs"], " ")
- if len(noticeInputs) != 3 {
- t.Errorf("number of input notice files: expected = 3, actual = %d", len(noticeInputs))
+ mergeNoticesRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("mergeNoticesRule")
+ noticeInputs := mergeNoticesRule.Inputs.Strings()
+ if len(noticeInputs) != 2 {
+ t.Errorf("number of input notice files: expected = 2, actual = %q", len(noticeInputs))
}
ensureListContains(t, noticeInputs, "NOTICE")
ensureListContains(t, noticeInputs, "custom_notice")
}
func TestBasicZipApex(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -405,7 +441,7 @@
}
func TestApexWithStubs(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -489,7 +525,7 @@
}
func TestApexWithExplicitStubsDependency(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -556,7 +592,7 @@
}
func TestApexWithSystemLibsStubs(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -677,7 +713,7 @@
}
func TestFilesInSubDir(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -737,7 +773,7 @@
}
func TestUseVendor(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -786,8 +822,32 @@
ensureNotContains(t, inputsString, "android_arm64_armv8-a_core_shared_myapex/mylib2.so")
}
+func TestUseVendorFailsIfNotVendorAvailable(t *testing.T) {
+ testApexError(t, `dependency "mylib" of "myapex" missing variant:\n.*image:vendor`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ use_vendor: true,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `)
+}
+
func TestStaticLinking(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -827,7 +887,7 @@
}
func TestKeys(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex_keytest",
key: "myapex.key",
@@ -881,7 +941,7 @@
}
func TestMacro(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -925,7 +985,7 @@
}
func TestHeaderLibsDependency(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -973,7 +1033,7 @@
}
func TestNonTestApex(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -1024,7 +1084,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",
@@ -1072,7 +1132,7 @@
}
func TestApexWithTarget(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -1152,7 +1212,7 @@
}
func TestApexWithShBinary(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -1180,7 +1240,7 @@
}
func TestApexInProductPartition(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -1212,7 +1272,7 @@
}
func TestApexKeyFromOtherModule(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
apex_key {
name: "myapex.key",
public_key: ":my.avbpubkey",
@@ -1245,7 +1305,7 @@
}
func TestPrebuilt(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
prebuilt_apex {
name: "myapex",
arch: {
@@ -1268,7 +1328,7 @@
}
func TestPrebuiltFilenameOverride(t *testing.T) {
- ctx := testApex(t, `
+ ctx, _ := testApex(t, `
prebuilt_apex {
name: "myapex",
src: "myapex-arm.apex",
@@ -1283,3 +1343,205 @@
t.Errorf("installFilename invalid. expected: %q, actual: %q", expected, p.installFilename)
}
}
+
+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, `
+ apex_test {
+ name: "myapex",
+ key: "myapex.key",
+ tests: [
+ "mytest",
+ "mytests",
+ ],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_test {
+ name: "mytest",
+ gtest: false,
+ srcs: ["mytest.cpp"],
+ relative_install_path: "test",
+ system_shared_libs: [],
+ 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")
+ copyCmds := apexRule.Args["copy_commands"]
+
+ // 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")
+}
+
+func TestApexUsesOtherApex(t *testing.T) {
+ ctx, _ := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ uses: ["commonapex"],
+ }
+
+ apex {
+ name: "commonapex",
+ key: "myapex.key",
+ native_shared_libs: ["libcommon"],
+ provide_cpp_shared_libs: true,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["libcommon"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libcommon",
+ srcs: ["mylib_common.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `)
+
+ module1 := ctx.ModuleForTests("myapex", "android_common_myapex")
+ apexRule1 := module1.Rule("apexRule")
+ copyCmds1 := apexRule1.Args["copy_commands"]
+
+ module2 := ctx.ModuleForTests("commonapex", "android_common_commonapex")
+ apexRule2 := module2.Rule("apexRule")
+ copyCmds2 := apexRule2.Args["copy_commands"]
+
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared_myapex")
+ ensureListContains(t, ctx.ModuleVariantsForTests("libcommon"), "android_arm64_armv8-a_core_shared_commonapex")
+ ensureContains(t, copyCmds1, "image.apex/lib64/mylib.so")
+ ensureContains(t, copyCmds2, "image.apex/lib64/libcommon.so")
+ ensureNotContains(t, copyCmds1, "image.apex/lib64/libcommon.so")
+}
+
+func TestApexUsesFailsIfNotProvided(t *testing.T) {
+ testApexError(t, `uses: "commonapex" does not provide native_shared_libs`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ uses: ["commonapex"],
+ }
+
+ apex {
+ name: "commonapex",
+ key: "myapex.key",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `)
+ testApexError(t, `uses: "commonapex" is not a provider`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ uses: ["commonapex"],
+ }
+
+ cc_library {
+ name: "commonapex",
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `)
+}
+
+func TestApexUsesFailsIfUseVenderMismatch(t *testing.T) {
+ testApexError(t, `use_vendor: "commonapex" has different value of use_vendor`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ use_vendor: true,
+ uses: ["commonapex"],
+ }
+
+ apex {
+ name: "commonapex",
+ key: "myapex.key",
+ provide_cpp_shared_libs: true,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `)
+}
+
+func TestMain(m *testing.M) {
+ run := func() int {
+ setUp()
+ defer tearDown()
+
+ return m.Run()
+ }
+
+ os.Exit(run())
+}
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index cac4d9a..5f1cce8 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -508,15 +508,15 @@
"TARGET_OUT": {{prefix: "/usr/share", modType: "prebuilt_usr_share"}, {prefix: "/fonts", modType: "prebuilt_font"},
{prefix: "/etc/firmware", modType: "prebuilt_firmware"}, {prefix: "/vendor/firmware", modType: "prebuilt_firmware", flags: []string{"proprietary"}},
{prefix: "/etc"}},
- "TARGET_OUT_ETC": {{prefix: "/firmware", modType: "prebuilt_firmware"}, {prefix: ""}},
- "TARGET_OUT_PRODUCT": {{prefix: "/etc", flags: []string{"product_specific"}}, {prefix: "/fonts", modType: "prebuilt_font", flags: []string{"product_specific"}}},
- "TARGET_OUT_PRODUCT_ETC": {{prefix: "", flags: []string{"product_specific"}}},
- "TARGET_OUT_ODM": {{prefix: "/etc", flags: []string{"device_specific"}}},
- "TARGET_OUT_PRODUCT_SERVICES": {{prefix: "/etc", flags: []string{"product_services_specific"}}},
- "TARGET_OUT_PRODUCT_SERVICES_ETC": {{prefix: "", flags: []string{"product_services_specific"}}},
- "TARGET_OUT_VENDOR": {{prefix: "/etc", flags: []string{"proprietary"}}, {prefix: "/firmware", modType: "prebuilt_firmware", flags: []string{"proprietary"}}},
- "TARGET_OUT_VENDOR_ETC": {{prefix: "", flags: []string{"proprietary"}}},
- "TARGET_RECOVERY_ROOT_OUT": {{prefix: "/system/etc", flags: []string{"recovery"}}},
+ "TARGET_OUT_ETC": {{prefix: "/firmware", modType: "prebuilt_firmware"}, {prefix: ""}},
+ "TARGET_OUT_PRODUCT": {{prefix: "/etc", flags: []string{"product_specific"}}, {prefix: "/fonts", modType: "prebuilt_font", flags: []string{"product_specific"}}},
+ "TARGET_OUT_PRODUCT_ETC": {{prefix: "", flags: []string{"product_specific"}}},
+ "TARGET_OUT_ODM": {{prefix: "/etc", flags: []string{"device_specific"}}},
+ "TARGET_OUT_SYSTEM_EXT": {{prefix: "/etc", flags: []string{"system_ext_specific"}}},
+ "TARGET_OUT_SYSTEM_EXT_ETC": {{prefix: "", flags: []string{"system_ext_specific"}}},
+ "TARGET_OUT_VENDOR": {{prefix: "/etc", flags: []string{"proprietary"}}, {prefix: "/firmware", modType: "prebuilt_firmware", flags: []string{"proprietary"}}},
+ "TARGET_OUT_VENDOR_ETC": {{prefix: "", flags: []string{"proprietary"}}},
+ "TARGET_RECOVERY_ROOT_OUT": {{prefix: "/system/etc", flags: []string{"recovery"}}},
}
// rewriteAndroidPrebuiltEtc fixes prebuilt_etc rule
diff --git a/cc/binary.go b/cc/binary.go
index 1757f1c..149a92e 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -444,7 +444,8 @@
// Bionic binaries (e.g. linker) is installed to the bootstrap subdirectory.
// The original path becomes a symlink to the corresponding file in the
// runtime APEX.
- if installToBootstrap(ctx.baseModuleName(), ctx.Config()) && ctx.Arch().Native && ctx.apexName() == "" && !ctx.inRecovery() {
+ translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled || !ctx.Arch().Native
+ if installToBootstrap(ctx.baseModuleName(), ctx.Config()) && !translatedArch && ctx.apexName() == "" && !ctx.inRecovery() {
if ctx.Device() && isBionic(ctx.baseModuleName()) {
binary.installSymlinkToRuntimeApex(ctx, file)
}
diff --git a/cc/builder.go b/cc/builder.go
index 1e12361..cbe2c88 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")
@@ -416,7 +416,7 @@
ccCmd = "clang"
moduleCflags = cflags
moduleToolingCflags = toolingCflags
- case ".cpp", ".cc", ".mm":
+ case ".cpp", ".cc", ".cxx", ".mm":
ccCmd = "clang++"
moduleCflags = cppflags
moduleToolingCflags = toolingCppflags
diff --git a/cc/cc.go b/cc/cc.go
index 53ec899..2cee807 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()
@@ -65,7 +65,7 @@
ctx.TopDown("tsan_deps", sanitizerDepsMutator(tsan))
ctx.BottomUp("tsan", sanitizerMutator(tsan)).Parallel()
- ctx.TopDown("sanitize_runtime_deps", sanitizerRuntimeDepsMutator)
+ ctx.TopDown("sanitize_runtime_deps", sanitizerRuntimeDepsMutator).Parallel()
ctx.BottomUp("sanitize_runtime", sanitizerRuntimeMutator).Parallel()
ctx.BottomUp("coverage", coverageMutator).Parallel()
@@ -179,6 +179,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"`
@@ -368,6 +371,8 @@
ndkLateStubDepTag = dependencyTag{name: "ndk late stub", library: true}
vndkExtDepTag = dependencyTag{name: "vndk extends", library: true}
runtimeDepTag = dependencyTag{name: "runtime lib"}
+ coverageDepTag = dependencyTag{name: "coverage"}
+ testPerSrcDepTag = dependencyTag{name: "test_per_src"}
)
// Module contains the properties and members used by all C/C++ module types, and implements
@@ -403,6 +408,9 @@
outputFile android.OptionalPath
+ // Test output files, in the case of a test module using `test_per_src`.
+ testPerSrcOutputFiles []android.Path
+
cachedToolchain config.Toolchain
subAndroidMkOnce map[subAndroidMkProvider]bool
@@ -425,6 +433,10 @@
return c.outputFile
}
+func (c *Module) TestPerSrcOutputFiles() []android.Path {
+ return c.testPerSrcOutputFiles
+}
+
func (c *Module) UnstrippedOutputFile() android.Path {
if c.linker != nil {
return c.linker.unstrippedOutputFilePath()
@@ -778,6 +790,10 @@
// APEX variants do not need ABI dumps.
return false
}
+ if ctx.isStubs() {
+ // Stubs do not need ABI dumps.
+ return false
+ }
if ctx.isNdk() {
return true
}
@@ -935,6 +951,29 @@
}
func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
+ // Handle the case of a test module split by `test_per_src` mutator.
+ if test, ok := c.linker.(testPerSrc); ok {
+ // The `test_per_src` mutator adds an extra variant named "", depending on all the
+ // other `test_per_src` variants of the test module. Collect the output files of
+ // these dependencies and record them in the `testPerSrcOutputFiles` for later use
+ // (see e.g. `apexBundle.GenerateAndroidBuildActions`).
+ if test.isAllTestsVariation() {
+ var testPerSrcOutputFiles []android.Path
+ for _, dep := range actx.GetDirectDepsWithTag(testPerSrcDepTag) {
+ if ccDep, ok := dep.(*Module); ok {
+ depOutputFile := ccDep.OutputFile().Path()
+ testPerSrcOutputFiles =
+ append(testPerSrcOutputFiles, depOutputFile)
+ }
+ }
+ c.testPerSrcOutputFiles = testPerSrcOutputFiles
+ // Set outputFile to an empty path, as this module does not produce an
+ // output file per se.
+ c.outputFile = android.OptionalPath{}
+ return
+ }
+ }
+
c.makeLinkType = c.getMakeLinkType(actx)
ctx := &moduleContext{
@@ -970,7 +1009,7 @@
flags = c.sanitize.flags(ctx, flags)
}
if c.coverage != nil {
- flags = c.coverage.flags(ctx, flags)
+ flags, deps = c.coverage.flags(ctx, flags, deps)
}
if c.lto != nil {
flags = c.lto.flags(ctx, flags)
@@ -2078,6 +2117,7 @@
&VendorProperties{},
&BaseCompilerProperties{},
&BaseLinkerProperties{},
+ &ObjectLinkerProperties{},
&LibraryProperties{},
&FlagExporterProperties{},
&BinaryLinkerProperties{},
diff --git a/cc/cc_test.go b/cc/cc_test.go
index ca34185..c619b5a 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -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/compdb.go b/cc/compdb.go
index 1102651..ecc67b8 100644
--- a/cc/compdb.go
+++ b/cc/compdb.go
@@ -141,7 +141,7 @@
isAsm = false
isCpp = false
clangPath = ccPath
- case ".cpp", ".cc", ".mm":
+ case ".cpp", ".cc", ".cxx", ".mm":
isAsm = false
isCpp = true
clangPath = cxxPath
diff --git a/cc/compiler.go b/cc/compiler.go
index fd6184b..0f9599e 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -485,7 +485,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/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 0f0420f..bcfae5d 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -63,14 +63,20 @@
}
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"},
+
+ // Not all cases there is performance gain by enabling -mavx -mavx2
+ // flags so these flags are not enabled by default.
+ // if there is performance gain in individual library components,
+ // the compiler flags can be set in corresponding bp files.
+ // "avx": []string{"-mavx"},
+ // "avx2": []string{"-mavx2"},
+ // "avx512": []string{"-mavx512"}
+
"popcnt": []string{"-mpopcnt"},
- "avx": []string{"-mavx"},
- "avx2": []string{"-mavx2"},
- "avx512": []string{"-mavx512"},
"aes_ni": []string{"-maes"},
}
)
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 500014e..64392dc 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -82,12 +82,19 @@
}
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"},
- "avx": []string{"-mavx"},
- "avx2": []string{"-mavx2"},
+
+ // Not all cases there is performance gain by enabling -mavx -mavx2
+ // flags so these flags are not enabled by default.
+ // if there is performance gain in individual library components,
+ // the compiler flags can be set in corresponding bp files.
+ // "avx": []string{"-mavx"},
+ // "avx2": []string{"-mavx2"},
+ // "avx512": []string{"-mavx512"}
+
"aes_ni": []string{"-maes"},
}
)
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 9dc7f06..0de0c1c 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -17,6 +17,8 @@
import (
"strconv"
+ "github.com/google/blueprint"
+
"android/soong/android"
)
@@ -41,30 +43,28 @@
return []interface{}{&cov.Properties}
}
-func (cov *coverage) deps(ctx BaseModuleContext, deps Deps) Deps {
- if cov.Properties.NeedCoverageBuild {
- // Link libprofile-extras/libprofile-extras_ndk when coverage
- // variant is required. This is a no-op unless coverage is
- // actually enabled during linking, when
- // '-uinit_profile_extras' is added (in flags()) to force the
- // setup code in libprofile-extras be linked into the
- // binary/library.
- //
- // We cannot narrow it further to only the 'cov' variant since
- // the mutator hasn't run (and we don't have the 'cov' variant
- // yet).
- if !ctx.useSdk() {
- deps.LateStaticLibs = append(deps.LateStaticLibs, "libprofile-extras")
- } else {
- deps.LateStaticLibs = append(deps.LateStaticLibs, "libprofile-extras_ndk")
- }
+func getProfileLibraryName(ctx ModuleContextIntf) string {
+ // This function should only ever be called for a cc.Module, so the
+ // following statement should always succeed.
+ if ctx.useSdk() {
+ return "libprofile-extras_ndk"
+ } else {
+ return "libprofile-extras"
+ }
+}
+
+func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
+ if cov.Properties.NeedCoverageVariant {
+ ctx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "static"},
+ }, coverageDepTag, getProfileLibraryName(ctx))
}
return deps
}
-func (cov *coverage) flags(ctx ModuleContext, flags Flags) Flags {
+func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
if !ctx.DeviceConfig().NativeCoverageEnabled() {
- return flags
+ return flags, deps
}
if cov.Properties.CoverageEnabled {
@@ -114,11 +114,13 @@
if cov.linkCoverage {
flags.LdFlags = append(flags.LdFlags, "--coverage")
- // Force linking of constructor/setup code in libprofile-extras
- flags.LdFlags = append(flags.LdFlags, "-uinit_profile_extras")
+ coverage := ctx.GetDirectDepWithTag(getProfileLibraryName(ctx), coverageDepTag).(*Module)
+ deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
+
+ flags.LdFlags = append(flags.LdFlags, "-Wl,--wrap,getenv")
}
- return flags
+ return flags, deps
}
func (cov *coverage) begin(ctx BaseModuleContext) {
diff --git a/cc/fuzz.go b/cc/fuzz.go
new file mode 100644
index 0000000..3b0c5c8
--- /dev/null
+++ b/cc/fuzz.go
@@ -0,0 +1,120 @@
+// 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) {
+ disableDarwin := struct {
+ Target struct {
+ Darwin struct {
+ Enabled *bool
+ }
+ }
+ }{}
+ disableDarwin.Target.Darwin.Enabled = BoolPtr(false)
+ ctx.AppendProperties(&disableDarwin)
+ })
+
+ return module
+}
diff --git a/cc/gen.go b/cc/gen.go
index 1d30dab..42b0cbe 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -25,6 +25,7 @@
func init() {
pctx.SourcePathVariable("lexCmd", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/flex")
+ pctx.SourcePathVariable("m4Cmd", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/m4")
pctx.HostBinToolVariable("aidlCmd", "aidl-cpp")
pctx.HostBinToolVariable("syspropCmd", "sysprop_cpp")
@@ -33,8 +34,8 @@
var (
lex = pctx.AndroidStaticRule("lex",
blueprint.RuleParams{
- Command: "$lexCmd -o$out $in",
- CommandDeps: []string{"$lexCmd"},
+ Command: "M4=$m4Cmd $lexCmd -o$out $in",
+ CommandDeps: []string{"$lexCmd", "$m4Cmd"},
})
sysprop = pctx.AndroidStaticRule("sysprop",
@@ -97,7 +98,8 @@
}
cmd.Text("BISON_PKGDATADIR=prebuilts/build-tools/common/bison").
- Tool(ctx.Config().PrebuiltBuildTool(ctx, "bison")).
+ FlagWithInput("M4=", ctx.Config().PrebuiltBuildTool(ctx, "m4")).
+ PrebuiltBuildTool(ctx, "bison").
Flag("-d").
Flags(flags).
FlagWithOutput("--defines=", headerFile).
@@ -118,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).
@@ -236,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 f98cd36..cb31979 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -113,8 +113,6 @@
}
type LibraryMutatedProperties struct {
- VariantName string `blueprint:"mutated"`
-
// Build a static variant
BuildStatic bool `blueprint:"mutated"`
// Build a shared variant
@@ -528,7 +526,7 @@
}
}
- return name + library.MutatedProperties.VariantName
+ return name
}
var versioningMacroNamesListMutex sync.Mutex
@@ -633,7 +631,7 @@
library.objects = deps.WholeStaticLibObjs.Copy()
library.objects = library.objects.Append(objs)
- fileName := ctx.ModuleName() + library.MutatedProperties.VariantName + staticLibraryExtension
+ fileName := ctx.ModuleName() + staticLibraryExtension
outputFile := android.PathForModuleOut(ctx, fileName)
builderFlags := flagsToBuilderFlags(flags)
@@ -651,8 +649,7 @@
TransformObjToStaticLib(ctx, library.objects.objFiles, builderFlags, outputFile, objs.tidyFiles)
- library.coverageOutputFile = TransformCoverageFilesToZip(ctx, library.objects,
- ctx.ModuleName()+library.MutatedProperties.VariantName)
+ library.coverageOutputFile = TransformCoverageFilesToZip(ctx, library.objects, ctx.ModuleName())
library.wholeStaticMissingDeps = ctx.GetMissingDependencies()
@@ -810,7 +807,7 @@
}
func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
- if len(objs.sAbiDumpFiles) > 0 && library.shouldCreateVndkSourceAbiDump(ctx) {
+ if library.shouldCreateVndkSourceAbiDump(ctx) {
vndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
if ver := ctx.DeviceConfig().VndkVersion(); ver != "" && ver != "current" {
vndkVersion = ver
@@ -955,7 +952,8 @@
// Bionic libraries (e.g. libc.so) is installed to the bootstrap subdirectory.
// The original path becomes a symlink to the corresponding file in the
// runtime APEX.
- if installToBootstrap(ctx.baseModuleName(), ctx.Config()) && !library.buildStubs() && ctx.Arch().Native && !ctx.inRecovery() {
+ translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled || !ctx.Arch().Native
+ if installToBootstrap(ctx.baseModuleName(), ctx.Config()) && !library.buildStubs() && !translatedArch && !ctx.inRecovery() {
if ctx.Device() && isBionic(ctx.baseModuleName()) {
library.installSymlinkToRuntimeApex(ctx, file)
}
diff --git a/cc/linker.go b/cc/linker.go
index dda2fcb..fa3b0a6 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -483,7 +483,7 @@
Input: in,
Output: out,
Args: map[string]string{
- "buildNumberFromFile": ctx.Config().BuildNumberFromFile(),
+ "buildNumberFromFile": proptools.NinjaEscape(ctx.Config().BuildNumberFromFile()),
},
})
}
diff --git a/cc/makevars.go b/cc/makevars.go
index a71f479..2b49772 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -138,7 +138,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, ","))
@@ -161,6 +160,8 @@
ctx.Strict("AIDL_CPP", "${aidlCmd}")
+ ctx.Strict("M4", "${m4Cmd}")
+
ctx.Strict("RS_GLOBAL_INCLUDES", "${config.RsGlobalIncludes}")
ctx.Strict("SOONG_STRIP_PATH", "${stripPath}")
diff --git a/cc/object.go b/cc/object.go
index 50ecc38..9fa0ac9 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
}
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/sanitize.go b/cc/sanitize.go
index 0af0659..b238b7e 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -33,7 +33,6 @@
asanCflags = []string{"-fno-omit-frame-pointer"}
asanLdflags = []string{"-Wl,-u,__asan_preinit"}
- asanLibs = []string{"libasan"}
// TODO(pcc): Stop passing -hwasan-allow-ifunc here once it has been made
// the default.
@@ -391,7 +390,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
@@ -445,7 +443,6 @@
// libraries needed with -fsanitize=address. http://b/18650275 (WAI)
flags.LdFlags = append(flags.LdFlags, "-Wl,--no-as-needed")
} else {
- flags.CFlags = append(flags.CFlags, "-mllvm", "-asan-globals=0")
if ctx.bootstrap() {
flags.DynamicLinker = "/system/bin/bootstrap/linker_asan"
} else {
@@ -468,6 +465,10 @@
// TODO(b/131771163): LTO and Fuzzer support is mutually incompatible.
_, flags.LdFlags = removeFromList("-flto", flags.LdFlags)
flags.LdFlags = append(flags.LdFlags, "-fno-lto")
+
+ // TODO(b/133876586): Experimental PM breaks sanitizer coverage.
+ _, flags.CFlags = removeFromList("-fexperimental-new-pass-manager", flags.CFlags)
+ flags.CFlags = append(flags.CFlags, "-fno-experimental-new-pass-manager")
}
if Bool(sanitize.Properties.Sanitize.Cfi) {
@@ -701,8 +702,8 @@
if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
return false
}
- if d, ok := child.(*Module); ok && d.static() && d.sanitize != nil {
+ if d, ok := child.(*Module); ok && d.static() && d.sanitize != nil {
if enableMinimalRuntime(d.sanitize) {
// If a static dependency is built with the minimal runtime,
// make sure we include the ubsan minimal runtime.
@@ -713,8 +714,17 @@
// make sure we include the ubsan runtime.
c.sanitize.Properties.UbsanRuntimeDep = true
}
+
+ if c.sanitize.Properties.MinimalRuntimeDep &&
+ c.sanitize.Properties.UbsanRuntimeDep {
+ // both flags that this mutator might set are true, so don't bother recursing
+ return false
+ }
+
+ return true
+ } else {
+ return false
}
- return true
})
}
}
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..f0ad33b 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,
@@ -196,6 +231,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 +265,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/cmd/diff_target_files/target_files.go b/cmd/diff_target_files/target_files.go
index 8705ca7..0fa04e8 100644
--- a/cmd/diff_target_files/target_files.go
+++ b/cmd/diff_target_files/target_files.go
@@ -28,7 +28,7 @@
"ODM/",
"OEM/",
"PRODUCT/",
- "PRODUCT_SERVICES/",
+ "SYSTEM_EXT/",
"ROOT/",
"SYSTEM/",
"SYSTEM_OTHER/",
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_build/main.go b/cmd/soong_build/main.go
index 41c7d46..30381e0 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -18,7 +18,12 @@
"flag"
"fmt"
"os"
+ "os/exec"
"path/filepath"
+ "strconv"
+ "strings"
+ "syscall"
+ "time"
"github.com/google/blueprint/bootstrap"
@@ -50,6 +55,42 @@
}
func main() {
+ if android.SoongDelveListen != "" {
+ if android.SoongDelvePath == "" {
+ fmt.Fprintln(os.Stderr, "SOONG_DELVE is set but failed to find dlv")
+ os.Exit(1)
+ }
+ pid := strconv.Itoa(os.Getpid())
+ cmd := []string{android.SoongDelvePath,
+ "attach", pid,
+ "--headless",
+ "-l", android.SoongDelveListen,
+ "--api-version=2",
+ "--accept-multiclient",
+ "--log",
+ }
+
+ fmt.Println("Starting", strings.Join(cmd, " "))
+ dlv := exec.Command(cmd[0], cmd[1:]...)
+ dlv.Stdout = os.Stdout
+ dlv.Stderr = os.Stderr
+ dlv.Stdin = nil
+
+ // Put dlv into its own process group so we can kill it and the child process it starts.
+ dlv.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+
+ err := dlv.Start()
+ if err != nil {
+ // Print the error starting dlv and continue.
+ fmt.Println(err)
+ } else {
+ // Kill the process group for dlv when soong_build exits.
+ defer syscall.Kill(-dlv.Process.Pid, syscall.SIGKILL)
+ // Wait to give dlv a chance to connect and pause the process.
+ time.Sleep(time.Second)
+ }
+ }
+
flag.Parse()
// The top-level Blueprints file is passed as the first argument.
@@ -72,7 +113,17 @@
ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
- bootstrap.Main(ctx.Context, configuration, configuration.ConfigFileName, configuration.ProductVariablesFileName)
+ extraNinjaDeps := []string{configuration.ConfigFileName, configuration.ProductVariablesFileName}
+
+ // Read the SOONG_DELVE again through configuration so that there is a dependency on the environment variable
+ // and soong_build will rerun when it is set for the first time.
+ if listen := configuration.Getenv("SOONG_DELVE"); listen != "" {
+ // Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is
+ // enabled even if it completed successfully.
+ extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.BuildDir(), "always_rerun_for_delve"))
+ }
+
+ bootstrap.Main(ctx.Context, configuration, extraNinjaDeps...)
if docFile != "" {
if err := writeDocs(ctx, docFile); err != nil {
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index f5276c3..0eaed76 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -61,10 +61,8 @@
config: func(ctx build.Context, args ...string) build.Config {
return build.NewConfig(ctx, args...)
},
- stdio: func() terminal.StdioInterface {
- return terminal.StdioImpl{}
- },
- run: make,
+ stdio: stdio,
+ run: make,
}, {
flag: "--dumpvar-mode",
description: "print the value of the legacy make variable VAR to stdout",
@@ -77,6 +75,12 @@
config: dumpVarConfig,
stdio: customStdio,
run: dumpVars,
+ }, {
+ flag: "--build-mode",
+ description: "build modules based on the specified build action",
+ config: buildActionConfig,
+ stdio: stdio,
+ run: make,
},
}
@@ -157,8 +161,10 @@
trace.SetOutput(filepath.Join(logsDir, "build.trace"))
stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, "verbose.log")))
stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, "error.log")))
+ stat.AddOutput(status.NewProtoErrorLog(log, filepath.Join(logsDir, "build_error")))
+ stat.AddOutput(status.NewCriticalPath(log))
- defer met.Dump(filepath.Join(logsDir, "build_metrics"))
+ defer met.Dump(filepath.Join(logsDir, "soong_metrics"))
if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
if !strings.HasSuffix(start, "N") {
@@ -184,13 +190,13 @@
func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
flags.Usage = func() {
- fmt.Fprintf(os.Stderr, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
- fmt.Fprintln(os.Stderr, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
- fmt.Fprintln(os.Stderr, "")
+ fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
+ fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
+ fmt.Fprintln(ctx.Writer, "")
- fmt.Fprintln(os.Stderr, "'report_config' is a special case that prints the human-readable config banner")
- fmt.Fprintln(os.Stderr, "from the beginning of the build.")
- fmt.Fprintln(os.Stderr, "")
+ fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
+ fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
+ fmt.Fprintln(ctx.Writer, "")
flags.PrintDefaults()
}
abs := flags.Bool("abs", false, "Print the absolute path of the value")
@@ -234,15 +240,15 @@
func dumpVars(ctx build.Context, config build.Config, args []string, _ string) {
flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
flags.Usage = func() {
- fmt.Fprintf(os.Stderr, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
- fmt.Fprintln(os.Stderr, "In dumpvars mode, dump the values of one or more legacy make variables, in")
- fmt.Fprintln(os.Stderr, "shell syntax. The resulting output may be sourced directly into a shell to")
- fmt.Fprintln(os.Stderr, "set corresponding shell variables.")
- fmt.Fprintln(os.Stderr, "")
+ fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
+ fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
+ fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
+ fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
+ fmt.Fprintln(ctx.Writer, "")
- fmt.Fprintln(os.Stderr, "'report_config' is a special case that dumps a variable containing the")
- fmt.Fprintln(os.Stderr, "human-readable config banner from the beginning of the build.")
- fmt.Fprintln(os.Stderr, "")
+ fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
+ fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
+ fmt.Fprintln(ctx.Writer, "")
flags.PrintDefaults()
}
@@ -299,6 +305,10 @@
}
}
+func stdio() terminal.StdioInterface {
+ return terminal.StdioImpl{}
+}
+
func customStdio() terminal.StdioInterface {
return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
}
@@ -308,6 +318,94 @@
return build.NewConfig(ctx)
}
+func buildActionConfig(ctx build.Context, args ...string) build.Config {
+ flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
+ flags.Usage = func() {
+ fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
+ fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
+ fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
+ fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
+ fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
+ fmt.Fprintln(ctx.Writer, "")
+ flags.PrintDefaults()
+ }
+
+ buildActionFlags := []struct {
+ name string
+ description string
+ action build.BuildAction
+ buildDependencies bool
+ set bool
+ }{{
+ name: "all-modules",
+ description: "Build action: build from the top of the source tree.",
+ action: build.BUILD_MODULES,
+ buildDependencies: true,
+ }, {
+ // buildDependencies is set to true as mm is being deprecated. 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,
+ buildDependencies: true,
+ }, {
+ // buildDependencies is set to true as mmm is being deprecated. 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,
+ 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,
+ 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,
+ buildDependencies: true,
+ }}
+ for i, flag := range buildActionFlags {
+ flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
+ }
+ dir := flags.String("dir", "", "Directory of the executed build command.")
+
+ // Only interested in the first two args which defines the build action and the directory.
+ // The remaining arguments are passed down to the config.
+ const numBuildActionFlags = 2
+ if len(args) < numBuildActionFlags {
+ flags.Usage()
+ ctx.Fatalln("Improper build action arguments.")
+ }
+ flags.Parse(args[0:numBuildActionFlags])
+
+ // The next block of code is to validate that exactly one build action is set and the dir flag
+ // 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 {
+ ctx.Fatalln("Build action not defined.")
+ }
+ if *dir == "" {
+ ctx.Fatalln("-dir not specified.")
+ }
+
+ // 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...)
+}
+
func make(ctx build.Context, config build.Config, _ []string, logsDir string) {
if config.IsVerbose() {
writer := ctx.Writer
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 5a1bd74..3e32958 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -79,13 +79,11 @@
InstructionSetFeatures map[android.ArchType]string // instruction set for each architecture
// Only used for boot image
- DirtyImageObjects android.OptionalPath // path to a dirty-image-objects file
- PreloadedClasses android.OptionalPath // path to a preloaded-classes file
- BootImageProfiles android.Paths // path to a boot-image-profile.txt file
- UseProfileForBootImage bool // whether a profile should be used to compile the boot image
- BootFlags string // extra flags to pass to dex2oat for the boot image
- Dex2oatImageXmx string // max heap size for dex2oat for the boot image
- Dex2oatImageXms string // initial heap size for dex2oat for the boot image
+ DirtyImageObjects android.OptionalPath // path to a dirty-image-objects file
+ BootImageProfiles android.Paths // path to a boot-image-profile.txt file
+ BootFlags string // extra flags to pass to dex2oat for the boot image
+ Dex2oatImageXmx string // max heap size for dex2oat for the boot image
+ Dex2oatImageXms string // initial heap size for dex2oat for the boot image
Tools Tools // paths to tools possibly used by the generated commands
}
@@ -121,8 +119,9 @@
UsesLibraries []string
LibraryPaths map[string]android.Path
- Archs []android.ArchType
- DexPreoptImages []android.Path
+ Archs []android.ArchType
+ DexPreoptImages []android.Path
+ DexPreoptImagesDeps []android.Paths
PreoptBootClassPathDexFiles android.Paths // file paths of boot class path files
PreoptBootClassPathDexLocations []string // virtual locations of boot class path files
@@ -182,7 +181,6 @@
// Copies of entries in GlobalConfig that are not constructable without extra parameters. They will be
// used to construct the real value manually below.
DirtyImageObjects string
- PreloadedClasses string
BootImageProfiles []string
Tools struct {
@@ -205,7 +203,6 @@
// Construct paths that require a PathContext.
config.GlobalConfig.DirtyImageObjects = android.OptionalPathForPath(constructPath(ctx, config.DirtyImageObjects))
- config.GlobalConfig.PreloadedClasses = android.OptionalPathForPath(constructPath(ctx, config.PreloadedClasses))
config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles)
config.GlobalConfig.Tools.Profman = constructPath(ctx, config.Tools.Profman)
@@ -257,6 +254,9 @@
config.ModuleConfig.StripInputPath = constructPath(ctx, config.StripInputPath)
config.ModuleConfig.StripOutputPath = constructWritablePath(ctx, config.StripOutputPath)
+ // This needs to exist, but dependencies are already handled in Make, so we don't need to pass them through JSON.
+ config.ModuleConfig.DexPreoptImagesDeps = make([]android.Paths, len(config.ModuleConfig.DexPreoptImages))
+
return config.ModuleConfig, nil
}
@@ -317,9 +317,7 @@
CpuVariant: nil,
InstructionSetFeatures: nil,
DirtyImageObjects: android.OptionalPath{},
- PreloadedClasses: android.OptionalPath{},
BootImageProfiles: nil,
- UseProfileForBootImage: false,
BootFlags: "",
Dex2oatImageXmx: "",
Dex2oatImageXms: "",
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 0be37d0..e02e60f 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -125,7 +125,8 @@
for i, arch := range module.Archs {
image := module.DexPreoptImages[i]
- dexpreoptCommand(ctx, global, module, rule, arch, profile, image, appImage, generateDM)
+ imageDeps := module.DexPreoptImagesDeps[i]
+ dexpreoptCommand(ctx, global, module, rule, arch, profile, image, imageDeps, appImage, generateDM)
}
}
}
@@ -190,7 +191,7 @@
}
func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig, rule *android.RuleBuilder,
- arch android.ArchType, profile, bootImage android.Path, appImage, generateDM bool) {
+ arch android.ArchType, profile, bootImage android.Path, bootImageDeps android.Paths, appImage, generateDM bool) {
// HACK: make soname in Soong-generated .odex files match Make.
base := filepath.Base(module.DexLocation)
@@ -353,7 +354,7 @@
Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", module.PreoptBootClassPathDexLocations, ":").
Flag("${class_loader_context_arg}").
Flag("${stored_class_loader_context_arg}").
- FlagWithArg("--boot-image=", bootImageLocation).Implicit(bootImage).
+ FlagWithArg("--boot-image=", bootImageLocation).Implicits(bootImageDeps).
FlagWithInput("--dex-file=", module.DexPath).
FlagWithArg("--dex-location=", dexLocationArg).
FlagWithOutput("--oat-file=", odexPath).ImplicitOutput(vdexPath).
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 0402f87..7f1fe42 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -38,6 +38,7 @@
LibraryPaths: nil,
Archs: []android.ArchType{android.Arm},
DexPreoptImages: android.Paths{android.PathForTesting("system/framework/arm/boot.art")},
+ DexPreoptImagesDeps: []android.Paths{android.Paths{}},
PreoptBootClassPathDexFiles: nil,
PreoptBootClassPathDexLocations: nil,
PreoptExtractedApk: false,
diff --git a/finder/finder_test.go b/finder/finder_test.go
index 29711fc..f6d0aa9 100644
--- a/finder/finder_test.go
+++ b/finder/finder_test.go
@@ -891,8 +891,8 @@
IncludeFiles: []string{"findme.txt"},
},
)
- foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
filesystem.Clock.Tick()
+ foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
finder.Shutdown()
// check the response of the first finder
assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
@@ -1522,8 +1522,8 @@
IncludeFiles: []string{"hi.txt"},
},
)
- foundPaths := finder.FindAll()
filesystem.Clock.Tick()
+ foundPaths := finder.FindAll()
finder.Shutdown()
// check results
assertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt"})
@@ -1583,8 +1583,8 @@
IncludeFiles: []string{"hi.txt"},
},
)
- foundPaths := finder.FindAll()
filesystem.Clock.Tick()
+ foundPaths := finder.FindAll()
finder.Shutdown()
allPaths := []string{"/tmp/hi.txt", "/tmp/a/hi.txt", "/tmp/a/a/hi.txt", "/tmp/b/hi.txt"}
// check results
@@ -1629,8 +1629,8 @@
IncludeFiles: []string{"hi.txt"},
},
)
- foundPaths := finder.FindAll()
filesystem.Clock.Tick()
+ foundPaths := finder.FindAll()
finder.Shutdown()
// check results
assertSameResponse(t, foundPaths, []string{"/tmp/hi.txt"})
@@ -1650,8 +1650,8 @@
IncludeFiles: []string{"hi.txt"},
},
)
- foundPaths := finder.FindAll()
filesystem.Clock.Tick()
+ foundPaths := finder.FindAll()
finder.Shutdown()
// check results
assertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt"})
diff --git a/genrule/genrule.go b/genrule/genrule.go
index b0657ff..411da05 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -284,12 +284,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 +304,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 +327,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 +337,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)
}
diff --git a/jar/Android.bp b/jar/Android.bp
index 6c2e60e..2563474 100644
--- a/jar/Android.bp
+++ b/jar/Android.bp
@@ -18,8 +18,10 @@
srcs: [
"jar.go",
],
+ testSrcs: [
+ "jar_test.go",
+ ],
deps: [
"android-archive-zip",
],
}
-
diff --git a/jar/jar.go b/jar/jar.go
index fa0e693..a8f06a4 100644
--- a/jar/jar.go
+++ b/jar/jar.go
@@ -17,9 +17,12 @@
import (
"bytes"
"fmt"
+ "io"
"os"
"strings"
+ "text/scanner"
"time"
+ "unicode"
"android/soong/third_party/zip"
)
@@ -112,3 +115,111 @@
return finalBytes, nil
}
+
+var javaIgnorableIdentifier = &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {0x00, 0x08, 1},
+ {0x0e, 0x1b, 1},
+ {0x7f, 0x9f, 1},
+ },
+ LatinOffset: 3,
+}
+
+func javaIdentRune(ch rune, i int) bool {
+ if unicode.IsLetter(ch) {
+ return true
+ }
+ if unicode.IsDigit(ch) && i > 0 {
+ return true
+ }
+
+ if unicode.In(ch,
+ unicode.Nl, // letter number
+ unicode.Sc, // currency symbol
+ unicode.Pc, // connecting punctuation
+ ) {
+ return true
+ }
+
+ if unicode.In(ch,
+ unicode.Cf, // format
+ unicode.Mc, // combining mark
+ unicode.Mn, // non-spacing mark
+ javaIgnorableIdentifier,
+ ) && i > 0 {
+ return true
+ }
+
+ return false
+}
+
+// JavaPackage parses the package out of a java source file by looking for the package statement, or the first valid
+// non-package statement, in which case it returns an empty string for the package.
+func JavaPackage(r io.Reader, src string) (string, error) {
+ var s scanner.Scanner
+ var sErr error
+
+ s.Init(r)
+ s.Filename = src
+ s.Error = func(s *scanner.Scanner, msg string) {
+ sErr = fmt.Errorf("error parsing %q: %s", src, msg)
+ }
+ s.IsIdentRune = javaIdentRune
+
+ tok := s.Scan()
+ if sErr != nil {
+ return "", sErr
+ }
+ if tok == scanner.Ident {
+ switch s.TokenText() {
+ case "package":
+ // Nothing
+ case "import":
+ // File has no package statement, first keyword is an import
+ return "", nil
+ case "class", "enum", "interface":
+ // File has no package statement, first keyword is a type declaration
+ return "", nil
+ case "public", "protected", "private", "abstract", "static", "final", "strictfp":
+ // File has no package statement, first keyword is a modifier
+ return "", nil
+ case "module", "open":
+ // File has no package statement, first keyword is a module declaration
+ return "", nil
+ default:
+ return "", fmt.Errorf(`expected first token of java file to be "package", got %q`, s.TokenText())
+ }
+ } else if tok == '@' {
+ // File has no package statement, first token is an annotation
+ return "", nil
+ } else if tok == scanner.EOF {
+ // File no package statement, it has no non-whitespace non-comment tokens
+ return "", nil
+ } else {
+ return "", fmt.Errorf(`expected first token of java file to be "package", got %q`, s.TokenText())
+ }
+
+ var pkg string
+ for {
+ tok = s.Scan()
+ if sErr != nil {
+ return "", sErr
+ }
+ if tok != scanner.Ident {
+ return "", fmt.Errorf(`expected "package <package>;", got "package %s%s"`, pkg, s.TokenText())
+ }
+ pkg += s.TokenText()
+
+ tok = s.Scan()
+ if sErr != nil {
+ return "", sErr
+ }
+ if tok == ';' {
+ return pkg, nil
+ } else if tok == '.' {
+ pkg += "."
+ } else {
+ return "", fmt.Errorf(`expected "package <package>;", got "package %s%s"`, pkg, s.TokenText())
+ }
+ }
+}
diff --git a/jar/jar_test.go b/jar/jar_test.go
new file mode 100644
index 0000000..c92011e
--- /dev/null
+++ b/jar/jar_test.go
@@ -0,0 +1,182 @@
+// Copyright 2017 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 jar
+
+import (
+ "bytes"
+ "io"
+ "testing"
+)
+
+func TestGetJavaPackage(t *testing.T) {
+ type args struct {
+ r io.Reader
+ src string
+ }
+ tests := []struct {
+ name string
+ in string
+ want string
+ wantErr bool
+ }{
+ {
+ name: "simple",
+ in: "package foo.bar;",
+ want: "foo.bar",
+ },
+ {
+ name: "comment",
+ in: "/* test */\npackage foo.bar;",
+ want: "foo.bar",
+ },
+ {
+ name: "no package",
+ in: "import foo.bar;",
+ want: "",
+ },
+ {
+ name: "missing semicolon error",
+ in: "package foo.bar",
+ wantErr: true,
+ },
+ {
+ name: "parser error",
+ in: "/*",
+ wantErr: true,
+ },
+ {
+ name: "parser ident error",
+ in: "package 0foo.bar;",
+ wantErr: true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ buf := bytes.NewBufferString(tt.in)
+ got, err := JavaPackage(buf, "<test>")
+ if (err != nil) != tt.wantErr {
+ t.Errorf("JavaPackage() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("JavaPackage() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func Test_javaIdentRune(t *testing.T) {
+ // runes that should be valid anywhere in an identifier
+ validAnywhere := []rune{
+ // letters, $, _
+ 'a',
+ 'A',
+ '$',
+ '_',
+
+ // assorted unicode
+ '𐐀',
+ '𐐨',
+ 'Dž',
+ 'ῼ',
+ 'ʰ',
+ '゚',
+ 'ƻ',
+ '㡢',
+ '₩',
+ '_',
+ 'Ⅰ',
+ '𐍊',
+ }
+
+ // runes that should be invalid as the first rune in an identifier, but valid anywhere else
+ validAfterFirst := []rune{
+ // digits
+ '0',
+
+ // assorted unicode
+ '᥍',
+ '𝟎',
+ 'ྂ',
+ '𝆀',
+
+ // control characters
+ '\x00',
+ '\b',
+ '\u000e',
+ '\u001b',
+ '\u007f',
+ '\u009f',
+ '\u00ad',
+ 0xE007F,
+
+ // zero width space
+ '\u200b',
+ }
+
+ // runes that should never be valid in an identifier
+ invalid := []rune{
+ ';',
+ 0x110000,
+ }
+
+ validFirst := validAnywhere
+ invalidFirst := append(validAfterFirst, invalid...)
+ validPart := append(validAnywhere, validAfterFirst...)
+ invalidPart := invalid
+
+ check := func(t *testing.T, ch rune, i int, want bool) {
+ t.Helper()
+ if got := javaIdentRune(ch, i); got != want {
+ t.Errorf("javaIdentRune() = %v, want %v", got, want)
+ }
+ }
+
+ t.Run("first", func(t *testing.T) {
+ t.Run("valid", func(t *testing.T) {
+ for _, ch := range validFirst {
+ t.Run(string(ch), func(t *testing.T) {
+ check(t, ch, 0, true)
+ })
+ }
+ })
+
+ t.Run("invalid", func(t *testing.T) {
+ for _, ch := range invalidFirst {
+ t.Run(string(ch), func(t *testing.T) {
+ check(t, ch, 0, false)
+ })
+ }
+ })
+ })
+
+ t.Run("part", func(t *testing.T) {
+ t.Run("valid", func(t *testing.T) {
+ for _, ch := range validPart {
+ t.Run(string(ch), func(t *testing.T) {
+ check(t, ch, 1, true)
+ })
+ }
+ })
+
+ t.Run("invalid", func(t *testing.T) {
+ for _, ch := range invalidPart {
+ t.Run(string(ch), func(t *testing.T) {
+ check(t, ch, 1, false)
+ })
+ }
+ })
+ })
+}
diff --git a/java/aapt2.go b/java/aapt2.go
index a815160..f0eb99c 100644
--- a/java/aapt2.go
+++ b/java/aapt2.go
@@ -55,12 +55,14 @@
var aapt2CompileRule = pctx.AndroidStaticRule("aapt2Compile",
blueprint.RuleParams{
- Command: `${config.Aapt2Cmd} compile -o $outDir $cFlags --legacy $in`,
+ Command: `${config.Aapt2Cmd} compile -o $outDir $cFlags $in`,
CommandDeps: []string{"${config.Aapt2Cmd}"},
},
"outDir", "cFlags")
-func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Paths) android.WritablePaths {
+func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Paths,
+ flags []string) android.WritablePaths {
+
shards := shardPaths(paths, AAPT2_SHARD_SIZE)
ret := make(android.WritablePaths, 0, len(paths))
@@ -81,9 +83,7 @@
Outputs: outPaths,
Args: map[string]string{
"outDir": android.PathForModuleOut(ctx, "aapt2", dir.String()).String(),
- // Always set --pseudo-localize, it will be stripped out later for release
- // builds that don't want it.
- "cFlags": "--pseudo-localize",
+ "cFlags": strings.Join(flags, " "),
},
})
}
@@ -97,14 +97,16 @@
var aapt2CompileZipRule = pctx.AndroidStaticRule("aapt2CompileZip",
blueprint.RuleParams{
Command: `${config.ZipSyncCmd} -d $resZipDir $zipSyncFlags $in && ` +
- `${config.Aapt2Cmd} compile -o $out $cFlags --legacy --dir $resZipDir`,
+ `${config.Aapt2Cmd} compile -o $out $cFlags --dir $resZipDir`,
CommandDeps: []string{
"${config.Aapt2Cmd}",
"${config.ZipSyncCmd}",
},
}, "cFlags", "resZipDir", "zipSyncFlags")
-func aapt2CompileZip(ctx android.ModuleContext, flata android.WritablePath, zip android.Path, zipPrefix string) {
+func aapt2CompileZip(ctx android.ModuleContext, flata android.WritablePath, zip android.Path, zipPrefix string,
+ flags []string) {
+
if zipPrefix != "" {
zipPrefix = "--zip-prefix " + zipPrefix
}
@@ -114,9 +116,7 @@
Input: zip,
Output: flata,
Args: map[string]string{
- // Always set --pseudo-localize, it will be stripped out later for release
- // builds that don't want it.
- "cFlags": "--pseudo-localize",
+ "cFlags": strings.Join(flags, " "),
"resZipDir": android.PathForModuleOut(ctx, "aapt2", "reszip", flata.Base()).String(),
"zipSyncFlags": zipPrefix,
},
diff --git a/java/aar.go b/java/aar.go
index 47f6e5f..ce3d126 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -17,6 +17,7 @@
import (
"android/soong/android"
"fmt"
+ "path/filepath"
"strings"
"github.com/google/blueprint"
@@ -80,6 +81,7 @@
rTxt android.Path
extraAaptPackagesFile android.Path
mergedManifestFile android.Path
+ noticeFile android.OptionalPath
isLibrary bool
useEmbeddedNativeLibs bool
useEmbeddedDex bool
@@ -111,8 +113,9 @@
return a.transitiveManifestPaths
}
-func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext sdkContext, manifestPath android.Path) (flags []string,
- deps android.Paths, resDirs, overlayDirs []globbedResourceDir, rroDirs []rroDir, resZips android.Paths) {
+func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext sdkContext,
+ manifestPath android.Path) (compileFlags, linkFlags []string, linkDeps android.Paths,
+ resDirs, overlayDirs []globbedResourceDir, rroDirs []rroDir, resZips android.Paths) {
hasVersionCode := false
hasVersionName := false
@@ -124,8 +127,6 @@
}
}
- var linkFlags []string
-
// Flags specified in Android.bp
linkFlags = append(linkFlags, a.aaptProperties.Aaptflags...)
@@ -136,8 +137,6 @@
resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res")
resourceZips := android.PathsForModuleSrc(ctx, a.aaptProperties.Resource_zips)
- var linkDeps android.Paths
-
// Glob directories into lists of paths
for _, dir := range resourceDirs {
resDirs = append(resDirs, globbedResourceDir{
@@ -154,10 +153,16 @@
assetFiles = append(assetFiles, androidResourceGlob(ctx, dir)...)
}
+ assetDirStrings := assetDirs.Strings()
+ if a.noticeFile.Valid() {
+ assetDirStrings = append(assetDirStrings, filepath.Dir(a.noticeFile.Path().String()))
+ assetFiles = append(assetFiles, a.noticeFile.Path())
+ }
+
linkFlags = append(linkFlags, "--manifest "+manifestPath.String())
linkDeps = append(linkDeps, manifestPath)
- linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirs.Strings(), "-A "))
+ linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirStrings, "-A "))
linkDeps = append(linkDeps, assetFiles...)
// SDK version flags
@@ -185,7 +190,13 @@
linkFlags = append(linkFlags, "--version-name ", versionName)
}
- return linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resourceZips
+ linkFlags, compileFlags = android.FilterList(linkFlags, []string{"--legacy"})
+
+ // Always set --pseudo-localize, it will be stripped out later for release
+ // builds that don't want it.
+ compileFlags = append(compileFlags, "--pseudo-localize")
+
+ return compileFlags, linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resourceZips
}
func (a *aapt) deps(ctx android.BottomUpMutatorContext, sdkDep sdkDep) {
@@ -220,7 +231,7 @@
a.mergedManifestFile = manifestPath
}
- linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resZips := a.aapt2Flags(ctx, sdkContext, manifestPath)
+ compileFlags, linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resZips := a.aapt2Flags(ctx, sdkContext, manifestPath)
rroDirs = append(rroDirs, staticRRODirs...)
linkFlags = append(linkFlags, libFlags...)
@@ -239,12 +250,12 @@
var compiledResDirs []android.Paths
for _, dir := range resDirs {
- compiledResDirs = append(compiledResDirs, aapt2Compile(ctx, dir.dir, dir.files).Paths())
+ compiledResDirs = append(compiledResDirs, aapt2Compile(ctx, dir.dir, dir.files, compileFlags).Paths())
}
for i, zip := range resZips {
flata := android.PathForModuleOut(ctx, fmt.Sprintf("reszip.%d.flata", i))
- aapt2CompileZip(ctx, flata, zip, "")
+ aapt2CompileZip(ctx, flata, zip, "", compileFlags)
compiledResDirs = append(compiledResDirs, android.Paths{flata})
}
@@ -273,7 +284,7 @@
}
for _, dir := range overlayDirs {
- compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files).Paths()...)
+ compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files, compileFlags).Paths()...)
}
var splitPackages android.WritablePaths
@@ -499,7 +510,7 @@
}
func (a *AARImport) sdkVersion() string {
- return String(a.properties.Sdk_version)
+ return proptools.StringDefault(a.properties.Sdk_version, defaultSdkVersion(a))
}
func (a *AARImport) minSdkVersion() string {
@@ -513,10 +524,6 @@
return a.sdkVersion()
}
-func (a *AARImport) noFrameworkLibs() bool {
- return false
-}
-
var _ AndroidLibraryDependency = (*AARImport)(nil)
func (a *AARImport) ExportPackage() android.Path {
@@ -598,9 +605,12 @@
},
})
+ // Always set --pseudo-localize, it will be stripped out later for release
+ // builds that don't want it.
+ compileFlags := []string{"--pseudo-localize"}
compiledResDir := android.PathForModuleOut(ctx, "flat-res")
flata := compiledResDir.Join(ctx, "gen_res.flata")
- aapt2CompileZip(ctx, flata, aar, "res")
+ aapt2CompileZip(ctx, flata, aar, "res", compileFlags)
a.exportPackage = android.PathForModuleOut(ctx, "package-res.apk")
srcJar := android.PathForModuleGen(ctx, "R.jar")
diff --git a/java/androidmk.go b/java/androidmk.go
index 5491b3e..90fdd0f 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -97,14 +97,6 @@
if library.proguardDictionary != nil {
fmt.Fprintln(w, "LOCAL_SOONG_PROGUARD_DICT :=", library.proguardDictionary.String())
}
-
- // Temporary hack: export sources used to compile framework.jar to Make
- // to be used for droiddoc
- // TODO(ccross): remove this once droiddoc is in soong
- if (library.Name() == "framework") || (library.Name() == "framework-annotation-proc") {
- fmt.Fprintln(w, "SOONG_FRAMEWORK_SRCS :=", strings.Join(library.compiledJavaSrcs.Strings(), " "))
- fmt.Fprintln(w, "SOONG_FRAMEWORK_SRCJARS :=", strings.Join(library.compiledSrcJars.Strings(), " "))
- }
},
},
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
@@ -335,6 +327,18 @@
install := "$(LOCAL_MODULE_PATH)/" + strings.TrimSuffix(app.installApkName, ".apk") + split.suffix + ".apk"
fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED +=", split.path.String()+":"+install)
}
+ if app.noticeOutputs.Merged.Valid() {
+ fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
+ app.installApkName, app.noticeOutputs.Merged.String(), app.installApkName+"_NOTICE")
+ }
+ if app.noticeOutputs.TxtOutput.Valid() {
+ fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
+ app.installApkName, app.noticeOutputs.TxtOutput.String(), app.installApkName+"_NOTICE.txt")
+ }
+ if app.noticeOutputs.HtmlOutput.Valid() {
+ fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
+ app.installApkName, app.noticeOutputs.HtmlOutput.String(), app.installApkName+"_NOTICE.html")
+ }
},
},
}
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 cab97de..a679e88 100644
--- a/java/app.go
+++ b/java/app.go
@@ -19,6 +19,7 @@
import (
"path/filepath"
"reflect"
+ "sort"
"strings"
"github.com/google/blueprint"
@@ -29,8 +30,7 @@
"android/soong/tradefed"
)
-var supportedDpis = [...]string{"Ldpi", "Mdpi", "Hdpi", "Xhdpi", "Xxhdpi", "Xxxhdpi"}
-var dpiVariantsStruct reflect.Type
+var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"}
func init() {
android.RegisterModuleType("android_app", AndroidAppFactory)
@@ -39,22 +39,6 @@
android.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
android.RegisterModuleType("override_android_app", OverrideAndroidAppModuleFactory)
android.RegisterModuleType("android_app_import", AndroidAppImportFactory)
-
- // Dynamically construct a struct for the dpi_variants property in android_app_import.
- perDpiStruct := reflect.StructOf([]reflect.StructField{
- {
- Name: "Apk",
- Type: reflect.TypeOf((*string)(nil)),
- },
- })
- dpiVariantsFields := make([]reflect.StructField, len(supportedDpis))
- for i, dpi := range supportedDpis {
- dpiVariantsFields[i] = reflect.StructField{
- Name: string(dpi),
- Type: perDpiStruct,
- }
- }
- dpiVariantsStruct = reflect.StructOf(dpiVariantsFields)
}
// AndroidManifest.xml merging
@@ -103,6 +87,10 @@
// Use_embedded_native_libs still selects whether they are stored uncompressed and aligned or compressed.
// True for android_test* modules.
AlwaysPackageNativeLibs bool `blueprint:"mutated"`
+
+ // If set, find and merge all NOTICE files that this module and its dependencies have and store
+ // it in the APK as an asset.
+ Embed_notices *bool
}
// android_app properties that can be overridden by override_android_app
@@ -136,6 +124,8 @@
installApkName string
additionalAaptFlags []string
+
+ noticeOutputs android.NoticeOutputs
}
func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths {
@@ -224,15 +214,16 @@
return true
}
- if ctx.Config().UnbundledBuild() {
- return false
- }
-
- // Uncompress dex in APKs of privileged apps
+ // Uncompress dex in APKs of privileged apps (even for unbundled builds, they may
+ // be preinstalled as prebuilts).
if ctx.Config().UncompressPrivAppDex() && Bool(a.appProperties.Privileged) {
return true
}
+ if ctx.Config().UnbundledBuild() {
+ return false
+ }
+
return shouldUncompressDex(ctx, &a.dexpreopter)
}
@@ -351,6 +342,49 @@
return jniJarFile
}
+func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext, installDir android.OutputPath) {
+ // Collect NOTICE files from all dependencies.
+ seenModules := make(map[android.Module]bool)
+ noticePathSet := make(map[android.Path]bool)
+
+ ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
+ // Have we already seen this?
+ if _, ok := seenModules[child]; ok {
+ return false
+ }
+ seenModules[child] = true
+
+ // Skip host modules.
+ if child.Target().Os.Class == android.Host || child.Target().Os.Class == android.HostCross {
+ return false
+ }
+
+ path := child.(android.Module).NoticeFile()
+ if path.Valid() {
+ noticePathSet[path.Path()] = true
+ }
+ return true
+ })
+
+ // If the app has one, add it too.
+ if a.NoticeFile().Valid() {
+ noticePathSet[a.NoticeFile().Path()] = true
+ }
+
+ if len(noticePathSet) == 0 {
+ return
+ }
+ var noticePaths []android.Path
+ for path := range noticePathSet {
+ noticePaths = append(noticePaths, path)
+ }
+ sort.Slice(noticePaths, func(i, j int) bool {
+ return noticePaths[i].String() < noticePaths[j].String()
+ })
+
+ a.noticeOutputs = android.BuildNoticeOutput(ctx, installDir, a.installApkName+".apk", noticePaths)
+}
+
// Reads and prepends a main cert from the default cert dir if it hasn't been set already, i.e. it
// isn't a cert module reference. Also checks and enforces system cert restriction if applicable.
func processMainCert(m android.ModuleBase, certPropValue string, certificates []Certificate, ctx android.ModuleContext) []Certificate {
@@ -391,6 +425,21 @@
// Check if the install APK name needs to be overridden.
a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Name())
+ var installDir android.OutputPath
+ if ctx.ModuleName() == "framework-res" {
+ // framework-res.apk is installed as system/framework/framework-res.apk
+ installDir = android.PathForModuleInstall(ctx, "framework")
+ } else if Bool(a.appProperties.Privileged) {
+ installDir = android.PathForModuleInstall(ctx, "priv-app", a.installApkName)
+ } else {
+ installDir = android.PathForModuleInstall(ctx, "app", a.installApkName)
+ }
+
+ a.noticeBuildActions(ctx, installDir)
+ if Bool(a.appProperties.Embed_notices) || ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") {
+ a.aapt.noticeFile = a.noticeOutputs.HtmlGzOutput
+ }
+
// Process all building blocks, from AAPT to certificates.
a.aaptBuildActions(ctx)
@@ -432,16 +481,6 @@
a.bundleFile = bundleFile
// Install the app package.
- var installDir android.OutputPath
- if ctx.ModuleName() == "framework-res" {
- // framework-res.apk is installed as system/framework/framework-res.apk
- installDir = android.PathForModuleInstall(ctx, "framework")
- } else if Bool(a.appProperties.Privileged) {
- installDir = android.PathForModuleInstall(ctx, "priv-app", a.installApkName)
- } else {
- installDir = android.PathForModuleInstall(ctx, "app", a.installApkName)
- }
-
ctx.InstallFile(installDir, a.installApkName+".apk", a.outputFile)
for _, split := range a.aapt.splits {
ctx.InstallFile(installDir, a.installApkName+"_"+split.suffix+".apk", split.path)
@@ -688,7 +727,8 @@
android.DefaultableModuleBase
prebuilt android.Prebuilt
- properties AndroidAppImportProperties
+ properties AndroidAppImportProperties
+ dpiVariants interface{}
outputFile android.Path
certificate *Certificate
@@ -700,27 +740,7 @@
type AndroidAppImportProperties struct {
// A prebuilt apk to import
- Apk string
-
- // Per-DPI settings. This property makes it possible to specify a different source apk path for
- // each DPI.
- //
- // Example:
- //
- // android_app_import {
- // name: "example_import",
- // apk: "prebuilts/example.apk",
- // dpi_variants: {
- // mdpi: {
- // apk: "prebuilts/example_mdpi.apk",
- // },
- // xhdpi: {
- // apk: "prebuilts/example_xhdpi.apk",
- // },
- // },
- // certificate: "PRESIGNED",
- // }
- Dpi_variants interface{}
+ Apk *string
// The name of a certificate in the default certificate directory, blank to use the default
// product certificate, or an android_app_certificate module name in the form ":module".
@@ -743,39 +763,43 @@
Overrides []string
}
-func getApkPathForDpi(dpiVariantsValue reflect.Value, dpi string) string {
- dpiField := dpiVariantsValue.FieldByName(proptools.FieldNameForProperty(dpi))
- if !dpiField.IsValid() {
- return ""
+// Chooses a source APK path to use based on the module and product specs.
+func (a *AndroidAppImport) updateSrcApkPath(ctx android.LoadHookContext) {
+ config := ctx.Config()
+
+ dpiProps := reflect.ValueOf(a.dpiVariants).Elem().FieldByName("Dpi_variants")
+ // Try DPI variant matches in the reverse-priority order so that the highest priority match
+ // 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")
+ }
}
- apkValue := dpiField.FieldByName("Apk").Elem()
- if apkValue.IsValid() {
- return apkValue.String()
+ if config.ProductAAPTPreferredConfig() != "" {
+ dpi := config.ProductAAPTPreferredConfig()
+ if inList(dpi, supportedDpis) {
+ MergePropertiesFromVariant(ctx, &a.properties, dpiProps, dpi, "dpi_variants")
+ }
}
- return ""
}
-// Chooses a source APK path to use based on the module's per-DPI settings and the product config.
-func (a *AndroidAppImport) getSrcApkPath(ctx android.ModuleContext) string {
- config := ctx.Config()
- dpiVariantsValue := reflect.ValueOf(a.properties.Dpi_variants).Elem()
- if !dpiVariantsValue.IsValid() {
- return a.properties.Apk
- }
- // Match PRODUCT_AAPT_PREF_CONFIG first and then PRODUCT_AAPT_PREBUILT_DPI.
- if config.ProductAAPTPreferredConfig() != "" {
- if apk := getApkPathForDpi(dpiVariantsValue, config.ProductAAPTPreferredConfig()); apk != "" {
- return apk
- }
- }
- for _, dpi := range config.ProductAAPTPrebuiltDPI() {
- if apk := getApkPathForDpi(dpiVariantsValue, dpi); apk != "" {
- return apk
- }
+func MergePropertiesFromVariant(ctx android.BaseModuleContext,
+ dst interface{}, variantGroup reflect.Value, variant, variantGroupPath string) {
+ src := variantGroup.FieldByName(proptools.FieldNameForProperty(variant))
+ if !src.IsValid() {
+ ctx.ModuleErrorf("field %q does not exist", variantGroupPath+"."+variant)
}
- // No match. Use the generic one.
- return a.properties.Apk
+ err := proptools.ExtendMatchingProperties([]interface{}{dst}, src.Interface(), nil, proptools.OrderAppend)
+ if err != nil {
+ if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+ ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+ } else {
+ panic(err)
+ }
+ }
}
func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -792,7 +816,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'").
@@ -819,7 +843,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'").
@@ -840,8 +864,7 @@
// TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
// TODO: LOCAL_PACKAGE_SPLITS
- var srcApk android.Path
- srcApk = android.PathForModuleSrc(ctx, a.getSrcApkPath(ctx))
+ srcApk := a.prebuilt.SingleSourcePath(ctx)
if a.usesLibrary.enforceUsesLibraries() {
srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
@@ -903,16 +926,56 @@
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()
+
+ dpiFields := make([]reflect.StructField, len(supportedDpis))
+ for i, dpi := range supportedDpis {
+ dpiFields[i] = reflect.StructField{
+ Name: proptools.FieldNameForProperty(dpi),
+ Type: props,
+ }
+ }
+ dpiStruct := reflect.StructOf(dpiFields)
+ a.dpiVariants = reflect.New(reflect.StructOf([]reflect.StructField{
+ {
+ Name: "Dpi_variants",
+ Type: dpiStruct,
+ },
+ })).Interface()
+ a.AddProperties(a.dpiVariants)
+}
+
// android_app_import imports a prebuilt apk with additional processing specified in the module.
+// DPI-specific apk source files can be specified using dpi_variants. Example:
+//
+// android_app_import {
+// name: "example_import",
+// apk: "prebuilts/example.apk",
+// dpi_variants: {
+// mdpi: {
+// apk: "prebuilts/example_mdpi.apk",
+// },
+// xhdpi: {
+// apk: "prebuilts/example_xhdpi.apk",
+// },
+// },
+// certificate: "PRESIGNED",
+// }
func AndroidAppImportFactory() android.Module {
module := &AndroidAppImport{}
- module.properties.Dpi_variants = reflect.New(dpiVariantsStruct).Interface()
module.AddProperties(&module.properties)
module.AddProperties(&module.dexpreoptProperties)
module.AddProperties(&module.usesLibrary.usesLibraryProperties)
+ module.addDpiVariants()
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ module.updateSrcApkPath(ctx)
+ })
InitJavaModule(module, android.DeviceSupported)
- android.InitSingleSourcePrebuiltModule(module, &module.properties.Apk)
+ android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
return module
}
@@ -1004,7 +1067,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 27802cd..32de019 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)
@@ -176,7 +176,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")
@@ -388,7 +388,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) {
@@ -515,7 +515,7 @@
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)
@@ -546,81 +546,8 @@
}
}
-func TestJNIABI_no_framework_libs_true(t *testing.T) {
- ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
- cc_library {
- name: "libjni",
- system_shared_libs: [],
- stl: "none",
- }
-
- android_test {
- name: "test",
- no_framework_libs: true,
- jni_libs: ["libjni"],
- }
-
- android_test {
- name: "test_first",
- no_framework_libs: true,
- compile_multilib: "first",
- jni_libs: ["libjni"],
- }
-
- android_test {
- name: "test_both",
- no_framework_libs: true,
- compile_multilib: "both",
- jni_libs: ["libjni"],
- }
-
- android_test {
- name: "test_32",
- no_framework_libs: true,
- compile_multilib: "32",
- jni_libs: ["libjni"],
- }
-
- android_test {
- name: "test_64",
- no_framework_libs: true,
- compile_multilib: "64",
- jni_libs: ["libjni"],
- }
- `)
-
- testCases := []struct {
- name string
- abis []string
- }{
- {"test", []string{"arm64-v8a"}},
- {"test_first", []string{"arm64-v8a"}},
- {"test_both", []string{"arm64-v8a", "armeabi-v7a"}},
- {"test_32", []string{"armeabi-v7a"}},
- {"test_64", []string{"arm64-v8a"}},
- }
-
- for _, test := range testCases {
- t.Run(test.name, func(t *testing.T) {
- app := ctx.ModuleForTests(test.name, "android_common")
- jniLibZip := app.Output("jnilibs.zip")
- var abis []string
- args := strings.Fields(jniLibZip.Args["jarArgs"])
- for i := 0; i < len(args); i++ {
- if args[i] == "-P" {
- abis = append(abis, filepath.Base(args[i+1]))
- i++
- }
- }
- if !reflect.DeepEqual(abis, test.abis) {
- t.Errorf("want abis %v, got %v", test.abis, abis)
- }
- })
- }
-}
-
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: [],
@@ -692,91 +619,8 @@
}
}
-func TestJNIPackaging_no_framework_libs_true(t *testing.T) {
- ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
- cc_library {
- name: "libjni",
- system_shared_libs: [],
- stl: "none",
- }
-
- android_app {
- name: "app",
- jni_libs: ["libjni"],
- }
-
- android_app {
- name: "app_noembed",
- jni_libs: ["libjni"],
- use_embedded_native_libs: false,
- }
-
- android_app {
- name: "app_embed",
- jni_libs: ["libjni"],
- use_embedded_native_libs: true,
- }
-
- android_test {
- name: "test",
- no_framework_libs: true,
- jni_libs: ["libjni"],
- }
-
- android_test {
- name: "test_noembed",
- no_framework_libs: true,
- jni_libs: ["libjni"],
- use_embedded_native_libs: false,
- }
-
- android_test_helper_app {
- name: "test_helper",
- no_framework_libs: true,
- jni_libs: ["libjni"],
- }
-
- android_test_helper_app {
- name: "test_helper_noembed",
- no_framework_libs: true,
- jni_libs: ["libjni"],
- use_embedded_native_libs: false,
- }
- `)
-
- testCases := []struct {
- name string
- packaged bool
- compressed bool
- }{
- {"app", false, false},
- {"app_noembed", false, false},
- {"app_embed", true, false},
- {"test", true, false},
- {"test_noembed", true, true},
- {"test_helper", true, false},
- {"test_helper_noembed", true, true},
- }
-
- for _, test := range testCases {
- t.Run(test.name, func(t *testing.T) {
- app := ctx.ModuleForTests(test.name, "android_common")
- jniLibZip := app.MaybeOutput("jnilibs.zip")
- if g, w := (jniLibZip.Rule != nil), test.packaged; g != w {
- t.Errorf("expected jni packaged %v, got %v", w, g)
- }
-
- if jniLibZip.Rule != nil {
- if g, w := !strings.Contains(jniLibZip.Args["jarArgs"], "-L 0"), test.compressed; g != w {
- t.Errorf("expected jni compressed %v, got %v", w, g)
- }
- }
- })
- }
-}
-
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: [],
@@ -930,7 +774,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")
@@ -988,7 +832,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")
@@ -1021,7 +865,7 @@
`
config := testConfig(nil)
config.TestProductVariables.ManifestPackageNameOverrides = []string{"foo:org.dandroid.bp"}
- ctx := testAppContext(config, bp, nil)
+ ctx := testAppContext(bp, nil)
run(t, ctx, config)
@@ -1035,7 +879,7 @@
}
func TestOverrideAndroidApp(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
android_app {
name: "foo",
srcs: ["a.java"],
@@ -1136,7 +980,7 @@
}
func TestOverrideAndroidAppDependency(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
android_app {
name: "foo",
srcs: ["a.java"],
@@ -1177,7 +1021,7 @@
}
func TestAndroidAppImport(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
android_app_import {
name: "foo",
apk: "prebuilts/apk/app.apk",
@@ -1206,7 +1050,7 @@
}
func TestAndroidAppImport_NoDexPreopt(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
android_app_import {
name: "foo",
apk: "prebuilts/apk/app.apk",
@@ -1227,7 +1071,7 @@
}
func TestAndroidAppImport_Presigned(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
android_app_import {
name: "foo",
apk: "prebuilts/apk/app.apk",
@@ -1294,7 +1138,7 @@
{
name: "AAPTPreferredConfig matches",
aaptPreferredConfig: proptools.StringPtr("xhdpi"),
- aaptPrebuiltDPI: []string{"xxhdpi", "lhdpi"},
+ aaptPrebuiltDPI: []string{"xxhdpi", "ldpi"},
expected: "prebuilts/apk/app_xhdpi.apk",
},
{
@@ -1322,7 +1166,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)
@@ -1339,7 +1183,7 @@
}
func TestStl(t *testing.T) {
- ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+ ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
cc_library {
name: "libjni",
}
@@ -1442,7 +1286,7 @@
config := testConfig(nil)
config.TestProductVariables.MissingUsesLibraries = []string{"baz"}
- ctx := testAppContext(config, bp, nil)
+ ctx := testAppContext(bp, nil)
run(t, ctx, config)
@@ -1552,3 +1396,185 @@
})
}
}
+
+func TestEmbedNotice(t *testing.T) {
+ ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ static_libs: ["javalib"],
+ jni_libs: ["libjni"],
+ notice: "APP_NOTICE",
+ embed_notices: true,
+ }
+
+ // No embed_notice flag
+ android_app {
+ name: "bar",
+ srcs: ["a.java"],
+ jni_libs: ["libjni"],
+ notice: "APP_NOTICE",
+ }
+
+ // No NOTICE files
+ android_app {
+ name: "baz",
+ srcs: ["a.java"],
+ embed_notices: true,
+ }
+
+ cc_library {
+ name: "libjni",
+ system_shared_libs: [],
+ stl: "none",
+ notice: "LIB_NOTICE",
+ }
+
+ java_library {
+ name: "javalib",
+ srcs: [
+ ":gen",
+ ],
+ }
+
+ genrule {
+ name: "gen",
+ tools: ["gentool"],
+ out: ["gen.java"],
+ notice: "GENRULE_NOTICE",
+ }
+
+ java_binary_host {
+ name: "gentool",
+ srcs: ["b.java"],
+ notice: "TOOL_NOTICE",
+ }
+ `)
+
+ // foo has NOTICE files to process, and embed_notices is true.
+ foo := ctx.ModuleForTests("foo", "android_common")
+ // verify merge notices rule.
+ mergeNotices := foo.Rule("mergeNoticesRule")
+ noticeInputs := mergeNotices.Inputs.Strings()
+ // TOOL_NOTICE should be excluded as it's a host module.
+ if len(mergeNotices.Inputs) != 3 {
+ t.Errorf("number of input notice files: expected = 3, actual = %q", noticeInputs)
+ }
+ if !inList("APP_NOTICE", noticeInputs) {
+ t.Errorf("APP_NOTICE is missing from notice files, %q", noticeInputs)
+ }
+ if !inList("LIB_NOTICE", noticeInputs) {
+ t.Errorf("LIB_NOTICE is missing from notice files, %q", noticeInputs)
+ }
+ if !inList("GENRULE_NOTICE", noticeInputs) {
+ t.Errorf("GENRULE_NOTICE is missing from notice files, %q", noticeInputs)
+ }
+ // aapt2 flags should include -A <NOTICE dir> so that its contents are put in the APK's /assets.
+ res := foo.Output("package-res.apk")
+ aapt2Flags := res.Args["flags"]
+ e := "-A " + buildDir + "/.intermediates/foo/android_common/NOTICE"
+ if !strings.Contains(aapt2Flags, e) {
+ t.Errorf("asset dir flag for NOTICE, %q is missing in aapt2 link flags, %q", e, aapt2Flags)
+ }
+
+ // bar has NOTICE files to process, but embed_notices is not set.
+ bar := ctx.ModuleForTests("bar", "android_common")
+ res = bar.Output("package-res.apk")
+ aapt2Flags = res.Args["flags"]
+ e = "-A " + buildDir + "/.intermediates/bar/android_common/NOTICE"
+ if strings.Contains(aapt2Flags, e) {
+ t.Errorf("bar shouldn't have the asset dir flag for NOTICE: %q", e)
+ }
+
+ // baz's embed_notice is true, but it doesn't have any NOTICE files.
+ baz := ctx.ModuleForTests("baz", "android_common")
+ res = baz.Output("package-res.apk")
+ aapt2Flags = res.Args["flags"]
+ e = "-A " + buildDir + "/.intermediates/baz/android_common/NOTICE"
+ if strings.Contains(aapt2Flags, e) {
+ t.Errorf("baz shouldn't have the asset dir flag for NOTICE: %q", e)
+ }
+}
+
+func TestUncompressDex(t *testing.T) {
+ testCases := []struct {
+ name string
+ bp string
+
+ uncompressedPlatform bool
+ uncompressedUnbundled bool
+ }{
+ {
+ name: "normal",
+ bp: `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ }
+ `,
+ uncompressedPlatform: true,
+ uncompressedUnbundled: false,
+ },
+ {
+ name: "use_embedded_dex",
+ bp: `
+ android_app {
+ name: "foo",
+ use_embedded_dex: true,
+ srcs: ["a.java"],
+ }
+ `,
+ uncompressedPlatform: true,
+ uncompressedUnbundled: true,
+ },
+ {
+ name: "privileged",
+ bp: `
+ android_app {
+ name: "foo",
+ privileged: true,
+ srcs: ["a.java"],
+ }
+ `,
+ uncompressedPlatform: true,
+ uncompressedUnbundled: true,
+ },
+ }
+
+ test := func(t *testing.T, bp string, want bool, unbundled bool) {
+ t.Helper()
+
+ config := testConfig(nil)
+ if unbundled {
+ config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+ }
+
+ ctx := testAppContext(bp, nil)
+
+ run(t, ctx, config)
+
+ foo := ctx.ModuleForTests("foo", "android_common")
+ dex := foo.Rule("r8")
+ uncompressedInDexJar := strings.Contains(dex.Args["zipFlags"], "-L 0")
+ aligned := foo.MaybeRule("zipalign").Rule != nil
+
+ if uncompressedInDexJar != want {
+ t.Errorf("want uncompressed in dex %v, got %v", want, uncompressedInDexJar)
+ }
+
+ if aligned != want {
+ t.Errorf("want aligned %v, got %v", want, aligned)
+ }
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Run("platform", func(t *testing.T) {
+ test(t, tt.bp, tt.uncompressedPlatform, false)
+ })
+ t.Run("unbundled", func(t *testing.T) {
+ test(t, tt.bp, tt.uncompressedUnbundled, true)
+ })
+ })
+ }
+}
diff --git a/java/builder.go b/java/builder.go
index e1a912b..22eff7c 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -153,7 +153,7 @@
classpath classpath
processorPath classpath
processor string
- systemModules classpath
+ systemModules *systemModules
aidlFlags string
aidlDeps android.Paths
javaVersion string
@@ -248,8 +248,9 @@
var bootClasspath string
if flags.javaVersion == "1.9" {
- deps = append(deps, flags.systemModules...)
- 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() {
@@ -410,7 +411,7 @@
})
}
-type classpath []android.Path
+type classpath android.Paths
func (x *classpath) FormJavaClassPath(optName string) string {
if optName != "" && !strings.HasSuffix(optName, "=") && !strings.HasSuffix(optName, " ") {
@@ -423,21 +424,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 + strings.TrimSuffix((*x)[0].String(), "lib/modules")
- } else if forceEmpty {
- return optName + "none"
- } else {
- return ""
- }
-}
-
func (x *classpath) FormTurbineClasspath(optName string) []string {
if x == nil || *x == nil {
return nil
@@ -465,3 +451,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 6ade649..6a0a10a 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -47,12 +47,17 @@
}
)
+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")
pctx.StaticVariable("JavacHeapSize", "2048M")
pctx.StaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
- pctx.StaticVariable("DexFlags", "-JXX:+TieredCompilation -JXX:TieredStopAtLevel=1")
+ pctx.StaticVariable("DexFlags", "-JXX:OnError='cat hs_err_pid%p.log' -JXX:CICompilerCount=6 -JXX:+UseDynamicNumberOfGCThreads")
pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{
`-Xmaxerrs 9999999`,
@@ -69,8 +74,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)
@@ -154,3 +160,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 146bf6f..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")
@@ -126,16 +124,14 @@
java_library {
name: "device_module",
- no_framework_libs: true,
+ sdk_version: "core_platform",
srcs: ["b.java"],
java_resources: ["java-res/b/b"],
static_libs: ["host_for_device_module"],
}
`
- 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 23d2aa6..ed12fe6 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -132,8 +132,10 @@
}
var images android.Paths
+ var imagesDeps []android.Paths
for _, arch := range archs {
images = append(images, bootImage.images[arch])
+ imagesDeps = append(imagesDeps, bootImage.imagesDeps[arch])
}
dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
@@ -173,8 +175,9 @@
UsesLibraries: d.usesLibs,
LibraryPaths: d.libraryPaths,
- Archs: archs,
- DexPreoptImages: images,
+ Archs: archs,
+ DexPreoptImages: images,
+ DexPreoptImagesDeps: imagesDeps,
// We use the dex paths and dex locations of the default boot image, as it
// contains the full dexpreopt boot classpath. Other images may just contain a subset of
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 2a1a901..4ef5bcf 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -58,9 +58,32 @@
symbolsDir android.OutputPath
targets []android.Target
images map[android.ArchType]android.OutputPath
+ imagesDeps map[android.ArchType]android.Paths
zip android.WritablePath
}
+func (image bootImageConfig) moduleFiles(ctx android.PathContext, dir android.OutputPath, exts ...string) []android.OutputPath {
+ ret := make([]android.OutputPath, 0, len(image.modules)*len(exts))
+
+ // dex preopt on the bootclasspath produces multiple files. The first dex file
+ // is converted into to 'name'.art (to match the legacy assumption that 'name'.art
+ // exists), and the rest are converted to 'name'-<jar>.art.
+ // In addition, each .art file has an associated .oat and .vdex file, and an
+ // unstripped .oat file
+ for i, m := range image.modules {
+ name := image.name
+ if i != 0 {
+ name += "-" + m
+ }
+
+ for _, ext := range exts {
+ ret = append(ret, dir.Join(ctx, name+ext))
+ }
+ }
+
+ return ret
+}
+
type bootImage struct {
bootImageConfig
@@ -206,7 +229,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 ")
@@ -263,8 +286,6 @@
if profile != nil {
cmd.FlagWithArg("--compiler-filter=", "speed-profile")
cmd.FlagWithInput("--profile-file=", profile)
- } else if global.PreloadedClasses.Valid() {
- cmd.FlagWithInput("--image-classes=", global.PreloadedClasses.Path())
}
if global.DirtyImageObjects.Valid() {
@@ -302,49 +323,38 @@
installDir := filepath.Join("/system/framework", arch.String())
vdexInstallDir := filepath.Join("/system/framework")
- var extraFiles android.WritablePaths
var vdexInstalls android.RuleBuilderInstalls
var unstrippedInstalls android.RuleBuilderInstalls
var zipFiles android.WritablePaths
- // dex preopt on the bootclasspath produces multiple files. The first dex file
- // is converted into to 'name'.art (to match the legacy assumption that 'name'.art
- // exists), and the rest are converted to 'name'-<jar>.art.
- // In addition, each .art file has an associated .oat and .vdex file, and an
- // unstripped .oat file
- for i, m := range image.modules {
- name := image.name
- if i != 0 {
- name += "-" + m
- }
+ for _, artOrOat := range image.moduleFiles(ctx, outputDir, ".art", ".oat") {
+ cmd.ImplicitOutput(artOrOat)
+ zipFiles = append(zipFiles, artOrOat)
- art := outputDir.Join(ctx, name+".art")
- oat := outputDir.Join(ctx, name+".oat")
- vdex := outputDir.Join(ctx, name+".vdex")
- unstrippedOat := symbolsDir.Join(ctx, name+".oat")
+ // Install the .oat and .art files
+ rule.Install(artOrOat, filepath.Join(installDir, artOrOat.Base()))
+ }
- extraFiles = append(extraFiles, art, oat, vdex, unstrippedOat)
-
- zipFiles = append(zipFiles, art, oat, vdex)
-
- // Install the .oat and .art files.
- rule.Install(art, filepath.Join(installDir, art.Base()))
- rule.Install(oat, filepath.Join(installDir, oat.Base()))
+ for _, vdex := range image.moduleFiles(ctx, outputDir, ".vdex") {
+ cmd.ImplicitOutput(vdex)
+ zipFiles = append(zipFiles, vdex)
// The vdex files are identical between architectures, install them to a shared location. The Make rules will
// only use the install rules for one architecture, and will create symlinks into the architecture-specific
// directories.
vdexInstalls = append(vdexInstalls,
android.RuleBuilderInstall{vdex, filepath.Join(vdexInstallDir, vdex.Base())})
+ }
+
+ for _, unstrippedOat := range image.moduleFiles(ctx, symbolsDir, ".oat") {
+ cmd.ImplicitOutput(unstrippedOat)
// Install the unstripped oat files. The Make rules will put these in $(TARGET_OUT_UNSTRIPPED)
unstrippedInstalls = append(unstrippedInstalls,
android.RuleBuilderInstall{unstrippedOat, filepath.Join(installDir, unstrippedOat.Base())})
}
- cmd.ImplicitOutputs(extraFiles)
-
rule.Build(pctx, ctx, image.name+"JarsDexpreopt_"+arch.String(), "dexpreopt "+image.name+" jars "+arch.String())
// save output and installed files for makevars
@@ -362,7 +372,7 @@
func bootImageProfileRule(ctx android.SingletonContext, image *bootImage, missingDeps []string) android.WritablePath {
global := dexpreoptGlobalConfig(ctx)
- if !global.UseProfileForBootImage || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
+ if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
return nil
}
return ctx.Config().Once(bootImageProfileRuleKey, func() interface{} {
@@ -428,7 +438,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]).
@@ -496,6 +506,7 @@
for _, arch := range arches {
ctx.Strict("DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_"+current.name+"_"+arch.String(), current.vdexInstalls[arch].String())
ctx.Strict("DEXPREOPT_IMAGE_"+current.name+"_"+arch.String(), current.images[arch].String())
+ ctx.Strict("DEXPREOPT_IMAGE_DEPS_"+current.name+"_"+arch.String(), strings.Join(current.imagesDeps[arch].Strings(), " "))
ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+current.name+"_"+arch.String(), current.installs[arch].String())
ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+current.name+"_"+arch.String(), current.unstrippedInstalls[arch].String())
if current.zip != nil {
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index cbb52f1..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))
@@ -62,6 +62,7 @@
bootArt := dexpreoptBootJars.Output("boot.art")
expectedInputs := []string{
+ "dex_bootjars/boot.prof",
"dex_bootjars_input/foo.jar",
"dex_bootjars_input/bar.jar",
"dex_bootjars_input/baz.jar",
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index d903f45..4a4d6d5 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -49,7 +49,8 @@
return ctx.Config().Once(dexpreoptTestGlobalConfigKey, func() interface{} {
// Nope, return a config with preopting disabled
return globalConfigAndRaw{dexpreopt.GlobalConfig{
- DisablePreopt: true,
+ DisablePreopt: true,
+ DisableGenerateProfile: true,
}, nil}
})
}).(globalConfigAndRaw)
@@ -137,27 +138,35 @@
dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_bootjars")
symbolsDir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_bootjars_unstripped")
- images := make(map[android.ArchType]android.OutputPath)
zip := dir.Join(ctx, "boot.zip")
targets := dexpreoptTargets(ctx)
- for _, target := range targets {
- images[target.Arch.ArchType] = dir.Join(ctx,
- "system/framework", target.Arch.ArchType.String()).Join(ctx, "boot.art")
- }
-
- return bootImageConfig{
+ imageConfig := bootImageConfig{
name: "boot",
modules: nonUpdatableBootModules,
dexLocations: nonUpdatableBootLocations,
dexPaths: nonUpdatableBootDexPaths,
dir: dir,
symbolsDir: symbolsDir,
- images: images,
+ images: make(map[android.ArchType]android.OutputPath),
+ imagesDeps: make(map[android.ArchType]android.Paths),
targets: targets,
zip: zip,
}
+
+ for _, target := range targets {
+ imageDir := dir.Join(ctx, "system/framework", target.Arch.ArchType.String())
+ imageConfig.images[target.Arch.ArchType] = imageDir.Join(ctx, "boot.art")
+
+ imagesDeps := make([]android.Path, 0, len(imageConfig.modules)*3)
+ for _, dep := range imageConfig.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex") {
+ imagesDeps = append(imagesDeps, dep)
+ }
+ imageConfig.imagesDeps[target.Arch.ArchType] = imagesDeps
+ }
+
+ return imageConfig
}).(bootImageConfig)
}
@@ -196,16 +205,10 @@
dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_apexjars")
symbolsDir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_apexjars_unstripped")
- images := make(map[android.ArchType]android.OutputPath)
targets := dexpreoptTargets(ctx)
- for _, target := range targets {
- images[target.Arch.ArchType] = dir.Join(ctx,
- "system/framework", target.Arch.ArchType.String(), "apex.art")
- }
-
- return bootImageConfig{
+ imageConfig := bootImageConfig{
name: "apex",
modules: imageModules,
dexLocations: bootLocations,
@@ -213,8 +216,22 @@
dir: dir,
symbolsDir: symbolsDir,
targets: targets,
- images: images,
+ images: make(map[android.ArchType]android.OutputPath),
+ imagesDeps: make(map[android.ArchType]android.Paths),
}
+
+ for _, target := range targets {
+ imageDir := dir.Join(ctx, "system/framework", target.Arch.ArchType.String())
+ imageConfig.images[target.Arch.ArchType] = imageDir.Join(ctx, "apex.art")
+
+ imagesDeps := make([]android.Path, 0, len(imageConfig.modules)*3)
+ for _, dep := range imageConfig.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex") {
+ imagesDeps = append(imagesDeps, dep)
+ }
+ imageConfig.imagesDeps[target.Arch.ArchType] = imagesDeps
+ }
+
+ return imageConfig
}).(bootImageConfig)
}
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 7d0109f..22b7bb9 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -142,7 +142,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 a8cf1c0..78bfa80 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -15,125 +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() {
@@ -171,9 +61,6 @@
// list of java libraries that will be in the classpath.
Libs []string `android:"arch_variant"`
- // don't build against the framework libraries (ext, and framework for device targets)
- No_framework_libs *bool
-
// the java library (in classpath) for documentation that provides java srcs and srcjars.
Srcs_lib *string
@@ -240,7 +127,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.
@@ -406,14 +293,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) {
@@ -456,23 +335,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
//
@@ -502,6 +364,7 @@
}
}
+// javadoc converts .java source files to documentation using javadoc.
func JavadocFactory() android.Module {
module := &Javadoc{}
@@ -511,6 +374,7 @@
return module
}
+// javadoc_host converts .java source files to documentation using javadoc.
func JavadocHostFactory() android.Module {
module := &Javadoc{}
@@ -523,7 +387,7 @@
var _ android.OutputFileProducer = (*Javadoc)(nil)
func (j *Javadoc) sdkVersion() string {
- return String(j.properties.Sdk_version)
+ return proptools.StringDefault(j.properties.Sdk_version, defaultSdkVersion(j))
}
func (j *Javadoc) minSdkVersion() string {
@@ -534,10 +398,6 @@
return j.sdkVersion()
}
-func (j *Javadoc) noFrameworkLibs() bool {
- return Bool(j.properties.No_framework_libs)
-}
-
func (j *Javadoc) addDeps(ctx android.BottomUpMutatorContext) {
if ctx.Device() {
sdkDep := decodeSdkDep(ctx, sdkContext(j))
@@ -659,6 +519,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()...)
@@ -692,10 +553,10 @@
panic("Found two system module dependencies")
}
sm := module.(*SystemModules)
- if sm.outputFile == nil {
+ if sm.outputDir == nil && len(sm.outputDeps) == 0 {
panic("Missing directory for system module dependency")
}
- deps.systemModules = sm.outputFile
+ deps.systemModules = &systemModules{sm.outputDir, sm.outputDeps}
}
})
// do not pass exclude_srcs directly when expanding srcFiles since exclude_srcs
@@ -711,9 +572,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, ".")
}
@@ -764,51 +622,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)
- }
- 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")
}
//
@@ -835,6 +685,7 @@
apiFilePath android.Path
}
+// droiddoc converts .java source files to documentation using doclava or dokka.
func DroiddocFactory() android.Module {
module := &Droiddoc{}
@@ -845,6 +696,7 @@
return module
}
+// droiddoc_host converts .java source files to documentation using doclava or dokka.
func DroiddocHostFactory() android.Module {
module := &Droiddoc{}
@@ -871,58 +723,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
@@ -931,23 +750,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 {
@@ -955,51 +773,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
}
@@ -1007,223 +817,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")
}
}
@@ -1262,6 +1173,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{}
@@ -1272,6 +1186,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{}
@@ -1312,34 +1230,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
}
@@ -1347,159 +1243,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))
@@ -1507,112 +1388,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) {
@@ -1620,29 +1446,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.
@@ -1650,61 +1476,150 @@
// "--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))
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)
+
+ 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))
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)
+
+ 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) != "" {
@@ -1712,9 +1627,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`+
@@ -1724,20 +1641,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.
@@ -1745,21 +1674,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")
}
}
@@ -1785,6 +1742,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)
@@ -1833,3 +1791,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..c83dda1 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")
}
diff --git a/java/java.go b/java/java.go
index 4b38451..f3e10be 100644
--- a/java/java.go
+++ b/java/java.go
@@ -82,9 +82,6 @@
// list of files that should be excluded from java_resources and java_resource_dirs
Exclude_java_resources []string `android:"path,arch_variant"`
- // don't build against the framework libraries (ext, and framework for device targets)
- No_framework_libs *bool
-
// list of module-specific flags that will be used for javac compiles
Javacflags []string `android:"arch_variant"`
@@ -426,6 +423,18 @@
usesLibTag = dependencyTag{name: "uses-library"}
)
+func defaultSdkVersion(ctx checkVendorModuleContext) string {
+ if ctx.SocSpecific() || ctx.DeviceSpecific() {
+ return "system_current"
+ }
+ return ""
+}
+
+type checkVendorModuleContext interface {
+ SocSpecific() bool
+ DeviceSpecific() bool
+}
+
type sdkDep struct {
useModule, useFiles, useDefaultLibs, invalidVersion bool
@@ -465,7 +474,7 @@
}
func (j *Module) sdkVersion() string {
- return String(j.deviceProperties.Sdk_version)
+ return proptools.StringDefault(j.deviceProperties.Sdk_version, defaultSdkVersion(j))
}
func (j *Module) minSdkVersion() string {
@@ -482,10 +491,6 @@
return j.sdkVersion()
}
-func (j *Module) noFrameworkLibs() bool {
- return Bool(j.properties.No_framework_libs)
-}
-
func (j *Module) deps(ctx android.BottomUpMutatorContext) {
if ctx.Device() {
sdkDep := decodeSdkDep(ctx, sdkContext(j))
@@ -506,7 +511,7 @@
}
} else if j.deviceProperties.System_modules == nil {
ctx.PropertyErrorf("sdk_version",
- `system_modules is required to be set when sdk_version is "none", did you mean no_framework_libs?`)
+ `system_modules is required to be set when sdk_version is "none", did you mean "core_platform"`)
} else if *j.deviceProperties.System_modules != "none" {
ctx.AddVariationDependencies(nil, systemModulesTag, *j.deviceProperties.System_modules)
}
@@ -627,7 +632,7 @@
aidlIncludeDirs android.Paths
srcs android.Paths
srcJars android.Paths
- systemModules android.Path
+ systemModules *systemModules
aidlPreprocess android.OptionalPath
kotlinStdlib android.Paths
kotlinAnnotations android.Paths
@@ -835,10 +840,10 @@
panic("Found two system module dependencies")
}
sm := module.(*SystemModules)
- if sm.outputFile == nil {
+ if sm.outputDir == nil || len(sm.outputDeps) == 0 {
panic("Missing directory for system module dependency")
}
- deps.systemModules = sm.outputFile
+ deps.systemModules = &systemModules{sm.outputDir, sm.outputDeps}
}
}
})
@@ -966,9 +971,7 @@
}
// systemModules
- if deps.systemModules != nil {
- flags.systemModules = append(flags.systemModules, deps.systemModules)
- }
+ flags.systemModules = deps.systemModules
// aidl flags.
flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
@@ -1314,11 +1317,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 {
@@ -1866,7 +1867,7 @@
}
func (j *Import) sdkVersion() string {
- return String(j.properties.Sdk_version)
+ return proptools.StringDefault(j.properties.Sdk_version, defaultSdkVersion(j))
}
func (j *Import) minSdkVersion() string {
@@ -2034,7 +2035,7 @@
// dex_import module
type DexImportProperties struct {
- Jars []string
+ Jars []string `android:"path"`
}
type DexImport struct {
@@ -2062,10 +2063,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")
@@ -2086,14 +2083,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).
diff --git a/java/java_test.go b/java/java_test.go
index 22dec07..f95f88b 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))
@@ -121,6 +120,10 @@
"b.kt": nil,
"a.jar": nil,
"b.jar": nil,
+ "APP_NOTICE": nil,
+ "GENRULE_NOTICE": nil,
+ "LIB_NOTICE": nil,
+ "TOOL_NOTICE": nil,
"java-res/a/a": nil,
"java-res/b/b": nil,
"java-res2/a": nil,
@@ -189,6 +192,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,
@@ -218,13 +222,13 @@
android.FailIfErrored(t, errs)
}
-func testJava(t *testing.T, bp string) *android.TestContext {
+func testJava(t *testing.T, bp string) (*android.TestContext, android.Config) {
t.Helper()
config := testConfig(nil)
- ctx := testContext(config, bp, nil)
+ ctx := testContext(bp, nil)
run(t, ctx, config)
- return ctx
+ return ctx, config
}
func moduleToPath(name string) string {
@@ -239,7 +243,7 @@
}
func TestSimple(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -282,8 +286,34 @@
}
}
+func TestSdkVersion(t *testing.T) {
+ ctx, _ := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ vendor: true,
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.java"],
+ }
+ `)
+
+ foo := ctx.ModuleForTests("foo", "android_common").Module().(*Library)
+ bar := ctx.ModuleForTests("bar", "android_common").Module().(*Library)
+
+ if foo.sdkVersion() != "system_current" {
+ t.Errorf("If sdk version of vendor module is empty, it must change to system_current.")
+ }
+
+ if bar.sdkVersion() != "" {
+ t.Errorf("If sdk version of non-vendor module is empty, it keeps empty.")
+ }
+}
+
func TestArchSpecific(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -302,7 +332,7 @@
}
func TestBinary(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library_host {
name: "foo",
srcs: ["a.java"],
@@ -331,7 +361,7 @@
}
func TestPrebuilts(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -382,7 +412,7 @@
}
func TestDefaults(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_defaults {
name: "defaults",
srcs: ["a.java"],
@@ -528,7 +558,7 @@
for _, test := range table {
t.Run(test.name, func(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: [
@@ -557,7 +587,7 @@
}
func TestIncludeSrcs(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: [
@@ -620,7 +650,7 @@
}
func TestGeneratedSources(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: [
@@ -653,7 +683,7 @@
}
func TestTurbine(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -702,7 +732,7 @@
}
func TestSharding(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "bar",
srcs: ["a.java","b.java","c.java"],
@@ -720,16 +750,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"
@@ -747,23 +783,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"],
@@ -817,7 +854,7 @@
}
func TestExcludeFileGroupInSrcs(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java", ":foo-srcs"],
@@ -844,7 +881,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",
@@ -856,7 +893,7 @@
}
func TestJavaSdkLibrary(t *testing.T) {
- ctx := testJava(t, `
+ ctx, _ := testJava(t, `
droiddoc_template {
name: "droiddoc-templates-sdk",
path: ".",
@@ -1027,7 +1064,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", "")
@@ -1037,7 +1074,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/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..0ec6499 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()).
diff --git a/java/sdk.go b/java/sdk.go
index 6ffe399..d1e2ae4 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -44,9 +44,6 @@
minSdkVersion() string
// targetSdkVersion returns the target_sdk_version property of the current module, or sdkVersion() if it is not set.
targetSdkVersion() string
-
- // Temporarily provide access to the no_frameworks_libs property (where present).
- noFrameworkLibs() bool
}
func sdkVersionOrDefault(ctx android.BaseModuleContext, v string) string {
@@ -84,6 +81,7 @@
func decodeSdkDep(ctx android.BaseModuleContext, sdkContext sdkContext) sdkDep {
v := sdkContext.sdkVersion()
+
// For PDK builds, use the latest SDK version instead of "current"
if ctx.Config().IsPdkBuild() && (v == "" || v == "current") {
sdkVersions := ctx.Config().Get(sdkVersionsKey).([]int)
@@ -141,9 +139,6 @@
useFiles: true,
jars: android.Paths{jarPath.Path(), lambdaStubsPath},
aidl: android.OptionalPathForPath(aidlPath.Path()),
-
- // Pass value straight through for now to match previous behavior.
- noFrameworksLibs: sdkContext.noFrameworkLibs(),
}
}
@@ -154,15 +149,12 @@
systemModules: m + "_system_modules",
frameworkResModule: r,
aidl: android.OptionalPathForPath(aidl),
-
- // Pass value straight through for now to match previous behavior.
- noFrameworksLibs: sdkContext.noFrameworkLibs(),
}
if m == "core.current.stubs" {
ret.systemModules = "core-current-stubs-system-modules"
- } else if m == "core.platform.api.stubs" {
- ret.systemModules = "core-platform-api-stubs-system-modules"
+ // core_current does not include framework classes.
+ ret.noFrameworksLibs = true
}
return ret
}
@@ -192,9 +184,6 @@
return sdkDep{
useDefaultLibs: true,
frameworkResModule: "framework-res",
-
- // Pass value straight through for now to match previous behavior.
- noFrameworksLibs: sdkContext.noFrameworkLibs(),
}
case "none":
return sdkDep{
@@ -307,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 b4a3f29..d38088d 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -63,12 +63,6 @@
javaSdkLibrariesLock sync.Mutex
)
-// java_sdk_library is to make a Java library that implements optional platform APIs to apps.
-// It is actually a wrapper of several modules: 1) stubs library that clients are linked against
-// to, 2) droiddoc module that internally generates API stubs source files, 3) the real runtime
-// shared library that implements the APIs, and 4) XML file for adding the runtime lib to the
-// classpath at runtime if requested via <uses-library>.
-//
// TODO: these are big features that are currently missing
// 1) disallowing linking to the runtime shared lib
// 2) HTML generation
@@ -155,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)
@@ -746,6 +745,11 @@
module.Library.Module.deviceProperties.IsSDKLibrary = true
}
+// java_sdk_library is a special Java library that provides optional platform APIs to apps.
+// In practice, it can be viewed as a combination of several modules: 1) stubs library that clients
+// are linked against to, 2) droiddoc module that internally generates API stubs source files,
+// 3) the real runtime shared library that implements the APIs, and 4) XML file for adding
+// the runtime lib to the classpath at runtime if requested via <uses-library>.
func SdkLibraryFactory() android.Module {
module := &SdkLibrary{}
module.InitSdkLibraryProperties()
@@ -787,6 +791,7 @@
var _ SdkLibraryDependency = (*sdkLibraryImport)(nil)
+// java_sdk_library_import imports a prebuilt java_sdk_library.
func sdkLibraryImportFactory() android.Module {
module := &sdkLibraryImport{}
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 915333e..6be17eb 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -47,14 +47,6 @@
aidl: "-Iframework/aidl",
},
{
- name: "no_framework_libs:true",
- properties: `no_framework_libs:true`,
- bootclasspath: config.DefaultBootclasspathLibraries,
- system: config.DefaultSystemModules,
- classpath: []string{},
- aidl: "",
- },
- {
name: `sdk_version:"core_platform"`,
properties: `sdk_version:"core_platform"`,
bootclasspath: config.DefaultBootclasspathLibraries,
@@ -254,7 +246,7 @@
if testcase.system == "none" {
system = "--system=none"
} else if testcase.system != "" {
- system = "--system=" + filepath.Join(buildDir, ".intermediates", testcase.system, "android_common", "system") + "/"
+ system = "--system=" + filepath.Join(buildDir, ".intermediates", testcase.system, "android_common", "system")
}
checkClasspath := func(t *testing.T, ctx *android.TestContext) {
@@ -290,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)
@@ -317,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")
@@ -343,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/java/system_modules.go b/java/system_modules.go
index 5a86f3c..c616249 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -61,7 +61,7 @@
"moduleName", "classpath", "outDir", "workDir")
)
-func TransformJarsToSystemModules(ctx android.ModuleContext, moduleName string, jars android.Paths) android.WritablePath {
+func TransformJarsToSystemModules(ctx android.ModuleContext, moduleName string, jars android.Paths) (android.Path, android.Paths) {
outDir := android.PathForModuleOut(ctx, "system")
workDir := android.PathForModuleOut(ctx, "modules")
outputFile := android.PathForModuleOut(ctx, "system/lib/modules")
@@ -84,7 +84,7 @@
},
})
- return outputFile
+ return outDir, outputs.Paths()
}
func SystemModulesFactory() android.Module {
@@ -101,7 +101,8 @@
properties SystemModulesProperties
- outputFile android.Path
+ outputDir android.Path
+ outputDeps android.Paths
}
type SystemModulesProperties struct {
@@ -117,7 +118,7 @@
jars = append(jars, dep.HeaderJars()...)
})
- system.outputFile = TransformJarsToSystemModules(ctx, "java.base", jars)
+ system.outputDir, system.outputDeps = TransformJarsToSystemModules(ctx, "java.base", jars)
}
func (system *SystemModules) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -127,16 +128,22 @@
func (system *SystemModules) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
- makevar := "SOONG_SYSTEM_MODULES_" + name
fmt.Fprintln(w)
- fmt.Fprintln(w, makevar, ":=", system.outputFile.String())
- fmt.Fprintln(w, ".KATI_READONLY", ":=", makevar)
+
+ makevar := "SOONG_SYSTEM_MODULES_" + name
+ fmt.Fprintln(w, makevar, ":=$=", system.outputDir.String())
+ fmt.Fprintln(w)
+
+ makevar = "SOONG_SYSTEM_MODULES_LIBS_" + name
+ fmt.Fprintln(w, makevar, ":=$=", strings.Join(system.properties.Libs, " "))
+ fmt.Fprintln(w)
+
+ makevar = "SOONG_SYSTEM_MODULES_DEPS_" + name
+ fmt.Fprintln(w, makevar, ":=$=", strings.Join(system.outputDeps.Strings(), " "))
+ fmt.Fprintln(w)
+
fmt.Fprintln(w, name+":", "$("+makevar+")")
fmt.Fprintln(w, ".PHONY:", name)
- fmt.Fprintln(w)
- makevar = "SOONG_SYSTEM_MODULES_LIBS_" + name
- fmt.Fprintln(w, makevar, ":=", strings.Join(system.properties.Libs, " "))
- fmt.Fprintln(w, ".KATI_READONLY :=", makevar)
},
}
}
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/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/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/scripts/system-clang-format b/scripts/system-clang-format
index 14abd93..a7614d2 100644
--- a/scripts/system-clang-format
+++ b/scripts/system-clang-format
@@ -1,4 +1,5 @@
BasedOnStyle: Google
+Standard: Cpp11
AccessModifierOffset: -2
AllowShortFunctionsOnASingleLine: Inline
ColumnLimit: 100
diff --git a/scripts/system-clang-format-2 b/scripts/system-clang-format-2
index e28b379..a4e23f8 100644
--- a/scripts/system-clang-format-2
+++ b/scripts/system-clang-format-2
@@ -1,4 +1,5 @@
BasedOnStyle: Google
+Standard: Cpp11
AllowShortFunctionsOnASingleLine: Inline
ColumnLimit: 100
CommentPragmas: NOLINT:.*
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index c47f614..8e7f96a 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -111,7 +111,7 @@
productOut("system_other"),
productOut("vendor"),
productOut("product"),
- productOut("product_services"),
+ productOut("system_ext"),
productOut("oem"),
productOut("obj/FAKE"),
productOut("breakpad"),
diff --git a/ui/build/config.go b/ui/build/config.go
index 6df9529..20bb3d1 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 (
@@ -71,6 +73,9 @@
// Builds all of the modules and their dependencies of a list of specified directories. All specified
// directories are relative to the root directory of the source tree.
BUILD_MODULES_IN_DIRECTORIES
+
+ // Build a list of specified modules. If none was specified, simply build the whole source tree.
+ BUILD_MODULES
)
// checkTopDir validates that the current directory is at the root directory of the source tree.
@@ -263,6 +268,10 @@
if err != nil {
ctx.Fatalf("Error retrieving top directory: %v", err)
}
+ dir, err = filepath.EvalSymlinks(dir)
+ if err != nil {
+ ctx.Fatalf("Unable to evaluate symlink of %s: %v", dir, err)
+ }
dir, err = filepath.Abs(dir)
if err != nil {
ctx.Fatalf("Unable to find absolute path %s: %v", dir, err)
@@ -290,18 +299,18 @@
var targets []string
switch action {
+ case BUILD_MODULES:
+ // No additional processing is required when building a list of specific modules or all modules.
case BUILD_MODULES_IN_A_DIRECTORY:
// If dir is the root source tree, all the modules are built of the source tree are built so
// no need to find the build file.
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)}
@@ -335,26 +344,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")
}
}
@@ -419,24 +459,23 @@
}
}
- 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)
}
+ buildFiles = append(buildFiles, filepath.Join(dir, "Android.mk"))
} 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)
}
- buildFiles = append(buildFiles, buildFile)
targets = append(targets, newTargets...)
}
@@ -599,7 +638,7 @@
func (c *configImpl) OutDir() string {
if outDir, ok := c.environ.Get("OUT_DIR"); ok {
- return filepath.Clean(outDir)
+ return outDir
}
return "out"
}
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index 1ef5456..463405a 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -441,7 +441,7 @@
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"},
@@ -545,8 +545,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 +558,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 +629,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()
@@ -690,6 +715,9 @@
// Build files that exists in the source tree.
buildFiles []string
+ // Create root symlink that points to topDir.
+ rootSymlink bool
+
// ********* Action *********
// Arguments passed in to soong_ui.
args []string
@@ -706,11 +734,23 @@
// 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) {
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",
@@ -738,6 +778,22 @@
createDirectories(t, topDir, tt.dirsInTrees)
createBuildFiles(t, topDir, tt.buildFiles)
+ if tt.rootSymlink {
+ // Create a secondary root source tree which points to the true root source tree.
+ symlinkTopDir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatalf("failed to create symlink temp dir: %v", err)
+ }
+ defer os.RemoveAll(symlinkTopDir)
+
+ symlinkTopDir = filepath.Join(symlinkTopDir, "root")
+ err = os.Symlink(topDir, symlinkTopDir)
+ if err != nil {
+ t.Fatalf("failed to create symlink: %v", err)
+ }
+ topDir = symlinkTopDir
+ }
+
r := setTop(t, topDir)
defer r()
@@ -761,6 +817,66 @@
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 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, 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 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 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{},
+ }}
+ 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.
@@ -801,6 +917,7 @@
envVar{
name: "ONE_SHOT_MAKEFILE",
value: "0/1/2/Android.mk"}},
+ expectedErrStr: "Build file not found for 0/1/2 directory",
}, {
description: "build action executed at root directory",
dirsInTrees: []string{},
@@ -887,13 +1004,24 @@
description: "build action executed at root directory",
dirsInTrees: []string{},
buildFiles: []string{},
+ rootSymlink: false,
args: []string{},
curDir: ".",
tidyOnly: "",
expectedArgs: []string{},
expectedEnvVars: []envVar{},
}, {
- description: "build file not found - no error is expected to return",
+ 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 file not found",
dirsInTrees: []string{"0/1/2"},
buildFiles: []string{},
args: []string{},
@@ -901,6 +1029,7 @@
tidyOnly: "",
expectedArgs: []string{"MODULES-IN-0-1-2"},
expectedEnvVars: []envVar{},
+ expectedErrStr: "Build file not found for 0/1/2 directory",
}, {
description: "GET-INSTALL-PATH specified,",
dirsInTrees: []string{"0/1/2"},
@@ -1035,6 +1164,20 @@
description: "normal execution from top dir directory",
dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/3/Android.bp", "0/2/Android.bp"},
+ rootSymlink: false,
+ args: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
+ 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"},
+ buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/3/Android.bp", "0/2/Android.bp"},
+ rootSymlink: true,
args: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
curDir: ".",
tidyOnly: "",
diff --git a/ui/build/context.go b/ui/build/context.go
index 7ff98ef..3945ce0 100644
--- a/ui/build/context.go
+++ b/ui/build/context.go
@@ -70,7 +70,7 @@
if c.Metrics != nil {
realTime := end - begin
c.Metrics.SetTimeMetrics(
- metrics_proto.PerfInfo{
+ soong_metrics_proto.PerfInfo{
Desc: &desc,
Name: &name,
StartTime: &begin,
diff --git a/ui/build/exec.go b/ui/build/exec.go
index 5c312bc..e435c53 100644
--- a/ui/build/exec.go
+++ b/ui/build/exec.go
@@ -15,7 +15,10 @@
package build
import (
+ "bufio"
+ "io"
"os/exec"
+ "strings"
)
// Cmd is a wrapper of os/exec.Cmd that integrates with the build context for
@@ -139,3 +142,34 @@
st.Finish()
c.reportError(err)
}
+
+// RunAndStreamOrFatal will run the command, while running print
+// any output, then handle any errors with a call to ctx.Fatal
+func (c *Cmd) RunAndStreamOrFatal() {
+ out, err := c.StdoutPipe()
+ if err != nil {
+ c.ctx.Fatal(err)
+ }
+ c.Stderr = c.Stdout
+
+ st := c.ctx.Status.StartTool()
+
+ c.StartOrFatal()
+
+ buf := bufio.NewReaderSize(out, 2*1024*1024)
+ for {
+ // Attempt to read whole lines, but write partial lines that are too long to fit in the buffer or hit EOF
+ line, err := buf.ReadString('\n')
+ if line != "" {
+ st.Print(strings.TrimSuffix(line, "\n"))
+ } else if err == io.EOF {
+ break
+ } else if err != nil {
+ c.ctx.Fatal(err)
+ }
+ }
+
+ err = c.Wait()
+ st.Finish()
+ c.reportError(err)
+}
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 7994f3a..b41ac20 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -103,7 +103,7 @@
}()
ctx.Status.Status("Starting ninja...")
- cmd.RunAndPrintOrFatal()
+ cmd.RunAndStreamOrFatal()
}
type statusChecker struct {
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index 7628fef..1a7412d 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -81,6 +81,7 @@
"bzip2": Allowed,
"dd": Allowed,
"diff": Allowed,
+ "dlv": Allowed,
"egrep": Allowed,
"expr": Allowed,
"find": Allowed,
@@ -88,13 +89,14 @@
"getopt": Allowed,
"git": Allowed,
"grep": Allowed,
+ "gzcat": Allowed,
"gzip": Allowed,
"hexdump": Allowed,
"jar": Allowed,
"java": Allowed,
"javap": Allowed,
"lsof": Allowed,
- "m4": Allowed,
+ "m4": Log,
"openssl": Allowed,
"patch": Allowed,
"pstree": Allowed,
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index b94db74..11ff667 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -162,6 +162,10 @@
c.ctx.Printf("AllowBuildBrokenUsesNetwork: %v", c.Sandbox.AllowBuildBrokenUsesNetwork)
c.ctx.Printf("BuildBrokenUsesNetwork: %v", c.config.BuildBrokenUsesNetwork())
sandboxArgs = append(sandboxArgs, "-N")
+ } else if dlv, _ := c.config.Environment().Get("SOONG_DELVE"); dlv != "" {
+ // The debugger is enabled and soong_build will pause until a remote delve process connects, allow
+ // network connections.
+ sandboxArgs = append(sandboxArgs, "-N")
}
// Stop nsjail from parsing arguments
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 2ce1ac9..3388417 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -120,7 +120,7 @@
"--frontend_file", fifo,
"-f", filepath.Join(config.SoongOutDir(), file))
cmd.Sandbox = soongSandbox
- cmd.RunAndPrintOrFatal()
+ cmd.RunAndStreamOrFatal()
}
ninja("minibootstrap", ".minibootstrap/build.ninja")
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 790b67a..bc86f0a 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -33,19 +33,19 @@
)
type Metrics struct {
- metrics metrics_proto.MetricsBase
+ metrics soong_metrics_proto.MetricsBase
TimeTracer TimeTracer
}
func New() (metrics *Metrics) {
m := &Metrics{
- metrics: metrics_proto.MetricsBase{},
+ metrics: soong_metrics_proto.MetricsBase{},
TimeTracer: &timeTracerImpl{},
}
return m
}
-func (m *Metrics) SetTimeMetrics(perf metrics_proto.PerfInfo) {
+func (m *Metrics) SetTimeMetrics(perf soong_metrics_proto.PerfInfo) {
switch perf.GetName() {
case RunKati:
m.metrics.KatiRuns = append(m.metrics.KatiRuns, &perf)
@@ -76,11 +76,11 @@
case "TARGET_BUILD_VARIANT":
switch v {
case "user":
- m.metrics.TargetBuildVariant = metrics_proto.MetricsBase_USER.Enum()
+ m.metrics.TargetBuildVariant = soong_metrics_proto.MetricsBase_USER.Enum()
case "userdebug":
- m.metrics.TargetBuildVariant = metrics_proto.MetricsBase_USERDEBUG.Enum()
+ m.metrics.TargetBuildVariant = soong_metrics_proto.MetricsBase_USERDEBUG.Enum()
case "eng":
- m.metrics.TargetBuildVariant = metrics_proto.MetricsBase_ENG.Enum()
+ m.metrics.TargetBuildVariant = soong_metrics_proto.MetricsBase_ENG.Enum()
default:
// ignored
}
@@ -112,18 +112,18 @@
}
}
-func (m *Metrics) getArch(arch string) *metrics_proto.MetricsBase_ARCH {
+func (m *Metrics) getArch(arch string) *soong_metrics_proto.MetricsBase_Arch {
switch arch {
case "arm":
- return metrics_proto.MetricsBase_ARM.Enum()
+ return soong_metrics_proto.MetricsBase_ARM.Enum()
case "arm64":
- return metrics_proto.MetricsBase_ARM64.Enum()
+ return soong_metrics_proto.MetricsBase_ARM64.Enum()
case "x86":
- return metrics_proto.MetricsBase_X86.Enum()
+ return soong_metrics_proto.MetricsBase_X86.Enum()
case "x86_64":
- return metrics_proto.MetricsBase_X86_64.Enum()
+ return soong_metrics_proto.MetricsBase_X86_64.Enum()
default:
- return metrics_proto.MetricsBase_UNKNOWN.Enum()
+ return soong_metrics_proto.MetricsBase_UNKNOWN.Enum()
}
}
@@ -148,7 +148,7 @@
return err
}
tempPath := outputPath + ".tmp"
- err = ioutil.WriteFile(tempPath, []byte(data), 0777)
+ err = ioutil.WriteFile(tempPath, []byte(data), 0644)
if err != nil {
return err
}
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index feefc89..5486ec1 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -1,11 +1,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: metrics.proto
-package metrics_proto
+package soong_metrics_proto
-import proto "github.com/golang/protobuf/proto"
-import fmt "fmt"
-import math "math"
+import (
+ fmt "fmt"
+ proto "github.com/golang/protobuf/proto"
+ math "math"
+)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
@@ -16,65 +18,70 @@
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
-type MetricsBase_BUILDVARIANT int32
+type MetricsBase_BuildVariant int32
const (
- MetricsBase_USER MetricsBase_BUILDVARIANT = 0
- MetricsBase_USERDEBUG MetricsBase_BUILDVARIANT = 1
- MetricsBase_ENG MetricsBase_BUILDVARIANT = 2
+ MetricsBase_USER MetricsBase_BuildVariant = 0
+ MetricsBase_USERDEBUG MetricsBase_BuildVariant = 1
+ MetricsBase_ENG MetricsBase_BuildVariant = 2
)
-var MetricsBase_BUILDVARIANT_name = map[int32]string{
+var MetricsBase_BuildVariant_name = map[int32]string{
0: "USER",
1: "USERDEBUG",
2: "ENG",
}
-var MetricsBase_BUILDVARIANT_value = map[string]int32{
+
+var MetricsBase_BuildVariant_value = map[string]int32{
"USER": 0,
"USERDEBUG": 1,
"ENG": 2,
}
-func (x MetricsBase_BUILDVARIANT) Enum() *MetricsBase_BUILDVARIANT {
- p := new(MetricsBase_BUILDVARIANT)
+func (x MetricsBase_BuildVariant) Enum() *MetricsBase_BuildVariant {
+ p := new(MetricsBase_BuildVariant)
*p = x
return p
}
-func (x MetricsBase_BUILDVARIANT) String() string {
- return proto.EnumName(MetricsBase_BUILDVARIANT_name, int32(x))
+
+func (x MetricsBase_BuildVariant) String() string {
+ return proto.EnumName(MetricsBase_BuildVariant_name, int32(x))
}
-func (x *MetricsBase_BUILDVARIANT) UnmarshalJSON(data []byte) error {
- value, err := proto.UnmarshalJSONEnum(MetricsBase_BUILDVARIANT_value, data, "MetricsBase_BUILDVARIANT")
+
+func (x *MetricsBase_BuildVariant) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(MetricsBase_BuildVariant_value, data, "MetricsBase_BuildVariant")
if err != nil {
return err
}
- *x = MetricsBase_BUILDVARIANT(value)
+ *x = MetricsBase_BuildVariant(value)
return nil
}
-func (MetricsBase_BUILDVARIANT) EnumDescriptor() ([]byte, []int) {
- return fileDescriptor_metrics_9e7b895801991242, []int{0, 0}
+
+func (MetricsBase_BuildVariant) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_6039342a2ba47b72, []int{0, 0}
}
-type MetricsBase_ARCH int32
+type MetricsBase_Arch int32
const (
- MetricsBase_UNKNOWN MetricsBase_ARCH = 0
- MetricsBase_ARM MetricsBase_ARCH = 1
- MetricsBase_ARM64 MetricsBase_ARCH = 2
- MetricsBase_X86 MetricsBase_ARCH = 3
- MetricsBase_X86_64 MetricsBase_ARCH = 4
+ MetricsBase_UNKNOWN MetricsBase_Arch = 0
+ MetricsBase_ARM MetricsBase_Arch = 1
+ MetricsBase_ARM64 MetricsBase_Arch = 2
+ MetricsBase_X86 MetricsBase_Arch = 3
+ MetricsBase_X86_64 MetricsBase_Arch = 4
)
-var MetricsBase_ARCH_name = map[int32]string{
+var MetricsBase_Arch_name = map[int32]string{
0: "UNKNOWN",
1: "ARM",
2: "ARM64",
3: "X86",
4: "X86_64",
}
-var MetricsBase_ARCH_value = map[string]int32{
+
+var MetricsBase_Arch_value = map[string]int32{
"UNKNOWN": 0,
"ARM": 1,
"ARM64": 2,
@@ -82,63 +89,70 @@
"X86_64": 4,
}
-func (x MetricsBase_ARCH) Enum() *MetricsBase_ARCH {
- p := new(MetricsBase_ARCH)
+func (x MetricsBase_Arch) Enum() *MetricsBase_Arch {
+ p := new(MetricsBase_Arch)
*p = x
return p
}
-func (x MetricsBase_ARCH) String() string {
- return proto.EnumName(MetricsBase_ARCH_name, int32(x))
+
+func (x MetricsBase_Arch) String() string {
+ return proto.EnumName(MetricsBase_Arch_name, int32(x))
}
-func (x *MetricsBase_ARCH) UnmarshalJSON(data []byte) error {
- value, err := proto.UnmarshalJSONEnum(MetricsBase_ARCH_value, data, "MetricsBase_ARCH")
+
+func (x *MetricsBase_Arch) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(MetricsBase_Arch_value, data, "MetricsBase_Arch")
if err != nil {
return err
}
- *x = MetricsBase_ARCH(value)
+ *x = MetricsBase_Arch(value)
return nil
}
-func (MetricsBase_ARCH) EnumDescriptor() ([]byte, []int) {
- return fileDescriptor_metrics_9e7b895801991242, []int{0, 1}
+
+func (MetricsBase_Arch) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_6039342a2ba47b72, []int{0, 1}
}
-type ModuleTypeInfo_BUILDSYSTEM int32
+type ModuleTypeInfo_BuildSystem int32
const (
- ModuleTypeInfo_UNKNOWN ModuleTypeInfo_BUILDSYSTEM = 0
- ModuleTypeInfo_SOONG ModuleTypeInfo_BUILDSYSTEM = 1
- ModuleTypeInfo_MAKE ModuleTypeInfo_BUILDSYSTEM = 2
+ ModuleTypeInfo_UNKNOWN ModuleTypeInfo_BuildSystem = 0
+ ModuleTypeInfo_SOONG ModuleTypeInfo_BuildSystem = 1
+ ModuleTypeInfo_MAKE ModuleTypeInfo_BuildSystem = 2
)
-var ModuleTypeInfo_BUILDSYSTEM_name = map[int32]string{
+var ModuleTypeInfo_BuildSystem_name = map[int32]string{
0: "UNKNOWN",
1: "SOONG",
2: "MAKE",
}
-var ModuleTypeInfo_BUILDSYSTEM_value = map[string]int32{
+
+var ModuleTypeInfo_BuildSystem_value = map[string]int32{
"UNKNOWN": 0,
"SOONG": 1,
"MAKE": 2,
}
-func (x ModuleTypeInfo_BUILDSYSTEM) Enum() *ModuleTypeInfo_BUILDSYSTEM {
- p := new(ModuleTypeInfo_BUILDSYSTEM)
+func (x ModuleTypeInfo_BuildSystem) Enum() *ModuleTypeInfo_BuildSystem {
+ p := new(ModuleTypeInfo_BuildSystem)
*p = x
return p
}
-func (x ModuleTypeInfo_BUILDSYSTEM) String() string {
- return proto.EnumName(ModuleTypeInfo_BUILDSYSTEM_name, int32(x))
+
+func (x ModuleTypeInfo_BuildSystem) String() string {
+ return proto.EnumName(ModuleTypeInfo_BuildSystem_name, int32(x))
}
-func (x *ModuleTypeInfo_BUILDSYSTEM) UnmarshalJSON(data []byte) error {
- value, err := proto.UnmarshalJSONEnum(ModuleTypeInfo_BUILDSYSTEM_value, data, "ModuleTypeInfo_BUILDSYSTEM")
+
+func (x *ModuleTypeInfo_BuildSystem) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(ModuleTypeInfo_BuildSystem_value, data, "ModuleTypeInfo_BuildSystem")
if err != nil {
return err
}
- *x = ModuleTypeInfo_BUILDSYSTEM(value)
+ *x = ModuleTypeInfo_BuildSystem(value)
return nil
}
-func (ModuleTypeInfo_BUILDSYSTEM) EnumDescriptor() ([]byte, []int) {
- return fileDescriptor_metrics_9e7b895801991242, []int{2, 0}
+
+func (ModuleTypeInfo_BuildSystem) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_6039342a2ba47b72, []int{2, 0}
}
type MetricsBase struct {
@@ -151,17 +165,17 @@
// The target product information, eg. aosp_arm.
TargetProduct *string `protobuf:"bytes,4,opt,name=target_product,json=targetProduct" json:"target_product,omitempty"`
// The target build variant information, eg. eng.
- TargetBuildVariant *MetricsBase_BUILDVARIANT `protobuf:"varint,5,opt,name=target_build_variant,json=targetBuildVariant,enum=build_metrics.MetricsBase_BUILDVARIANT,def=2" json:"target_build_variant,omitempty"`
+ TargetBuildVariant *MetricsBase_BuildVariant `protobuf:"varint,5,opt,name=target_build_variant,json=targetBuildVariant,enum=soong_build_metrics.MetricsBase_BuildVariant,def=2" json:"target_build_variant,omitempty"`
// The target arch information, eg. arm.
- TargetArch *MetricsBase_ARCH `protobuf:"varint,6,opt,name=target_arch,json=targetArch,enum=build_metrics.MetricsBase_ARCH,def=0" json:"target_arch,omitempty"`
+ TargetArch *MetricsBase_Arch `protobuf:"varint,6,opt,name=target_arch,json=targetArch,enum=soong_build_metrics.MetricsBase_Arch,def=0" json:"target_arch,omitempty"`
// The target arch variant information, eg. armv7-a-neon.
TargetArchVariant *string `protobuf:"bytes,7,opt,name=target_arch_variant,json=targetArchVariant" json:"target_arch_variant,omitempty"`
// The target cpu variant information, eg. generic.
TargetCpuVariant *string `protobuf:"bytes,8,opt,name=target_cpu_variant,json=targetCpuVariant" json:"target_cpu_variant,omitempty"`
// The host arch information, eg. x86_64.
- HostArch *MetricsBase_ARCH `protobuf:"varint,9,opt,name=host_arch,json=hostArch,enum=build_metrics.MetricsBase_ARCH,def=0" json:"host_arch,omitempty"`
+ HostArch *MetricsBase_Arch `protobuf:"varint,9,opt,name=host_arch,json=hostArch,enum=soong_build_metrics.MetricsBase_Arch,def=0" json:"host_arch,omitempty"`
// The host 2nd arch information, eg. x86.
- Host_2NdArch *MetricsBase_ARCH `protobuf:"varint,10,opt,name=host_2nd_arch,json=host2ndArch,enum=build_metrics.MetricsBase_ARCH,def=0" json:"host_2nd_arch,omitempty"`
+ Host_2NdArch *MetricsBase_Arch `protobuf:"varint,10,opt,name=host_2nd_arch,json=host2ndArch,enum=soong_build_metrics.MetricsBase_Arch,def=0" json:"host_2nd_arch,omitempty"`
// The host os information, eg. linux.
HostOs *string `protobuf:"bytes,11,opt,name=host_os,json=hostOs" json:"host_os,omitempty"`
// The host os extra information, eg. Linux-4.17.0-3rodete2-amd64-x86_64-Debian-GNU.
@@ -191,16 +205,17 @@
func (m *MetricsBase) String() string { return proto.CompactTextString(m) }
func (*MetricsBase) ProtoMessage() {}
func (*MetricsBase) Descriptor() ([]byte, []int) {
- return fileDescriptor_metrics_9e7b895801991242, []int{0}
+ return fileDescriptor_6039342a2ba47b72, []int{0}
}
+
func (m *MetricsBase) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MetricsBase.Unmarshal(m, b)
}
func (m *MetricsBase) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MetricsBase.Marshal(b, m, deterministic)
}
-func (dst *MetricsBase) XXX_Merge(src proto.Message) {
- xxx_messageInfo_MetricsBase.Merge(dst, src)
+func (m *MetricsBase) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MetricsBase.Merge(m, src)
}
func (m *MetricsBase) XXX_Size() int {
return xxx_messageInfo_MetricsBase.Size(m)
@@ -211,10 +226,10 @@
var xxx_messageInfo_MetricsBase proto.InternalMessageInfo
-const Default_MetricsBase_TargetBuildVariant MetricsBase_BUILDVARIANT = MetricsBase_ENG
-const Default_MetricsBase_TargetArch MetricsBase_ARCH = MetricsBase_UNKNOWN
-const Default_MetricsBase_HostArch MetricsBase_ARCH = MetricsBase_UNKNOWN
-const Default_MetricsBase_Host_2NdArch MetricsBase_ARCH = MetricsBase_UNKNOWN
+const Default_MetricsBase_TargetBuildVariant MetricsBase_BuildVariant = MetricsBase_ENG
+const Default_MetricsBase_TargetArch MetricsBase_Arch = MetricsBase_UNKNOWN
+const Default_MetricsBase_HostArch MetricsBase_Arch = MetricsBase_UNKNOWN
+const Default_MetricsBase_Host_2NdArch MetricsBase_Arch = MetricsBase_UNKNOWN
func (m *MetricsBase) GetBuildDateTimestamp() int64 {
if m != nil && m.BuildDateTimestamp != nil {
@@ -244,14 +259,14 @@
return ""
}
-func (m *MetricsBase) GetTargetBuildVariant() MetricsBase_BUILDVARIANT {
+func (m *MetricsBase) GetTargetBuildVariant() MetricsBase_BuildVariant {
if m != nil && m.TargetBuildVariant != nil {
return *m.TargetBuildVariant
}
return Default_MetricsBase_TargetBuildVariant
}
-func (m *MetricsBase) GetTargetArch() MetricsBase_ARCH {
+func (m *MetricsBase) GetTargetArch() MetricsBase_Arch {
if m != nil && m.TargetArch != nil {
return *m.TargetArch
}
@@ -272,14 +287,14 @@
return ""
}
-func (m *MetricsBase) GetHostArch() MetricsBase_ARCH {
+func (m *MetricsBase) GetHostArch() MetricsBase_Arch {
if m != nil && m.HostArch != nil {
return *m.HostArch
}
return Default_MetricsBase_HostArch
}
-func (m *MetricsBase) GetHost_2NdArch() MetricsBase_ARCH {
+func (m *MetricsBase) GetHost_2NdArch() MetricsBase_Arch {
if m != nil && m.Host_2NdArch != nil {
return *m.Host_2NdArch
}
@@ -378,16 +393,17 @@
func (m *PerfInfo) String() string { return proto.CompactTextString(m) }
func (*PerfInfo) ProtoMessage() {}
func (*PerfInfo) Descriptor() ([]byte, []int) {
- return fileDescriptor_metrics_9e7b895801991242, []int{1}
+ return fileDescriptor_6039342a2ba47b72, []int{1}
}
+
func (m *PerfInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PerfInfo.Unmarshal(m, b)
}
func (m *PerfInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PerfInfo.Marshal(b, m, deterministic)
}
-func (dst *PerfInfo) XXX_Merge(src proto.Message) {
- xxx_messageInfo_PerfInfo.Merge(dst, src)
+func (m *PerfInfo) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_PerfInfo.Merge(m, src)
}
func (m *PerfInfo) XXX_Size() int {
return xxx_messageInfo_PerfInfo.Size(m)
@@ -435,7 +451,7 @@
type ModuleTypeInfo struct {
// The build system, eg. Soong or Make.
- BuildSystem *ModuleTypeInfo_BUILDSYSTEM `protobuf:"varint,1,opt,name=build_system,json=buildSystem,enum=build_metrics.ModuleTypeInfo_BUILDSYSTEM,def=0" json:"build_system,omitempty"`
+ BuildSystem *ModuleTypeInfo_BuildSystem `protobuf:"varint,1,opt,name=build_system,json=buildSystem,enum=soong_build_metrics.ModuleTypeInfo_BuildSystem,def=0" json:"build_system,omitempty"`
// The module type, eg. java_library, cc_binary, and etc.
ModuleType *string `protobuf:"bytes,2,opt,name=module_type,json=moduleType" json:"module_type,omitempty"`
// The number of logical modules.
@@ -449,16 +465,17 @@
func (m *ModuleTypeInfo) String() string { return proto.CompactTextString(m) }
func (*ModuleTypeInfo) ProtoMessage() {}
func (*ModuleTypeInfo) Descriptor() ([]byte, []int) {
- return fileDescriptor_metrics_9e7b895801991242, []int{2}
+ return fileDescriptor_6039342a2ba47b72, []int{2}
}
+
func (m *ModuleTypeInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ModuleTypeInfo.Unmarshal(m, b)
}
func (m *ModuleTypeInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ModuleTypeInfo.Marshal(b, m, deterministic)
}
-func (dst *ModuleTypeInfo) XXX_Merge(src proto.Message) {
- xxx_messageInfo_ModuleTypeInfo.Merge(dst, src)
+func (m *ModuleTypeInfo) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ModuleTypeInfo.Merge(m, src)
}
func (m *ModuleTypeInfo) XXX_Size() int {
return xxx_messageInfo_ModuleTypeInfo.Size(m)
@@ -469,9 +486,9 @@
var xxx_messageInfo_ModuleTypeInfo proto.InternalMessageInfo
-const Default_ModuleTypeInfo_BuildSystem ModuleTypeInfo_BUILDSYSTEM = ModuleTypeInfo_UNKNOWN
+const Default_ModuleTypeInfo_BuildSystem ModuleTypeInfo_BuildSystem = ModuleTypeInfo_UNKNOWN
-func (m *ModuleTypeInfo) GetBuildSystem() ModuleTypeInfo_BUILDSYSTEM {
+func (m *ModuleTypeInfo) GetBuildSystem() ModuleTypeInfo_BuildSystem {
if m != nil && m.BuildSystem != nil {
return *m.BuildSystem
}
@@ -493,65 +510,65 @@
}
func init() {
- proto.RegisterType((*MetricsBase)(nil), "build_metrics.MetricsBase")
- proto.RegisterType((*PerfInfo)(nil), "build_metrics.PerfInfo")
- proto.RegisterType((*ModuleTypeInfo)(nil), "build_metrics.ModuleTypeInfo")
- proto.RegisterEnum("build_metrics.MetricsBase_BUILDVARIANT", MetricsBase_BUILDVARIANT_name, MetricsBase_BUILDVARIANT_value)
- proto.RegisterEnum("build_metrics.MetricsBase_ARCH", MetricsBase_ARCH_name, MetricsBase_ARCH_value)
- proto.RegisterEnum("build_metrics.ModuleTypeInfo_BUILDSYSTEM", ModuleTypeInfo_BUILDSYSTEM_name, ModuleTypeInfo_BUILDSYSTEM_value)
+ proto.RegisterEnum("soong_build_metrics.MetricsBase_BuildVariant", MetricsBase_BuildVariant_name, MetricsBase_BuildVariant_value)
+ proto.RegisterEnum("soong_build_metrics.MetricsBase_Arch", MetricsBase_Arch_name, MetricsBase_Arch_value)
+ proto.RegisterEnum("soong_build_metrics.ModuleTypeInfo_BuildSystem", ModuleTypeInfo_BuildSystem_name, ModuleTypeInfo_BuildSystem_value)
+ proto.RegisterType((*MetricsBase)(nil), "soong_build_metrics.MetricsBase")
+ proto.RegisterType((*PerfInfo)(nil), "soong_build_metrics.PerfInfo")
+ proto.RegisterType((*ModuleTypeInfo)(nil), "soong_build_metrics.ModuleTypeInfo")
}
-func init() { proto.RegisterFile("metrics.proto", fileDescriptor_metrics_9e7b895801991242) }
+func init() { proto.RegisterFile("metrics.proto", fileDescriptor_6039342a2ba47b72) }
-var fileDescriptor_metrics_9e7b895801991242 = []byte{
- // 783 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0xdd, 0x6e, 0xdb, 0x36,
- 0x14, 0xae, 0x62, 0x25, 0x96, 0x8e, 0x62, 0x57, 0x61, 0x02, 0x44, 0xc5, 0x50, 0x34, 0x30, 0xf6,
- 0x93, 0x01, 0x9b, 0x57, 0x18, 0x81, 0x11, 0x04, 0xbb, 0xb1, 0x13, 0xa3, 0x35, 0x5a, 0xdb, 0x85,
- 0x6c, 0x67, 0xdd, 0x2e, 0x46, 0x68, 0x12, 0xdd, 0x68, 0xb3, 0x44, 0x81, 0xa4, 0x8a, 0xf9, 0x21,
- 0xf6, 0x8c, 0x7b, 0x91, 0x5d, 0x0c, 0x3c, 0xb4, 0x5c, 0xa5, 0x17, 0x29, 0x72, 0x47, 0x9d, 0xef,
- 0x87, 0xdf, 0x91, 0xc8, 0x23, 0x68, 0x65, 0x4c, 0x89, 0x34, 0x96, 0xdd, 0x42, 0x70, 0xc5, 0x49,
- 0xeb, 0x8f, 0x32, 0x5d, 0x27, 0x74, 0x5b, 0xec, 0xfc, 0xe7, 0x80, 0x37, 0x31, 0xeb, 0x61, 0x24,
- 0x19, 0x79, 0x09, 0x27, 0x86, 0x90, 0x44, 0x8a, 0x51, 0x95, 0x66, 0x4c, 0xaa, 0x28, 0x2b, 0x02,
- 0xeb, 0xcc, 0x3a, 0x6f, 0x84, 0x04, 0xb1, 0x9b, 0x48, 0xb1, 0x45, 0x85, 0x90, 0x67, 0xe0, 0x18,
- 0x45, 0x9a, 0x04, 0x7b, 0x67, 0xd6, 0xb9, 0x1b, 0x36, 0xf1, 0x79, 0x9c, 0x90, 0x2b, 0x78, 0x56,
- 0xac, 0x23, 0xb5, 0xe2, 0x22, 0xa3, 0x1f, 0x99, 0x90, 0x29, 0xcf, 0x69, 0xcc, 0x13, 0x96, 0x47,
- 0x19, 0x0b, 0x1a, 0xc8, 0x3d, 0xad, 0x08, 0xb7, 0x06, 0xbf, 0xde, 0xc2, 0xe4, 0x1b, 0x68, 0xab,
- 0x48, 0x7c, 0x60, 0x8a, 0x16, 0x82, 0x27, 0x65, 0xac, 0x02, 0x1b, 0x05, 0x2d, 0x53, 0x7d, 0x67,
- 0x8a, 0xe4, 0x77, 0x38, 0xd9, 0xd2, 0x4c, 0x88, 0x8f, 0x91, 0x48, 0xa3, 0x5c, 0x05, 0xfb, 0x67,
- 0xd6, 0x79, 0xbb, 0xf7, 0x5d, 0xf7, 0x5e, 0xb7, 0xdd, 0x5a, 0xa7, 0xdd, 0xe1, 0x72, 0xfc, 0xf6,
- 0xe6, 0x76, 0x10, 0x8e, 0x07, 0xd3, 0xc5, 0x55, 0x63, 0x34, 0x7d, 0x15, 0x12, 0xe3, 0x34, 0xd4,
- 0x92, 0x5b, 0xe3, 0x43, 0xc6, 0xe0, 0x6d, 0xfd, 0x23, 0x11, 0xdf, 0x05, 0x07, 0x68, 0xfb, 0xe2,
- 0x01, 0xdb, 0x41, 0x78, 0xfd, 0xfa, 0xaa, 0xb9, 0x9c, 0xbe, 0x99, 0xce, 0x7e, 0x99, 0x86, 0x60,
- 0xc4, 0x03, 0x11, 0xdf, 0x91, 0x2e, 0x1c, 0xd7, 0xac, 0x76, 0x49, 0x9b, 0xd8, 0xd6, 0xd1, 0x27,
- 0x62, 0xb5, 0xf5, 0x0f, 0xb0, 0x0d, 0x44, 0xe3, 0xa2, 0xdc, 0xd1, 0x1d, 0xa4, 0xfb, 0x06, 0xb9,
- 0x2e, 0xca, 0x8a, 0x3d, 0x02, 0xf7, 0x8e, 0xcb, 0x6d, 0x4c, 0xf7, 0x91, 0x31, 0x1d, 0x2d, 0xc5,
- 0x90, 0x6f, 0xa1, 0x85, 0x36, 0xbd, 0x3c, 0x31, 0x56, 0xf0, 0x48, 0x2b, 0x4f, 0xcb, 0x7b, 0x79,
- 0x82, 0x6e, 0xa7, 0xd0, 0x44, 0x37, 0x2e, 0x03, 0x0f, 0x73, 0x1f, 0xe8, 0xc7, 0x99, 0x24, 0x9d,
- 0xed, 0x36, 0x5c, 0x52, 0xf6, 0xb7, 0x12, 0x51, 0x70, 0x88, 0xb0, 0x67, 0xe0, 0x91, 0x2e, 0xed,
- 0x38, 0xb1, 0xe0, 0x52, 0x6a, 0x8b, 0xd6, 0x27, 0xce, 0xb5, 0xae, 0xcd, 0x24, 0xf9, 0x16, 0x9e,
- 0xd6, 0x38, 0x18, 0xb8, 0x6d, 0x8e, 0xc9, 0x8e, 0x85, 0x41, 0x7e, 0x84, 0xe3, 0x1a, 0x6f, 0xd7,
- 0xdc, 0x53, 0xf3, 0x32, 0x77, 0xdc, 0x5a, 0x6e, 0x5e, 0x2a, 0x9a, 0xa4, 0x22, 0xf0, 0x4d, 0x6e,
- 0x5e, 0xaa, 0x9b, 0x54, 0x90, 0x4b, 0xf0, 0x24, 0x53, 0x65, 0x41, 0x15, 0xe7, 0x6b, 0x19, 0x1c,
- 0x9d, 0x35, 0xce, 0xbd, 0xde, 0xe9, 0x67, 0x2f, 0xe7, 0x1d, 0x13, 0xab, 0x71, 0xbe, 0xe2, 0x21,
- 0x20, 0x77, 0xa1, 0xa9, 0xe4, 0x02, 0xdc, 0xbf, 0x22, 0x95, 0x52, 0x51, 0xe6, 0x32, 0x20, 0x0f,
- 0xeb, 0x1c, 0xcd, 0x0c, 0xcb, 0x5c, 0x92, 0x3e, 0x80, 0xe4, 0x3c, 0xff, 0x60, 0x64, 0xc7, 0x0f,
- 0xcb, 0x5c, 0xa4, 0x56, 0xba, 0x3c, 0xcd, 0xff, 0x8c, 0x8c, 0xee, 0xe4, 0x0b, 0x3a, 0xa4, 0x6a,
- 0x5d, 0xe7, 0x25, 0x1c, 0xd6, 0xef, 0x05, 0x71, 0xc0, 0x5e, 0xce, 0x47, 0xa1, 0xff, 0x84, 0xb4,
- 0xc0, 0xd5, 0xab, 0x9b, 0xd1, 0x70, 0xf9, 0xca, 0xb7, 0x48, 0x13, 0xf4, 0x95, 0xf1, 0xf7, 0x3a,
- 0x3f, 0x83, 0xad, 0x0f, 0x00, 0xf1, 0xa0, 0x3a, 0x02, 0xfe, 0x13, 0x8d, 0x0e, 0xc2, 0x89, 0x6f,
- 0x11, 0x17, 0xf6, 0x07, 0xe1, 0xa4, 0x7f, 0xe1, 0xef, 0xe9, 0xda, 0xfb, 0xcb, 0xbe, 0xdf, 0x20,
- 0x00, 0x07, 0xef, 0x2f, 0xfb, 0xb4, 0x7f, 0xe1, 0xdb, 0x9d, 0x7f, 0x2c, 0x70, 0xaa, 0x1c, 0x84,
- 0x80, 0x9d, 0x30, 0x19, 0xe3, 0xac, 0x71, 0x43, 0x5c, 0xeb, 0x1a, 0x4e, 0x0b, 0x33, 0x59, 0x70,
- 0x4d, 0x9e, 0x03, 0x48, 0x15, 0x09, 0x85, 0xe3, 0x09, 0xe7, 0x88, 0x1d, 0xba, 0x58, 0xd1, 0x53,
- 0x89, 0x7c, 0x05, 0xae, 0x60, 0xd1, 0xda, 0xa0, 0x36, 0xa2, 0x8e, 0x2e, 0x20, 0xf8, 0x1c, 0x20,
- 0x63, 0x19, 0x17, 0x1b, 0x5a, 0x4a, 0x86, 0x53, 0xc2, 0x0e, 0x5d, 0x53, 0x59, 0x4a, 0xd6, 0xf9,
- 0xd7, 0x82, 0xf6, 0x84, 0x27, 0xe5, 0x9a, 0x2d, 0x36, 0x05, 0xc3, 0x54, 0x4b, 0x38, 0x34, 0xef,
- 0x4d, 0x6e, 0xa4, 0x62, 0x19, 0xa6, 0x6b, 0xf7, 0xbe, 0xff, 0xfc, 0x42, 0xdc, 0x13, 0x99, 0xe1,
- 0x32, 0xff, 0x75, 0xbe, 0x18, 0x4d, 0x6a, 0x57, 0x03, 0x25, 0x73, 0xb4, 0x21, 0x2f, 0xc0, 0xcb,
- 0x50, 0x43, 0xd5, 0xa6, 0xa8, 0xfa, 0x83, 0x6c, 0x67, 0x43, 0xbe, 0x86, 0x76, 0x5e, 0x66, 0x94,
- 0xaf, 0xa8, 0x29, 0x4a, 0xec, 0xb4, 0x15, 0x1e, 0xe6, 0x65, 0x36, 0x5b, 0x99, 0xfd, 0x64, 0xe7,
- 0x27, 0xf0, 0x6a, 0x7b, 0xdd, 0xff, 0x0a, 0x2e, 0xec, 0xcf, 0x67, 0xb3, 0xa9, 0xfe, 0x5c, 0x0e,
- 0xd8, 0x93, 0xc1, 0x9b, 0x91, 0xbf, 0x37, 0x3c, 0x7a, 0xdd, 0xf8, 0xad, 0xfa, 0x25, 0x50, 0xfc,
- 0x25, 0xfc, 0x1f, 0x00, 0x00, 0xff, 0xff, 0xd4, 0x8d, 0x19, 0x89, 0x22, 0x06, 0x00, 0x00,
+var fileDescriptor_6039342a2ba47b72 = []byte{
+ // 769 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0x6f, 0x6b, 0xdb, 0x46,
+ 0x18, 0xaf, 0x62, 0x25, 0x96, 0x1e, 0xc5, 0xae, 0x7a, 0xc9, 0xa8, 0xca, 0x08, 0x33, 0x66, 0x1d,
+ 0x7e, 0xb1, 0xba, 0xc5, 0x14, 0x53, 0x4c, 0x19, 0xd8, 0x89, 0x29, 0x25, 0xd8, 0x2e, 0x4a, 0xdc,
+ 0x95, 0xed, 0xc5, 0xa1, 0x4a, 0xe7, 0x46, 0x9b, 0xa5, 0x13, 0x77, 0xa7, 0x32, 0x7f, 0x88, 0x7d,
+ 0x93, 0x7d, 0xad, 0x7d, 0x8f, 0x71, 0xcf, 0x49, 0x8e, 0x02, 0x81, 0x85, 0xbe, 0x3b, 0x3d, 0xbf,
+ 0x3f, 0xf7, 0x7b, 0x4e, 0xba, 0x47, 0xd0, 0xc9, 0x98, 0x12, 0x69, 0x2c, 0x87, 0x85, 0xe0, 0x8a,
+ 0x93, 0x13, 0xc9, 0x79, 0xfe, 0x85, 0x7e, 0x2e, 0xd3, 0x6d, 0x42, 0x2b, 0xa8, 0xff, 0x8f, 0x0b,
+ 0xde, 0xc2, 0xac, 0x67, 0x91, 0x64, 0xe4, 0x15, 0x9c, 0x1a, 0x42, 0x12, 0x29, 0x46, 0x55, 0x9a,
+ 0x31, 0xa9, 0xa2, 0xac, 0x08, 0xac, 0x9e, 0x35, 0x68, 0x85, 0x04, 0xb1, 0x8b, 0x48, 0xb1, 0xeb,
+ 0x1a, 0x21, 0xcf, 0xc0, 0x31, 0x8a, 0x34, 0x09, 0x0e, 0x7a, 0xd6, 0xc0, 0x0d, 0xdb, 0xf8, 0xfc,
+ 0x3e, 0x21, 0x13, 0x78, 0x56, 0x6c, 0x23, 0xb5, 0xe1, 0x22, 0xa3, 0x5f, 0x99, 0x90, 0x29, 0xcf,
+ 0x69, 0xcc, 0x13, 0x96, 0x47, 0x19, 0x0b, 0x5a, 0xc8, 0x7d, 0x5a, 0x13, 0x3e, 0x1a, 0xfc, 0xbc,
+ 0x82, 0xc9, 0x73, 0xe8, 0xaa, 0x48, 0x7c, 0x61, 0x8a, 0x16, 0x82, 0x27, 0x65, 0xac, 0x02, 0x1b,
+ 0x05, 0x1d, 0x53, 0xfd, 0x60, 0x8a, 0x24, 0x81, 0xd3, 0x8a, 0x66, 0x42, 0x7c, 0x8d, 0x44, 0x1a,
+ 0xe5, 0x2a, 0x38, 0xec, 0x59, 0x83, 0xee, 0xe8, 0xc5, 0xf0, 0x9e, 0x9e, 0x87, 0x8d, 0x7e, 0x87,
+ 0x33, 0x8d, 0x7c, 0x34, 0xa2, 0x49, 0x6b, 0xbe, 0x7c, 0x17, 0x12, 0xe3, 0xd7, 0x04, 0xc8, 0x0a,
+ 0xbc, 0x6a, 0x97, 0x48, 0xc4, 0x37, 0xc1, 0x11, 0x9a, 0x3f, 0xff, 0x5f, 0xf3, 0xa9, 0x88, 0x6f,
+ 0x26, 0xed, 0xf5, 0xf2, 0x72, 0xb9, 0xfa, 0x75, 0x19, 0x82, 0xb1, 0xd0, 0x45, 0x32, 0x84, 0x93,
+ 0x86, 0xe1, 0x3e, 0x75, 0x1b, 0x5b, 0x7c, 0x72, 0x4b, 0xac, 0x03, 0xfc, 0x0c, 0x55, 0x2c, 0x1a,
+ 0x17, 0xe5, 0x9e, 0xee, 0x20, 0xdd, 0x37, 0xc8, 0x79, 0x51, 0xd6, 0xec, 0x4b, 0x70, 0x6f, 0xb8,
+ 0xac, 0xc2, 0xba, 0xdf, 0x14, 0xd6, 0xd1, 0x06, 0x18, 0x35, 0x84, 0x0e, 0x9a, 0x8d, 0xf2, 0xc4,
+ 0x18, 0xc2, 0x37, 0x19, 0x7a, 0xda, 0x64, 0x94, 0x27, 0xe8, 0xf9, 0x14, 0xda, 0xe8, 0xc9, 0x65,
+ 0xe0, 0x61, 0x0f, 0x47, 0xfa, 0x71, 0x25, 0x49, 0xbf, 0xda, 0x8c, 0x4b, 0xca, 0xfe, 0x52, 0x22,
+ 0x0a, 0x8e, 0x11, 0xf6, 0x0c, 0x3c, 0xd7, 0xa5, 0x3d, 0x27, 0x16, 0x5c, 0x4a, 0x6d, 0xd1, 0xb9,
+ 0xe5, 0x9c, 0xeb, 0xda, 0x4a, 0x92, 0x9f, 0xe0, 0x71, 0x83, 0x83, 0xb1, 0xbb, 0xe6, 0xf3, 0xd9,
+ 0xb3, 0x30, 0xc8, 0x0b, 0x38, 0x69, 0xf0, 0xf6, 0x2d, 0x3e, 0x36, 0x07, 0xbb, 0xe7, 0x36, 0x72,
+ 0xf3, 0x52, 0xd1, 0x24, 0x15, 0x81, 0x6f, 0x72, 0xf3, 0x52, 0x5d, 0xa4, 0x82, 0xfc, 0x02, 0x9e,
+ 0x64, 0xaa, 0x2c, 0xa8, 0xe2, 0x7c, 0x2b, 0x83, 0x27, 0xbd, 0xd6, 0xc0, 0x1b, 0x9d, 0xdd, 0x7b,
+ 0x44, 0x1f, 0x98, 0xd8, 0xbc, 0xcf, 0x37, 0x3c, 0x04, 0x54, 0x5c, 0x6b, 0x01, 0x99, 0x80, 0xfb,
+ 0x67, 0xa4, 0x52, 0x2a, 0xca, 0x5c, 0x06, 0xe4, 0x21, 0x6a, 0x47, 0xf3, 0xc3, 0x32, 0x97, 0xe4,
+ 0x2d, 0x80, 0x61, 0xa2, 0xf8, 0xe4, 0x21, 0x62, 0x17, 0xd1, 0x5a, 0x9d, 0xa7, 0xf9, 0x1f, 0x91,
+ 0x51, 0x9f, 0x3e, 0x48, 0x8d, 0x02, 0xad, 0xee, 0xbf, 0x82, 0xe3, 0x3b, 0x17, 0xc5, 0x01, 0x7b,
+ 0x7d, 0x35, 0x0f, 0xfd, 0x47, 0xa4, 0x03, 0xae, 0x5e, 0x5d, 0xcc, 0x67, 0xeb, 0x77, 0xbe, 0x45,
+ 0xda, 0xa0, 0x2f, 0x97, 0x7f, 0xd0, 0x7f, 0x0b, 0x36, 0x1e, 0xa5, 0x07, 0xf5, 0xa7, 0xe1, 0x3f,
+ 0xd2, 0xe8, 0x34, 0x5c, 0xf8, 0x16, 0x71, 0xe1, 0x70, 0x1a, 0x2e, 0xc6, 0xaf, 0xfd, 0x03, 0x5d,
+ 0xfb, 0xf4, 0x66, 0xec, 0xb7, 0x08, 0xc0, 0xd1, 0xa7, 0x37, 0x63, 0x3a, 0x7e, 0xed, 0xdb, 0xfd,
+ 0xbf, 0x2d, 0x70, 0xea, 0x1c, 0x84, 0x80, 0x9d, 0x30, 0x19, 0xe3, 0x6c, 0x72, 0x43, 0x5c, 0xeb,
+ 0x1a, 0x4e, 0x17, 0x33, 0x89, 0x70, 0x4d, 0xce, 0x00, 0xa4, 0x8a, 0x84, 0xc2, 0x71, 0x86, 0x73,
+ 0xc7, 0x0e, 0x5d, 0xac, 0xe8, 0x29, 0x46, 0xbe, 0x07, 0x57, 0xb0, 0x68, 0x6b, 0x50, 0x1b, 0x51,
+ 0x47, 0x17, 0x10, 0x3c, 0x03, 0xc8, 0x58, 0xc6, 0xc5, 0x8e, 0x96, 0x92, 0xe1, 0x54, 0xb1, 0x43,
+ 0xd7, 0x54, 0xd6, 0x92, 0xf5, 0xff, 0xb5, 0xa0, 0xbb, 0xe0, 0x49, 0xb9, 0x65, 0xd7, 0xbb, 0x82,
+ 0x61, 0xaa, 0xdf, 0xe1, 0xd8, 0x9c, 0x9b, 0xdc, 0x49, 0xc5, 0x32, 0x4c, 0xd7, 0x1d, 0xbd, 0xbc,
+ 0xff, 0xba, 0xdc, 0x91, 0x9a, 0x61, 0x74, 0x85, 0xb2, 0xc6, 0xc5, 0xf9, 0x7c, 0x5b, 0x25, 0x3f,
+ 0x80, 0x97, 0xa1, 0x86, 0xaa, 0x5d, 0x51, 0x77, 0x09, 0xd9, 0xde, 0x86, 0xfc, 0x08, 0xdd, 0xbc,
+ 0xcc, 0x28, 0xdf, 0x50, 0x53, 0x94, 0xd8, 0x6f, 0x27, 0x3c, 0xce, 0xcb, 0x6c, 0xb5, 0x31, 0xfb,
+ 0xc9, 0xfe, 0x4b, 0xf0, 0x1a, 0x7b, 0xdd, 0x7d, 0x17, 0x2e, 0x1c, 0x5e, 0xad, 0x56, 0x4b, 0xfd,
+ 0xd2, 0x1c, 0xb0, 0x17, 0xd3, 0xcb, 0xb9, 0x7f, 0x30, 0xfb, 0xee, 0xb7, 0xea, 0xef, 0x51, 0x25,
+ 0xa7, 0xf8, 0x4b, 0xf9, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x81, 0xd0, 0x84, 0x23, 0x62, 0x06, 0x00,
+ 0x00,
}
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index b3de2f4..93034eb 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -14,10 +14,8 @@
syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
-
-package build_metrics;
-option go_package = "metrics_proto";
+package soong_build_metrics;
+option go_package = "soong_metrics_proto";
message MetricsBase {
// Timestamp generated when the build starts.
@@ -32,15 +30,15 @@
// The target product information, eg. aosp_arm.
optional string target_product = 4;
- enum BUILDVARIANT {
+ enum BuildVariant {
USER = 0;
USERDEBUG = 1;
ENG = 2;
}
// The target build variant information, eg. eng.
- optional BUILDVARIANT target_build_variant = 5 [default = ENG];
+ optional BuildVariant target_build_variant = 5 [default = ENG];
- enum ARCH {
+ enum Arch {
UNKNOWN = 0;
ARM = 1;
ARM64 = 2;
@@ -48,7 +46,7 @@
X86_64 = 4;
}
// The target arch information, eg. arm.
- optional ARCH target_arch = 6 [default = UNKNOWN];
+ optional Arch target_arch = 6 [default = UNKNOWN];
// The target arch variant information, eg. armv7-a-neon.
optional string target_arch_variant = 7;
@@ -57,10 +55,10 @@
optional string target_cpu_variant = 8;
// The host arch information, eg. x86_64.
- optional ARCH host_arch = 9 [default = UNKNOWN];
+ optional Arch host_arch = 9 [default = UNKNOWN];
// The host 2nd arch information, eg. x86.
- optional ARCH host_2nd_arch = 10 [default = UNKNOWN];
+ optional Arch host_2nd_arch = 10 [default = UNKNOWN];
// The host os information, eg. linux.
optional string host_os = 11;
@@ -113,13 +111,13 @@
}
message ModuleTypeInfo {
- enum BUILDSYSTEM {
+ enum BuildSystem {
UNKNOWN = 0;
SOONG = 1;
MAKE = 2;
}
// The build system, eg. Soong or Make.
- optional BUILDSYSTEM build_system = 1 [default = UNKNOWN];
+ optional BuildSystem build_system = 1 [default = UNKNOWN];
// The module type, eg. java_library, cc_binary, and etc.
optional string module_type = 2;
diff --git a/ui/metrics/time.go b/ui/metrics/time.go
index 7e8801a..b8baf16 100644
--- a/ui/metrics/time.go
+++ b/ui/metrics/time.go
@@ -30,7 +30,7 @@
type TimeTracer interface {
Begin(name, desc string, thread tracer.Thread)
- End(thread tracer.Thread) metrics_proto.PerfInfo
+ End(thread tracer.Thread) soong_metrics_proto.PerfInfo
}
type timeTracerImpl struct {
@@ -51,11 +51,11 @@
t.activeEvents = append(t.activeEvents, timeEvent{name: name, desc: desc, atNanos: atNanos})
}
-func (t *timeTracerImpl) End(thread tracer.Thread) metrics_proto.PerfInfo {
+func (t *timeTracerImpl) End(thread tracer.Thread) soong_metrics_proto.PerfInfo {
return t.endAt(t.now())
}
-func (t *timeTracerImpl) endAt(atNanos uint64) metrics_proto.PerfInfo {
+func (t *timeTracerImpl) endAt(atNanos uint64) soong_metrics_proto.PerfInfo {
if len(t.activeEvents) < 1 {
panic("Internal error: No pending events for endAt to end!")
}
@@ -63,7 +63,7 @@
t.activeEvents = t.activeEvents[:len(t.activeEvents)-1]
realTime := atNanos - lastEvent.atNanos
- return metrics_proto.PerfInfo{
+ return soong_metrics_proto.PerfInfo{
Desc: &lastEvent.desc,
Name: &lastEvent.name,
StartTime: &lastEvent.atNanos,
diff --git a/ui/status/Android.bp b/ui/status/Android.bp
index 901a713..ec929b3 100644
--- a/ui/status/Android.bp
+++ b/ui/status/Android.bp
@@ -19,14 +19,17 @@
"golang-protobuf-proto",
"soong-ui-logger",
"soong-ui-status-ninja_frontend",
+ "soong-ui-status-build_error_proto",
],
srcs: [
+ "critical_path.go",
"kati.go",
"log.go",
"ninja.go",
"status.go",
],
testSrcs: [
+ "critical_path_test.go",
"kati_test.go",
"ninja_test.go",
"status_test.go",
@@ -41,3 +44,12 @@
"ninja_frontend/frontend.pb.go",
],
}
+
+bootstrap_go_package {
+ name: "soong-ui-status-build_error_proto",
+ pkgPath: "android/soong/ui/status/build_error_proto",
+ deps: ["golang-protobuf-proto"],
+ srcs: [
+ "build_error_proto/build_error.pb.go",
+ ],
+}
diff --git a/ui/status/build_error_proto/build_error.pb.go b/ui/status/build_error_proto/build_error.pb.go
new file mode 100644
index 0000000..d4d0a6e
--- /dev/null
+++ b/ui/status/build_error_proto/build_error.pb.go
@@ -0,0 +1,175 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: build_error.proto
+
+package soong_build_error_proto
+
+import (
+ fmt "fmt"
+ proto "github.com/golang/protobuf/proto"
+ math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type BuildError struct {
+ // List of error messages of the overall build. The error messages
+ // are not associated with a build action.
+ ErrorMessages []string `protobuf:"bytes,1,rep,name=error_messages,json=errorMessages" json:"error_messages,omitempty"`
+ // List of build action errors.
+ ActionErrors []*BuildActionError `protobuf:"bytes,2,rep,name=action_errors,json=actionErrors" json:"action_errors,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *BuildError) Reset() { *m = BuildError{} }
+func (m *BuildError) String() string { return proto.CompactTextString(m) }
+func (*BuildError) ProtoMessage() {}
+func (*BuildError) Descriptor() ([]byte, []int) {
+ return fileDescriptor_a2e15b05802a5501, []int{0}
+}
+
+func (m *BuildError) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_BuildError.Unmarshal(m, b)
+}
+func (m *BuildError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_BuildError.Marshal(b, m, deterministic)
+}
+func (m *BuildError) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_BuildError.Merge(m, src)
+}
+func (m *BuildError) XXX_Size() int {
+ return xxx_messageInfo_BuildError.Size(m)
+}
+func (m *BuildError) XXX_DiscardUnknown() {
+ xxx_messageInfo_BuildError.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_BuildError proto.InternalMessageInfo
+
+func (m *BuildError) GetErrorMessages() []string {
+ if m != nil {
+ return m.ErrorMessages
+ }
+ return nil
+}
+
+func (m *BuildError) GetActionErrors() []*BuildActionError {
+ if m != nil {
+ return m.ActionErrors
+ }
+ return nil
+}
+
+// Build is composed of a list of build action. There can be a set of build
+// actions that can failed.
+type BuildActionError struct {
+ // Description of the command.
+ Description *string `protobuf:"bytes,1,opt,name=description" json:"description,omitempty"`
+ // The command name that raised the error.
+ Command *string `protobuf:"bytes,2,opt,name=command" json:"command,omitempty"`
+ // The command output stream.
+ Output *string `protobuf:"bytes,3,opt,name=output" json:"output,omitempty"`
+ // List of artifacts (i.e. files) that was produced by the command.
+ Artifacts []string `protobuf:"bytes,4,rep,name=artifacts" json:"artifacts,omitempty"`
+ // The error string produced by the build action.
+ Error *string `protobuf:"bytes,5,opt,name=error" json:"error,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *BuildActionError) Reset() { *m = BuildActionError{} }
+func (m *BuildActionError) String() string { return proto.CompactTextString(m) }
+func (*BuildActionError) ProtoMessage() {}
+func (*BuildActionError) Descriptor() ([]byte, []int) {
+ return fileDescriptor_a2e15b05802a5501, []int{1}
+}
+
+func (m *BuildActionError) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_BuildActionError.Unmarshal(m, b)
+}
+func (m *BuildActionError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_BuildActionError.Marshal(b, m, deterministic)
+}
+func (m *BuildActionError) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_BuildActionError.Merge(m, src)
+}
+func (m *BuildActionError) XXX_Size() int {
+ return xxx_messageInfo_BuildActionError.Size(m)
+}
+func (m *BuildActionError) XXX_DiscardUnknown() {
+ xxx_messageInfo_BuildActionError.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_BuildActionError proto.InternalMessageInfo
+
+func (m *BuildActionError) GetDescription() string {
+ if m != nil && m.Description != nil {
+ return *m.Description
+ }
+ return ""
+}
+
+func (m *BuildActionError) GetCommand() string {
+ if m != nil && m.Command != nil {
+ return *m.Command
+ }
+ return ""
+}
+
+func (m *BuildActionError) GetOutput() string {
+ if m != nil && m.Output != nil {
+ return *m.Output
+ }
+ return ""
+}
+
+func (m *BuildActionError) GetArtifacts() []string {
+ if m != nil {
+ return m.Artifacts
+ }
+ return nil
+}
+
+func (m *BuildActionError) GetError() string {
+ if m != nil && m.Error != nil {
+ return *m.Error
+ }
+ return ""
+}
+
+func init() {
+ proto.RegisterType((*BuildError)(nil), "soong_build_error.BuildError")
+ proto.RegisterType((*BuildActionError)(nil), "soong_build_error.BuildActionError")
+}
+
+func init() { proto.RegisterFile("build_error.proto", fileDescriptor_a2e15b05802a5501) }
+
+var fileDescriptor_a2e15b05802a5501 = []byte{
+ // 229 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0xc1, 0x4a, 0xc3, 0x40,
+ 0x10, 0x86, 0x49, 0x63, 0x95, 0x4c, 0xad, 0xd8, 0x41, 0x74, 0x04, 0x0f, 0xa1, 0x22, 0xe4, 0x94,
+ 0x83, 0x6f, 0x60, 0x41, 0xf0, 0xe2, 0x25, 0x47, 0x2f, 0x61, 0xdd, 0xac, 0x65, 0xc1, 0x64, 0xc2,
+ 0xce, 0xe6, 0xe8, 0x8b, 0xf8, 0xb4, 0x92, 0x69, 0xa5, 0xa5, 0x39, 0x7e, 0xdf, 0x3f, 0xfb, 0xef,
+ 0xce, 0xc2, 0xea, 0x73, 0xf0, 0xdf, 0x4d, 0xed, 0x42, 0xe0, 0x50, 0xf6, 0x81, 0x23, 0xe3, 0x4a,
+ 0x98, 0xbb, 0x6d, 0x7d, 0x14, 0xac, 0x7f, 0x00, 0x36, 0x23, 0xbe, 0x8e, 0x84, 0x4f, 0x70, 0xa5,
+ 0xba, 0x6e, 0x9d, 0x88, 0xd9, 0x3a, 0xa1, 0x24, 0x4f, 0x8b, 0xac, 0x5a, 0xaa, 0x7d, 0xdf, 0x4b,
+ 0x7c, 0x83, 0xa5, 0xb1, 0xd1, 0x73, 0xb7, 0x2b, 0x11, 0x9a, 0xe5, 0x69, 0xb1, 0x78, 0x7e, 0x2c,
+ 0x27, 0xfd, 0xa5, 0x96, 0xbf, 0xe8, 0xb0, 0x5e, 0x51, 0x5d, 0x9a, 0x03, 0xc8, 0xfa, 0x37, 0x81,
+ 0xeb, 0xd3, 0x11, 0xcc, 0x61, 0xd1, 0x38, 0xb1, 0xc1, 0xf7, 0xa3, 0xa3, 0x24, 0x4f, 0x8a, 0xac,
+ 0x3a, 0x56, 0x48, 0x70, 0x61, 0xb9, 0x6d, 0x4d, 0xd7, 0xd0, 0x4c, 0xd3, 0x7f, 0xc4, 0x5b, 0x38,
+ 0xe7, 0x21, 0xf6, 0x43, 0xa4, 0x54, 0x83, 0x3d, 0xe1, 0x03, 0x64, 0x26, 0x44, 0xff, 0x65, 0x6c,
+ 0x14, 0x3a, 0xd3, 0xa5, 0x0e, 0x02, 0x6f, 0x60, 0xae, 0xcf, 0xa5, 0xb9, 0x1e, 0xda, 0xc1, 0xe6,
+ 0xfe, 0xe3, 0x6e, 0xb2, 0x50, 0xad, 0x3f, 0xf9, 0x17, 0x00, 0x00, 0xff, 0xff, 0xb6, 0x18, 0x9e,
+ 0x17, 0x5d, 0x01, 0x00, 0x00,
+}
diff --git a/ui/status/build_error_proto/build_error.proto b/ui/status/build_error_proto/build_error.proto
new file mode 100644
index 0000000..9c8470d
--- /dev/null
+++ b/ui/status/build_error_proto/build_error.proto
@@ -0,0 +1,46 @@
+// 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.
+
+syntax = "proto2";
+
+package soong_build_error;
+option go_package = "soong_build_error_proto";
+
+message BuildError {
+ // List of error messages of the overall build. The error messages
+ // are not associated with a build action.
+ repeated string error_messages = 1;
+
+ // List of build action errors.
+ repeated BuildActionError action_errors = 2;
+}
+
+// Build is composed of a list of build action. There can be a set of build
+// actions that can failed.
+message BuildActionError {
+ // Description of the command.
+ optional string description = 1;
+
+ // The command name that raised the error.
+ optional string command = 2;
+
+ // The command output stream.
+ optional string output = 3;
+
+ // List of artifacts (i.e. files) that was produced by the command.
+ repeated string artifacts = 4;
+
+ // The error string produced by the build action.
+ optional string error = 5;
+}
diff --git a/ui/status/build_error_proto/regen.sh b/ui/status/build_error_proto/regen.sh
new file mode 100755
index 0000000..7c3ec8f
--- /dev/null
+++ b/ui/status/build_error_proto/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. build_error.proto
diff --git a/ui/status/critical_path.go b/ui/status/critical_path.go
new file mode 100644
index 0000000..444327b
--- /dev/null
+++ b/ui/status/critical_path.go
@@ -0,0 +1,152 @@
+// 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 status
+
+import (
+ "time"
+
+ "android/soong/ui/logger"
+)
+
+func NewCriticalPath(log logger.Logger) StatusOutput {
+ return &criticalPath{
+ log: log,
+ running: make(map[*Action]time.Time),
+ nodes: make(map[string]*node),
+ clock: osClock{},
+ }
+}
+
+type criticalPath struct {
+ log logger.Logger
+
+ nodes map[string]*node
+ running map[*Action]time.Time
+
+ start, end time.Time
+
+ clock clock
+}
+
+type clock interface {
+ Now() time.Time
+}
+
+type osClock struct{}
+
+func (osClock) Now() time.Time { return time.Now() }
+
+// A critical path node stores the critical path (the minimum time to build the node and all of its dependencies given
+// perfect parallelism) for an node.
+type node struct {
+ action *Action
+ cumulativeDuration time.Duration
+ duration time.Duration
+ input *node
+}
+
+func (cp *criticalPath) StartAction(action *Action, counts Counts) {
+ start := cp.clock.Now()
+ if cp.start.IsZero() {
+ cp.start = start
+ }
+ cp.running[action] = start
+}
+
+func (cp *criticalPath) FinishAction(result ActionResult, counts Counts) {
+ if start, ok := cp.running[result.Action]; ok {
+ delete(cp.running, result.Action)
+
+ // Determine the input to this edge with the longest cumulative duration
+ var criticalPathInput *node
+ for _, input := range result.Action.Inputs {
+ if x := cp.nodes[input]; x != nil {
+ if criticalPathInput == nil || x.cumulativeDuration > criticalPathInput.cumulativeDuration {
+ criticalPathInput = x
+ }
+ }
+ }
+
+ end := cp.clock.Now()
+ duration := end.Sub(start)
+
+ cumulativeDuration := duration
+ if criticalPathInput != nil {
+ cumulativeDuration += criticalPathInput.cumulativeDuration
+ }
+
+ node := &node{
+ action: result.Action,
+ cumulativeDuration: cumulativeDuration,
+ duration: duration,
+ input: criticalPathInput,
+ }
+
+ for _, output := range result.Action.Outputs {
+ cp.nodes[output] = node
+ }
+
+ cp.end = end
+ }
+}
+
+func (cp *criticalPath) Flush() {
+ criticalPath := cp.criticalPath()
+
+ if len(criticalPath) > 0 {
+ // Log the critical path to the verbose log
+ criticalTime := criticalPath[0].cumulativeDuration.Round(time.Second)
+ cp.log.Verbosef("critical path took %s", criticalTime.String())
+ if !cp.start.IsZero() {
+ elapsedTime := cp.end.Sub(cp.start).Round(time.Second)
+ cp.log.Verbosef("elapsed time %s", elapsedTime.String())
+ cp.log.Verbosef("perfect parallelism ratio %d%%",
+ int(float64(criticalTime)/float64(elapsedTime)*100))
+ }
+ cp.log.Verbose("critical path:")
+ for i := len(criticalPath) - 1; i >= 0; i-- {
+ duration := criticalPath[i].duration
+ duration = duration.Round(time.Second)
+ seconds := int(duration.Seconds())
+ cp.log.Verbosef(" %2d:%02d %s",
+ seconds/60, seconds%60, criticalPath[i].action.Description)
+ }
+ }
+}
+
+func (cp *criticalPath) Message(level MsgLevel, msg string) {}
+
+func (cp *criticalPath) Write(p []byte) (n int, err error) { return len(p), nil }
+
+func (cp *criticalPath) criticalPath() []*node {
+ var max *node
+
+ // Find the node with the longest critical path
+ for _, node := range cp.nodes {
+ if max == nil || node.cumulativeDuration > max.cumulativeDuration {
+ max = node
+ }
+ }
+
+ // Follow the critical path back to the leaf node
+ var criticalPath []*node
+ node := max
+ for node != nil {
+ criticalPath = append(criticalPath, node)
+ node = node.input
+ }
+
+ return criticalPath
+}
diff --git a/ui/status/critical_path_test.go b/ui/status/critical_path_test.go
new file mode 100644
index 0000000..965e0ad
--- /dev/null
+++ b/ui/status/critical_path_test.go
@@ -0,0 +1,166 @@
+// 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 status
+
+import (
+ "reflect"
+ "testing"
+ "time"
+)
+
+type testCriticalPath struct {
+ *criticalPath
+ Counts
+
+ actions map[int]*Action
+}
+
+type testClock time.Time
+
+func (t testClock) Now() time.Time { return time.Time(t) }
+
+func (t *testCriticalPath) start(id int, startTime time.Duration, outputs, inputs []string) {
+ t.clock = testClock(time.Unix(0, 0).Add(startTime))
+ action := &Action{
+ Description: outputs[0],
+ Outputs: outputs,
+ Inputs: inputs,
+ }
+
+ t.actions[id] = action
+ t.StartAction(action, t.Counts)
+}
+
+func (t *testCriticalPath) finish(id int, endTime time.Duration) {
+ t.clock = testClock(time.Unix(0, 0).Add(endTime))
+ t.FinishAction(ActionResult{
+ Action: t.actions[id],
+ }, t.Counts)
+}
+
+func TestCriticalPath(t *testing.T) {
+ tests := []struct {
+ name string
+ msgs func(*testCriticalPath)
+ want []string
+ wantTime time.Duration
+ }{
+ {
+ name: "empty",
+ msgs: func(cp *testCriticalPath) {},
+ },
+ {
+ name: "duplicate",
+ msgs: func(cp *testCriticalPath) {
+ cp.start(0, 0, []string{"a"}, nil)
+ cp.start(1, 0, []string{"a"}, nil)
+ cp.finish(0, 1000)
+ cp.finish(0, 2000)
+ },
+ want: []string{"a"},
+ wantTime: 1000,
+ },
+ {
+ name: "linear",
+ // a
+ // |
+ // b
+ // |
+ // c
+ msgs: func(cp *testCriticalPath) {
+ cp.start(0, 0, []string{"a"}, nil)
+ cp.finish(0, 1000)
+ cp.start(1, 1000, []string{"b"}, []string{"a"})
+ cp.finish(1, 2000)
+ cp.start(2, 3000, []string{"c"}, []string{"b"})
+ cp.finish(2, 4000)
+ },
+ want: []string{"c", "b", "a"},
+ wantTime: 3000,
+ },
+ {
+ name: "diamond",
+ // a
+ // |\
+ // b c
+ // |/
+ // d
+ msgs: func(cp *testCriticalPath) {
+ cp.start(0, 0, []string{"a"}, nil)
+ cp.finish(0, 1000)
+ cp.start(1, 1000, []string{"b"}, []string{"a"})
+ cp.start(2, 1000, []string{"c"}, []string{"a"})
+ cp.finish(1, 2000)
+ cp.finish(2, 3000)
+ cp.start(3, 3000, []string{"d"}, []string{"b", "c"})
+ cp.finish(3, 4000)
+ },
+ want: []string{"d", "c", "a"},
+ wantTime: 4000,
+ },
+ {
+ name: "multiple",
+ // a d
+ // | |
+ // b e
+ // |
+ // c
+ msgs: func(cp *testCriticalPath) {
+ cp.start(0, 0, []string{"a"}, nil)
+ cp.start(3, 0, []string{"d"}, nil)
+ cp.finish(0, 1000)
+ cp.finish(3, 1000)
+ cp.start(1, 1000, []string{"b"}, []string{"a"})
+ cp.start(4, 1000, []string{"e"}, []string{"d"})
+ cp.finish(1, 2000)
+ cp.start(2, 2000, []string{"c"}, []string{"b"})
+ cp.finish(2, 3000)
+ cp.finish(4, 4000)
+
+ },
+ want: []string{"e", "d"},
+ wantTime: 4000,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ cp := &testCriticalPath{
+ criticalPath: NewCriticalPath(nil).(*criticalPath),
+ actions: make(map[int]*Action),
+ }
+
+ tt.msgs(cp)
+
+ criticalPath := cp.criticalPath.criticalPath()
+
+ var descs []string
+ for _, x := range criticalPath {
+ descs = append(descs, x.action.Description)
+ }
+
+ if !reflect.DeepEqual(descs, tt.want) {
+ t.Errorf("criticalPath.criticalPath() = %v, want %v", descs, tt.want)
+ }
+
+ var gotTime time.Duration
+ if len(criticalPath) > 0 {
+ gotTime = criticalPath[0].cumulativeDuration
+ }
+ if gotTime != tt.wantTime {
+ t.Errorf("cumulativeDuration[0].cumulativeDuration = %v, want %v", gotTime, tt.wantTime)
+ }
+ })
+ }
+}
diff --git a/ui/status/log.go b/ui/status/log.go
index 7badac7..9090f49 100644
--- a/ui/status/log.go
+++ b/ui/status/log.go
@@ -15,11 +15,17 @@
package status
import (
- "android/soong/ui/logger"
"compress/gzip"
+ "errors"
"fmt"
"io"
+ "io/ioutil"
"strings"
+
+ "github.com/golang/protobuf/proto"
+
+ "android/soong/ui/logger"
+ "android/soong/ui/status/build_error_proto"
)
type verboseLog struct {
@@ -77,8 +83,7 @@
}
type errorLog struct {
- w io.WriteCloser
-
+ w io.WriteCloser
empty bool
}
@@ -102,20 +107,17 @@
return
}
- cmd := result.Command
- if cmd == "" {
- cmd = result.Description
- }
-
if !e.empty {
fmt.Fprintf(e.w, "\n\n")
}
e.empty = false
fmt.Fprintf(e.w, "FAILED: %s\n", result.Description)
+
if len(result.Outputs) > 0 {
fmt.Fprintf(e.w, "Outputs: %s\n", strings.Join(result.Outputs, " "))
}
+
fmt.Fprintf(e.w, "Error: %s\n", result.Error)
if result.Command != "" {
fmt.Fprintf(e.w, "Command: %s\n", result.Command)
@@ -144,3 +146,55 @@
fmt.Fprint(e.w, string(p))
return len(p), nil
}
+
+type errorProtoLog struct {
+ errorProto soong_build_error_proto.BuildError
+ filename string
+ log logger.Logger
+}
+
+func NewProtoErrorLog(log logger.Logger, filename string) StatusOutput {
+ return &errorProtoLog{
+ errorProto: soong_build_error_proto.BuildError{},
+ filename: filename,
+ log: log,
+ }
+}
+
+func (e *errorProtoLog) StartAction(action *Action, counts Counts) {}
+
+func (e *errorProtoLog) FinishAction(result ActionResult, counts Counts) {
+ if result.Error == nil {
+ return
+ }
+
+ e.errorProto.ActionErrors = append(e.errorProto.ActionErrors, &soong_build_error_proto.BuildActionError{
+ Description: proto.String(result.Description),
+ Command: proto.String(result.Command),
+ Output: proto.String(result.Output),
+ Artifacts: result.Outputs,
+ Error: proto.String(result.Error.Error()),
+ })
+}
+
+func (e *errorProtoLog) Flush() {
+ data, err := proto.Marshal(&e.errorProto)
+ if err != nil {
+ e.log.Println("Failed to marshal build status proto: %v", err)
+ return
+ }
+ err = ioutil.WriteFile(e.filename, []byte(data), 0644)
+ if err != nil {
+ e.log.Println("Failed to write file %s: %v", e.errorProto, err)
+ }
+}
+
+func (e *errorProtoLog) Message(level MsgLevel, message string) {
+ if level > ErrorLvl {
+ e.errorProto.ErrorMessages = append(e.errorProto.ErrorMessages, message)
+ }
+}
+
+func (e *errorProtoLog) Write(p []byte) (int, error) {
+ return 0, errors.New("not supported")
+}
diff --git a/ui/status/ninja.go b/ui/status/ninja.go
index ee2a2da..9cf2f6a 100644
--- a/ui/status/ninja.go
+++ b/ui/status/ninja.go
@@ -142,6 +142,7 @@
action := &Action{
Description: msg.EdgeStarted.GetDesc(),
Outputs: msg.EdgeStarted.Outputs,
+ Inputs: msg.EdgeStarted.Inputs,
Command: msg.EdgeStarted.GetCommand(),
}
n.status.StartAction(action)
diff --git a/ui/status/status.go b/ui/status/status.go
index 3d8cd7a..df33baa 100644
--- a/ui/status/status.go
+++ b/ui/status/status.go
@@ -32,6 +32,10 @@
// but they can be any string.
Outputs []string
+ // Inputs is the (optional) list of inputs. Usually these are files,
+ // but they can be any string.
+ Inputs []string
+
// Command is the actual command line executed to perform the action.
// It's optional, but one of either Description or Command should be
// set.
diff --git a/ui/terminal/smart_status.go b/ui/terminal/smart_status.go
index 8fa9eff..8659d4d 100644
--- a/ui/terminal/smart_status.go
+++ b/ui/terminal/smart_status.go
@@ -17,12 +17,24 @@
import (
"fmt"
"io"
+ "os"
+ "os/signal"
+ "strconv"
"strings"
"sync"
+ "syscall"
+ "time"
"android/soong/ui/status"
)
+const tableHeightEnVar = "SOONG_UI_TABLE_HEIGHT"
+
+type actionTableEntry struct {
+ action *status.Action
+ startTime time.Time
+}
+
type smartStatusOutput struct {
writer io.Writer
formatter formatter
@@ -30,18 +42,61 @@
lock sync.Mutex
haveBlankLine bool
+
+ tableMode bool
+ tableHeight int
+ requestedTableHeight int
+ termWidth, termHeight int
+
+ runningActions []actionTableEntry
+ ticker *time.Ticker
+ done chan bool
+ sigwinch chan os.Signal
+ sigwinchHandled chan bool
}
// NewSmartStatusOutput returns a StatusOutput that represents the
// current build status similarly to Ninja's built-in terminal
// output.
func NewSmartStatusOutput(w io.Writer, formatter formatter) status.StatusOutput {
- return &smartStatusOutput{
+ tableHeight, _ := strconv.Atoi(os.Getenv(tableHeightEnVar))
+
+ s := &smartStatusOutput{
writer: w,
formatter: formatter,
haveBlankLine: true,
+
+ tableMode: tableHeight > 0,
+ requestedTableHeight: tableHeight,
+
+ done: make(chan bool),
+ sigwinch: make(chan os.Signal),
}
+
+ s.updateTermSize()
+
+ if s.tableMode {
+ // Add empty lines at the bottom of the screen to scroll back the existing history
+ // and make room for the action table.
+ // TODO: read the cursor position to see if the empty lines are necessary?
+ for i := 0; i < s.tableHeight; i++ {
+ fmt.Fprintln(w)
+ }
+
+ // Hide the cursor to prevent seeing it bouncing around
+ fmt.Fprintf(s.writer, ansi.hideCursor())
+
+ // Configure the empty action table
+ s.actionTable()
+
+ // Start a tick to update the action table periodically
+ s.startActionTableTick()
+ }
+
+ s.startSigwinch()
+
+ return s
}
func (s *smartStatusOutput) Message(level status.MsgLevel, message string) {
@@ -62,6 +117,8 @@
}
func (s *smartStatusOutput) StartAction(action *status.Action, counts status.Counts) {
+ startTime := time.Now()
+
str := action.Description
if str == "" {
str = action.Command
@@ -72,6 +129,11 @@
s.lock.Lock()
defer s.lock.Unlock()
+ s.runningActions = append(s.runningActions, actionTableEntry{
+ action: action,
+ startTime: startTime,
+ })
+
s.statusLine(progress + str)
}
@@ -88,6 +150,13 @@
s.lock.Lock()
defer s.lock.Unlock()
+ for i, runningAction := range s.runningActions {
+ if runningAction.action == result.Action {
+ s.runningActions = append(s.runningActions[:i], s.runningActions[i+1:]...)
+ break
+ }
+ }
+
if output != "" {
s.statusLine(progress)
s.requestLine()
@@ -101,7 +170,26 @@
s.lock.Lock()
defer s.lock.Unlock()
+ s.stopSigwinch()
+
s.requestLine()
+
+ s.runningActions = nil
+
+ if s.tableMode {
+ s.stopActionTableTick()
+
+ // Update the table after clearing runningActions to clear it
+ s.actionTable()
+
+ // Reset the scrolling region to the whole terminal
+ fmt.Fprintf(s.writer, ansi.resetScrollingMargins())
+ _, height, _ := termSize(s.writer)
+ // Move the cursor to the top of the now-blank, previously non-scrolling region
+ fmt.Fprintf(s.writer, ansi.setCursor(height-s.tableHeight, 0))
+ // Turn the cursor back on
+ fmt.Fprintf(s.writer, ansi.showCursor())
+ }
}
func (s *smartStatusOutput) Write(p []byte) (int, error) {
@@ -120,7 +208,7 @@
func (s *smartStatusOutput) print(str string) {
if !s.haveBlankLine {
- fmt.Fprint(s.writer, "\r", "\x1b[K")
+ fmt.Fprint(s.writer, "\r", ansi.clearToEndOfLine())
s.haveBlankLine = true
}
fmt.Fprint(s.writer, str)
@@ -137,22 +225,203 @@
// Limit line width to the terminal width, otherwise we'll wrap onto
// another line and we won't delete the previous line.
- //
- // Run this on every line in case the window has been resized while
- // we're printing. This could be optimized to only re-run when we get
- // SIGWINCH if it ever becomes too time consuming.
- if max, ok := termWidth(s.writer); ok {
- if len(str) > max {
- // TODO: Just do a max. Ninja elides the middle, but that's
- // more complicated and these lines aren't that important.
- str = str[:max]
- }
- }
+ str = elide(str, s.termWidth)
// Move to the beginning on the line, turn on bold, print the output,
// turn off bold, then clear the rest of the line.
- start := "\r\x1b[1m"
- end := "\x1b[0m\x1b[K"
+ start := "\r" + ansi.bold()
+ end := ansi.regular() + ansi.clearToEndOfLine()
fmt.Fprint(s.writer, start, str, end)
s.haveBlankLine = false
}
+
+func elide(str string, width int) string {
+ if width > 0 && len(str) > width {
+ // TODO: Just do a max. Ninja elides the middle, but that's
+ // more complicated and these lines aren't that important.
+ str = str[:width]
+ }
+
+ return str
+}
+
+func (s *smartStatusOutput) startActionTableTick() {
+ s.ticker = time.NewTicker(time.Second)
+ go func() {
+ for {
+ select {
+ case <-s.ticker.C:
+ s.lock.Lock()
+ s.actionTable()
+ s.lock.Unlock()
+ case <-s.done:
+ return
+ }
+ }
+ }()
+}
+
+func (s *smartStatusOutput) stopActionTableTick() {
+ s.ticker.Stop()
+ s.done <- true
+}
+
+func (s *smartStatusOutput) startSigwinch() {
+ signal.Notify(s.sigwinch, syscall.SIGWINCH)
+ go func() {
+ for _ = range s.sigwinch {
+ s.lock.Lock()
+ s.updateTermSize()
+ if s.tableMode {
+ s.actionTable()
+ }
+ s.lock.Unlock()
+ if s.sigwinchHandled != nil {
+ s.sigwinchHandled <- true
+ }
+ }
+ }()
+}
+
+func (s *smartStatusOutput) stopSigwinch() {
+ signal.Stop(s.sigwinch)
+ close(s.sigwinch)
+}
+
+func (s *smartStatusOutput) updateTermSize() {
+ if w, h, ok := termSize(s.writer); ok {
+ firstUpdate := s.termHeight == 0 && s.termWidth == 0
+ oldScrollingHeight := s.termHeight - s.tableHeight
+
+ s.termWidth, s.termHeight = w, h
+
+ if s.tableMode {
+ tableHeight := s.requestedTableHeight
+ if tableHeight > s.termHeight-1 {
+ tableHeight = s.termHeight - 1
+ }
+ s.tableHeight = tableHeight
+
+ scrollingHeight := s.termHeight - s.tableHeight
+
+ if !firstUpdate {
+ // If the scrolling region has changed, attempt to pan the existing text so that it is
+ // not overwritten by the table.
+ if scrollingHeight < oldScrollingHeight {
+ pan := oldScrollingHeight - scrollingHeight
+ if pan > s.tableHeight {
+ pan = s.tableHeight
+ }
+ fmt.Fprint(s.writer, ansi.panDown(pan))
+ }
+ }
+ }
+ }
+}
+
+func (s *smartStatusOutput) actionTable() {
+ scrollingHeight := s.termHeight - s.tableHeight
+
+ // Update the scrolling region in case the height of the terminal changed
+ fmt.Fprint(s.writer, ansi.setScrollingMargins(0, scrollingHeight))
+ // Move the cursor to the first line of the non-scrolling region
+ fmt.Fprint(s.writer, ansi.setCursor(scrollingHeight+1, 0))
+
+ // Write as many status lines as fit in the table
+ var tableLine int
+ var runningAction actionTableEntry
+ for tableLine, runningAction = range s.runningActions {
+ if tableLine >= s.tableHeight {
+ break
+ }
+
+ seconds := int(time.Since(runningAction.startTime).Round(time.Second).Seconds())
+
+ desc := runningAction.action.Description
+ if desc == "" {
+ desc = runningAction.action.Command
+ }
+
+ color := ""
+ if seconds >= 60 {
+ color = ansi.red() + ansi.bold()
+ } else if seconds >= 30 {
+ color = ansi.yellow() + ansi.bold()
+ }
+
+ durationStr := fmt.Sprintf(" %2d:%02d ", seconds/60, seconds%60)
+ desc = elide(desc, s.termWidth-len(durationStr))
+ durationStr = color + durationStr + ansi.regular()
+
+ fmt.Fprint(s.writer, durationStr, desc, ansi.clearToEndOfLine())
+ if tableLine < s.tableHeight-1 {
+ fmt.Fprint(s.writer, "\n")
+ }
+ }
+
+ // Clear any remaining lines in the table
+ for ; tableLine < s.tableHeight; tableLine++ {
+ fmt.Fprint(s.writer, ansi.clearToEndOfLine())
+ if tableLine < s.tableHeight-1 {
+ fmt.Fprint(s.writer, "\n")
+ }
+ }
+
+ // Move the cursor back to the last line of the scrolling region
+ fmt.Fprint(s.writer, ansi.setCursor(scrollingHeight, 0))
+}
+
+var ansi = ansiImpl{}
+
+type ansiImpl struct{}
+
+func (ansiImpl) clearToEndOfLine() string {
+ return "\x1b[K"
+}
+
+func (ansiImpl) setCursor(row, column int) string {
+ // Direct cursor address
+ return fmt.Sprintf("\x1b[%d;%dH", row, column)
+}
+
+func (ansiImpl) setScrollingMargins(top, bottom int) string {
+ // Set Top and Bottom Margins DECSTBM
+ return fmt.Sprintf("\x1b[%d;%dr", top, bottom)
+}
+
+func (ansiImpl) resetScrollingMargins() string {
+ // Set Top and Bottom Margins DECSTBM
+ return fmt.Sprintf("\x1b[r")
+}
+
+func (ansiImpl) red() string {
+ return "\x1b[31m"
+}
+
+func (ansiImpl) yellow() string {
+ return "\x1b[33m"
+}
+
+func (ansiImpl) bold() string {
+ return "\x1b[1m"
+}
+
+func (ansiImpl) regular() string {
+ return "\x1b[0m"
+}
+
+func (ansiImpl) showCursor() string {
+ return "\x1b[?25h"
+}
+
+func (ansiImpl) hideCursor() string {
+ return "\x1b[?25l"
+}
+
+func (ansiImpl) panDown(lines int) string {
+ return fmt.Sprintf("\x1b[%dS", lines)
+}
+
+func (ansiImpl) panUp(lines int) string {
+ return fmt.Sprintf("\x1b[%dT", lines)
+}
diff --git a/ui/terminal/status_test.go b/ui/terminal/status_test.go
index a87a7f0..81aa238 100644
--- a/ui/terminal/status_test.go
+++ b/ui/terminal/status_test.go
@@ -17,6 +17,8 @@
import (
"bytes"
"fmt"
+ "os"
+ "syscall"
"testing"
"android/soong/ui/status"
@@ -85,8 +87,11 @@
},
}
+ os.Setenv(tableHeightEnVar, "")
+
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
+
t.Run("smart", func(t *testing.T) {
smart := &fakeSmartTerminal{termWidth: 40}
stat := NewStatusOutput(smart, "", false)
@@ -250,8 +255,12 @@
}
func TestSmartStatusOutputWidthChange(t *testing.T) {
+ os.Setenv(tableHeightEnVar, "")
+
smart := &fakeSmartTerminal{termWidth: 40}
stat := NewStatusOutput(smart, "", false)
+ smartStat := stat.(*smartStatusOutput)
+ smartStat.sigwinchHandled = make(chan bool)
runner := newRunner(stat, 2)
@@ -260,6 +269,9 @@
runner.startAction(action)
smart.termWidth = 30
+ // Fake a SIGWINCH
+ smartStat.sigwinch <- syscall.SIGWINCH
+ <-smartStat.sigwinchHandled
runner.finishAction(result)
stat.Flush()
diff --git a/ui/terminal/util.go b/ui/terminal/util.go
index 3a11b79..7a603d7 100644
--- a/ui/terminal/util.go
+++ b/ui/terminal/util.go
@@ -24,6 +24,9 @@
func isSmartTerminal(w io.Writer) bool {
if f, ok := w.(*os.File); ok {
+ if term, ok := os.LookupEnv("TERM"); ok && term == "dumb" {
+ return false
+ }
var termios syscall.Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
ioctlGetTermios, uintptr(unsafe.Pointer(&termios)),
@@ -35,7 +38,7 @@
return false
}
-func termWidth(w io.Writer) (int, bool) {
+func termSize(w io.Writer) (width int, height int, ok bool) {
if f, ok := w.(*os.File); ok {
var winsize struct {
ws_row, ws_column uint16
@@ -44,11 +47,11 @@
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&winsize)),
0, 0, 0)
- return int(winsize.ws_column), err == 0
+ return int(winsize.ws_column), int(winsize.ws_row), err == 0
} else if f, ok := w.(*fakeSmartTerminal); ok {
- return f.termWidth, true
+ return f.termWidth, f.termHeight, true
}
- return 0, false
+ return 0, 0, false
}
// stripAnsiEscapes strips ANSI control codes from a byte array in place.
@@ -106,5 +109,5 @@
type fakeSmartTerminal struct {
bytes.Buffer
- termWidth int
+ termWidth, termHeight int
}
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index 6f40a3e..fba2e4b 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -136,6 +136,7 @@
writeIfChanged := flags.Bool("write_if_changed", false, "only update resultant .zip if it has changed")
ignoreMissingFiles := flags.Bool("ignore_missing_files", false, "continue if a requested file does not exist")
symlinks := flags.Bool("symlinks", true, "store symbolic links in zip instead of following them")
+ srcJar := flags.Bool("srcjar", false, "move .java files to locations that match their package statement")
parallelJobs := flags.Int("parallel", runtime.NumCPU(), "number of parallel threads to use")
cpuProfile := flags.String("cpuprofile", "", "write cpu profile to file")
@@ -191,6 +192,7 @@
FileArgs: fileArgsBuilder.FileArgs(),
OutputFilePath: *out,
EmulateJar: *emulateJar,
+ SrcJar: *srcJar,
AddDirectoryEntriesToZip: *directories,
CompressionLevel: *compLevel,
ManifestSourcePath: *manifest,
diff --git a/zip/zip.go b/zip/zip.go
index 1f5fe43..707c4ef 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -210,6 +210,7 @@
FileArgs []FileArg
OutputFilePath string
EmulateJar bool
+ SrcJar bool
AddDirectoryEntriesToZip bool
CompressionLevel int
ManifestSourcePath string
@@ -364,7 +365,7 @@
}
}
- return z.write(w, pathMappings, args.ManifestSourcePath, args.EmulateJar, args.NumParallelJobs)
+ return z.write(w, pathMappings, args.ManifestSourcePath, args.EmulateJar, args.SrcJar, args.NumParallelJobs)
}
func Zip(args ZipArgs) error {
@@ -446,7 +447,9 @@
sort.SliceStable(mappings, less)
}
-func (z *ZipWriter) write(f io.Writer, pathMappings []pathMapping, manifest string, emulateJar bool, parallelJobs int) error {
+func (z *ZipWriter) write(f io.Writer, pathMappings []pathMapping, manifest string, emulateJar, srcJar bool,
+ parallelJobs int) error {
+
z.errors = make(chan error)
defer close(z.errors)
@@ -489,7 +492,7 @@
if emulateJar && ele.dest == jar.ManifestFile {
err = z.addManifest(ele.dest, ele.src, ele.zipMethod)
} else {
- err = z.addFile(ele.dest, ele.src, ele.zipMethod, emulateJar)
+ err = z.addFile(ele.dest, ele.src, ele.zipMethod, emulateJar, srcJar)
}
if err != nil {
z.errors <- err
@@ -588,7 +591,7 @@
}
// imports (possibly with compression) <src> into the zip at sub-path <dest>
-func (z *ZipWriter) addFile(dest, src string, method uint16, emulateJar bool) error {
+func (z *ZipWriter) addFile(dest, src string, method uint16, emulateJar, srcJar bool) error {
var fileSize int64
var executable bool
@@ -606,12 +609,9 @@
return nil
}
return err
- } else if s.IsDir() {
- if z.directories {
- return z.writeDirectory(dest, src, emulateJar)
- }
- return nil
- } else {
+ }
+
+ createParentDirs := func(dest, src string) error {
if err := z.writeDirectory(filepath.Dir(dest), src, emulateJar); err != nil {
return err
}
@@ -625,32 +625,64 @@
z.createdFiles[dest] = src
- if s.Mode()&os.ModeSymlink != 0 {
- return z.writeSymlink(dest, src)
- } else if !s.Mode().IsRegular() {
- return fmt.Errorf("%s is not a file, directory, or symlink", src)
+ return nil
+ }
+
+ if s.IsDir() {
+ if z.directories {
+ return z.writeDirectory(dest, src, emulateJar)
+ }
+ return nil
+ } else if s.Mode()&os.ModeSymlink != 0 {
+ err = createParentDirs(dest, src)
+ if err != nil {
+ return err
+ }
+
+ return z.writeSymlink(dest, src)
+ } else if s.Mode().IsRegular() {
+ r, err := z.fs.Open(src)
+ if err != nil {
+ return err
+ }
+
+ if srcJar && filepath.Ext(src) == ".java" {
+ // rewrite the destination using the package path if it can be determined
+ pkg, err := jar.JavaPackage(r, src)
+ if err != nil {
+ // ignore errors for now, leaving the file at in its original location in the zip
+ } else {
+ dest = filepath.Join(filepath.Join(strings.Split(pkg, ".")...), filepath.Base(src))
+ }
+
+ _, err = r.Seek(0, io.SeekStart)
+ if err != nil {
+ return err
+ }
}
fileSize = s.Size()
executable = s.Mode()&0100 != 0
- }
- r, err := z.fs.Open(src)
- if err != nil {
- return err
- }
+ header := &zip.FileHeader{
+ Name: dest,
+ Method: method,
+ UncompressedSize64: uint64(fileSize),
+ }
- header := &zip.FileHeader{
- Name: dest,
- Method: method,
- UncompressedSize64: uint64(fileSize),
- }
+ if executable {
+ header.SetMode(0700)
+ }
- if executable {
- header.SetMode(0700)
- }
+ err = createParentDirs(dest, src)
+ if err != nil {
+ return err
+ }
- return z.writeFileContents(header, r)
+ return z.writeFileContents(header, r)
+ } else {
+ return fmt.Errorf("%s is not a file, directory, or symlink", src)
+ }
}
func (z *ZipWriter) addManifest(dest string, src string, method uint16) error {
diff --git a/zip/zip_test.go b/zip/zip_test.go
index 93c5f3d..84317d1 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -40,14 +40,15 @@
)
var mockFs = pathtools.MockFs(map[string][]byte{
- "a/a/a": fileA,
- "a/a/b": fileB,
- "a/a/c -> ../../c": nil,
- "a/a/d -> b": nil,
- "c": fileC,
- "l": []byte("a/a/a\na/a/b\nc\n"),
- "l2": []byte("missing\n"),
- "manifest.txt": fileCustomManifest,
+ "a/a/a": fileA,
+ "a/a/b": fileB,
+ "a/a/c -> ../../c": nil,
+ "dangling -> missing": nil,
+ "a/a/d -> b": nil,
+ "c": fileC,
+ "l": []byte("a/a/a\na/a/b\nc\n"),
+ "l2": []byte("missing\n"),
+ "manifest.txt": fileCustomManifest,
})
func fh(name string, contents []byte, method uint16) zip.FileHeader {
@@ -210,6 +211,17 @@
},
},
{
+ name: "dangling symlinks",
+ args: fileArgsBuilder().
+ File("dangling"),
+ compressionLevel: 9,
+ storeSymlinks: true,
+
+ files: []zip.FileHeader{
+ fhLink("dangling", "missing"),
+ },
+ },
+ {
name: "list",
args: fileArgsBuilder().
List("l"),
@@ -554,3 +566,70 @@
})
}
}
+
+func TestSrcJar(t *testing.T) {
+ mockFs := pathtools.MockFs(map[string][]byte{
+ "wrong_package.java": []byte("package foo;"),
+ "foo/correct_package.java": []byte("package foo;"),
+ "src/no_package.java": nil,
+ "src2/parse_error.java": []byte("error"),
+ })
+
+ want := []string{
+ "foo/",
+ "foo/wrong_package.java",
+ "foo/correct_package.java",
+ "no_package.java",
+ "src2/",
+ "src2/parse_error.java",
+ }
+
+ args := ZipArgs{}
+ args.FileArgs = NewFileArgsBuilder().File("**/*.java").FileArgs()
+
+ args.SrcJar = true
+ args.AddDirectoryEntriesToZip = true
+ args.Filesystem = mockFs
+ args.Stderr = &bytes.Buffer{}
+
+ buf := &bytes.Buffer{}
+ err := ZipTo(args, buf)
+ if err != nil {
+ t.Fatalf("got error %v", err)
+ }
+
+ br := bytes.NewReader(buf.Bytes())
+ zr, err := zip.NewReader(br, int64(br.Len()))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var got []string
+ for _, f := range zr.File {
+ r, err := f.Open()
+ if err != nil {
+ t.Fatalf("error when opening %s: %s", f.Name, err)
+ }
+
+ crc := crc32.NewIEEE()
+ len, err := io.Copy(crc, r)
+ r.Close()
+ if err != nil {
+ t.Fatalf("error when reading %s: %s", f.Name, err)
+ }
+
+ if uint64(len) != f.UncompressedSize64 {
+ t.Errorf("incorrect length for %s, want %d got %d", f.Name, f.UncompressedSize64, len)
+ }
+
+ if crc.Sum32() != f.CRC32 {
+ t.Errorf("incorrect crc for %s, want %x got %x", f.Name, f.CRC32, crc)
+ }
+
+ got = append(got, f.Name)
+ }
+
+ if !reflect.DeepEqual(want, got) {
+ t.Errorf("want files %q, got %q", want, got)
+ }
+}