Merge "Document stubs in cc_library."
diff --git a/Android.bp b/Android.bp
index 866ed25..8f7f3e2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
subdirs = [
"androidmk",
"bpfix",
@@ -43,101 +47,6 @@
//
toolchain_library {
- name: "libatomic",
- defaults: ["linux_bionic_supported"],
- vendor_available: true,
- product_available: true,
- ramdisk_available: true,
- vendor_ramdisk_available: true,
- recovery_available: true,
- native_bridge_supported: true,
-
- arch: {
- arm: {
- src: "prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/arm-linux-androideabi/lib/libatomic.a",
- },
- arm64: {
- src: "prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/aarch64-linux-android/lib64/libatomic.a",
- },
- x86: {
- src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/x86_64-linux-android/lib/libatomic.a",
- },
- x86_64: {
- src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/x86_64-linux-android/lib64/libatomic.a",
- },
- },
-}
-
-toolchain_library {
- name: "libgcc",
- defaults: ["linux_bionic_supported"],
- vendor_available: true,
- product_available: true,
- recovery_available: true,
- native_bridge_supported: true,
- apex_available: [
- "//apex_available:platform",
- "//apex_available:anyapex",
- ],
-
- arch: {
- arm: {
- src: "prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a",
- },
- arm64: {
- src: "prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/lib/gcc/aarch64-linux-android/4.9.x/libgcc.a",
- },
- x86: {
- src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/lib/gcc/x86_64-linux-android/4.9.x/32/libgcc.a",
- },
- x86_64: {
- src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/lib/gcc/x86_64-linux-android/4.9.x/libgcc.a",
- },
- },
-}
-
-toolchain_library {
- name: "libgcc_stripped",
- defaults: ["linux_bionic_supported"],
- vendor_available: true,
- product_available: true,
- ramdisk_available: true,
- vendor_ramdisk_available: true,
- recovery_available: true,
- native_bridge_supported: true,
- sdk_version: "current",
-
- arch: {
- arm: {
- src: "prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a",
- repack_objects_to_keep: [],
- enabled: false,
- },
- arm64: {
- src: "prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/lib/gcc/aarch64-linux-android/4.9.x/libgcc.a",
- repack_objects_to_keep: [
- "unwind-dw2.o",
- "unwind-dw2-fde-dip.o",
- ],
- },
- x86: {
- src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/lib/gcc/x86_64-linux-android/4.9.x/32/libgcc.a",
- repack_objects_to_keep: [
- "unwind-dw2.o",
- "unwind-dw2-fde-dip.o",
- ],
- },
- x86_64: {
- src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/lib/gcc/x86_64-linux-android/4.9.x/libgcc.a",
- repack_objects_to_keep: [
- "unwind-dw2.o",
- "unwind-dw2-fde-dip.o",
- ],
- },
- },
-}
-
-toolchain_library {
name: "libwinpthread",
host_supported: true,
enabled: false,
@@ -155,26 +64,6 @@
notice: ":mingw-libwinpthread-notice",
}
-toolchain_library {
- name: "libgcov",
- defaults: ["linux_bionic_supported"],
-
- arch: {
- arm: {
- src: "prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/lib/gcc/arm-linux-androideabi/4.9.x/libgcov.a",
- },
- arm64: {
- src: "prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/lib/gcc/aarch64-linux-android/4.9.x/libgcov.a",
- },
- x86: {
- src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/lib/gcc/x86_64-linux-android/4.9.x/32/libgcov.a",
- },
- x86_64: {
- src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/lib/gcc/x86_64-linux-android/4.9.x/libgcov.a",
- },
- },
-}
-
kernel_headers {
name: "device_kernel_headers",
vendor: true,
@@ -222,3 +111,8 @@
srcs: [":linker"],
out: ["linker.flags"],
}
+
+// Instantiate the dex_bootjars singleton module.
+dex_bootjars {
+ name: "dex_bootjars",
+}
diff --git a/OWNERS b/OWNERS
index 3a5a8a7..e851bf7 100644
--- a/OWNERS
+++ b/OWNERS
@@ -11,10 +11,8 @@
joeo@google.com
jungjw@google.com
lberki@google.com
-patricearruda@google.com
ruperts@google.com
# To expedite LON reviews
hansson@google.com
paulduffin@google.com
-
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index ff85661..317f5c4 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,3 +1,6 @@
[Builtin Hooks]
gofmt = true
bpfmt = true
+
+[Hook Scripts]
+do_not_use_DO_NOT_MERGE = ${REPO_ROOT}/build/soong/scripts/check_do_not_merge.sh ${PREUPLOAD_COMMIT}
diff --git a/README.md b/README.md
index f1857f8..b7e93f4 100644
--- a/README.md
+++ b/README.md
@@ -430,14 +430,24 @@
soong_config_string_variable {
name: "board",
- values: ["soc_a", "soc_b"],
+ values: ["soc_a", "soc_b", "soc_c"],
}
```
This example describes a new `acme_cc_defaults` module type that extends the
`cc_defaults` module type, with three additional conditionals based on
variables `board`, `feature` and `width`, which can affect properties `cflags`
-and `srcs`.
+and `srcs`. Additionally, each conditional will contain a `conditions_default`
+property can affect `cflags` and `srcs` in the following conditions:
+
+* bool variable (e.g. `feature`): the variable is unspecified or not set to a true value
+* value variable (e.g. `width`): the variable is unspecified
+* string variable (e.g. `board`): the variable is unspecified or the variable is set to a string unused in the
+given module. For example, with `board`, if the `board`
+conditional contains the properties `soc_a` and `conditions_default`, when
+board=soc_b, the `cflags` and `srcs` values under `conditions_default` will be
+used. To specify that no properties should be amended for `soc_b`, you can set
+`soc_b: {},`.
The values of the variables can be set from a product's `BoardConfig.mk` file:
```
@@ -445,6 +455,7 @@
SOONG_CONFIG_acme += \
board \
feature \
+ width \
SOONG_CONFIG_acme_board := soc_a
SOONG_CONFIG_acme_feature := true
@@ -473,12 +484,21 @@
soc_b: {
cflags: ["-DSOC_B"],
},
+ conditions_default: {
+ cflags: ["-DSOC_DEFAULT"],
+ },
},
feature: {
cflags: ["-DFEATURE"],
+ conditions_default: {
+ cflags: ["-DFEATURE_DEFAULT"],
+ },
},
width: {
cflags: ["-DWIDTH=%s"],
+ conditions_default: {
+ cflags: ["-DWIDTH=DEFAULT"],
+ },
},
},
}
@@ -490,8 +510,37 @@
}
```
-With the `BoardConfig.mk` snippet above, libacme_foo would build with
-cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
+With the `BoardConfig.mk` snippet above, `libacme_foo` would build with
+`cflags: "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200"`.
+
+Alternatively, with `DefaultBoardConfig.mk`:
+
+```
+SOONG_CONFIG_NAMESPACES += acme
+SOONG_CONFIG_acme += \
+ board \
+ feature \
+ width \
+
+SOONG_CONFIG_acme_feature := false
+```
+
+then `libacme_foo` would build with `cflags: "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT"`.
+
+Alternatively, with `DefaultBoardConfig.mk`:
+
+```
+SOONG_CONFIG_NAMESPACES += acme
+SOONG_CONFIG_acme += \
+ board \
+ feature \
+ width \
+
+SOONG_CONFIG_acme_board := soc_c
+```
+
+then `libacme_foo` would build with `cflags: "-DGENERIC -DSOC_DEFAULT
+-DFEATURE_DEFAULT -DSIZE=DEFAULT"`.
`soong_config_module_type` modules will work best when used to wrap defaults
modules (`cc_defaults`, `java_defaults`, etc.), which can then be referenced
diff --git a/android/Android.bp b/android/Android.bp
index f8c1d55..f5e5606 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-android",
pkgPath: "android/soong/android",
@@ -8,7 +12,9 @@
"soong",
"soong-android-soongconfig",
"soong-bazel",
- "soong-env",
+ "soong-cquery",
+ "soong-remoteexec",
+ "soong-response",
"soong-shared",
"soong-ui-metrics_proto",
],
@@ -18,9 +24,11 @@
"api_levels.go",
"arch.go",
"arch_list.go",
+ "bazel.go",
"bazel_handler.go",
"config.go",
"csuite_config.go",
+ "deapexer.go",
"defaults.go",
"defs.go",
"depset_generic.go",
@@ -28,8 +36,12 @@
"deptag.go",
"expand.go",
"filegroup.go",
+ "fixture.go",
"hooks.go",
"image.go",
+ "license.go",
+ "license_kind.go",
+ "licenses.go",
"makefile_goal.go",
"makevars.go",
"metrics.go",
@@ -55,28 +67,33 @@
"rule_builder.go",
"sandbox.go",
"sdk.go",
+ "sdk_version.go",
"singleton.go",
+ "singleton_module.go",
"soong_config_modules.go",
+ "test_asserts.go",
"test_suites.go",
"testing.go",
"util.go",
"variable.go",
"visibility.go",
"writedocs.go",
-
- // Lock down environment access last
- "env.go",
],
testSrcs: [
"android_test.go",
"androidmk_test.go",
"apex_test.go",
"arch_test.go",
+ "bazel_test.go",
"config_test.go",
"csuite_config_test.go",
"depset_test.go",
"deptag_test.go",
"expand_test.go",
+ "fixture_test.go",
+ "license_kind_test.go",
+ "license_test.go",
+ "licenses_test.go",
"module_test.go",
"mutator_test.go",
"namespace_test.go",
@@ -89,6 +106,7 @@
"paths_test.go",
"prebuilt_test.go",
"rule_builder_test.go",
+ "singleton_module_test.go",
"soong_config_modules_test.go",
"util_test.go",
"variable_test.go",
diff --git a/android/android_test.go b/android/android_test.go
index 46b7054..fb82e37 100644
--- a/android/android_test.go
+++ b/android/android_test.go
@@ -15,32 +15,10 @@
package android
import (
- "io/ioutil"
"os"
"testing"
)
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_android_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ os.Exit(m.Run())
}
diff --git a/android/androidmk.go b/android/androidmk.go
index 73f60d0..66a1036 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -28,6 +28,7 @@
"io/ioutil"
"os"
"path/filepath"
+ "reflect"
"sort"
"strings"
@@ -43,6 +44,14 @@
ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
}
+// Enable androidmk support.
+// * Register the singleton
+// * Configure that we are inside make
+var PrepareForTestWithAndroidMk = GroupFixturePreparers(
+ FixtureRegisterWithContext(RegisterAndroidMkBuildComponents),
+ FixtureModifyConfig(SetKatiEnabledForTests),
+)
+
// Deprecated: Use AndroidMkEntriesProvider instead, especially if you're not going to use the
// Custom function. It's easier to use and test.
type AndroidMkDataProvider interface {
@@ -140,7 +149,20 @@
entryOrder []string
}
-type AndroidMkExtraEntriesFunc func(entries *AndroidMkEntries)
+type AndroidMkExtraEntriesContext interface {
+ Provider(provider blueprint.ProviderKey) interface{}
+}
+
+type androidMkExtraEntriesContext struct {
+ ctx fillInEntriesContext
+ mod blueprint.Module
+}
+
+func (a *androidMkExtraEntriesContext) Provider(provider blueprint.ProviderKey) interface{} {
+ return a.ctx.ModuleProvider(a.mod, provider)
+}
+
+type AndroidMkExtraEntriesFunc func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries)
type AndroidMkExtraFootersFunc func(w io.Writer, name, prefix, moduleDir string)
// Utility funcs to manipulate Android.mk variable entries.
@@ -434,9 +456,26 @@
return generateDistContributionsForMake(distContributions)
}
+// Write the license variables to Make for AndroidMkData.Custom(..) methods that do not call WriteAndroidMkData(..)
+// It's required to propagate the license metadata even for module types that have non-standard interfaces to Make.
+func (a *AndroidMkEntries) WriteLicenseVariables(w io.Writer) {
+ fmt.Fprintln(w, "LOCAL_LICENSE_KINDS :=", strings.Join(a.EntryMap["LOCAL_LICENSE_KINDS"], " "))
+ fmt.Fprintln(w, "LOCAL_LICENSE_CONDITIONS :=", strings.Join(a.EntryMap["LOCAL_LICENSE_CONDITIONS"], " "))
+ fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", strings.Join(a.EntryMap["LOCAL_NOTICE_FILE"], " "))
+ if pn, ok := a.EntryMap["LOCAL_LICENSE_PACKAGE_NAME"]; ok {
+ fmt.Fprintln(w, "LOCAL_LICENSE_PACKAGE_NAME :=", strings.Join(pn, " "))
+ }
+}
+
// fillInEntries goes through the common variable processing and calls the extra data funcs to
// generate and fill in AndroidMkEntries's in-struct data, ready to be flushed to a file.
-func (a *AndroidMkEntries) fillInEntries(config Config, bpPath string, mod blueprint.Module) {
+type fillInEntriesContext interface {
+ ModuleDir(module blueprint.Module) string
+ Config() Config
+ ModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{}
+}
+
+func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
a.EntryMap = make(map[string][]string)
amod := mod.(Module).base()
name := amod.BaseModuleName()
@@ -458,8 +497,15 @@
fmt.Fprintln(&a.header, "\ninclude $(CLEAR_VARS)")
// Collect make variable assignment entries.
- a.SetString("LOCAL_PATH", filepath.Dir(bpPath))
+ a.SetString("LOCAL_PATH", ctx.ModuleDir(mod))
a.SetString("LOCAL_MODULE", name+a.SubName)
+ a.AddStrings("LOCAL_LICENSE_KINDS", amod.commonProperties.Effective_license_kinds...)
+ a.AddStrings("LOCAL_LICENSE_CONDITIONS", amod.commonProperties.Effective_license_conditions...)
+ a.AddStrings("LOCAL_NOTICE_FILE", amod.commonProperties.Effective_license_text...)
+ // TODO(b/151177513): Does this code need to set LOCAL_MODULE_IS_CONTAINER ?
+ if amod.commonProperties.Effective_package_name != nil {
+ a.SetString("LOCAL_LICENSE_PACKAGE_NAME", *amod.commonProperties.Effective_package_name)
+ }
a.SetString("LOCAL_MODULE_CLASS", a.Class)
a.SetString("LOCAL_PREBUILT_MODULE_FILE", a.OutputFile.String())
a.AddStrings("LOCAL_REQUIRED_MODULES", a.Required...)
@@ -500,7 +546,7 @@
}
if !amod.InRamdisk() && !amod.InVendorRamdisk() {
- a.AddStrings("LOCAL_INIT_RC", amod.commonProperties.Init_rc...)
+ a.AddPaths("LOCAL_FULL_INIT_RC", amod.initRcPaths)
}
a.AddStrings("LOCAL_VINTF_FRAGMENTS", amod.commonProperties.Vintf_fragments...)
a.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(amod.commonProperties.Proprietary))
@@ -542,17 +588,23 @@
}
- if amod.Arch().ArchType != config.Targets[amod.Os()][0].Arch.ArchType {
+ if amod.Arch().ArchType != ctx.Config().Targets[amod.Os()][0].Arch.ArchType {
prefix = "2ND_" + prefix
}
}
+
+ extraCtx := &androidMkExtraEntriesContext{
+ ctx: ctx,
+ mod: mod,
+ }
+
for _, extra := range a.ExtraEntries {
- extra(a)
+ extra(extraCtx, a)
}
// Write to footer.
fmt.Fprintln(&a.footer, "include "+a.Include)
- blueprintDir := filepath.Dir(bpPath)
+ blueprintDir := ctx.ModuleDir(mod)
for _, footerFunc := range a.ExtraFooters {
footerFunc(&a.footer, name, prefix, blueprintDir)
}
@@ -682,6 +734,7 @@
}
}()
+ // Additional cases here require review for correct license propagation to make.
switch x := mod.(type) {
case AndroidMkDataProvider:
return translateAndroidModule(ctx, w, mod, x)
@@ -690,6 +743,7 @@
case AndroidMkEntriesProvider:
return translateAndroidMkEntriesModule(ctx, w, mod, x)
default:
+ // Not exported to make so no make variables to set.
return nil
}
}
@@ -703,11 +757,15 @@
fmt.Fprintln(w, ".PHONY:", name)
fmt.Fprintln(w, name+":", goBinary.InstallPath())
fmt.Fprintln(w, "")
+ // Assuming no rules in make include go binaries in distributables.
+ // If the assumption is wrong, make will fail to build without the necessary .meta_lic and .meta_module files.
+ // In that case, add the targets and rules here to build a .meta_lic file for `name` and a .meta_module for
+ // `goBinary.InstallPath()` pointing to the `name`.meta_lic file.
return nil
}
-func (data *AndroidMkData) fillInData(config Config, bpPath string, mod blueprint.Module) {
+func (data *AndroidMkData) fillInData(ctx fillInEntriesContext, mod blueprint.Module) {
// Get the preamble content through AndroidMkEntries logic.
data.Entries = AndroidMkEntries{
Class: data.Class,
@@ -720,7 +778,7 @@
Host_required: data.Host_required,
Target_required: data.Target_required,
}
- data.Entries.fillInEntries(config, bpPath, mod)
+ data.Entries.fillInEntries(ctx, mod)
// copy entries back to data since it is used in Custom
data.Required = data.Entries.Required
@@ -743,7 +801,7 @@
data.Include = "$(BUILD_PREBUILT)"
}
- data.fillInData(ctx.Config(), ctx.BlueprintFile(mod), mod)
+ data.fillInData(ctx, mod)
prefix := ""
if amod.ArchSpecific() {
@@ -768,6 +826,25 @@
blueprintDir := filepath.Dir(ctx.BlueprintFile(mod))
if data.Custom != nil {
+ // List of module types allowed to use .Custom(...)
+ // Additions to the list require careful review for proper license handling.
+ switch reflect.TypeOf(mod).String() { // ctx.ModuleType(mod) doesn't work: aidl_interface creates phony without type
+ case "*aidl.aidlApi": // writes non-custom before adding .phony
+ case "*aidl.aidlMapping": // writes non-custom before adding .phony
+ case "*android.customModule": // appears in tests only
+ case "*apex.apexBundle": // license properties written
+ case "*bpf.bpf": // license properties written (both for module and objs)
+ case "*genrule.Module": // writes non-custom before adding .phony
+ case "*java.SystemModules": // doesn't go through base_rules
+ case "*java.systemModulesImport": // doesn't go through base_rules
+ case "*phony.phony": // license properties written
+ case "*selinux.selinuxContextsModule": // license properties written
+ case "*sysprop.syspropLibrary": // license properties written
+ default:
+ if ctx.Config().IsEnvTrue("ANDROID_REQUIRE_LICENSES") {
+ return fmt.Errorf("custom make rules not allowed for %q (%q) module %q", ctx.ModuleType(mod), reflect.TypeOf(mod), ctx.ModuleName(mod))
+ }
+ }
data.Custom(w, name, prefix, blueprintDir, data)
} else {
WriteAndroidMkData(w, data)
@@ -804,8 +881,9 @@
return nil
}
+ // Any new or special cases here need review to verify correct propagation of license information.
for _, entries := range provider.AndroidMkEntries() {
- entries.fillInEntries(ctx.Config(), ctx.BlueprintFile(mod), mod)
+ entries.fillInEntries(ctx, mod)
entries.write(w)
}
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
index 347b92e..8eda9b2 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -137,25 +137,21 @@
return module
}
-// buildConfigAndCustomModuleFoo creates a config object, processes the supplied
+// buildContextAndCustomModuleFoo creates a config object, processes the supplied
// bp module and then returns the config and the custom module called "foo".
-func buildConfigAndCustomModuleFoo(t *testing.T, bp string) (Config, *customModule) {
+func buildContextAndCustomModuleFoo(t *testing.T, bp string) (*TestContext, *customModule) {
t.Helper()
- config := TestConfig(buildDir, nil, bp, nil)
- config.katiEnabled = true // Enable androidmk Singleton
+ result := GroupFixturePreparers(
+ // Enable androidmk Singleton
+ PrepareForTestWithAndroidMk,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("custom", customModuleFactory)
+ }),
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
- ctx := NewTestContext(config)
- ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
- ctx.RegisterModuleType("custom", customModuleFactory)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
-
- module := ctx.ModuleForTests("foo", "").Module().(*customModule)
- return config, module
+ module := result.ModuleForTests("foo", "").Module().(*customModule)
+ return result.TestContext, module
}
func TestAndroidMkSingleton_PassesUpdatedAndroidMkDataToCustomCallback(t *testing.T) {
@@ -168,7 +164,7 @@
}
`
- _, m := buildConfigAndCustomModuleFoo(t, bp)
+ _, m := buildContextAndCustomModuleFoo(t, bp)
assertEqual := func(expected interface{}, actual interface{}) {
if !reflect.DeepEqual(expected, actual) {
@@ -253,8 +249,8 @@
"$(call dist-for-goals,my_goal my_other_goal,three/four.out:four.out)\n",
}
- config, module := buildConfigAndCustomModuleFoo(t, bp)
- entries := AndroidMkEntriesForTest(t, config, "", module)
+ ctx, module := buildContextAndCustomModuleFoo(t, bp)
+ entries := AndroidMkEntriesForTest(t, ctx, module)
if len(entries) != 1 {
t.Errorf("Expected a single AndroidMk entry, got %d", len(entries))
}
@@ -343,8 +339,8 @@
t.Run(name, func(t *testing.T) {
t.Helper()
- config, module := buildConfigAndCustomModuleFoo(t, bp)
- entries := AndroidMkEntriesForTest(t, config, "", module)
+ ctx, module := buildContextAndCustomModuleFoo(t, bp)
+ entries := AndroidMkEntriesForTest(t, ctx, module)
if len(entries) != 1 {
t.Errorf("Expected a single AndroidMk entry, got %d", len(entries))
}
diff --git a/android/apex.go b/android/apex.go
index 47d14cc..25a07b8 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -43,10 +43,8 @@
// mergeApexVariations.
ApexVariationName string
- // Serialized ApiLevel that this module has to support at minimum. Should be accessed via
- // MinSdkVersion() method. Cannot be stored in its struct form because this is cloned into
- // properties structs, and ApiLevel has private members.
- MinSdkVersionStr string
+ // ApiLevel that this module has to support at minimum.
+ MinSdkVersion ApiLevel
// True if this module comes from an updatable apexBundle.
Updatable bool
@@ -64,6 +62,14 @@
// module is part of. The ApexContents gives information about which modules the apexBundle
// has and whether a module became part of the apexBundle via a direct dependency or not.
ApexContents []*ApexContents
+
+ // True if this is for a prebuilt_apex.
+ //
+ // If true then this will customize the apex processing to make it suitable for handling
+ // prebuilt_apex, e.g. it will prevent ApexInfos from being merged together.
+ //
+ // See Prebuilt.ApexInfoMutator for more information.
+ ForPrebuiltApex bool
}
var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex")
@@ -74,19 +80,13 @@
// have to be built twice, but only once. In that case, the two apex variations apex.a and apex.b
// are configured to have the same alias variation named apex29.
func (i ApexInfo) mergedName(ctx PathContext) string {
- name := "apex" + strconv.Itoa(i.MinSdkVersion(ctx).FinalOrFutureInt())
+ name := "apex" + strconv.Itoa(i.MinSdkVersion.FinalOrFutureInt())
for _, sdk := range i.RequiredSdks {
name += "_" + sdk.Name + "_" + sdk.Version
}
return name
}
-// MinSdkVersion gives the api level that this module has to support at minimum. This is from the
-// min_sdk_version property of the containing apexBundle.
-func (i ApexInfo) MinSdkVersion(ctx PathContext) ApiLevel {
- return ApiLevelOrPanic(ctx, i.MinSdkVersionStr)
-}
-
// IsForPlatform tells whether this module is for the platform or not. If false is returned, it
// means that this apex variant of the module is built for an APEX.
func (i ApexInfo) IsForPlatform() bool {
@@ -103,6 +103,19 @@
return false
}
+// InApexByBaseName tells whether this apex variant of the module is part of the given APEX or not,
+// where the APEX is specified by its canonical base name, i.e. typically beginning with
+// "com.android.". In particular this function doesn't differentiate between source and prebuilt
+// APEXes, where the latter may have "prebuilt_" prefixes.
+func (i ApexInfo) InApexByBaseName(apex string) bool {
+ for _, a := range i.InApexes {
+ if RemoveOptionalPrebuiltPrefix(a) == apex {
+ return true
+ }
+ }
+ return false
+}
+
// ApexTestForInfo stores the contents of APEXes for which this module is a test - although this
// module is not part of the APEX - and thus has access to APEX internals.
type ApexTestForInfo struct {
@@ -119,9 +132,24 @@
// DepIsInSameApex tests if the other module 'dep' is considered as part of the same APEX as
// this module. For example, a static lib dependency usually returns true here, while a
// shared lib dependency to a stub library returns false.
+ //
+ // This method must not be called directly without first ignoring dependencies whose tags
+ // implement ExcludeFromApexContentsTag. Calls from within the func passed to WalkPayloadDeps()
+ // are fine as WalkPayloadDeps() will ignore those dependencies automatically. Otherwise, use
+ // IsDepInSameApex instead.
DepIsInSameApex(ctx BaseModuleContext, dep Module) bool
}
+func IsDepInSameApex(ctx BaseModuleContext, module, dep Module) bool {
+ depTag := ctx.OtherModuleDependencyTag(dep)
+ if _, ok := depTag.(ExcludeFromApexContentsTag); ok {
+ // The tag defines a dependency that never requires the child module to be part of the same
+ // apex as the parent.
+ return false
+ }
+ return module.(DepIsInSameApex).DepIsInSameApex(ctx, dep)
+}
+
// ApexModule is the interface that a module type is expected to implement if the module has to be
// built differently depending on whether the module is destined for an APEX or not (i.e., installed
// to one of the regular partitions).
@@ -236,6 +264,13 @@
}
// Marker interface that identifies dependencies that are excluded from APEX contents.
+//
+// Unless the tag also implements the AlwaysRequireApexVariantTag this will prevent an apex variant
+// from being created for the module.
+//
+// At the moment the sdk.sdkRequirementsMutator relies on the fact that the existing tags which
+// implement this interface do not define dependencies onto members of an sdk_snapshot. If that
+// changes then sdk.sdkRequirementsMutator will need fixing.
type ExcludeFromApexContentsTag interface {
blueprint.DependencyTag
@@ -243,6 +278,17 @@
ExcludeFromApexContents()
}
+// Marker interface that identifies dependencies that always requires an APEX variant to be created.
+//
+// It is possible for a dependency to require an apex variant but exclude the module from the APEX
+// contents. See sdk.sdkMemberDependencyTag.
+type AlwaysRequireApexVariantTag interface {
+ blueprint.DependencyTag
+
+ // Return true if this tag requires that the target dependency has an apex variant.
+ AlwaysRequireApexVariant() bool
+}
+
// Marker interface that identifies dependencies that should inherit the DirectlyInAnyApex state
// from the parent to the child. For example, stubs libraries are marked as DirectlyInAnyApex if
// their implementation is in an apex.
@@ -399,6 +445,23 @@
}
}
+// AvailableToSameApexes returns true if the two modules are apex_available to
+// exactly the same set of APEXes (and platform), i.e. if their apex_available
+// properties have the same elements.
+func AvailableToSameApexes(mod1, mod2 ApexModule) bool {
+ mod1ApexAvail := SortedUniqueStrings(mod1.apexModuleBase().ApexProperties.Apex_available)
+ mod2ApexAvail := SortedUniqueStrings(mod2.apexModuleBase().ApexProperties.Apex_available)
+ if len(mod1ApexAvail) != len(mod2ApexAvail) {
+ return false
+ }
+ for i, v := range mod1ApexAvail {
+ if v != mod2ApexAvail[i] {
+ return false
+ }
+ }
+ return true
+}
+
type byApexName []ApexInfo
func (a byApexName) Len() int { return len(a) }
@@ -412,6 +475,16 @@
sort.Sort(byApexName(apexInfos))
seen := make(map[string]int)
for _, apexInfo := range apexInfos {
+ // If this is for a prebuilt apex then use the actual name of the apex variation to prevent this
+ // from being merged with other ApexInfo. See Prebuilt.ApexInfoMutator for more information.
+ if apexInfo.ForPrebuiltApex {
+ merged = append(merged, apexInfo)
+ continue
+ }
+
+ // Merge the ApexInfo together. If a compatible ApexInfo exists then merge the information from
+ // this one into it, otherwise create a new merged ApexInfo from this one and save it away so
+ // other ApexInfo instances can be merged into it.
apexName := apexInfo.ApexVariationName
mergedName := apexInfo.mergedName(ctx)
if index, exists := seen[mergedName]; exists {
@@ -582,10 +655,14 @@
// apexContents, and modules in that apex have a provider containing the apexContents of each
// apexBundle they are part of.
type ApexContents struct {
- // map from a module name to its membership to this apexBUndle
+ // map from a module name to its membership in this apexBundle
contents map[string]ApexMembership
}
+// NewApexContents creates and initializes an ApexContents that is suitable
+// for use with an apex module.
+// * contents is a map from a module name to information about its membership within
+// the apex.
func NewApexContents(contents map[string]ApexMembership) *ApexContents {
return &ApexContents{
contents: contents,
@@ -684,6 +761,8 @@
// Generate two module out files:
// 1. FullList with transitive deps and their parents in the dep graph
// 2. FlatList with a flat list of transitive deps
+// In both cases transitive deps of external deps are not included. Neither are deps that are only
+// available to APEXes; they are developed with updatability in mind and don't need manual approval.
func (d *ApexBundleDepsInfo) BuildDepsInfoLists(ctx ModuleContext, minSdkVersion string, depInfos DepNameToDepInfoMap) {
var fullContent strings.Builder
var flatContent strings.Builder
@@ -704,6 +783,8 @@
d.flatListPath = PathForModuleOut(ctx, "depsinfo", "flatlist.txt").OutputPath
WriteFileRule(ctx, d.flatListPath, flatContent.String())
+
+ ctx.Phony(fmt.Sprintf("%s-depsinfo", ctx.ModuleName()), d.fullListPath, d.flatListPath)
}
// TODO(b/158059172): remove minSdkVersion allowlist
@@ -714,74 +795,73 @@
}
return list
}(map[string]int{
- "adbd": 30,
- "android.net.ipsec.ike": 30,
- "androidx-constraintlayout_constraintlayout-solver": 30,
- "androidx.annotation_annotation": 28,
- "androidx.arch.core_core-common": 28,
- "androidx.collection_collection": 28,
- "androidx.lifecycle_lifecycle-common": 28,
- "apache-commons-compress": 29,
- "bouncycastle_ike_digests": 30,
- "brotli-java": 29,
- "captiveportal-lib": 28,
- "flatbuffer_headers": 30,
- "framework-permission": 30,
- "framework-statsd": 30,
- "gemmlowp_headers": 30,
- "ike-internals": 30,
- "kotlinx-coroutines-android": 28,
- "kotlinx-coroutines-core": 28,
- "libadb_crypto": 30,
- "libadb_pairing_auth": 30,
- "libadb_pairing_connection": 30,
- "libadb_pairing_server": 30,
- "libadb_protos": 30,
- "libadb_tls_connection": 30,
- "libadbconnection_client": 30,
- "libadbconnection_server": 30,
- "libadbd_core": 30,
- "libadbd_services": 30,
- "libadbd": 30,
- "libapp_processes_protos_lite": 30,
- "libasyncio": 30,
- "libbrotli": 30,
- "libbuildversion": 30,
- "libcrypto_static": 30,
- "libcrypto_utils": 30,
- "libdiagnose_usb": 30,
- "libeigen": 30,
- "liblz4": 30,
- "libmdnssd": 30,
- "libneuralnetworks_common": 30,
- "libneuralnetworks_headers": 30,
- "libneuralnetworks": 30,
- "libprocpartition": 30,
- "libprotobuf-java-lite": 30,
- "libprotoutil": 30,
- "libqemu_pipe": 30,
- "libstats_jni": 30,
- "libstatslog_statsd": 30,
- "libstatsmetadata": 30,
- "libstatspull": 30,
- "libstatssocket": 30,
- "libsync": 30,
- "libtextclassifier_hash_headers": 30,
- "libtextclassifier_hash_static": 30,
- "libtflite_kernel_utils": 30,
- "libwatchdog": 29,
- "libzstd": 30,
- "metrics-constants-protos": 28,
- "net-utils-framework-common": 29,
- "permissioncontroller-statsd": 28,
- "philox_random_headers": 30,
- "philox_random": 30,
- "service-permission": 30,
- "service-statsd": 30,
- "statsd-aidl-ndk_platform": 30,
- "statsd": 30,
- "tensorflow_headers": 30,
- "xz-java": 29,
+ "adbd": 30,
+ "android.net.ipsec.ike": 30,
+ "androidx.annotation_annotation-nodeps": 29,
+ "androidx.arch.core_core-common-nodeps": 29,
+ "androidx.collection_collection-nodeps": 29,
+ "androidx.collection_collection-ktx-nodeps": 30,
+ "androidx.concurrent_concurrent-futures-nodeps": 30,
+ "androidx.lifecycle_lifecycle-common-java8-nodeps": 30,
+ "androidx.lifecycle_lifecycle-common-nodeps": 29,
+ "androidx.room_room-common-nodeps": 30,
+ "androidx-constraintlayout_constraintlayout-solver-nodeps": 29,
+ "apache-commons-compress": 29,
+ "bouncycastle_ike_digests": 30,
+ "brotli-java": 29,
+ "captiveportal-lib": 28,
+ "error_prone_annotations": 30,
+ "flatbuffer_headers": 30,
+ "framework-permission": 30,
+ "gemmlowp_headers": 30,
+ "guava-listenablefuture-prebuilt-jar": 30,
+ "ike-internals": 30,
+ "kotlinx-coroutines-android": 28,
+ "kotlinx-coroutines-android-nodeps": 30,
+ "kotlinx-coroutines-core": 28,
+ "kotlinx-coroutines-core-nodeps": 30,
+ "libadb_crypto": 30,
+ "libadb_pairing_auth": 30,
+ "libadb_pairing_connection": 30,
+ "libadb_pairing_server": 30,
+ "libadb_protos": 30,
+ "libadb_tls_connection": 30,
+ "libadbconnection_client": 30,
+ "libadbconnection_server": 30,
+ "libadbd_core": 30,
+ "libadbd_services": 30,
+ "libadbd": 30,
+ "libapp_processes_protos_lite": 30,
+ "libasyncio": 30,
+ "libbrotli": 30,
+ "libbuildversion": 30,
+ "libcrypto_static": 30,
+ "libcrypto_utils": 30,
+ "libdiagnose_usb": 30,
+ "libeigen": 30,
+ "liblz4": 30,
+ "libmdnssd": 30,
+ "libneuralnetworks_common": 30,
+ "libneuralnetworks_headers": 30,
+ "libneuralnetworks": 30,
+ "libprocpartition": 30,
+ "libprotobuf-java-lite": 30,
+ "libprotoutil": 30,
+ "libqemu_pipe": 30,
+ "libsync": 30,
+ "libtextclassifier_hash_headers": 30,
+ "libtextclassifier_hash_static": 30,
+ "libtflite_kernel_utils": 30,
+ "libwatchdog": 29,
+ "libzstd": 30,
+ "metrics-constants-protos": 28,
+ "net-utils-framework-common": 29,
+ "permissioncontroller-statsd": 28,
+ "philox_random_headers": 30,
+ "philox_random": 30,
+ "service-permission": 30,
+ "tensorflow_headers": 30,
+ "xz-java": 29,
})
// Function called while walking an APEX's payload dependencies.
@@ -808,9 +888,8 @@
return
}
- // do not enforce deps.min_sdk_version if APEX/APK doesn't set min_sdk_version or
- // min_sdk_version is not finalized (e.g. current or codenames)
- if minSdkVersion.IsCurrent() {
+ // do not enforce deps.min_sdk_version if APEX/APK doesn't set min_sdk_version
+ if minSdkVersion.IsNone() {
return
}
@@ -827,8 +906,12 @@
if err := to.ShouldSupportSdkVersion(ctx, minSdkVersion); err != nil {
toName := ctx.OtherModuleName(to)
if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver.GreaterThan(minSdkVersion) {
- ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v. Dependency path: %s",
- minSdkVersion, ctx.ModuleName(), err.Error(), ctx.GetPathString(false))
+ ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v."+
+ "\n\nDependency path: %s\n\n"+
+ "Consider adding 'min_sdk_version: %q' to %q",
+ minSdkVersion, ctx.ModuleName(), err.Error(),
+ ctx.GetPathString(false),
+ minSdkVersion, toName)
return false
}
}
diff --git a/android/apex_test.go b/android/apex_test.go
index 512b50f..109b1c8 100644
--- a/android/apex_test.go
+++ b/android/apex_test.go
@@ -20,6 +20,10 @@
)
func Test_mergeApexVariations(t *testing.T) {
+ const (
+ ForPrebuiltApex = true
+ NotForPrebuiltApex = false
+ )
tests := []struct {
name string
in []ApexInfo
@@ -29,10 +33,10 @@
{
name: "single",
in: []ApexInfo{
- {"foo", "current", false, nil, []string{"foo"}, nil},
+ {"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000", "current", false, nil, []string{"foo"}, nil},
+ {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"foo", "apex10000"},
@@ -41,11 +45,11 @@
{
name: "merge",
in: []ApexInfo{
- {"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil},
- {"bar", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar"}, nil},
+ {"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, nil}},
+ {"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, nil, false}},
wantAliases: [][2]string{
{"bar", "apex10000_baz_1"},
{"foo", "apex10000_baz_1"},
@@ -54,12 +58,12 @@
{
name: "don't merge version",
in: []ApexInfo{
- {"foo", "current", false, nil, []string{"foo"}, nil},
- {"bar", "30", false, nil, []string{"bar"}, nil},
+ {"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex30", "30", false, nil, []string{"bar"}, nil},
- {"apex10000", "current", false, nil, []string{"foo"}, nil},
+ {"apex30", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"bar", "apex30"},
@@ -69,11 +73,11 @@
{
name: "merge updatable",
in: []ApexInfo{
- {"foo", "current", false, nil, []string{"foo"}, nil},
- {"bar", "current", true, nil, []string{"bar"}, nil},
+ {"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", FutureApiLevel, true, nil, []string{"bar"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000", "current", true, nil, []string{"bar", "foo"}, nil},
+ {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"bar", "apex10000"},
@@ -83,22 +87,41 @@
{
name: "don't merge sdks",
in: []ApexInfo{
- {"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil},
- {"bar", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil},
+ {"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000_baz_2", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil},
- {"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil},
+ {"apex10000_baz_2", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"bar", "apex10000_baz_2"},
{"foo", "apex10000_baz_1"},
},
},
+ {
+ name: "don't merge when for prebuilt_apex",
+ in: []ApexInfo{
+ {"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", FutureApiLevel, true, nil, []string{"bar"}, nil, NotForPrebuiltApex},
+ // This one should not be merged in with the others because it is for
+ // a prebuilt_apex.
+ {"baz", FutureApiLevel, true, nil, []string{"baz"}, nil, ForPrebuiltApex},
+ },
+ wantMerged: []ApexInfo{
+ {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+ {"baz", FutureApiLevel, true, nil, []string{"baz"}, nil, ForPrebuiltApex},
+ },
+ wantAliases: [][2]string{
+ {"bar", "apex10000"},
+ {"foo", "apex10000"},
+ },
+ },
}
+
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- config := TestConfig(buildDir, nil, "", nil)
+ config := TestConfig(t.TempDir(), nil, "", nil)
ctx := &configErrorWrapper{config: config}
gotMerged, gotAliases := mergeApexVariations(ctx, tt.in)
if !reflect.DeepEqual(gotMerged, tt.wantMerged) {
diff --git a/android/api_levels.go b/android/api_levels.go
index 08328e1..9bc7e83 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -24,14 +24,16 @@
RegisterSingletonType("api_levels", ApiLevelsSingleton)
}
+const previewAPILevelBase = 9000
+
// An API level, which may be a finalized (numbered) API, a preview (codenamed)
// API, or the future API level (10000). Can be parsed from a string with
// ApiLevelFromUser or ApiLevelOrPanic.
//
// The different *types* of API levels are handled separately. Currently only
-// Java has these, and they're managed with the sdkKind enum of the sdkSpec. A
-// future cleanup should be to migrate sdkSpec to using ApiLevel instead of its
-// sdkVersion int, and to move sdkSpec into this package.
+// Java has these, and they're managed with the SdkKind enum of the SdkSpec. A
+// future cleanup should be to migrate SdkSpec to using ApiLevel instead of its
+// SdkVersion int, and to move SdkSpec into this package.
type ApiLevel struct {
// The string representation of the API level.
value string
@@ -57,6 +59,21 @@
}
}
+// FinalOrPreviewInt distinguishes preview versions from "current" (future).
+// This is for "native" stubs and should be in sync with ndkstubgen/getApiLevelsMap().
+// - "current" -> future (10000)
+// - preview codenames -> preview base (9000) + index
+// - otherwise -> cast to int
+func (this ApiLevel) FinalOrPreviewInt() int {
+ if this.IsCurrent() {
+ return this.number
+ }
+ if this.IsPreview() {
+ return previewAPILevelBase + this.number
+ }
+ return this.number
+}
+
// Returns the canonical name for this API level. For a finalized API level
// this will be the API number as a string. For a preview API level this
// will be the codename, or "current".
@@ -81,6 +98,10 @@
return this.value == "current"
}
+func (this ApiLevel) IsNone() bool {
+ return this.number == -1
+}
+
// Returns -1 if the current API level is less than the argument, 0 if they
// are equal, and 1 if it is greater than the argument.
func (this ApiLevel) CompareTo(other ApiLevel) int {
@@ -278,7 +299,6 @@
func getApiLevelsMap(config Config) map[string]int {
return config.Once(apiLevelsMapKey, func() interface{} {
- baseApiLevel := 9000
apiLevelsMap := map[string]int{
"G": 9,
"I": 14,
@@ -298,7 +318,7 @@
"R": 30,
}
for i, codename := range config.PlatformVersionActiveCodenames() {
- apiLevelsMap[codename] = baseApiLevel + i
+ apiLevelsMap[codename] = previewAPILevelBase + i
}
return apiLevelsMap
diff --git a/android/arch.go b/android/arch.go
index 34f9b29..9f93752 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -19,7 +19,6 @@
"fmt"
"reflect"
"runtime"
- "strconv"
"strings"
"github.com/google/blueprint"
@@ -167,7 +166,8 @@
return archType
}
-// ArchTypeList returns the 4 supported ArchTypes for arm, arm64, x86 and x86_64.
+// ArchTypeList returns the a slice copy of the 4 supported ArchTypes for arm,
+// arm64, x86 and x86_64.
func ArchTypeList() []ArchType {
return append([]ArchType(nil), archTypeList...)
}
@@ -175,7 +175,7 @@
// MarshalText allows an ArchType to be serialized through any encoder that supports
// encoding.TextMarshaler.
func (a ArchType) MarshalText() ([]byte, error) {
- return []byte(strconv.Quote(a.String())), nil
+ return []byte(a.String()), nil
}
var _ encoding.TextMarshaler = ArchType{}
@@ -266,7 +266,7 @@
DefaultDisabled: defDisabled,
}
- OsTypeList = append(OsTypeList, os)
+ osTypeList = append(osTypeList, os)
if _, found := commonTargetMap[name]; found {
panic(fmt.Errorf("Found Os type duplicate during OsType registration: %q", name))
@@ -280,7 +280,7 @@
// osByName returns the OsType that has the given name, or NoOsType if none match.
func osByName(name string) OsType {
- for _, os := range OsTypeList {
+ for _, os := range osTypeList {
if os.Name == name {
return os
}
@@ -312,9 +312,9 @@
}()
var (
- // OsTypeList contains a list of all the supported OsTypes, including ones not supported
+ // osTypeList contains a list of all the supported OsTypes, including ones not supported
// by the current build host or the target device.
- OsTypeList []OsType
+ osTypeList []OsType
// commonTargetMap maps names of OsTypes to the corresponding common Target, i.e. the
// Target with the same OsType and the common ArchType.
commonTargetMap = make(map[string]Target)
@@ -347,6 +347,11 @@
CommonArch = Arch{ArchType: Common}
)
+// OsTypeList returns a slice copy of the supported OsTypes.
+func OsTypeList() []OsType {
+ return append([]OsType(nil), osTypeList...)
+}
+
// Target specifies the OS and architecture that a module is being compiled for.
type Target struct {
// Os the OS that the module is being compiled for (e.g. "linux_glibc", "android").
@@ -436,7 +441,7 @@
// blueprint.BottomUpMutatorContext because android.BottomUpMutatorContext
// filters out non-Soong modules. Now that we've handled them, create a
// normal android.BottomUpMutatorContext.
- mctx := bottomUpMutatorContextFactory(bpctx, module, false)
+ mctx := bottomUpMutatorContextFactory(bpctx, module, false, false)
base := module.base()
@@ -448,7 +453,7 @@
// Collect a list of OSTypes supported by this module based on the HostOrDevice value
// passed to InitAndroidArchModule and the device_supported and host_supported properties.
var moduleOSList []OsType
- for _, os := range OsTypeList {
+ for _, os := range osTypeList {
for _, t := range mctx.Config().Targets[os] {
if base.supportsTarget(t) {
moduleOSList = append(moduleOSList, os)
@@ -576,7 +581,7 @@
// blueprint.BottomUpMutatorContext because android.BottomUpMutatorContext
// filters out non-Soong modules. Now that we've handled them, create a
// normal android.BottomUpMutatorContext.
- mctx := bottomUpMutatorContextFactory(bpctx, module, false)
+ mctx := bottomUpMutatorContextFactory(bpctx, module, false, false)
base := module.base()
@@ -612,16 +617,12 @@
}
// only the primary arch in the ramdisk / vendor_ramdisk / recovery partition
- if os == Android && (module.InstallInRecovery() || module.InstallInRamdisk() || module.InstallInVendorRamdisk()) {
+ if os == Android && (module.InstallInRecovery() || module.InstallInRamdisk() || module.InstallInVendorRamdisk() || module.InstallInDebugRamdisk()) {
osTargets = []Target{osTargets[0]}
}
- // Some modules want compile_multilib: "first" to mean 32-bit, not 64-bit.
- // This is used for Windows support and for HOST_PREFER_32_BIT=true support for Art modules.
- prefer32 := false
- if base.prefer32 != nil {
- prefer32 = base.prefer32(mctx, base, os)
- }
+ // Windows builds always prefer 32-bit
+ prefer32 := os == Windows
// Determine the multilib selection for this module.
multilib, extraMultilib := decodeMultilib(base, os.Class)
@@ -643,10 +644,11 @@
}
// Recovery is always the primary architecture, filter out any other architectures.
+ // Common arch is also allowed
if image == RecoveryVariation {
primaryArch := mctx.Config().DevicePrimaryArchType()
- targets = filterToArch(targets, primaryArch)
- multiTargets = filterToArch(multiTargets, primaryArch)
+ targets = filterToArch(targets, primaryArch, Common)
+ multiTargets = filterToArch(multiTargets, primaryArch, Common)
}
// If there are no supported targets disable the module.
@@ -715,10 +717,17 @@
}
// filterToArch takes a list of Targets and an ArchType, and returns a modified list that contains
-// only Targets that have the specified ArchType.
-func filterToArch(targets []Target, arch ArchType) []Target {
+// only Targets that have the specified ArchTypes.
+func filterToArch(targets []Target, archs ...ArchType) []Target {
for i := 0; i < len(targets); i++ {
- if targets[i].Arch.ArchType != arch {
+ found := false
+ for _, arch := range archs {
+ if targets[i].Arch.ArchType == arch {
+ found = true
+ break
+ }
+ }
+ if !found {
targets = append(targets[:i], targets[i+1:]...)
i--
}
@@ -834,7 +843,7 @@
"Arm_on_x86_64",
"Native_bridge",
}
- for _, os := range OsTypeList {
+ for _, os := range osTypeList {
// Add all the OSes.
targets = append(targets, os.Field)
@@ -1430,7 +1439,7 @@
func getNdkAbisConfig() []archConfig {
return []archConfig{
{"arm", "armv7-a", "", []string{"armeabi-v7a"}},
- {"arm64", "armv8-a", "", []string{"arm64-v8a"}},
+ {"arm64", "armv8-a-branchprot", "", []string{"arm64-v8a"}},
{"x86", "", "", []string{"x86"}},
{"x86_64", "", "", []string{"x86_64"}},
}
@@ -1597,15 +1606,198 @@
} else {
buildTargets = firstTarget(targets, "lib64", "lib32")
}
+ case "first_prefer32":
+ buildTargets = firstTarget(targets, "lib32", "lib64")
case "prefer32":
buildTargets = filterMultilibTargets(targets, "lib32")
if len(buildTargets) == 0 {
buildTargets = filterMultilibTargets(targets, "lib64")
}
default:
- return nil, fmt.Errorf(`compile_multilib must be "both", "first", "32", "64", or "prefer32" found %q`,
+ return nil, fmt.Errorf(`compile_multilib must be "both", "first", "32", "64", "prefer32" or "first_prefer32" found %q`,
multilib)
}
return buildTargets, nil
}
+
+// GetArchProperties returns a map of architectures to the values of the
+// properties of the 'dst' struct that are specific to that architecture.
+//
+// For example, passing a struct { Foo bool, Bar string } will return an
+// interface{} that can be type asserted back into the same struct, containing
+// the arch specific property value specified by the module if defined.
+func (m *ModuleBase) GetArchProperties(dst interface{}) map[ArchType]interface{} {
+ // Return value of the arch types to the prop values for that arch.
+ archToProp := map[ArchType]interface{}{}
+
+ // Nothing to do for non-arch-specific modules.
+ if !m.ArchSpecific() {
+ return archToProp
+ }
+
+ // archProperties has the type of [][]interface{}. Looks complicated, so let's
+ // explain this step by step.
+ //
+ // Loop over the outer index, which determines the property struct that
+ // contains a matching set of properties in dst that we're interested in.
+ // For example, BaseCompilerProperties or BaseLinkerProperties.
+ for i := range m.archProperties {
+ if m.archProperties[i] == nil {
+ // Skip over nil arch props
+ continue
+ }
+
+ // Non-nil arch prop, let's see if the props match up.
+ for _, arch := range ArchTypeList() {
+ // e.g X86, Arm
+ field := arch.Field
+
+ // If it's not nil, loop over the inner index, which determines the arch variant
+ // of the prop type. In an Android.bp file, this is like looping over:
+ //
+ // arch: { arm: { key: value, ... }, x86: { key: value, ... } }
+ for _, archProperties := range m.archProperties[i] {
+ archPropValues := reflect.ValueOf(archProperties).Elem()
+
+ // This is the archPropRoot struct. Traverse into the Arch nested struct.
+ src := archPropValues.FieldByName("Arch").Elem()
+
+ // Step into non-nil pointers to structs in the src value.
+ if src.Kind() == reflect.Ptr {
+ if src.IsNil() {
+ // Ignore nil pointers.
+ continue
+ }
+ src = src.Elem()
+ }
+
+ // Find the requested field (e.g. x86, x86_64) in the src struct.
+ src = src.FieldByName(field)
+ if !src.IsValid() {
+ continue
+ }
+
+ // We only care about structs. These are not the droids you are looking for.
+ if src.Kind() != reflect.Struct {
+ continue
+ }
+
+ // If the value of the field is a struct then step into the
+ // BlueprintEmbed field. The special "BlueprintEmbed" name is
+ // used by createArchPropTypeDesc to embed the arch properties
+ // in the parent struct, so the src arch prop should be in this
+ // field.
+ //
+ // See createArchPropTypeDesc for more details on how Arch-specific
+ // module properties are processed from the nested props and written
+ // into the module's archProperties.
+ src = src.FieldByName("BlueprintEmbed")
+
+ // Clone the destination prop, since we want a unique prop struct per arch.
+ dstClone := reflect.New(reflect.ValueOf(dst).Elem().Type()).Interface()
+
+ // Copy the located property struct into the cloned destination property struct.
+ err := proptools.ExtendMatchingProperties([]interface{}{dstClone}, src.Interface(), nil, proptools.OrderReplace)
+ if err != nil {
+ // This is fine, it just means the src struct doesn't match.
+ continue
+ }
+
+ // Found the prop for the arch, you have.
+ archToProp[arch] = dstClone
+
+ // Go to the next prop.
+ break
+ }
+ }
+ }
+ return archToProp
+}
+
+// GetTargetProperties returns a map of OS target (e.g. android, windows) to the
+// values of the properties of the 'dst' struct that are specific to that OS
+// target.
+//
+// For example, passing a struct { Foo bool, Bar string } will return an
+// interface{} that can be type asserted back into the same struct, containing
+// the os-specific property value specified by the module if defined.
+//
+// While this looks similar to GetArchProperties, the internal representation of
+// the properties have a slightly different layout to warrant a standalone
+// lookup function.
+func (m *ModuleBase) GetTargetProperties(dst interface{}) map[OsType]interface{} {
+ // Return value of the arch types to the prop values for that arch.
+ osToProp := map[OsType]interface{}{}
+
+ // Nothing to do for non-OS/arch-specific modules.
+ if !m.ArchSpecific() {
+ return osToProp
+ }
+
+ // archProperties has the type of [][]interface{}. Looks complicated, so
+ // let's explain this step by step.
+ //
+ // Loop over the outer index, which determines the property struct that
+ // contains a matching set of properties in dst that we're interested in.
+ // For example, BaseCompilerProperties or BaseLinkerProperties.
+ for i := range m.archProperties {
+ if m.archProperties[i] == nil {
+ continue
+ }
+
+ // Iterate over the supported OS types
+ for _, os := range osTypeList {
+ // e.g android, linux_bionic
+ field := os.Field
+
+ // If it's not nil, loop over the inner index, which determines the arch variant
+ // of the prop type. In an Android.bp file, this is like looping over:
+ //
+ // target: { android: { key: value, ... }, linux_bionic: { key: value, ... } }
+ for _, archProperties := range m.archProperties[i] {
+ archPropValues := reflect.ValueOf(archProperties).Elem()
+
+ // This is the archPropRoot struct. Traverse into the Targetnested struct.
+ src := archPropValues.FieldByName("Target").Elem()
+
+ // Step into non-nil pointers to structs in the src value.
+ if src.Kind() == reflect.Ptr {
+ if src.IsNil() {
+ continue
+ }
+ src = src.Elem()
+ }
+
+ // Find the requested field (e.g. android, linux_bionic) in the src struct.
+ src = src.FieldByName(field)
+
+ // Validation steps. We want valid non-nil pointers to structs.
+ if !src.IsValid() || src.IsNil() {
+ continue
+ }
+
+ if src.Kind() != reflect.Ptr || src.Elem().Kind() != reflect.Struct {
+ continue
+ }
+
+ // Clone the destination prop, since we want a unique prop struct per arch.
+ dstClone := reflect.New(reflect.ValueOf(dst).Elem().Type()).Interface()
+
+ // Copy the located property struct into the cloned destination property struct.
+ err := proptools.ExtendMatchingProperties([]interface{}{dstClone}, src.Interface(), nil, proptools.OrderReplace)
+ if err != nil {
+ // This is fine, it just means the src struct doesn't match.
+ continue
+ }
+
+ // Found the prop for the os, you have.
+ osToProp[os] = dstClone
+
+ // Go to the next prop.
+ break
+ }
+ }
+ }
+ return osToProp
+}
diff --git a/android/arch_list.go b/android/arch_list.go
index 0c33b9d..d68a0d1 100644
--- a/android/arch_list.go
+++ b/android/arch_list.go
@@ -41,6 +41,7 @@
},
Arm64: {
"armv8_a",
+ "armv8_a_branchprot",
"armv8_2a",
"armv8-2a-dotprod",
"cortex-a53",
diff --git a/android/arch_test.go b/android/arch_test.go
index 4cef4c8..633ddaa 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -273,6 +273,13 @@
return m
}
+var prepareForArchTest = GroupFixturePreparers(
+ PrepareForTestWithArchMutator,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("module", archTestModuleFactory)
+ }),
+)
+
func TestArchMutator(t *testing.T) {
var buildOSVariants []string
var buildOS32Variants []string
@@ -309,7 +316,7 @@
testCases := []struct {
name string
- config func(Config)
+ preparer FixturePreparer
fooVariants []string
barVariants []string
bazVariants []string
@@ -317,7 +324,7 @@
}{
{
name: "normal",
- config: nil,
+ preparer: nil,
fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
barVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"),
bazVariants: nil,
@@ -325,11 +332,11 @@
},
{
name: "host-only",
- config: func(config Config) {
+ preparer: FixtureModifyConfig(func(config Config) {
config.BuildOSTarget = Target{}
config.BuildOSCommonTarget = Target{}
config.Targets[Android] = nil
- },
+ }),
fooVariants: nil,
barVariants: buildOSVariants,
bazVariants: nil,
@@ -351,19 +358,13 @@
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
- config := TestArchConfig(buildDir, nil, bp, nil)
-
- ctx := NewTestArchContext(config)
- ctx.RegisterModuleType("module", archTestModuleFactory)
- ctx.Register()
- if tt.config != nil {
- tt.config(config)
- }
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
+ result := GroupFixturePreparers(
+ prepareForArchTest,
+ // Test specific preparer
+ OptionalFixturePreparer(tt.preparer),
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
+ ctx := result.TestContext
if g, w := enabledVariants(ctx, "foo"), tt.fooVariants; !reflect.DeepEqual(w, g) {
t.Errorf("want foo variants:\n%q\ngot:\n%q\n", w, g)
@@ -412,14 +413,14 @@
testCases := []struct {
name string
- config func(Config)
+ preparer FixturePreparer
fooVariants []string
barVariants []string
bazVariants []string
}{
{
name: "normal",
- config: nil,
+ preparer: nil,
fooVariants: []string{"android_x86_64_silvermont", "android_x86_silvermont"},
barVariants: []string{"android_x86_64_silvermont", "android_native_bridge_arm64_armv8-a", "android_x86_silvermont", "android_native_bridge_arm_armv7-a-neon"},
bazVariants: []string{"android_native_bridge_arm64_armv8-a", "android_native_bridge_arm_armv7-a-neon"},
@@ -440,19 +441,23 @@
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
- config := TestArchConfigNativeBridge(buildDir, nil, bp, nil)
+ result := GroupFixturePreparers(
+ prepareForArchTest,
+ // Test specific preparer
+ OptionalFixturePreparer(tt.preparer),
+ // Prepare for native bridge test
+ FixtureModifyConfig(func(config Config) {
+ config.Targets[Android] = []Target{
+ {Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
+ {Android, Arch{ArchType: X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", "", false},
+ {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled, "x86_64", "arm64", false},
+ {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled, "x86", "arm", false},
+ }
+ }),
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
- ctx := NewTestArchContext(config)
- ctx.RegisterModuleType("module", archTestModuleFactory)
- ctx.Register()
- if tt.config != nil {
- tt.config(config)
- }
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
+ ctx := result.TestContext
if g, w := enabledVariants(ctx, "foo"), tt.fooVariants; !reflect.DeepEqual(w, g) {
t.Errorf("want foo variants:\n%q\ngot:\n%q\n", w, g)
diff --git a/android/bazel.go b/android/bazel.go
new file mode 100644
index 0000000..9468891
--- /dev/null
+++ b/android/bazel.go
@@ -0,0 +1,287 @@
+// Copyright 2021 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"
+ "io/ioutil"
+ "path/filepath"
+ "strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+type bazelModuleProperties struct {
+ // The label of the Bazel target replacing this Soong module. When run in conversion mode, this
+ // will import the handcrafted build target into the autogenerated file. Note: this may result in
+ // a conflict due to duplicate targets if bp2build_available is also set.
+ Label *string
+
+ // If true, bp2build will generate the converted Bazel target for this module. Note: this may
+ // cause a conflict due to the duplicate targets if label is also set.
+ //
+ // This is a bool pointer to support tristates: true, false, not set.
+ //
+ // To opt-in a module, set bazel_module: { bp2build_available: true }
+ // To opt-out a module, set bazel_module: { bp2build_available: false }
+ // To defer the default setting for the directory, do not set the value.
+ Bp2build_available *bool
+}
+
+// Properties contains common module properties for Bazel migration purposes.
+type properties struct {
+ // In USE_BAZEL_ANALYSIS=1 mode, this represents the Bazel target replacing
+ // this Soong module.
+ Bazel_module bazelModuleProperties
+}
+
+// BazelModuleBase contains the property structs with metadata for modules which can be converted to
+// Bazel.
+type BazelModuleBase struct {
+ bazelProperties properties
+}
+
+// Bazelable is specifies the interface for modules that can be converted to Bazel.
+type Bazelable interface {
+ bazelProps() *properties
+ HasHandcraftedLabel() bool
+ HandcraftedLabel() string
+ GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string
+ ConvertWithBp2build(ctx BazelConversionPathContext) bool
+ GetBazelBuildFileContents(c Config, path, name string) (string, error)
+ ConvertedToBazel(ctx BazelConversionPathContext) bool
+}
+
+// BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
+type BazelModule interface {
+ Module
+ Bazelable
+}
+
+// InitBazelModule is a wrapper function that decorates a BazelModule with Bazel-conversion
+// properties.
+func InitBazelModule(module BazelModule) {
+ module.AddProperties(module.bazelProps())
+}
+
+// bazelProps returns the Bazel properties for the given BazelModuleBase.
+func (b *BazelModuleBase) bazelProps() *properties {
+ return &b.bazelProperties
+}
+
+// HasHandcraftedLabel returns whether this module has a handcrafted Bazel label.
+func (b *BazelModuleBase) HasHandcraftedLabel() bool {
+ return b.bazelProperties.Bazel_module.Label != nil
+}
+
+// HandcraftedLabel returns the handcrafted label for this module, or empty string if there is none
+func (b *BazelModuleBase) HandcraftedLabel() string {
+ return proptools.String(b.bazelProperties.Bazel_module.Label)
+}
+
+// GetBazelLabel returns the Bazel label for the given BazelModuleBase.
+func (b *BazelModuleBase) GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string {
+ if b.HasHandcraftedLabel() {
+ return b.HandcraftedLabel()
+ }
+ if b.ConvertWithBp2build(ctx) {
+ return bp2buildModuleLabel(ctx, module)
+ }
+ return "" // no label for unconverted module
+}
+
+// Configuration to decide if modules in a directory should default to true/false for bp2build_available
+type Bp2BuildConfig map[string]BazelConversionConfigEntry
+type BazelConversionConfigEntry int
+
+const (
+ // A sentinel value to be used as a key in Bp2BuildConfig for modules with
+ // no package path. This is also the module dir for top level Android.bp
+ // modules.
+ BP2BUILD_TOPLEVEL = "."
+
+ // iota + 1 ensures that the int value is not 0 when used in the Bp2buildAllowlist map,
+ // which can also mean that the key doesn't exist in a lookup.
+
+ // all modules in this package and subpackages default to bp2build_available: true.
+ // allows modules to opt-out.
+ Bp2BuildDefaultTrueRecursively BazelConversionConfigEntry = iota + 1
+
+ // all modules in this package (not recursively) default to bp2build_available: false.
+ // allows modules to opt-in.
+ Bp2BuildDefaultFalse
+)
+
+var (
+ // Configure modules in these directories to enable bp2build_available: true or false by default.
+ bp2buildDefaultConfig = Bp2BuildConfig{
+ "bionic": Bp2BuildDefaultTrueRecursively,
+ "external/gwp_asan": Bp2BuildDefaultTrueRecursively,
+ "system/core/libcutils": Bp2BuildDefaultTrueRecursively,
+ "system/logging/liblog": Bp2BuildDefaultTrueRecursively,
+ }
+
+ // Per-module denylist to always opt modules out.
+ bp2buildModuleDoNotConvertList = []string{
+ "libBionicBenchmarksUtils", // ruperts@, cc_library_static, 'map' file not found
+ "libbionic_spawn_benchmark", // ruperts@, cc_library_static, depends on //system/libbase
+ "libc_jemalloc_wrapper", // ruperts@, cc_library_static, depends on //external/jemalloc_new
+ "libc_bootstrap", // ruperts@, cc_library_static, 'private/bionic_auxv.h' file not found
+ "libc_init_static", // ruperts@, cc_library_static, 'private/bionic_elf_tls.h' file not found
+ "libc_init_dynamic", // ruperts@, cc_library_static, 'private/bionic_defs.h' file not found
+ "libc_tzcode", // ruperts@, cc_library_static, error: expected expression
+ "libc_netbsd", // ruperts@, cc_library_static, 'engine.c' file not found
+ "libc_openbsd_large_stack", // ruperts@, cc_library_static, 'android/log.h' file not found
+ "libc_openbsd", // ruperts@, cc_library_static, 'android/log.h' file not found
+ "libc_fortify", // ruperts@, cc_library_static, 'private/bionic_fortify.h' file not found
+ "libc_bionic", // ruperts@, cc_library_static, 'private/bionic_asm.h' file not found
+ "libc_bionic_ndk", // ruperts@, cc_library_static, depends on //bionic/libc/system_properties
+ "libc_bionic_systrace", // ruperts@, cc_library_static, 'private/bionic_systrace.h' file not found
+ "libc_pthread", // ruperts@, cc_library_static, 'private/bionic_defs.h' file not found
+ "libc_syscalls", // ruperts@, cc_library_static, mutator panic cannot get direct dep syscalls-arm64.S of libc_syscalls
+ "libc_ndk", // ruperts@, cc_library_static, depends on //bionic/libm:libm
+ "libc_nopthread", // ruperts@, cc_library_static, depends on //external/arm-optimized-routines
+ "libc_common", // ruperts@, cc_library_static, depends on //bionic/libc:libc_nopthread
+ "libc_common_static", // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
+ "libc_common_shared", // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
+ "libc_unwind_static", // ruperts@, cc_library_static, 'private/bionic_elf_tls.h' file not found
+ "libc_nomalloc", // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
+ "libasync_safe", // ruperts@, cc_library_static, 'private/CachedProperty.h' file not found
+ "libc_malloc_debug_backtrace", // ruperts@, cc_library_static, depends on //system/libbase
+ "libsystemproperties", // ruperts@, cc_library_static, depends on //system/core/property_service/libpropertyinfoparser
+ "libdl_static", // ruperts@, cc_library_static, 'private/CFIShadow.h' file not found
+ "liblinker_main", // ruperts@, cc_library_static, depends on //system/libbase
+ "liblinker_malloc", // ruperts@, cc_library_static, depends on //system/logging/liblog:liblog
+ "liblinker_debuggerd_stub", // ruperts@, cc_library_static, depends on //system/libbase
+ "libbionic_tests_headers_posix", // ruperts@, cc_library_static, 'complex.h' file not found
+ "libc_dns", // ruperts@, cc_library_static, 'android/log.h' file not found
+
+ // List of all full_cc_libraries in //bionic, with their immediate failures
+ "libc", // jingwen@, cc_library, depends on //external/gwp_asan
+ "libc_malloc_debug", // jingwen@, cc_library, fatal error: 'assert.h' file not found
+ "libc_malloc_hooks", // jingwen@, cc_library, fatal error: 'errno.h' file not found
+ "libdl", // jingwen@, cc_library, ld.lld: error: no input files
+ "libm", // jingwen@, cc_library, fatal error: 'freebsd-compat.h' file not found
+ "libseccomp_policy", // jingwen@, cc_library, fatal error: 'seccomp_policy.h' file not found
+ "libstdc++", // jingwen@, cc_library, depends on //external/gwp_asan
+
+ // For mixed builds specifically
+ "note_memtag_heap_async", // jingwen@, cc_library_static, OK for bp2build but features.h includes not found for mixed builds (b/185079815)
+ "note_memtag_heap_sync", // jingwen@, cc_library_static, OK for bp2build but features.h includes not found for mixed builds (b/185079815)
+ "libc_gdtoa", // ruperts@, cc_library_static, OK for bp2build but undefined symbol: __strtorQ for mixed builds
+ }
+
+ // Used for quicker lookups
+ bp2buildModuleDoNotConvert = map[string]bool{}
+)
+
+func init() {
+ for _, moduleName := range bp2buildModuleDoNotConvertList {
+ bp2buildModuleDoNotConvert[moduleName] = true
+ }
+}
+
+// ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
+func (b *BazelModuleBase) ConvertWithBp2build(ctx BazelConversionPathContext) bool {
+ if bp2buildModuleDoNotConvert[ctx.Module().Name()] {
+ return false
+ }
+
+ // Ensure that the module type of this module has a bp2build converter. This
+ // prevents mixed builds from using auto-converted modules just by matching
+ // the package dir; it also has to have a bp2build mutator as well.
+ if ctx.Config().bp2buildModuleTypeConfig[ctx.ModuleType()] == false {
+ return false
+ }
+
+ packagePath := ctx.ModuleDir()
+ config := ctx.Config().bp2buildPackageConfig
+
+ // This is a tristate value: true, false, or unset.
+ propValue := b.bazelProperties.Bazel_module.Bp2build_available
+ if bp2buildDefaultTrueRecursively(packagePath, config) {
+ // Allow modules to explicitly opt-out.
+ return proptools.BoolDefault(propValue, true)
+ }
+
+ // Allow modules to explicitly opt-in.
+ return proptools.BoolDefault(propValue, false)
+}
+
+// bp2buildDefaultTrueRecursively checks that the package contains a prefix from the
+// set of package prefixes where all modules must be converted. That is, if the
+// package is x/y/z, and the list contains either x, x/y, or x/y/z, this function will
+// return true.
+//
+// However, if the package is x/y, and it matches a Bp2BuildDefaultFalse "x/y" entry
+// exactly, this module will return false early.
+//
+// This function will also return false if the package doesn't match anything in
+// the config.
+func bp2buildDefaultTrueRecursively(packagePath string, config Bp2BuildConfig) bool {
+ ret := false
+
+ // Return exact matches in the config.
+ if config[packagePath] == Bp2BuildDefaultTrueRecursively {
+ return true
+ }
+ if config[packagePath] == Bp2BuildDefaultFalse {
+ return false
+ }
+
+ // If not, check for the config recursively.
+ packagePrefix := ""
+ // e.g. for x/y/z, iterate over x, x/y, then x/y/z, taking the final value from the allowlist.
+ for _, part := range strings.Split(packagePath, "/") {
+ packagePrefix += part
+ if config[packagePrefix] == Bp2BuildDefaultTrueRecursively {
+ // package contains this prefix and this prefix should convert all modules
+ return true
+ }
+ // Continue to the next part of the package dir.
+ packagePrefix += "/"
+ }
+
+ return ret
+}
+
+// GetBazelBuildFileContents returns the file contents of a hand-crafted BUILD file if available or
+// an error if there are errors reading the file.
+// TODO(b/181575318): currently we append the whole BUILD file, let's change that to do
+// something more targeted based on the rule type and target.
+func (b *BazelModuleBase) GetBazelBuildFileContents(c Config, path, name string) (string, error) {
+ if !strings.Contains(b.HandcraftedLabel(), path) {
+ return "", fmt.Errorf("%q not found in bazel_module.label %q", path, b.HandcraftedLabel())
+ }
+ name = filepath.Join(path, name)
+ f, err := c.fs.Open(name)
+ if err != nil {
+ return "", err
+ }
+ defer f.Close()
+
+ data, err := ioutil.ReadAll(f)
+ if err != nil {
+ return "", err
+ }
+ return string(data[:]), nil
+}
+
+// ConvertedToBazel returns whether this module has been converted to Bazel, whether automatically
+// or manually
+func (b *BazelModuleBase) ConvertedToBazel(ctx BazelConversionPathContext) bool {
+ return b.ConvertWithBp2build(ctx) || b.HasHandcraftedLabel()
+}
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 357b99f..8d561d2 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -26,22 +26,35 @@
"strings"
"sync"
+ "android/soong/bazel/cquery"
+
"github.com/google/blueprint/bootstrap"
"android/soong/bazel"
"android/soong/shared"
)
-type CqueryRequestType int
+type cqueryRequest interface {
+ // Name returns a string name for this request type. Such request type names must be unique,
+ // and must only consist of alphanumeric characters.
+ Name() string
-const (
- getAllFiles CqueryRequestType = iota
-)
+ // StarlarkFunctionBody returns a starlark function body to process this request type.
+ // The returned string is the body of a Starlark function which obtains
+ // all request-relevant information about a target and returns a string containing
+ // this information.
+ // The function should have the following properties:
+ // - `target` is the only parameter to this function (a configured target).
+ // - The return value must be a string.
+ // - The function body should not be indented outside of its own scope.
+ StarlarkFunctionBody() string
+}
// Map key to describe bazel cquery requests.
type cqueryKey struct {
label string
- requestType CqueryRequestType
+ requestType cqueryRequest
+ archType ArchType
}
type BazelContext interface {
@@ -50,9 +63,12 @@
// has been queued to be run later.
// Returns result files built by building the given bazel target label.
- GetAllFiles(label string) ([]string, bool)
+ GetOutputFiles(label string, archType ArchType) ([]string, bool)
// TODO(cparsons): Other cquery-related methods should be added here.
+ // Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order).
+ GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error)
+
// ** End cquery methods
// Issues commands to Bazel to receive results for all cquery requests
@@ -69,16 +85,24 @@
BuildStatementsToRegister() []bazel.BuildStatement
}
-// A context object which tracks queued requests that need to be made to Bazel,
-// and their results after the requests have been made.
-type bazelContext struct {
+type bazelRunner interface {
+ issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) (string, string, error)
+}
+
+type bazelPaths struct {
homeDir string
bazelPath string
outputBase string
workspaceDir string
buildDir string
metricsDir string
+}
+// A context object which tracks queued requests that need to be made to Bazel,
+// and their results after the requests have been made.
+type bazelContext struct {
+ bazelRunner
+ paths *bazelPaths
requests map[cqueryKey]bool // cquery requests that have not yet been issued to Bazel
requestMutex sync.Mutex // requests can be written in parallel
@@ -97,14 +121,22 @@
// A bazel context to use for tests.
type MockBazelContext struct {
- AllFiles map[string][]string
+ OutputBaseDir string
+
+ LabelToOutputFiles map[string][]string
+ LabelToCcInfo map[string]cquery.CcInfo
}
-func (m MockBazelContext) GetAllFiles(label string) ([]string, bool) {
- result, ok := m.AllFiles[label]
+func (m MockBazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
+ result, ok := m.LabelToOutputFiles[label]
return result, ok
}
+func (m MockBazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error) {
+ result, ok := m.LabelToCcInfo[label]
+ return result, ok, nil
+}
+
func (m MockBazelContext) InvokeBazel() error {
panic("unimplemented")
}
@@ -113,9 +145,7 @@
return true
}
-func (m MockBazelContext) OutputBase() string {
- return "outputbase"
-}
+func (m MockBazelContext) OutputBase() string { return m.OutputBaseDir }
func (m MockBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
return []bazel.BuildStatement{}
@@ -123,17 +153,36 @@
var _ BazelContext = MockBazelContext{}
-func (bazelCtx *bazelContext) GetAllFiles(label string) ([]string, bool) {
- result, ok := bazelCtx.cquery(label, getAllFiles)
+func (bazelCtx *bazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
+ rawString, ok := bazelCtx.cquery(label, cquery.GetOutputFiles, archType)
+ var ret []string
if ok {
- bazelOutput := strings.TrimSpace(result)
- return strings.Split(bazelOutput, ", "), true
- } else {
- return nil, false
+ bazelOutput := strings.TrimSpace(rawString)
+ ret = cquery.GetOutputFiles.ParseResult(bazelOutput)
}
+ return ret, ok
}
-func (n noopBazelContext) GetAllFiles(label string) ([]string, bool) {
+func (bazelCtx *bazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error) {
+ result, ok := bazelCtx.cquery(label, cquery.GetCcInfo, archType)
+ if !ok {
+ return cquery.CcInfo{}, ok, nil
+ }
+
+ bazelOutput := strings.TrimSpace(result)
+ ret, err := cquery.GetCcInfo.ParseResult(bazelOutput)
+ return ret, ok, err
+}
+
+func (n noopBazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
+ panic("unimplemented")
+}
+
+func (n noopBazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error) {
+ panic("unimplemented")
+}
+
+func (n noopBazelContext) GetPrebuiltCcStaticLibraryFiles(label string, archType ArchType) ([]string, bool) {
panic("unimplemented")
}
@@ -160,42 +209,56 @@
return noopBazelContext{}, nil
}
- bazelCtx := bazelContext{buildDir: c.buildDir, requests: make(map[cqueryKey]bool)}
+ p, err := bazelPathsFromConfig(c)
+ if err != nil {
+ return nil, err
+ }
+ return &bazelContext{
+ bazelRunner: &builtinBazelRunner{},
+ paths: p,
+ requests: make(map[cqueryKey]bool),
+ }, nil
+}
+
+func bazelPathsFromConfig(c *config) (*bazelPaths, error) {
+ p := bazelPaths{
+ buildDir: c.buildDir,
+ }
missingEnvVars := []string{}
if len(c.Getenv("BAZEL_HOME")) > 1 {
- bazelCtx.homeDir = c.Getenv("BAZEL_HOME")
+ p.homeDir = c.Getenv("BAZEL_HOME")
} else {
missingEnvVars = append(missingEnvVars, "BAZEL_HOME")
}
if len(c.Getenv("BAZEL_PATH")) > 1 {
- bazelCtx.bazelPath = c.Getenv("BAZEL_PATH")
+ p.bazelPath = c.Getenv("BAZEL_PATH")
} else {
missingEnvVars = append(missingEnvVars, "BAZEL_PATH")
}
if len(c.Getenv("BAZEL_OUTPUT_BASE")) > 1 {
- bazelCtx.outputBase = c.Getenv("BAZEL_OUTPUT_BASE")
+ p.outputBase = c.Getenv("BAZEL_OUTPUT_BASE")
} else {
missingEnvVars = append(missingEnvVars, "BAZEL_OUTPUT_BASE")
}
if len(c.Getenv("BAZEL_WORKSPACE")) > 1 {
- bazelCtx.workspaceDir = c.Getenv("BAZEL_WORKSPACE")
+ p.workspaceDir = c.Getenv("BAZEL_WORKSPACE")
} else {
missingEnvVars = append(missingEnvVars, "BAZEL_WORKSPACE")
}
if len(c.Getenv("BAZEL_METRICS_DIR")) > 1 {
- bazelCtx.metricsDir = c.Getenv("BAZEL_METRICS_DIR")
+ p.metricsDir = c.Getenv("BAZEL_METRICS_DIR")
} else {
missingEnvVars = append(missingEnvVars, "BAZEL_METRICS_DIR")
}
if len(missingEnvVars) > 0 {
return nil, errors.New(fmt.Sprintf("missing required env vars to use bazel: %s", missingEnvVars))
} else {
- return &bazelCtx, nil
+ return &p, nil
}
}
-func (context *bazelContext) BazelMetricsDir() string {
- return context.metricsDir
+func (p *bazelPaths) BazelMetricsDir() string {
+ return p.metricsDir
}
func (context *bazelContext) BazelEnabled() bool {
@@ -207,8 +270,9 @@
// If the given request was already made (and the results are available), then
// returns (result, true). If the request is queued but no results are available,
// then returns ("", false).
-func (context *bazelContext) cquery(label string, requestType CqueryRequestType) (string, bool) {
- key := cqueryKey{label, requestType}
+func (context *bazelContext) cquery(label string, requestType cqueryRequest,
+ archType ArchType) (string, bool) {
+ key := cqueryKey{label, requestType, archType}
if result, ok := context.results[key]; ok {
return result, true
} else {
@@ -227,26 +291,75 @@
return ""
}
-func (context *bazelContext) issueBazelCommand(runName bazel.RunName, command string, labels []string,
- extraFlags ...string) (string, error) {
+type bazelCommand struct {
+ command string
+ // query or label
+ expression string
+}
- cmdFlags := []string{"--output_base=" + context.outputBase, command}
- cmdFlags = append(cmdFlags, labels...)
- cmdFlags = append(cmdFlags, "--package_path=%workspace%/"+context.intermediatesDir())
- cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(context, runName))
+type mockBazelRunner struct {
+ bazelCommandResults map[bazelCommand]string
+ commands []bazelCommand
+}
+
+func (r *mockBazelRunner) issueBazelCommand(paths *bazelPaths,
+ runName bazel.RunName,
+ command bazelCommand,
+ extraFlags ...string) (string, string, error) {
+ r.commands = append(r.commands, command)
+ if ret, ok := r.bazelCommandResults[command]; ok {
+ return ret, "", nil
+ }
+ return "", "", nil
+}
+
+type builtinBazelRunner struct{}
+
+// Issues the given bazel command with given build label and additional flags.
+// Returns (stdout, stderr, error). The first and second return values are strings
+// containing the stdout and stderr of the run command, and an error is returned if
+// the invocation returned an error code.
+func (r *builtinBazelRunner) issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand,
+ extraFlags ...string) (string, string, error) {
+ cmdFlags := []string{"--output_base=" + paths.outputBase, command.command}
+ cmdFlags = append(cmdFlags, command.expression)
+ cmdFlags = append(cmdFlags, "--package_path=%workspace%/"+paths.intermediatesDir())
+ cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(paths, runName))
+
+ // Set default platforms to canonicalized values for mixed builds requests.
+ // If these are set in the bazelrc, they will have values that are
+ // non-canonicalized to @sourceroot labels, and thus be invalid when
+ // referenced from the buildroot.
+ //
+ // The actual platform values here may be overridden by configuration
+ // transitions from the buildroot.
+ cmdFlags = append(cmdFlags,
+ fmt.Sprintf("--platforms=%s", canonicalizeLabel("//build/bazel/platforms:android_x86_64")))
+ cmdFlags = append(cmdFlags,
+ fmt.Sprintf("--extra_toolchains=%s", canonicalizeLabel("//prebuilts/clang/host/linux-x86:all")))
+ // This should be parameterized on the host OS, but let's restrict to linux
+ // to keep things simple for now.
+ cmdFlags = append(cmdFlags,
+ fmt.Sprintf("--host_platform=%s", canonicalizeLabel("//build/bazel/platforms:linux_x86_64")))
+
+ // Explicitly disable downloading rules (such as canonical C++ and Java rules) from the network.
+ cmdFlags = append(cmdFlags, "--experimental_repository_disable_download")
cmdFlags = append(cmdFlags, extraFlags...)
- bazelCmd := exec.Command(context.bazelPath, cmdFlags...)
- bazelCmd.Dir = context.workspaceDir
- bazelCmd.Env = append(os.Environ(), "HOME="+context.homeDir, pwdPrefix())
-
+ bazelCmd := exec.Command(paths.bazelPath, cmdFlags...)
+ bazelCmd.Dir = paths.workspaceDir
+ bazelCmd.Env = append(os.Environ(), "HOME="+paths.homeDir, pwdPrefix(),
+ // Disables local host detection of gcc; toolchain information is defined
+ // explicitly in BUILD files.
+ "BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1")
stderr := &bytes.Buffer{}
bazelCmd.Stderr = stderr
if output, err := bazelCmd.Output(); err != nil {
- return "", fmt.Errorf("bazel command failed. command: [%s], error [%s]", bazelCmd, stderr)
+ return "", string(stderr.Bytes()),
+ fmt.Errorf("bazel command failed. command: [%s], env: [%s], error [%s]", bazelCmd, bazelCmd.Env, stderr)
} else {
- return string(output), nil
+ return string(output), string(stderr.Bytes()), nil
}
}
@@ -259,27 +372,64 @@
# This file is generated by soong_build. Do not edit.
local_repository(
name = "sourceroot",
- path = "%s",
+ path = "%[1]s",
+)
+
+local_repository(
+ name = "rules_cc",
+ path = "%[1]s/build/bazel/rules_cc",
+)
+
+local_repository(
+ name = "bazel_skylib",
+ path = "%[1]s/build/bazel/bazel_skylib",
)
`
- return []byte(fmt.Sprintf(formatString, context.workspaceDir))
+ return []byte(fmt.Sprintf(formatString, context.paths.workspaceDir))
}
func (context *bazelContext) mainBzlFileContents() []byte {
+ // TODO(cparsons): Define configuration transitions programmatically based
+ // on available archs.
contents := `
#####################################################
# This file is generated by soong_build. Do not edit.
#####################################################
-def _mixed_build_root_impl(ctx):
+def _config_node_transition_impl(settings, attr):
+ return {
+ "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:android_%s" % attr.arch,
+ }
+
+_config_node_transition = transition(
+ implementation = _config_node_transition_impl,
+ inputs = [],
+ outputs = [
+ "//command_line_option:platforms",
+ ],
+)
+
+def _passthrough_rule_impl(ctx):
return [DefaultInfo(files = depset(ctx.files.deps))]
+config_node = rule(
+ implementation = _passthrough_rule_impl,
+ attrs = {
+ "arch" : attr.string(mandatory = True),
+ "deps" : attr.label_list(cfg = _config_node_transition),
+ "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
+ },
+)
+
+
# Rule representing the root of the build, to depend on all Bazel targets that
# are required for the build. Building this target will build the entire Bazel
# build tree.
mixed_build_root = rule(
- implementation = _mixed_build_root_impl,
- attrs = {"deps" : attr.label_list()},
+ implementation = _passthrough_rule_impl,
+ attrs = {
+ "deps" : attr.label_list(),
+ },
)
def _phony_root_impl(ctx):
@@ -310,9 +460,13 @@
}
func (context *bazelContext) mainBuildFileContents() []byte {
+ // TODO(cparsons): Map label to attribute programmatically; don't use hard-coded
+ // architecture mapping.
formatString := `
# This file is generated by soong_build. Do not edit.
-load(":main.bzl", "mixed_build_root", "phony_root")
+load(":main.bzl", "config_node", "mixed_build_root", "phony_root")
+
+%s
mixed_build_root(name = "buildroot",
deps = [%s],
@@ -322,45 +476,127 @@
deps = [":buildroot"],
)
`
- var buildRootDeps []string = nil
- for val, _ := range context.requests {
- buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\"", canonicalizeLabel(val.label)))
- }
- buildRootDepsString := strings.Join(buildRootDeps, ",\n ")
+ configNodeFormatString := `
+config_node(name = "%s",
+ arch = "%s",
+ deps = [%s],
+)
+`
- return []byte(fmt.Sprintf(formatString, buildRootDepsString))
+ configNodesSection := ""
+
+ labelsByArch := map[string][]string{}
+ for val, _ := range context.requests {
+ labelString := fmt.Sprintf("\"%s\"", canonicalizeLabel(val.label))
+ archString := getArchString(val)
+ labelsByArch[archString] = append(labelsByArch[archString], labelString)
+ }
+
+ configNodeLabels := []string{}
+ for archString, labels := range labelsByArch {
+ configNodeLabels = append(configNodeLabels, fmt.Sprintf("\":%s\"", archString))
+ labelsString := strings.Join(labels, ",\n ")
+ configNodesSection += fmt.Sprintf(configNodeFormatString, archString, archString, labelsString)
+ }
+
+ return []byte(fmt.Sprintf(formatString, configNodesSection, strings.Join(configNodeLabels, ",\n ")))
}
+func indent(original string) string {
+ result := ""
+ for _, line := range strings.Split(original, "\n") {
+ result += " " + line + "\n"
+ }
+ return result
+}
+
+// Returns the file contents of the buildroot.cquery file that should be used for the cquery
+// expression in order to obtain information about buildroot and its dependencies.
+// The contents of this file depend on the bazelContext's requests; requests are enumerated
+// and grouped by their request type. The data retrieved for each label depends on its
+// request type.
func (context *bazelContext) cqueryStarlarkFileContents() []byte {
- formatString := `
-# This file is generated by soong_build. Do not edit.
-getAllFilesLabels = {
+ requestTypeToCqueryIdEntries := map[cqueryRequest][]string{}
+ for val, _ := range context.requests {
+ cqueryId := getCqueryId(val)
+ mapEntryString := fmt.Sprintf("%q : True", cqueryId)
+ requestTypeToCqueryIdEntries[val.requestType] =
+ append(requestTypeToCqueryIdEntries[val.requestType], mapEntryString)
+ }
+ labelRegistrationMapSection := ""
+ functionDefSection := ""
+ mainSwitchSection := ""
+
+ mapDeclarationFormatString := `
+%s = {
%s
}
+`
+ functionDefFormatString := `
+def %s(target):
+%s
+`
+ mainSwitchSectionFormatString := `
+ if id_string in %s:
+ return id_string + ">>" + %s(target)
+`
+
+ for requestType, _ := range requestTypeToCqueryIdEntries {
+ labelMapName := requestType.Name() + "_Labels"
+ functionName := requestType.Name() + "_Fn"
+ labelRegistrationMapSection += fmt.Sprintf(mapDeclarationFormatString,
+ labelMapName,
+ strings.Join(requestTypeToCqueryIdEntries[requestType], ",\n "))
+ functionDefSection += fmt.Sprintf(functionDefFormatString,
+ functionName,
+ indent(requestType.StarlarkFunctionBody()))
+ mainSwitchSection += fmt.Sprintf(mainSwitchSectionFormatString,
+ labelMapName, functionName)
+ }
+
+ formatString := `
+# This file is generated by soong_build. Do not edit.
+
+# Label Map Section
+%s
+
+# Function Def Section
+%s
+
+def get_arch(target):
+ buildoptions = build_options(target)
+ platforms = build_options(target)["//command_line_option:platforms"]
+ if len(platforms) != 1:
+ # An individual configured target should have only one platform architecture.
+ # Note that it's fine for there to be multiple architectures for the same label,
+ # but each is its own configured target.
+ fail("expected exactly 1 platform for " + str(target.label) + " but got " + str(platforms))
+ platform_name = build_options(target)["//command_line_option:platforms"][0].name
+ if platform_name == "host":
+ return "HOST"
+ elif not platform_name.startswith("android_"):
+ fail("expected platform name of the form 'android_<arch>', but was " + str(platforms))
+ return "UNKNOWN"
+ return platform_name[len("android_"):]
def format(target):
- if str(target.label) in getAllFilesLabels:
- return str(target.label) + ">>" + ', '.join([f.path for f in target.files.to_list()])
- else:
- # This target was not requested via cquery, and thus must be a dependency
- # of a requested target.
- return ""
-`
- var buildRootDeps []string = nil
- // TODO(cparsons): Sort by request type instead of assuming all requests
- // are of GetAllFiles type.
- for val, _ := range context.requests {
- buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\" : True", canonicalizeLabel(val.label)))
- }
- buildRootDepsString := strings.Join(buildRootDeps, ",\n ")
+ id_string = str(target.label) + "|" + get_arch(target)
- return []byte(fmt.Sprintf(formatString, buildRootDepsString))
+ # Main switch section
+ %s
+ # This target was not requested via cquery, and thus must be a dependency
+ # of a requested target.
+ return id_string + ">>NONE"
+`
+
+ return []byte(fmt.Sprintf(formatString, labelRegistrationMapSection, functionDefSection,
+ mainSwitchSection))
}
// Returns a workspace-relative path containing build-related metadata required
// for interfacing with Bazel. Example: out/soong/bazel.
-func (context *bazelContext) intermediatesDir() string {
- return filepath.Join(context.buildDir, "bazel")
+func (p *bazelPaths) intermediatesDir() string {
+ return filepath.Join(p.buildDir, "bazel")
}
// Issues commands to Bazel to receive results for all cquery requests
@@ -369,43 +605,54 @@
context.results = make(map[cqueryKey]string)
var cqueryOutput string
+ var cqueryErr string
var err error
- err = os.Mkdir(absolutePath(context.intermediatesDir()), 0777)
+ intermediatesDirPath := absolutePath(context.paths.intermediatesDir())
+ if _, err := os.Stat(intermediatesDirPath); os.IsNotExist(err) {
+ err = os.Mkdir(intermediatesDirPath, 0777)
+ }
+
if err != nil {
return err
}
err = ioutil.WriteFile(
- absolutePath(filepath.Join(context.intermediatesDir(), "main.bzl")),
+ filepath.Join(intermediatesDirPath, "main.bzl"),
context.mainBzlFileContents(), 0666)
if err != nil {
return err
}
err = ioutil.WriteFile(
- absolutePath(filepath.Join(context.intermediatesDir(), "BUILD.bazel")),
+ filepath.Join(intermediatesDirPath, "BUILD.bazel"),
context.mainBuildFileContents(), 0666)
if err != nil {
return err
}
- cqueryFileRelpath := filepath.Join(context.intermediatesDir(), "buildroot.cquery")
+ cqueryFileRelpath := filepath.Join(context.paths.intermediatesDir(), "buildroot.cquery")
err = ioutil.WriteFile(
absolutePath(cqueryFileRelpath),
context.cqueryStarlarkFileContents(), 0666)
if err != nil {
return err
}
- workspaceFileRelpath := filepath.Join(context.intermediatesDir(), "WORKSPACE.bazel")
err = ioutil.WriteFile(
- absolutePath(workspaceFileRelpath),
+ filepath.Join(intermediatesDirPath, "WORKSPACE.bazel"),
context.workspaceFileContents(), 0666)
if err != nil {
return err
}
buildrootLabel := "//:buildroot"
- cqueryOutput, err = context.issueBazelCommand(bazel.CqueryBuildRootRunName, "cquery",
- []string{fmt.Sprintf("deps(%s)", buildrootLabel)},
+ cqueryOutput, cqueryErr, err = context.issueBazelCommand(
+ context.paths,
+ bazel.CqueryBuildRootRunName,
+ bazelCommand{"cquery", fmt.Sprintf("kind(rule, deps(%s))", buildrootLabel)},
"--output=starlark",
"--starlark:file="+cqueryFileRelpath)
+ err = ioutil.WriteFile(filepath.Join(intermediatesDirPath, "cquery.out"),
+ []byte(cqueryOutput), 0666)
+ if err != nil {
+ return err
+ }
if err != nil {
return err
@@ -420,10 +667,11 @@
}
for val, _ := range context.requests {
- if cqueryResult, ok := cqueryResults[canonicalizeLabel(val.label)]; ok {
+ if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok {
context.results[val] = string(cqueryResult)
} else {
- return fmt.Errorf("missing result for bazel target %s", val.label)
+ return fmt.Errorf("missing result for bazel target %s. query output: [%s], cquery err: [%s]",
+ getCqueryId(val), cqueryOutput, cqueryErr)
}
}
@@ -431,29 +679,35 @@
//
// TODO(cparsons): Use --target_pattern_file to avoid command line limits.
var aqueryOutput string
- aqueryOutput, err = context.issueBazelCommand(bazel.AqueryBuildRootRunName, "aquery",
- []string{fmt.Sprintf("deps(%s)", buildrootLabel),
- // Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
- // proto sources, which would add a number of unnecessary dependencies.
- "--output=jsonproto"})
+ aqueryOutput, _, err = context.issueBazelCommand(
+ context.paths,
+ bazel.AqueryBuildRootRunName,
+ bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)},
+ // Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
+ // proto sources, which would add a number of unnecessary dependencies.
+ "--output=jsonproto")
if err != nil {
return err
}
- context.buildStatements = bazel.AqueryBuildStatements([]byte(aqueryOutput))
+ context.buildStatements, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
+ if err != nil {
+ return err
+ }
// Issue a build command of the phony root to generate symlink forests for dependencies of the
// Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
// but some of symlinks may be required to resolve source dependencies of the build.
- _, err = context.issueBazelCommand(bazel.BazelBuildPhonyRootRunName, "build",
- []string{"//:phonyroot"})
+ _, _, err = context.issueBazelCommand(
+ context.paths,
+ bazel.BazelBuildPhonyRootRunName,
+ bazelCommand{"build", "//:phonyroot"})
if err != nil {
return err
}
- fmt.Printf("Build statements %s", context.buildStatements)
// Clear requests.
context.requests = map[cqueryKey]bool{}
return nil
@@ -464,7 +718,7 @@
}
func (context *bazelContext) OutputBase() string {
- return context.outputBase
+ return context.paths.outputBase
}
// Singleton used for registering BUILD file ninja dependencies (needed
@@ -483,7 +737,7 @@
// Add ninja file dependencies for files which all bazel invocations require.
bazelBuildList := absolutePath(filepath.Join(
- filepath.Dir(bootstrap.ModuleListFile), "bazel.list"))
+ filepath.Dir(bootstrap.CmdlineArgs.ModuleListFile), "bazel.list"))
ctx.AddNinjaFileDeps(bazelBuildList)
data, err := ioutil.ReadFile(bazelBuildList)
@@ -497,6 +751,9 @@
// Register bazel-owned build statements (obtained from the aquery invocation).
for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
+ if len(buildStatement.Command) < 1 {
+ panic(fmt.Sprintf("unhandled build statement: %v", buildStatement))
+ }
rule := NewRuleBuilder(pctx, ctx)
cmd := rule.Command()
cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && %s",
@@ -509,12 +766,29 @@
cmd.Implicit(PathForBazelOut(ctx, inputPath))
}
+ if depfile := buildStatement.Depfile; depfile != nil {
+ cmd.ImplicitDepFile(PathForBazelOut(ctx, *depfile))
+ }
+
// This is required to silence warnings pertaining to unexpected timestamps. Particularly,
// some Bazel builtins (such as files in the bazel_tools directory) have far-future
// timestamps. Without restat, Ninja would emit warnings that the input files of a
// build statement have later timestamps than the outputs.
rule.Restat()
- rule.Build(fmt.Sprintf("bazel %s", index), buildStatement.Mnemonic)
+ rule.Build(fmt.Sprintf("bazel %d", index), buildStatement.Mnemonic)
+ }
+}
+
+func getCqueryId(key cqueryKey) string {
+ return canonicalizeLabel(key.label) + "|" + getArchString(key)
+}
+
+func getArchString(key cqueryKey) string {
+ arch := key.archType.Name
+ if len(arch) > 0 {
+ return arch
+ } else {
+ return "x86_64"
}
}
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
new file mode 100644
index 0000000..85f701f
--- /dev/null
+++ b/android/bazel_handler_test.go
@@ -0,0 +1,118 @@
+package android
+
+import (
+ "os"
+ "path/filepath"
+ "reflect"
+ "testing"
+)
+
+func TestRequestResultsAfterInvokeBazel(t *testing.T) {
+ label := "//foo:bar"
+ arch := Arm64
+ bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
+ bazelCommand{command: "cquery", expression: "kind(rule, deps(//:buildroot))"}: `@sourceroot//foo:bar|arm64>>out/foo/bar.txt`,
+ })
+ g, ok := bazelContext.GetOutputFiles(label, arch)
+ if ok {
+ t.Errorf("Did not expect cquery results prior to running InvokeBazel(), but got %s", g)
+ }
+ err := bazelContext.InvokeBazel()
+ if err != nil {
+ t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
+ }
+ g, ok = bazelContext.GetOutputFiles(label, arch)
+ if !ok {
+ t.Errorf("Expected cquery results after running InvokeBazel(), but got none")
+ } else if w := []string{"out/foo/bar.txt"}; !reflect.DeepEqual(w, g) {
+ t.Errorf("Expected output %s, got %s", w, g)
+ }
+}
+
+func TestInvokeBazelWritesBazelFiles(t *testing.T) {
+ bazelContext, baseDir := testBazelContext(t, map[bazelCommand]string{})
+ err := bazelContext.InvokeBazel()
+ if err != nil {
+ t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
+ }
+ if _, err := os.Stat(filepath.Join(baseDir, "bazel", "main.bzl")); os.IsNotExist(err) {
+ t.Errorf("Expected main.bzl to exist, but it does not")
+ } else if err != nil {
+ t.Errorf("Unexpected error stating main.bzl %s", err)
+ }
+
+ if _, err := os.Stat(filepath.Join(baseDir, "bazel", "BUILD.bazel")); os.IsNotExist(err) {
+ t.Errorf("Expected BUILD.bazel to exist, but it does not")
+ } else if err != nil {
+ t.Errorf("Unexpected error stating BUILD.bazel %s", err)
+ }
+
+ if _, err := os.Stat(filepath.Join(baseDir, "bazel", "WORKSPACE.bazel")); os.IsNotExist(err) {
+ t.Errorf("Expected WORKSPACE.bazel to exist, but it does not")
+ } else if err != nil {
+ t.Errorf("Unexpected error stating WORKSPACE.bazel %s", err)
+ }
+}
+
+func TestInvokeBazelPopulatesBuildStatements(t *testing.T) {
+ bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
+ bazelCommand{command: "aquery", expression: "deps(//:buildroot)"}: `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "inputDepSetIds": [1],
+ "outputIds": [1],
+ "primaryOutputId": 1
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1, 2]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "two"
+ }]
+}`,
+ })
+ err := bazelContext.InvokeBazel()
+ if err != nil {
+ t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
+ }
+
+ got := bazelContext.BuildStatementsToRegister()
+ if want := 1; len(got) != want {
+ t.Errorf("Expected %d registered build statements, got %#v", want, got)
+ }
+}
+
+func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*bazelContext, string) {
+ t.Helper()
+ p := bazelPaths{
+ buildDir: t.TempDir(),
+ outputBase: "outputbase",
+ workspaceDir: "workspace_dir",
+ }
+ aqueryCommand := bazelCommand{command: "aquery", expression: "deps(//:buildroot)"}
+ if _, exists := bazelCommandResults[aqueryCommand]; !exists {
+ bazelCommandResults[aqueryCommand] = "{}\n"
+ }
+ runner := &mockBazelRunner{bazelCommandResults: bazelCommandResults}
+ return &bazelContext{
+ bazelRunner: runner,
+ paths: &p,
+ requests: map[cqueryKey]bool{},
+ }, p.buildDir
+}
diff --git a/android/bazel_test.go b/android/bazel_test.go
new file mode 100644
index 0000000..e5d8fbb
--- /dev/null
+++ b/android/bazel_test.go
@@ -0,0 +1,134 @@
+// Copyright 2021 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 "testing"
+
+func TestConvertAllModulesInPackage(t *testing.T) {
+ testCases := []struct {
+ prefixes Bp2BuildConfig
+ packageDir string
+ }{
+ {
+ prefixes: Bp2BuildConfig{
+ "a": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a/b": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a/b",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a/b": Bp2BuildDefaultTrueRecursively,
+ "a/b/c": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a/b",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a": Bp2BuildDefaultTrueRecursively,
+ "d/e/f": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a/b",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a": Bp2BuildDefaultFalse,
+ "a/b": Bp2BuildDefaultTrueRecursively,
+ "a/b/c": Bp2BuildDefaultFalse,
+ },
+ packageDir: "a/b",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a": Bp2BuildDefaultTrueRecursively,
+ "a/b": Bp2BuildDefaultFalse,
+ "a/b/c": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a",
+ },
+ }
+
+ for _, test := range testCases {
+ if !bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes) {
+ t.Errorf("Expected to convert all modules in %s based on %v, but failed.", test.packageDir, test.prefixes)
+ }
+ }
+}
+
+func TestModuleOptIn(t *testing.T) {
+ testCases := []struct {
+ prefixes Bp2BuildConfig
+ packageDir string
+ }{
+ {
+ prefixes: Bp2BuildConfig{
+ "a/b": Bp2BuildDefaultFalse,
+ },
+ packageDir: "a/b",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a": Bp2BuildDefaultFalse,
+ "a/b": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a/b": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a", // opt-in by default
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a/b/c": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a/b",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a": Bp2BuildDefaultTrueRecursively,
+ "d/e/f": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "foo/bar",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a": Bp2BuildDefaultTrueRecursively,
+ "a/b": Bp2BuildDefaultFalse,
+ "a/b/c": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a/b",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a": Bp2BuildDefaultFalse,
+ "a/b": Bp2BuildDefaultTrueRecursively,
+ "a/b/c": Bp2BuildDefaultFalse,
+ },
+ packageDir: "a",
+ },
+ }
+
+ for _, test := range testCases {
+ if bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes) {
+ t.Errorf("Expected to allow module opt-in in %s based on %v, but failed.", test.packageDir, test.prefixes)
+ }
+ }
+}
diff --git a/android/config.go b/android/config.go
index 89467d8..3db7980 100644
--- a/android/config.go
+++ b/android/config.go
@@ -19,11 +19,13 @@
import (
"encoding/json"
+ "errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
+ "strconv"
"strings"
"sync"
@@ -33,6 +35,7 @@
"github.com/google/blueprint/proptools"
"android/soong/android/soongconfig"
+ "android/soong/remoteexec"
)
// Bool re-exports proptools.Bool for the android package.
@@ -67,6 +70,18 @@
return c.buildDir
}
+func (c Config) NinjaBuildDir() string {
+ return c.buildDir
+}
+
+func (c Config) DebugCompilation() bool {
+ return false // Never compile Go code in the main build for debugging
+}
+
+func (c Config) SrcDir() string {
+ return c.srcDir
+}
+
// A DeviceConfig object represents the configuration for a particular device
// being built. For now there will only be one of these, but in the future there
// may be multiple devices being built.
@@ -125,9 +140,12 @@
fs pathtools.FileSystem
mockBpList string
+ bp2buildPackageConfig Bp2BuildConfig
+ bp2buildModuleTypeConfig map[string]bool
+
// If testAllowNonExistentPaths is true then PathForSource and PathForModuleSrc won't error
// in tests when a path doesn't exist.
- testAllowNonExistentPaths bool
+ TestAllowNonExistentPaths bool
// The list of files that when changed, must invalidate soong_build to
// regenerate build.ninja.
@@ -231,7 +249,7 @@
// Copy the real PATH value to the test environment, it's needed by
// NonHermeticHostSystemTool() used in x86_darwin_host.go
- envCopy["PATH"] = originalEnv["PATH"]
+ envCopy["PATH"] = os.Getenv("PATH")
config := &config{
productVariables: productVariables{
@@ -246,6 +264,7 @@
AAPTCharacteristics: stringPtr("nosdcard"),
AAPTPrebuiltDPI: []string{"xhdpi", "xxhdpi"},
UncompressPrivAppDex: boolPtr(true),
+ ShippingApiLevel: stringPtr("30"),
},
buildDir: buildDir,
@@ -254,7 +273,7 @@
// Set testAllowNonExistentPaths so that test contexts don't need to specify every path
// passed to PathForSource or PathForModuleSrc.
- testAllowNonExistentPaths: true,
+ TestAllowNonExistentPaths: true,
BazelContext: noopBazelContext{},
}
@@ -265,48 +284,27 @@
config.mockFileSystem(bp, fs)
+ config.bp2buildModuleTypeConfig = map[string]bool{}
+
return Config{config}
}
-// TestArchConfigNativeBridge returns a Config object suitable for using
-// for tests that need to run the arch mutator for native bridge supported
-// archs.
-func TestArchConfigNativeBridge(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
- testConfig := TestArchConfig(buildDir, env, bp, fs)
- config := testConfig.config
-
- config.Targets[Android] = []Target{
- {Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
- {Android, Arch{ArchType: X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", "", false},
- {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled, "x86_64", "arm64", false},
- {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled, "x86", "arm", false},
- }
-
- return testConfig
-}
-
-// TestArchConfigFuchsia returns a Config object suitable for using for
-// tests that need to run the arch mutator for the Fuchsia arch.
-func TestArchConfigFuchsia(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
- testConfig := TestConfig(buildDir, env, bp, fs)
- config := testConfig.config
-
- config.Targets = map[OsType][]Target{
- Fuchsia: []Target{
+func fuchsiaTargets() map[OsType][]Target {
+ return map[OsType][]Target{
+ Fuchsia: {
{Fuchsia, Arch{ArchType: Arm64, ArchVariant: "", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
},
- BuildOs: []Target{
+ BuildOs: {
{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
},
}
-
- return testConfig
}
-// TestArchConfig returns a Config object suitable for using for tests that
-// need to run the arch mutator.
-func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
- testConfig := TestConfig(buildDir, env, bp, fs)
+var PrepareForTestSetDeviceToFuchsia = FixtureModifyConfig(func(config Config) {
+ config.Targets = fuchsiaTargets()
+})
+
+func modifyTestConfigToSupportArchMutator(testConfig Config) {
config := testConfig.config
config.Targets = map[OsType][]Target{
@@ -332,7 +330,13 @@
config.TestProductVariables.DeviceArchVariant = proptools.StringPtr("armv8-a")
config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm")
config.TestProductVariables.DeviceSecondaryArchVariant = proptools.StringPtr("armv7-a-neon")
+}
+// TestArchConfig returns a Config object suitable for using for tests that
+// need to run the arch mutator.
+func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
+ testConfig := TestConfig(buildDir, env, bp, fs)
+ modifyTestConfigToSupportArchMutator(testConfig)
return testConfig
}
@@ -341,7 +345,7 @@
// multiple runs in the same program execution is carried over (such as Bazel
// context or environment deps).
func ConfigForAdditionalRun(c Config) (Config, error) {
- newConfig, err := NewConfig(c.srcDir, c.buildDir, c.moduleListFile)
+ newConfig, err := NewConfig(c.srcDir, c.buildDir, c.moduleListFile, c.env)
if err != nil {
return Config{}, err
}
@@ -352,12 +356,12 @@
// NewConfig creates a new Config object. The srcDir argument specifies the path
// to the root source directory. It also loads the config file, if found.
-func NewConfig(srcDir, buildDir string, moduleListFile string) (Config, error) {
+func NewConfig(srcDir, buildDir string, moduleListFile string, availableEnv map[string]string) (Config, error) {
// Make a config with default options.
config := &config{
ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
- env: originalEnv,
+ env: availableEnv,
srcDir: srcDir,
buildDir: buildDir,
@@ -453,6 +457,8 @@
Bool(config.productVariables.ClangCoverage))
config.BazelContext, err = NewBazelContext(config)
+ config.bp2buildPackageConfig = bp2buildDefaultConfig
+ config.bp2buildModuleTypeConfig = make(map[string]bool)
return Config{config}, err
}
@@ -496,6 +502,10 @@
c.stopBefore = stopBefore
}
+func (c *config) SetAllowMissingDependencies() {
+ c.productVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
+}
+
var _ bootstrap.ConfigStopBefore = (*config)(nil)
// BlueprintToolLocation returns the directory containing build system tools
@@ -522,26 +532,6 @@
return PathForOutput(ctx, "host", c.PrebuiltOS(), "framework", path)
}
-// NonHermeticHostSystemTool looks for non-hermetic tools from the system we're
-// running on. These tools are not checked-in to AOSP, and therefore could lead
-// to reproducibility problems. Should not be used for other than finding the
-// XCode SDK (xcrun, sw_vers), etc. See ui/build/paths/config.go for the
-// allowlist of host system tools.
-func (c *config) NonHermeticHostSystemTool(name string) string {
- for _, dir := range filepath.SplitList(c.Getenv("PATH")) {
- path := filepath.Join(dir, name)
- if s, err := os.Stat(path); err != nil {
- continue
- } else if m := s.Mode(); !s.IsDir() && m&0111 != 0 {
- return path
- }
- }
- panic(fmt.Errorf(
- "Unable to use '%s' as a host system tool for build system "+
- "hermeticity reasons. See build/soong/ui/build/paths/config.go "+
- "for the full list of allowed host tools on your system.", name))
-}
-
// PrebuiltOS returns the name of the host OS used in prebuilts directories.
func (c *config) PrebuiltOS() string {
switch runtime.GOOS {
@@ -911,6 +901,25 @@
return "json"
}
+// XrefCuJavaSourceMax returns the maximum number of the Java source files
+// in a single compilation unit
+const xrefJavaSourceFileMaxDefault = "1000"
+
+func (c Config) XrefCuJavaSourceMax() string {
+ v := c.Getenv("KYTHE_JAVA_SOURCE_BATCH_SIZE")
+ if v == "" {
+ return xrefJavaSourceFileMaxDefault
+ }
+ if _, err := strconv.ParseUint(v, 0, 0); err != nil {
+ fmt.Fprintf(os.Stderr,
+ "bad KYTHE_JAVA_SOURCE_BATCH_SIZE value: %s, will use %s",
+ err, xrefJavaSourceFileMaxDefault)
+ return xrefJavaSourceFileMaxDefault
+ }
+ return v
+
+}
+
func (c *config) EmitXrefRules() bool {
return c.XrefCorpusName() != ""
}
@@ -944,13 +953,7 @@
// More info: https://source.android.com/devices/architecture/rros
func (c *config) EnforceRROForModule(name string) bool {
enforceList := c.productVariables.EnforceRROTargets
- // TODO(b/150820813) Some modules depend on static overlay, remove this after eliminating the dependency.
- exemptedList := c.productVariables.EnforceRROExemptedTargets
- if len(exemptedList) > 0 {
- if InList(name, exemptedList) {
- return false
- }
- }
+
if len(enforceList) > 0 {
if InList("*", enforceList) {
return true
@@ -959,11 +962,6 @@
}
return false
}
-
-func (c *config) EnforceRROExemptedForModule(name string) bool {
- return InList(name, c.productVariables.EnforceRROExemptedTargets)
-}
-
func (c *config) EnforceRROExcludedOverlay(path string) bool {
excluded := c.productVariables.EnforceRROExcludedOverlays
if len(excluded) > 0 {
@@ -1014,8 +1012,12 @@
return ioutil.ReadFile(absolutePath(path.String()))
}
+func (c *deviceConfig) WithDexpreopt() bool {
+ return c.config.productVariables.WithDexpreopt
+}
+
func (c *config) FrameworksBaseDirExists(ctx PathContext) bool {
- return ExistentPathForSource(ctx, "frameworks", "base").Valid()
+ return ExistentPathForSource(ctx, "frameworks", "base", "Android.bp").Valid()
}
func (c *config) VndkSnapshotBuildArtifacts() bool {
@@ -1026,6 +1028,10 @@
return c.multilibConflicts[arch]
}
+func (c *config) PrebuiltHiddenApiDir(ctx PathContext) string {
+ return String(c.productVariables.PrebuiltHiddenApiDir)
+}
+
func (c *deviceConfig) Arches() []Arch {
var arches []Arch
for _, target := range c.config.Targets[Android] {
@@ -1053,6 +1059,10 @@
return String(c.config.productVariables.DeviceVndkVersion)
}
+func (c *deviceConfig) RecoverySnapshotVersion() string {
+ return String(c.config.productVariables.RecoverySnapshotVersion)
+}
+
func (c *deviceConfig) CurrentApiLevelForVendorModules() string {
return StringDefault(c.config.productVariables.DeviceCurrentApiLevelForVendorModules, "current")
}
@@ -1249,7 +1259,28 @@
if len(c.productVariables.CFIIncludePaths) == 0 {
return false
}
- return HasAnyPrefix(path, c.productVariables.CFIIncludePaths)
+ return HasAnyPrefix(path, c.productVariables.CFIIncludePaths) && !c.CFIDisabledForPath(path)
+}
+
+func (c *config) MemtagHeapDisabledForPath(path string) bool {
+ if len(c.productVariables.MemtagHeapExcludePaths) == 0 {
+ return false
+ }
+ return HasAnyPrefix(path, c.productVariables.MemtagHeapExcludePaths)
+}
+
+func (c *config) MemtagHeapAsyncEnabledForPath(path string) bool {
+ if len(c.productVariables.MemtagHeapAsyncIncludePaths) == 0 {
+ return false
+ }
+ return HasAnyPrefix(path, c.productVariables.MemtagHeapAsyncIncludePaths) && !c.MemtagHeapDisabledForPath(path)
+}
+
+func (c *config) MemtagHeapSyncEnabledForPath(path string) bool {
+ if len(c.productVariables.MemtagHeapSyncIncludePaths) == 0 {
+ return false
+ }
+ return HasAnyPrefix(path, c.productVariables.MemtagHeapSyncIncludePaths) && !c.MemtagHeapDisabledForPath(path)
}
func (c *config) VendorConfig(name string) VendorConfig {
@@ -1264,14 +1295,14 @@
return Bool(c.productVariables.Aml_abis)
}
-func (c *config) ExcludeDraftNdkApis() bool {
- return Bool(c.productVariables.Exclude_draft_ndk_apis)
-}
-
func (c *config) FlattenApex() bool {
return Bool(c.productVariables.Flatten_apex)
}
+func (c *config) ForceApexSymlinkOptimization() bool {
+ return Bool(c.productVariables.ForceApexSymlinkOptimization)
+}
+
func (c *config) CompressedApex() bool {
return Bool(c.productVariables.CompressedApex)
}
@@ -1360,6 +1391,129 @@
return Bool(c.config.productVariables.BoardMoveRecoveryResourcesToVendorBoot)
}
+func (c *deviceConfig) PlatformSepolicyVersion() string {
+ return String(c.config.productVariables.PlatformSepolicyVersion)
+}
+
+func (c *deviceConfig) BoardSepolicyVers() string {
+ if ver := String(c.config.productVariables.BoardSepolicyVers); ver != "" {
+ return ver
+ }
+ return c.PlatformSepolicyVersion()
+}
+
+func (c *deviceConfig) BoardReqdMaskPolicy() []string {
+ return c.config.productVariables.BoardReqdMaskPolicy
+}
+
+func (c *deviceConfig) DirectedVendorSnapshot() bool {
+ return c.config.productVariables.DirectedVendorSnapshot
+}
+
+func (c *deviceConfig) VendorSnapshotModules() map[string]bool {
+ return c.config.productVariables.VendorSnapshotModules
+}
+
+func (c *deviceConfig) DirectedRecoverySnapshot() bool {
+ return c.config.productVariables.DirectedRecoverySnapshot
+}
+
+func (c *deviceConfig) RecoverySnapshotModules() map[string]bool {
+ return c.config.productVariables.RecoverySnapshotModules
+}
+
+func createDirsMap(previous map[string]bool, dirs []string) (map[string]bool, error) {
+ var ret = make(map[string]bool)
+ for _, dir := range dirs {
+ clean := filepath.Clean(dir)
+ if previous[clean] || ret[clean] {
+ return nil, fmt.Errorf("Duplicate entry %s", dir)
+ }
+ ret[clean] = true
+ }
+ return ret, nil
+}
+
+func (c *deviceConfig) createDirsMapOnce(onceKey OnceKey, previous map[string]bool, dirs []string) map[string]bool {
+ dirMap := c.Once(onceKey, func() interface{} {
+ ret, err := createDirsMap(previous, dirs)
+ if err != nil {
+ panic(fmt.Errorf("%s: %w", onceKey.key, err))
+ }
+ return ret
+ })
+ if dirMap == nil {
+ return nil
+ }
+ return dirMap.(map[string]bool)
+}
+
+var vendorSnapshotDirsExcludedKey = NewOnceKey("VendorSnapshotDirsExcludedMap")
+
+func (c *deviceConfig) VendorSnapshotDirsExcludedMap() map[string]bool {
+ return c.createDirsMapOnce(vendorSnapshotDirsExcludedKey, nil,
+ c.config.productVariables.VendorSnapshotDirsExcluded)
+}
+
+var vendorSnapshotDirsIncludedKey = NewOnceKey("VendorSnapshotDirsIncludedMap")
+
+func (c *deviceConfig) VendorSnapshotDirsIncludedMap() map[string]bool {
+ excludedMap := c.VendorSnapshotDirsExcludedMap()
+ return c.createDirsMapOnce(vendorSnapshotDirsIncludedKey, excludedMap,
+ c.config.productVariables.VendorSnapshotDirsIncluded)
+}
+
+var recoverySnapshotDirsExcludedKey = NewOnceKey("RecoverySnapshotDirsExcludedMap")
+
+func (c *deviceConfig) RecoverySnapshotDirsExcludedMap() map[string]bool {
+ return c.createDirsMapOnce(recoverySnapshotDirsExcludedKey, nil,
+ c.config.productVariables.RecoverySnapshotDirsExcluded)
+}
+
+var recoverySnapshotDirsIncludedKey = NewOnceKey("RecoverySnapshotDirsIncludedMap")
+
+func (c *deviceConfig) RecoverySnapshotDirsIncludedMap() map[string]bool {
+ excludedMap := c.RecoverySnapshotDirsExcludedMap()
+ return c.createDirsMapOnce(recoverySnapshotDirsIncludedKey, excludedMap,
+ c.config.productVariables.RecoverySnapshotDirsIncluded)
+}
+
+func (c *deviceConfig) ShippingApiLevel() ApiLevel {
+ if c.config.productVariables.ShippingApiLevel == nil {
+ return NoneApiLevel
+ }
+ apiLevel, _ := strconv.Atoi(*c.config.productVariables.ShippingApiLevel)
+ return uncheckedFinalApiLevel(apiLevel)
+}
+
+func (c *deviceConfig) BuildBrokenEnforceSyspropOwner() bool {
+ return c.config.productVariables.BuildBrokenEnforceSyspropOwner
+}
+
+func (c *deviceConfig) BuildBrokenTrebleSyspropNeverallow() bool {
+ return c.config.productVariables.BuildBrokenTrebleSyspropNeverallow
+}
+
+func (c *deviceConfig) BuildDebugfsRestrictionsEnabled() bool {
+ return c.config.productVariables.BuildDebugfsRestrictionsEnabled
+}
+
+func (c *deviceConfig) BuildBrokenVendorPropertyNamespace() bool {
+ return c.config.productVariables.BuildBrokenVendorPropertyNamespace
+}
+
+func (c *deviceConfig) RequiresInsecureExecmemForSwiftshader() bool {
+ return c.config.productVariables.RequiresInsecureExecmemForSwiftshader
+}
+
+func (c *config) SelinuxIgnoreNeverallows() bool {
+ return c.productVariables.SelinuxIgnoreNeverallows
+}
+
+func (c *deviceConfig) SepolicySplit() bool {
+ return c.config.productVariables.SepolicySplit
+}
+
// The ConfiguredJarList struct provides methods for handling a list of (apex, jar) pairs.
// Such lists are used in the build system for things like bootclasspath jars or system server jars.
// The apex part is either an apex name, or a special names "platform" or "system_ext". Jar is a
@@ -1504,6 +1658,20 @@
return nil
}
+func (l *ConfiguredJarList) MarshalJSON() ([]byte, error) {
+ if len(l.apexes) != len(l.jars) {
+ return nil, errors.New(fmt.Sprintf("Inconsistent ConfiguredJarList: apexes: %q, jars: %q", l.apexes, l.jars))
+ }
+
+ list := make([]string, 0, len(l.apexes))
+
+ for i := 0; i < len(l.apexes); i++ {
+ list = append(list, l.apexes[i]+":"+l.jars[i])
+ }
+
+ return json.Marshal(list)
+}
+
// ModuleStem hardcodes the stem of framework-minus-apex to return "framework".
//
// TODO(b/139391334): hard coded until we find a good way to query the stem of a
@@ -1571,21 +1739,33 @@
func splitConfiguredJarPair(str string) (string, string, error) {
pair := strings.SplitN(str, ":", 2)
if len(pair) == 2 {
- return pair[0], pair[1], nil
+ apex := pair[0]
+ jar := pair[1]
+ if apex == "" {
+ return apex, jar, fmt.Errorf("invalid apex '%s' in <apex>:<jar> pair '%s', expected format: <apex>:<jar>", apex, str)
+ }
+ return apex, jar, nil
} else {
return "error-apex", "error-jar", fmt.Errorf("malformed (apex, jar) pair: '%s', expected format: <apex>:<jar>", str)
}
}
-// CreateTestConfiguredJarList is a function to create ConfiguredJarList for
-// tests.
+// CreateTestConfiguredJarList is a function to create ConfiguredJarList for tests.
func CreateTestConfiguredJarList(list []string) ConfiguredJarList {
- apexes, jars, err := splitListOfPairsIntoPairOfLists(list)
+ // Create the ConfiguredJarList in as similar way as it is created at runtime by marshalling to
+ // a json list of strings and then unmarshalling into a ConfiguredJarList instance.
+ b, err := json.Marshal(list)
if err != nil {
panic(err)
}
- return ConfiguredJarList{apexes, jars}
+ var jarList ConfiguredJarList
+ err = json.Unmarshal(b, &jarList)
+ if err != nil {
+ panic(err)
+ }
+
+ return jarList
}
// EmptyConfiguredJarList returns an empty jar list.
@@ -1609,3 +1789,7 @@
func (c *config) UpdatableBootJars() ConfiguredJarList {
return c.productVariables.UpdatableBootJars
}
+
+func (c *config) RBEWrapper() string {
+ return c.GetenvWithDefault("RBE_WRAPPER", remoteexec.DefaultWrapperPath)
+}
diff --git a/android/config_test.go b/android/config_test.go
index 7bfc800..9df5288 100644
--- a/android/config_test.go
+++ b/android/config_test.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "path/filepath"
"reflect"
"strings"
"testing"
@@ -87,6 +88,37 @@
}
}
+func verifyProductVariableMarshaling(t *testing.T, v productVariables) {
+ dir := t.TempDir()
+ path := filepath.Join(dir, "test.variables")
+ err := saveToConfigFile(&v, path)
+ if err != nil {
+ t.Errorf("Couldn't save default product config: %q", err)
+ }
+
+ var v2 productVariables
+ err = loadFromConfigFile(&v2, path)
+ if err != nil {
+ t.Errorf("Couldn't load default product config: %q", err)
+ }
+}
+func TestDefaultProductVariableMarshaling(t *testing.T) {
+ v := productVariables{}
+ v.SetDefaultConfig()
+ verifyProductVariableMarshaling(t, v)
+}
+
+func TestBootJarsMarshaling(t *testing.T) {
+ v := productVariables{}
+ v.SetDefaultConfig()
+ v.BootJars = ConfiguredJarList{
+ apexes: []string{"apex"},
+ jars: []string{"jar"},
+ }
+
+ verifyProductVariableMarshaling(t, v)
+}
+
func assertStringEquals(t *testing.T, expected, actual string) {
if actual != expected {
t.Errorf("expected %q found %q", expected, actual)
@@ -100,6 +132,22 @@
assertStringEquals(t, "apex1:jarA", list1.String())
})
+ t.Run("create invalid - missing apex", func(t *testing.T) {
+ defer func() {
+ err := recover().(error)
+ assertStringEquals(t, "malformed (apex, jar) pair: 'jarA', expected format: <apex>:<jar>", err.Error())
+ }()
+ CreateTestConfiguredJarList([]string{"jarA"})
+ })
+
+ t.Run("create invalid - empty apex", func(t *testing.T) {
+ defer func() {
+ err := recover().(error)
+ assertStringEquals(t, "invalid apex '' in <apex>:<jar> pair ':jarA', expected format: <apex>:<jar>", err.Error())
+ }()
+ CreateTestConfiguredJarList([]string{":jarA"})
+ })
+
list2 := list1.Append("apex2", "jarB")
t.Run("append", func(t *testing.T) {
assertStringEquals(t, "apex1:jarA,apex2:jarB", list2.String())
diff --git a/android/csuite_config.go b/android/csuite_config.go
index bf24d98..20bd035 100644
--- a/android/csuite_config.go
+++ b/android/csuite_config.go
@@ -15,7 +15,11 @@
package android
func init() {
- RegisterModuleType("csuite_config", CSuiteConfigFactory)
+ registerCSuiteBuildComponents(InitRegistrationContext)
+}
+
+func registerCSuiteBuildComponents(ctx RegistrationContext) {
+ ctx.RegisterModuleType("csuite_config", CSuiteConfigFactory)
}
type csuiteConfigProperties struct {
@@ -40,7 +44,7 @@
OutputFile: OptionalPathForPath(me.OutputFilePath),
}
androidMkEntries.ExtraEntries = []AndroidMkExtraEntriesFunc{
- func(entries *AndroidMkEntries) {
+ func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
if me.properties.Test_config != nil {
entries.SetString("LOCAL_TEST_CONFIG", *me.properties.Test_config)
}
diff --git a/android/csuite_config_test.go b/android/csuite_config_test.go
index 9ac959e..b8a176e 100644
--- a/android/csuite_config_test.go
+++ b/android/csuite_config_test.go
@@ -18,32 +18,21 @@
"testing"
)
-func testCSuiteConfig(test *testing.T, bpFileContents string) *TestContext {
- config := TestArchConfig(buildDir, nil, bpFileContents, nil)
-
- ctx := NewTestArchContext(config)
- ctx.RegisterModuleType("csuite_config", CSuiteConfigFactory)
- ctx.Register()
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(test, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(test, errs)
- return ctx
-}
-
func TestCSuiteConfig(t *testing.T) {
- ctx := testCSuiteConfig(t, `
-csuite_config { name: "plain"}
-csuite_config { name: "with_manifest", test_config: "manifest.xml" }
-`)
+ result := GroupFixturePreparers(
+ PrepareForTestWithArchMutator,
+ FixtureRegisterWithContext(registerCSuiteBuildComponents),
+ FixtureWithRootAndroidBp(`
+ csuite_config { name: "plain"}
+ csuite_config { name: "with_manifest", test_config: "manifest.xml" }
+ `),
+ ).RunTest(t)
- variants := ctx.ModuleVariantsForTests("plain")
+ variants := result.ModuleVariantsForTests("plain")
if len(variants) > 1 {
t.Errorf("expected 1, got %d", len(variants))
}
- expectedOutputFilename := ctx.ModuleForTests(
+ outputFilename := result.ModuleForTests(
"plain", variants[0]).Module().(*CSuiteConfig).OutputFilePath.Base()
- if expectedOutputFilename != "plain" {
- t.Errorf("expected plain, got %q", expectedOutputFilename)
- }
+ AssertStringEquals(t, "output file name", "plain", outputFilename)
}
diff --git a/android/deapexer.go b/android/deapexer.go
new file mode 100644
index 0000000..63508d7
--- /dev/null
+++ b/android/deapexer.go
@@ -0,0 +1,83 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/google/blueprint"
+)
+
+// Provides support for interacting with the `deapexer` module to which a `prebuilt_apex` module
+// will delegate the work to export files from a prebuilt '.apex` file.
+
+// The information exported by the `deapexer` module, access it using `DeapxerInfoProvider`.
+type DeapexerInfo struct {
+ // map from the name of an exported file from a prebuilt_apex to the path to that file. The
+ // exported file name is of the form <module>{<tag>} where <tag> is currently only allowed to be
+ // ".dexjar".
+ //
+ // See Prebuilt.ApexInfoMutator for more information.
+ exports map[string]Path
+}
+
+// The set of supported prebuilt export tags. Used to verify the tag parameter for
+// `PrebuiltExportPath`.
+var supportedPrebuiltExportTags = map[string]struct{}{
+ ".dexjar": {},
+}
+
+// PrebuiltExportPath provides the path, or nil if not available, of a file exported from the
+// prebuilt_apex that created this ApexInfo.
+//
+// The exported file is identified by the module name and the tag:
+// * The module name is the name of the module that contributed the file when the .apex file
+// referenced by the prebuilt_apex was built. It must be specified in one of the exported_...
+// properties on the prebuilt_apex module.
+// * The tag identifies the type of file and is dependent on the module type.
+//
+// See apex/deapexer.go for more information.
+func (i DeapexerInfo) PrebuiltExportPath(name, tag string) Path {
+
+ if _, ok := supportedPrebuiltExportTags[tag]; !ok {
+ panic(fmt.Errorf("unsupported prebuilt export tag %q, expected one of %s",
+ tag, strings.Join(SortedStringKeys(supportedPrebuiltExportTags), ", ")))
+ }
+
+ path := i.exports[name+"{"+tag+"}"]
+ return path
+}
+
+// Provider that can be used from within the `GenerateAndroidBuildActions` of a module that depends
+// on a `deapexer` module to retrieve its `DeapexerInfo`.
+var DeapexerProvider = blueprint.NewProvider(DeapexerInfo{})
+
+// NewDeapexerInfo creates and initializes a DeapexerInfo that is suitable
+// for use with a prebuilt_apex module.
+//
+// See apex/deapexer.go for more information.
+func NewDeapexerInfo(exports map[string]Path) DeapexerInfo {
+ return DeapexerInfo{
+ exports: exports,
+ }
+}
+
+type deapexerTagStruct struct {
+ blueprint.BaseDependencyTag
+}
+
+// A tag that is used for dependencies on the `deapexer` module.
+var DeapexerTag = deapexerTagStruct{}
diff --git a/android/defaults.go b/android/defaults.go
index 44753ce..aacfbac 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -204,6 +204,9 @@
// its checking phase and parsing phase so add it to the list as a normal property.
AddVisibilityProperty(module, "visibility", &commonProperties.Visibility)
+ // The applicable licenses property for defaults is 'licenses'.
+ setPrimaryLicensesProperty(module, "licenses", &commonProperties.Licenses)
+
base.module = module
}
diff --git a/android/defaults_test.go b/android/defaults_test.go
index 2689d86..a7542ab 100644
--- a/android/defaults_test.go
+++ b/android/defaults_test.go
@@ -15,10 +15,7 @@
package android
import (
- "reflect"
"testing"
-
- "github.com/google/blueprint/proptools"
)
type defaultsTestProperties struct {
@@ -58,6 +55,14 @@
return defaults
}
+var prepareForDefaultsTest = GroupFixturePreparers(
+ PrepareForTestWithDefaults,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("test", defaultsTestModuleFactory)
+ ctx.RegisterModuleType("defaults", defaultsTestDefaultsFactory)
+ }),
+)
+
func TestDefaults(t *testing.T) {
bp := `
defaults {
@@ -78,27 +83,14 @@
}
`
- config := TestConfig(buildDir, nil, bp, nil)
+ result := GroupFixturePreparers(
+ prepareForDefaultsTest,
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
- ctx := NewTestContext(config)
+ foo := result.Module("foo", "").(*defaultsTestModule)
- ctx.RegisterModuleType("test", defaultsTestModuleFactory)
- ctx.RegisterModuleType("defaults", defaultsTestDefaultsFactory)
-
- ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
-
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
-
- foo := ctx.ModuleForTests("foo", "").Module().(*defaultsTestModule)
-
- if g, w := foo.properties.Foo, []string{"transitive", "defaults", "module"}; !reflect.DeepEqual(g, w) {
- t.Errorf("expected foo %q, got %q", w, g)
- }
+ AssertDeepEquals(t, "foo", []string{"transitive", "defaults", "module"}, foo.properties.Foo)
}
func TestDefaultsAllowMissingDependencies(t *testing.T) {
@@ -122,34 +114,18 @@
}
`
- config := TestConfig(buildDir, nil, bp, nil)
- config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
+ result := GroupFixturePreparers(
+ prepareForDefaultsTest,
+ PrepareForTestWithAllowMissingDependencies,
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
- ctx := NewTestContext(config)
- ctx.SetAllowMissingDependencies(true)
+ missingDefaults := result.ModuleForTests("missing_defaults", "").Output("out")
+ missingTransitiveDefaults := result.ModuleForTests("missing_transitive_defaults", "").Output("out")
- ctx.RegisterModuleType("test", defaultsTestModuleFactory)
- ctx.RegisterModuleType("defaults", defaultsTestDefaultsFactory)
+ AssertSame(t, "missing_defaults rule", ErrorRule, missingDefaults.Rule)
- ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
-
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
-
- missingDefaults := ctx.ModuleForTests("missing_defaults", "").Output("out")
- missingTransitiveDefaults := ctx.ModuleForTests("missing_transitive_defaults", "").Output("out")
-
- if missingDefaults.Rule != ErrorRule {
- t.Errorf("expected missing_defaults rule to be ErrorRule, got %#v", missingDefaults.Rule)
- }
-
- if g, w := missingDefaults.Args["error"], "module missing_defaults missing dependencies: missing\n"; g != w {
- t.Errorf("want error %q, got %q", w, g)
- }
+ AssertStringEquals(t, "missing_defaults", "module missing_defaults missing dependencies: missing\n", missingDefaults.Args["error"])
// TODO: missing transitive defaults is currently not handled
_ = missingTransitiveDefaults
diff --git a/android/defs.go b/android/defs.go
index 38ecb05..b3ff376 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -57,6 +57,15 @@
},
"cpFlags")
+ // A copy rule that only updates the output if it changed.
+ CpIfChanged = pctx.AndroidStaticRule("CpIfChanged",
+ blueprint.RuleParams{
+ Command: "if ! cmp -s $in $out; then cp $in $out; fi",
+ Description: "cp if changed $out",
+ Restat: true,
+ },
+ "cpFlags")
+
CpExecutable = pctx.AndroidStaticRule("CpExecutable",
blueprint.RuleParams{
Command: "rm -f $out && cp $cpPreserveSymlinks $cpFlags $in $out && chmod +x $out",
@@ -115,6 +124,10 @@
func init() {
pctx.Import("github.com/google/blueprint/bootstrap")
+
+ pctx.VariableFunc("RBEWrapper", func(ctx PackageVarContext) string {
+ return ctx.Config().RBEWrapper()
+ })
}
var (
@@ -136,7 +149,7 @@
func buildWriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) {
content = echoEscaper.Replace(content)
- content = proptools.ShellEscape(content)
+ content = proptools.NinjaEscape(proptools.ShellEscapeIncludingSpaces(content))
if content == "" {
content = "''"
}
diff --git a/android/deptag_test.go b/android/deptag_test.go
index bdd449e..eb4fa89 100644
--- a/android/deptag_test.go
+++ b/android/deptag_test.go
@@ -80,21 +80,20 @@
}
`
- config := TestArchConfig(buildDir, nil, bp, nil)
- ctx := NewTestArchContext(config)
+ result := GroupFixturePreparers(
+ PrepareForTestWithArchMutator,
+ FixtureWithRootAndroidBp(bp),
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("test_module", testInstallDependencyTagModuleFactory)
+ }),
+ ).RunTest(t)
- ctx.RegisterModuleType("test_module", testInstallDependencyTagModuleFactory)
+ config := result.Config
- ctx.Register()
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
-
- hostFoo := ctx.ModuleForTests("foo", config.BuildOSCommonTarget.String()).Description("install")
- hostInstallDep := ctx.ModuleForTests("install_dep", config.BuildOSCommonTarget.String()).Description("install")
- hostTransitive := ctx.ModuleForTests("transitive", config.BuildOSCommonTarget.String()).Description("install")
- hostDep := ctx.ModuleForTests("dep", config.BuildOSCommonTarget.String()).Description("install")
+ hostFoo := result.ModuleForTests("foo", config.BuildOSCommonTarget.String()).Description("install")
+ hostInstallDep := result.ModuleForTests("install_dep", config.BuildOSCommonTarget.String()).Description("install")
+ hostTransitive := result.ModuleForTests("transitive", config.BuildOSCommonTarget.String()).Description("install")
+ hostDep := result.ModuleForTests("dep", config.BuildOSCommonTarget.String()).Description("install")
if g, w := hostFoo.Implicits.Strings(), hostInstallDep.Output.String(); !InList(w, g) {
t.Errorf("expected host dependency %q, got %q", w, g)
@@ -112,10 +111,10 @@
t.Errorf("expected no host dependency %q, got %q", w, g)
}
- deviceFoo := ctx.ModuleForTests("foo", "android_common").Description("install")
- deviceInstallDep := ctx.ModuleForTests("install_dep", "android_common").Description("install")
- deviceTransitive := ctx.ModuleForTests("transitive", "android_common").Description("install")
- deviceDep := ctx.ModuleForTests("dep", "android_common").Description("install")
+ deviceFoo := result.ModuleForTests("foo", "android_common").Description("install")
+ deviceInstallDep := result.ModuleForTests("install_dep", "android_common").Description("install")
+ deviceTransitive := result.ModuleForTests("transitive", "android_common").Description("install")
+ deviceDep := result.ModuleForTests("dep", "android_common").Description("install")
if g, w := deviceFoo.OrderOnly.Strings(), deviceInstallDep.Output.String(); !InList(w, g) {
t.Errorf("expected device dependency %q, got %q", w, g)
diff --git a/android/env.go b/android/env.go
deleted file mode 100644
index c2a09aa..0000000
--- a/android/env.go
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2015 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"
- "os"
- "os/exec"
- "strings"
- "syscall"
-
- "android/soong/env"
-)
-
-// This file supports dependencies on environment variables. During build manifest generation,
-// any dependency on an environment variable is added to a list. During the singleton phase
-// a JSON file is written containing the current value of all used environment variables.
-// The next time the top-level build script is run, it uses the soong_env executable to
-// compare the contents of the environment variables, rewriting the file if necessary to cause
-// a manifest regeneration.
-
-var originalEnv map[string]string
-var soongDelveListen string
-var soongDelvePath string
-var soongDelveEnv []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 = os.Getenv("SOONG_DELVE_PATH")
- if soongDelvePath == "" {
- soongDelvePath, _ = exec.LookPath("dlv")
- }
-
- originalEnv = make(map[string]string)
- soongDelveEnv = []string{}
- for _, env := range os.Environ() {
- idx := strings.IndexRune(env, '=')
- if idx != -1 {
- originalEnv[env[:idx]] = env[idx+1:]
- if env[:idx] != "SOONG_DELVE" && env[:idx] != "SOONG_DELVE_PATH" {
- soongDelveEnv = append(soongDelveEnv, env)
- }
- }
- }
-
- // 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()
-}
-
-func ReexecWithDelveMaybe() {
- if soongDelveListen == "" {
- return
- }
-
- if soongDelvePath == "" {
- fmt.Fprintln(os.Stderr, "SOONG_DELVE is set but failed to find dlv")
- os.Exit(1)
- }
- dlvArgv := []string{
- soongDelvePath,
- "--listen=:" + soongDelveListen,
- "--headless=true",
- "--api-version=2",
- "exec",
- os.Args[0],
- "--",
- }
- dlvArgv = append(dlvArgv, os.Args[1:]...)
- os.Chdir(absSrcDir)
- syscall.Exec(soongDelvePath, dlvArgv, soongDelveEnv)
- fmt.Fprintln(os.Stderr, "exec() failed while trying to reexec with Delve")
- os.Exit(1)
-}
-
-// getenv checks either os.Getenv or originalEnv so that it works before or after the init()
-// function above. It doesn't add any dependencies on the environment variable, so it should
-// only be used for values that won't change. For values that might change use ctx.Config().Getenv.
-func getenv(key string) string {
- if originalEnv == nil {
- return os.Getenv(key)
- } else {
- return originalEnv[key]
- }
-}
-
-func EnvSingleton() Singleton {
- return &envSingleton{}
-}
-
-type envSingleton struct{}
-
-func (c *envSingleton) GenerateBuildActions(ctx SingletonContext) {
- envDeps := ctx.Config().EnvDeps()
-
- envFile := PathForOutput(ctx, ".soong.environment")
- if ctx.Failed() {
- return
- }
-
- data, err := env.EnvFileContents(envDeps)
- if err != nil {
- ctx.Errorf(err.Error())
- }
-
- err = WriteFileToOutputDir(envFile, data, 0666)
- if err != nil {
- ctx.Errorf(err.Error())
- }
-
- ctx.AddNinjaFileDeps(envFile.String())
-}
diff --git a/android/filegroup.go b/android/filegroup.go
index 9425616..2f13ab8 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -21,6 +21,51 @@
func init() {
RegisterModuleType("filegroup", FileGroupFactory)
+ RegisterBp2BuildMutator("filegroup", FilegroupBp2Build)
+}
+
+var PrepareForTestWithFilegroup = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("filegroup", FileGroupFactory)
+})
+
+// https://docs.bazel.build/versions/master/be/general.html#filegroup
+type bazelFilegroupAttributes struct {
+ Srcs bazel.LabelListAttribute
+}
+
+type bazelFilegroup struct {
+ BazelTargetModuleBase
+ bazelFilegroupAttributes
+}
+
+func BazelFileGroupFactory() Module {
+ module := &bazelFilegroup{}
+ module.AddProperties(&module.bazelFilegroupAttributes)
+ InitBazelTargetModule(module)
+ return module
+}
+
+func (bfg *bazelFilegroup) Name() string {
+ return bfg.BaseModuleName()
+}
+
+func (bfg *bazelFilegroup) GenerateAndroidBuildActions(ctx ModuleContext) {}
+
+func FilegroupBp2Build(ctx TopDownMutatorContext) {
+ fg, ok := ctx.Module().(*fileGroup)
+ if !ok || !fg.ConvertWithBp2build(ctx) {
+ return
+ }
+
+ srcs := bazel.MakeLabelListAttribute(
+ BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs))
+ attrs := &bazelFilegroupAttributes{
+ Srcs: srcs,
+ }
+
+ props := bazel.BazelTargetModuleProperties{Rule_class: "filegroup"}
+
+ ctx.CreateBazelTargetModule(BazelFileGroupFactory, fg.Name(), props, attrs)
}
type fileGroupProperties struct {
@@ -38,13 +83,11 @@
// Create a make variable with the specified name that contains the list of files in the
// filegroup, relative to the root of the source tree.
Export_to_make_var *string
-
- // Properties for Bazel migration purposes.
- bazel.Properties
}
type fileGroup struct {
ModuleBase
+ BazelModuleBase
properties fileGroupProperties
srcs Paths
}
@@ -58,6 +101,7 @@
module := &fileGroup{}
module.AddProperties(&module.properties)
InitAndroidModule(module)
+ InitBazelModule(module)
return module
}
diff --git a/android/fixture.go b/android/fixture.go
new file mode 100644
index 0000000..5fc668a
--- /dev/null
+++ b/android/fixture.go
@@ -0,0 +1,821 @@
+// Copyright 2021 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"
+ "strings"
+ "testing"
+)
+
+// Provides support for creating test fixtures on which tests can be run. Reduces duplication
+// of test setup by allow tests to easily reuse setup code.
+//
+// Fixture
+// =======
+// These determine the environment within which a test can be run. Fixtures are mutable and are
+// created and mutated by FixturePreparer instances. They are created by first creating a base
+// Fixture (which is essentially empty) and then applying FixturePreparer instances to it to modify
+// the environment.
+//
+// FixturePreparer
+// ===============
+// These are responsible for modifying a Fixture in preparation for it to run a test. Preparers are
+// intended to be immutable and able to prepare multiple Fixture objects simultaneously without
+// them sharing any data.
+//
+// They provide the basic capabilities for running tests too.
+//
+// FixturePreparers are only ever applied once per test fixture. Prior to application the list of
+// FixturePreparers are flattened and deduped while preserving the order they first appear in the
+// list. This makes it easy to reuse, group and combine FixturePreparers together.
+//
+// Each small self contained piece of test setup should be their own FixturePreparer. e.g.
+// * A group of related modules.
+// * A group of related mutators.
+// * A combination of both.
+// * Configuration.
+//
+// They should not overlap, e.g. the same module type should not be registered by different
+// FixturePreparers as using them both would cause a build error. In that case the preparer should
+// be split into separate parts and combined together using FixturePreparers(...).
+//
+// e.g. attempting to use AllPreparers in preparing a Fixture would break as it would attempt to
+// register module bar twice:
+// var Preparer1 = FixtureRegisterWithContext(RegisterModuleFooAndBar)
+// var Preparer2 = FixtureRegisterWithContext(RegisterModuleBarAndBaz)
+// var AllPreparers = GroupFixturePreparers(Preparer1, Preparer2)
+//
+// However, when restructured like this it would work fine:
+// var PreparerFoo = FixtureRegisterWithContext(RegisterModuleFoo)
+// var PreparerBar = FixtureRegisterWithContext(RegisterModuleBar)
+// var PreparerBaz = FixtureRegisterWithContext(RegisterModuleBaz)
+// var Preparer1 = GroupFixturePreparers(RegisterModuleFoo, RegisterModuleBar)
+// var Preparer2 = GroupFixturePreparers(RegisterModuleBar, RegisterModuleBaz)
+// var AllPreparers = GroupFixturePreparers(Preparer1, Preparer2)
+//
+// As after deduping and flattening AllPreparers would result in the following preparers being
+// applied:
+// 1. PreparerFoo
+// 2. PreparerBar
+// 3. PreparerBaz
+//
+// Preparers can be used for both integration and unit tests.
+//
+// Integration tests typically use all the module types, mutators and singletons that are available
+// for that package to try and replicate the behavior of the runtime build as closely as possible.
+// However, that realism comes at a cost of increased fragility (as they can be broken by changes in
+// many different parts of the build) and also increased runtime, especially if they use lots of
+// singletons and mutators.
+//
+// Unit tests on the other hand try and minimize the amount of code being tested which makes them
+// less susceptible to changes elsewhere in the build and quick to run but at a cost of potentially
+// not testing realistic scenarios.
+//
+// Supporting unit tests effectively require that preparers are available at the lowest granularity
+// possible. Supporting integration tests effectively require that the preparers are organized into
+// groups that provide all the functionality available.
+//
+// At least in terms of tests that check the behavior of build components via processing
+// `Android.bp` there is no clear separation between a unit test and an integration test. Instead
+// they vary from one end that tests a single module (e.g. filegroup) to the other end that tests a
+// whole system of modules, mutators and singletons (e.g. apex + hiddenapi).
+//
+// TestResult
+// ==========
+// These are created by running tests in a Fixture and provide access to the Config and TestContext
+// in which the tests were run.
+//
+// Example
+// =======
+//
+// An exported preparer for use by other packages that need to use java modules.
+//
+// package java
+// var PrepareForIntegrationTestWithJava = GroupFixturePreparers(
+// android.PrepareForIntegrationTestWithAndroid,
+// FixtureRegisterWithContext(RegisterAGroupOfRelatedModulesMutatorsAndSingletons),
+// FixtureRegisterWithContext(RegisterAnotherGroupOfRelatedModulesMutatorsAndSingletons),
+// ...
+// )
+//
+// Some files to use in tests in the java package.
+//
+// var javaMockFS = android.MockFS{
+// "api/current.txt": nil,
+// "api/removed.txt": nil,
+// ...
+// }
+//
+// A package private preparer for use for testing java within the java package.
+//
+// var prepareForJavaTest = android.GroupFixturePreparers(
+// PrepareForIntegrationTestWithJava,
+// FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+// ctx.RegisterModuleType("test_module", testModule)
+// }),
+// javaMockFS.AddToFixture(),
+// ...
+// }
+//
+// func TestJavaStuff(t *testing.T) {
+// result := android.GroupFixturePreparers(
+// prepareForJavaTest,
+// android.FixtureWithRootAndroidBp(`java_library {....}`),
+// android.MockFS{...}.AddToFixture(),
+// ).RunTest(t)
+// ... test result ...
+// }
+//
+// package cc
+// var PrepareForTestWithCC = android.GroupFixturePreparers(
+// android.PrepareForArchMutator,
+// android.prepareForPrebuilts,
+// FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
+// ...
+// )
+//
+// package apex
+//
+// var PrepareForApex = GroupFixturePreparers(
+// ...
+// )
+//
+// Use modules and mutators from java, cc and apex. Any duplicate preparers (like
+// android.PrepareForArchMutator) will be automatically deduped.
+//
+// var prepareForApexTest = android.GroupFixturePreparers(
+// PrepareForJava,
+// PrepareForCC,
+// PrepareForApex,
+// )
+//
+
+// A set of mock files to add to the mock file system.
+type MockFS map[string][]byte
+
+// Merge adds the extra entries from the supplied map to this one.
+//
+// Fails if the supplied map files with the same paths are present in both of them.
+func (fs MockFS) Merge(extra map[string][]byte) {
+ for p, c := range extra {
+ validateFixtureMockFSPath(p)
+ if _, ok := fs[p]; ok {
+ panic(fmt.Errorf("attempted to add file %s to the mock filesystem but it already exists", p))
+ }
+ fs[p] = c
+ }
+}
+
+// Ensure that tests cannot add paths into the mock file system which would not be allowed in the
+// runtime, e.g. absolute paths, paths relative to the 'out/' directory.
+func validateFixtureMockFSPath(path string) {
+ // This uses validateSafePath rather than validatePath because the latter prevents adding files
+ // that include a $ but there are tests that allow files with a $ to be used, albeit only by
+ // globbing.
+ validatedPath, err := validateSafePath(path)
+ if err != nil {
+ panic(err)
+ }
+
+ // Make sure that the path is canonical.
+ if validatedPath != path {
+ panic(fmt.Errorf("path %q is not a canonical path, use %q instead", path, validatedPath))
+ }
+
+ if path == "out" || strings.HasPrefix(path, "out/") {
+ panic(fmt.Errorf("cannot add output path %q to the mock file system", path))
+ }
+}
+
+func (fs MockFS) AddToFixture() FixturePreparer {
+ return FixtureMergeMockFs(fs)
+}
+
+// FixtureCustomPreparer allows for the modification of any aspect of the fixture.
+//
+// This should only be used if one of the other more specific preparers are not suitable.
+func FixtureCustomPreparer(mutator func(fixture Fixture)) FixturePreparer {
+ return newSimpleFixturePreparer(func(f *fixture) {
+ mutator(f)
+ })
+}
+
+// Modify the config
+func FixtureModifyConfig(mutator func(config Config)) FixturePreparer {
+ return newSimpleFixturePreparer(func(f *fixture) {
+ mutator(f.config)
+ })
+}
+
+// Modify the config and context
+func FixtureModifyConfigAndContext(mutator func(config Config, ctx *TestContext)) FixturePreparer {
+ return newSimpleFixturePreparer(func(f *fixture) {
+ mutator(f.config, f.ctx)
+ })
+}
+
+// Modify the context
+func FixtureModifyContext(mutator func(ctx *TestContext)) FixturePreparer {
+ return newSimpleFixturePreparer(func(f *fixture) {
+ mutator(f.ctx)
+ })
+}
+
+func FixtureRegisterWithContext(registeringFunc func(ctx RegistrationContext)) FixturePreparer {
+ return FixtureModifyContext(func(ctx *TestContext) { registeringFunc(ctx) })
+}
+
+// Modify the mock filesystem
+func FixtureModifyMockFS(mutator func(fs MockFS)) FixturePreparer {
+ return newSimpleFixturePreparer(func(f *fixture) {
+ mutator(f.mockFS)
+
+ // Make sure that invalid paths were not added to the mock filesystem.
+ for p, _ := range f.mockFS {
+ validateFixtureMockFSPath(p)
+ }
+ })
+}
+
+// Merge the supplied file system into the mock filesystem.
+//
+// Paths that already exist in the mock file system are overridden.
+func FixtureMergeMockFs(mockFS MockFS) FixturePreparer {
+ return FixtureModifyMockFS(func(fs MockFS) {
+ fs.Merge(mockFS)
+ })
+}
+
+// Add a file to the mock filesystem
+//
+// Fail if the filesystem already contains a file with that path, use FixtureOverrideFile instead.
+func FixtureAddFile(path string, contents []byte) FixturePreparer {
+ return FixtureModifyMockFS(func(fs MockFS) {
+ validateFixtureMockFSPath(path)
+ if _, ok := fs[path]; ok {
+ panic(fmt.Errorf("attempted to add file %s to the mock filesystem but it already exists, use FixtureOverride*File instead", path))
+ }
+ fs[path] = contents
+ })
+}
+
+// Add a text file to the mock filesystem
+//
+// Fail if the filesystem already contains a file with that path.
+func FixtureAddTextFile(path string, contents string) FixturePreparer {
+ return FixtureAddFile(path, []byte(contents))
+}
+
+// Override a file in the mock filesystem
+//
+// If the file does not exist this behaves as FixtureAddFile.
+func FixtureOverrideFile(path string, contents []byte) FixturePreparer {
+ return FixtureModifyMockFS(func(fs MockFS) {
+ fs[path] = contents
+ })
+}
+
+// Override a text file in the mock filesystem
+//
+// If the file does not exist this behaves as FixtureAddTextFile.
+func FixtureOverrideTextFile(path string, contents string) FixturePreparer {
+ return FixtureOverrideFile(path, []byte(contents))
+}
+
+// Add the root Android.bp file with the supplied contents.
+func FixtureWithRootAndroidBp(contents string) FixturePreparer {
+ return FixtureAddTextFile("Android.bp", contents)
+}
+
+// Merge some environment variables into the fixture.
+func FixtureMergeEnv(env map[string]string) FixturePreparer {
+ return FixtureModifyConfig(func(config Config) {
+ for k, v := range env {
+ if k == "PATH" {
+ panic("Cannot set PATH environment variable")
+ }
+ config.env[k] = v
+ }
+ })
+}
+
+// Modify the env.
+//
+// Will panic if the mutator changes the PATH environment variable.
+func FixtureModifyEnv(mutator func(env map[string]string)) FixturePreparer {
+ return FixtureModifyConfig(func(config Config) {
+ oldPath := config.env["PATH"]
+ mutator(config.env)
+ newPath := config.env["PATH"]
+ if newPath != oldPath {
+ panic(fmt.Errorf("Cannot change PATH environment variable from %q to %q", oldPath, newPath))
+ }
+ })
+}
+
+// Allow access to the product variables when preparing the fixture.
+type FixtureProductVariables struct {
+ *productVariables
+}
+
+// Modify product variables.
+func FixtureModifyProductVariables(mutator func(variables FixtureProductVariables)) FixturePreparer {
+ return FixtureModifyConfig(func(config Config) {
+ productVariables := FixtureProductVariables{&config.productVariables}
+ mutator(productVariables)
+ })
+}
+
+// GroupFixturePreparers creates a composite FixturePreparer that is equivalent to applying each of
+// the supplied FixturePreparer instances in order.
+//
+// Before preparing the fixture the list of preparers is flattened by replacing each
+// instance of GroupFixturePreparers with its contents.
+func GroupFixturePreparers(preparers ...FixturePreparer) FixturePreparer {
+ all := dedupAndFlattenPreparers(nil, preparers)
+ return newFixturePreparer(all)
+}
+
+// NullFixturePreparer is a preparer that does nothing.
+var NullFixturePreparer = GroupFixturePreparers()
+
+// OptionalFixturePreparer will return the supplied preparer if it is non-nil, otherwise it will
+// return the NullFixturePreparer
+func OptionalFixturePreparer(preparer FixturePreparer) FixturePreparer {
+ if preparer == nil {
+ return NullFixturePreparer
+ } else {
+ return preparer
+ }
+}
+
+// FixturePreparer provides the ability to create, modify and then run tests within a fixture.
+type FixturePreparer interface {
+ // Return the flattened and deduped list of simpleFixturePreparer pointers.
+ list() []*simpleFixturePreparer
+
+ // Create a Fixture.
+ Fixture(t *testing.T) Fixture
+
+ // ExtendWithErrorHandler creates a new FixturePreparer that will use the supplied error handler
+ // to check the errors (may be 0) reported by the test.
+ //
+ // The default handlers is FixtureExpectsNoErrors which will fail the go test immediately if any
+ // errors are reported.
+ ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixturePreparer
+
+ // Run the test, checking any errors reported and returning a TestResult instance.
+ //
+ // Shorthand for Fixture(t).RunTest()
+ RunTest(t *testing.T) *TestResult
+
+ // Run the test with the supplied Android.bp file.
+ //
+ // preparer.RunTestWithBp(t, bp) is shorthand for
+ // android.GroupFixturePreparers(preparer, android.FixtureWithRootAndroidBp(bp)).RunTest(t)
+ RunTestWithBp(t *testing.T, bp string) *TestResult
+
+ // RunTestWithConfig is a temporary method added to help ease the migration of existing tests to
+ // the test fixture.
+ //
+ // In order to allow the Config object to be customized separately to the TestContext a lot of
+ // existing test code has `test...WithConfig` funcs that allow the Config object to be supplied
+ // from the test and then have the TestContext created and configured automatically. e.g.
+ // testCcWithConfig, testCcErrorWithConfig, testJavaWithConfig, etc.
+ //
+ // This method allows those methods to be migrated to use the test fixture pattern without
+ // requiring that every test that uses those methods be migrated at the same time. That allows
+ // those tests to benefit from correctness in the order of registration quickly.
+ //
+ // This method discards the config (along with its mock file system, product variables,
+ // environment, etc.) that may have been set up by FixturePreparers.
+ //
+ // deprecated
+ RunTestWithConfig(t *testing.T, config Config) *TestResult
+}
+
+// dedupAndFlattenPreparers removes any duplicates and flattens any composite FixturePreparer
+// instances.
+//
+// base - a list of already flattened and deduped preparers that will be applied first before
+// the list of additional preparers. Any duplicates of these in the additional preparers
+// will be ignored.
+//
+// preparers - a list of additional unflattened, undeduped preparers that will be applied after the
+// base preparers.
+//
+// Returns a deduped and flattened list of the preparers starting with the ones in base with any
+// additional ones from the preparers list added afterwards.
+func dedupAndFlattenPreparers(base []*simpleFixturePreparer, preparers []FixturePreparer) []*simpleFixturePreparer {
+ if len(preparers) == 0 {
+ return base
+ }
+
+ list := make([]*simpleFixturePreparer, len(base))
+ visited := make(map[*simpleFixturePreparer]struct{})
+
+ // Mark the already flattened and deduped preparers, if any, as having been seen so that
+ // duplicates of these in the additional preparers will be discarded. Add them to the output
+ // list.
+ for i, s := range base {
+ visited[s] = struct{}{}
+ list[i] = s
+ }
+
+ for _, p := range preparers {
+ for _, s := range p.list() {
+ if _, seen := visited[s]; !seen {
+ visited[s] = struct{}{}
+ list = append(list, s)
+ }
+ }
+ }
+
+ return list
+}
+
+// compositeFixturePreparer is a FixturePreparer created from a list of fixture preparers.
+type compositeFixturePreparer struct {
+ baseFixturePreparer
+ // The flattened and deduped list of simpleFixturePreparer pointers encapsulated within this
+ // composite preparer.
+ preparers []*simpleFixturePreparer
+}
+
+func (c *compositeFixturePreparer) list() []*simpleFixturePreparer {
+ return c.preparers
+}
+
+func newFixturePreparer(preparers []*simpleFixturePreparer) FixturePreparer {
+ if len(preparers) == 1 {
+ return preparers[0]
+ }
+ p := &compositeFixturePreparer{
+ preparers: preparers,
+ }
+ p.initBaseFixturePreparer(p)
+ return p
+}
+
+// simpleFixturePreparer is a FixturePreparer that applies a function to a fixture.
+type simpleFixturePreparer struct {
+ baseFixturePreparer
+ function func(fixture *fixture)
+}
+
+func (s *simpleFixturePreparer) list() []*simpleFixturePreparer {
+ return []*simpleFixturePreparer{s}
+}
+
+func newSimpleFixturePreparer(preparer func(fixture *fixture)) FixturePreparer {
+ p := &simpleFixturePreparer{function: preparer}
+ p.initBaseFixturePreparer(p)
+ return p
+}
+
+// FixtureErrorHandler determines how to respond to errors reported by the code under test.
+//
+// Some possible responses:
+// * Fail the test if any errors are reported, see FixtureExpectsNoErrors.
+// * Fail the test if at least one error that matches a pattern is not reported see
+// FixtureExpectsAtLeastOneErrorMatchingPattern
+// * Fail the test if any unexpected errors are reported.
+//
+// Although at the moment all the error handlers are implemented as simply a wrapper around a
+// function this is defined as an interface to allow future enhancements, e.g. provide different
+// ways other than patterns to match an error and to combine handlers together.
+type FixtureErrorHandler interface {
+ // CheckErrors checks the errors reported.
+ //
+ // The supplied result can be used to access the state of the code under test just as the main
+ // body of the test would but if any errors other than ones expected are reported the state may
+ // be indeterminate.
+ CheckErrors(t *testing.T, result *TestResult)
+}
+
+type simpleErrorHandler struct {
+ function func(t *testing.T, result *TestResult)
+}
+
+func (h simpleErrorHandler) CheckErrors(t *testing.T, result *TestResult) {
+ t.Helper()
+ h.function(t, result)
+}
+
+// The default fixture error handler.
+//
+// Will fail the test immediately if any errors are reported.
+//
+// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
+// which the test is being run which means that the RunTest() method will not return.
+var FixtureExpectsNoErrors = FixtureCustomErrorHandler(
+ func(t *testing.T, result *TestResult) {
+ t.Helper()
+ FailIfErrored(t, result.Errs)
+ },
+)
+
+// FixtureIgnoreErrors ignores any errors.
+//
+// If this is used then it is the responsibility of the test to check the TestResult.Errs does not
+// contain any unexpected errors.
+var FixtureIgnoreErrors = FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) {
+ // Ignore the errors
+})
+
+// FixtureExpectsAtLeastOneMatchingError returns an error handler that will cause the test to fail
+// if at least one error that matches the regular expression is not found.
+//
+// The test will be failed if:
+// * No errors are reported.
+// * One or more errors are reported but none match the pattern.
+//
+// The test will not fail if:
+// * Multiple errors are reported that do not match the pattern as long as one does match.
+//
+// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
+// which the test is being run which means that the RunTest() method will not return.
+func FixtureExpectsAtLeastOneErrorMatchingPattern(pattern string) FixtureErrorHandler {
+ return FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) {
+ t.Helper()
+ if !FailIfNoMatchingErrors(t, pattern, result.Errs) {
+ t.FailNow()
+ }
+ })
+}
+
+// FixtureExpectsOneErrorToMatchPerPattern returns an error handler that will cause the test to fail
+// if there are any unexpected errors.
+//
+// The test will be failed if:
+// * The number of errors reported does not exactly match the patterns.
+// * One or more of the reported errors do not match a pattern.
+// * No patterns are provided and one or more errors are reported.
+//
+// The test will not fail if:
+// * One or more of the patterns does not match an error.
+//
+// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
+// which the test is being run which means that the RunTest() method will not return.
+func FixtureExpectsAllErrorsToMatchAPattern(patterns []string) FixtureErrorHandler {
+ return FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) {
+ t.Helper()
+ CheckErrorsAgainstExpectations(t, result.Errs, patterns)
+ })
+}
+
+// FixtureCustomErrorHandler creates a custom error handler
+func FixtureCustomErrorHandler(function func(t *testing.T, result *TestResult)) FixtureErrorHandler {
+ return simpleErrorHandler{
+ function: function,
+ }
+}
+
+// Fixture defines the test environment.
+type Fixture interface {
+ // Config returns the fixture's configuration.
+ Config() Config
+
+ // Context returns the fixture's test context.
+ Context() *TestContext
+
+ // MockFS returns the fixture's mock filesystem.
+ MockFS() MockFS
+
+ // Run the test, checking any errors reported and returning a TestResult instance.
+ RunTest() *TestResult
+}
+
+// Struct to allow TestResult to embed a *TestContext and allow call forwarding to its methods.
+type testContext struct {
+ *TestContext
+}
+
+// The result of running a test.
+type TestResult struct {
+ testContext
+
+ fixture *fixture
+ Config Config
+
+ // The errors that were reported during the test.
+ Errs []error
+
+ // The ninja deps is a list of the ninja files dependencies that were added by the modules and
+ // singletons via the *.AddNinjaFileDeps() methods.
+ NinjaDeps []string
+}
+
+func createFixture(t *testing.T, buildDir string, preparers []*simpleFixturePreparer) Fixture {
+ config := TestConfig(buildDir, nil, "", nil)
+ ctx := NewTestContext(config)
+ fixture := &fixture{
+ preparers: preparers,
+ t: t,
+ config: config,
+ ctx: ctx,
+ mockFS: make(MockFS),
+ // Set the default error handler.
+ errorHandler: FixtureExpectsNoErrors,
+ }
+
+ for _, preparer := range preparers {
+ preparer.function(fixture)
+ }
+
+ return fixture
+}
+
+type baseFixturePreparer struct {
+ self FixturePreparer
+}
+
+func (b *baseFixturePreparer) initBaseFixturePreparer(self FixturePreparer) {
+ b.self = self
+}
+
+func (b *baseFixturePreparer) Fixture(t *testing.T) Fixture {
+ return createFixture(t, t.TempDir(), b.self.list())
+}
+
+func (b *baseFixturePreparer) ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixturePreparer {
+ return GroupFixturePreparers(b.self, newSimpleFixturePreparer(func(fixture *fixture) {
+ fixture.errorHandler = errorHandler
+ }))
+}
+
+func (b *baseFixturePreparer) RunTest(t *testing.T) *TestResult {
+ t.Helper()
+ fixture := b.self.Fixture(t)
+ return fixture.RunTest()
+}
+
+func (b *baseFixturePreparer) RunTestWithBp(t *testing.T, bp string) *TestResult {
+ t.Helper()
+ return GroupFixturePreparers(b.self, FixtureWithRootAndroidBp(bp)).RunTest(t)
+}
+
+func (b *baseFixturePreparer) RunTestWithConfig(t *testing.T, config Config) *TestResult {
+ t.Helper()
+ // Create the fixture as normal.
+ fixture := b.self.Fixture(t).(*fixture)
+
+ // Discard the mock filesystem as otherwise that will override the one in the config.
+ fixture.mockFS = nil
+
+ // Replace the config with the supplied one in the fixture.
+ fixture.config = config
+
+ // Ditto with config derived information in the TestContext.
+ ctx := fixture.ctx
+ ctx.config = config
+ ctx.SetFs(ctx.config.fs)
+ if ctx.config.mockBpList != "" {
+ ctx.SetModuleListFile(ctx.config.mockBpList)
+ }
+
+ return fixture.RunTest()
+}
+
+type fixture struct {
+ // The preparers used to create this fixture.
+ preparers []*simpleFixturePreparer
+
+ // The gotest state of the go test within which this was created.
+ t *testing.T
+
+ // The configuration prepared for this fixture.
+ config Config
+
+ // The test context prepared for this fixture.
+ ctx *TestContext
+
+ // The mock filesystem prepared for this fixture.
+ mockFS MockFS
+
+ // The error handler used to check the errors, if any, that are reported.
+ errorHandler FixtureErrorHandler
+}
+
+func (f *fixture) Config() Config {
+ return f.config
+}
+
+func (f *fixture) Context() *TestContext {
+ return f.ctx
+}
+
+func (f *fixture) MockFS() MockFS {
+ return f.mockFS
+}
+
+func (f *fixture) RunTest() *TestResult {
+ f.t.Helper()
+
+ ctx := f.ctx
+
+ // Do not use the fixture's mockFS to initialize the config's mock file system if it has been
+ // cleared by RunTestWithConfig.
+ if f.mockFS != nil {
+ // The TestConfig() method assumes that the mock filesystem is available when creating so
+ // creates the mock file system immediately. Similarly, the NewTestContext(Config) method
+ // assumes that the supplied Config's FileSystem has been properly initialized before it is
+ // called and so it takes its own reference to the filesystem. However, fixtures create the
+ // Config and TestContext early so they can be modified by preparers at which time the mockFS
+ // has not been populated (because it too is modified by preparers). So, this reinitializes the
+ // Config and TestContext's FileSystem using the now populated mockFS.
+ f.config.mockFileSystem("", f.mockFS)
+
+ ctx.SetFs(ctx.config.fs)
+ if ctx.config.mockBpList != "" {
+ ctx.SetModuleListFile(ctx.config.mockBpList)
+ }
+ }
+
+ ctx.Register()
+ var ninjaDeps []string
+ extraNinjaDeps, errs := ctx.ParseBlueprintsFiles("ignored")
+ if len(errs) == 0 {
+ ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
+ extraNinjaDeps, errs = ctx.PrepareBuildActions(f.config)
+ if len(errs) == 0 {
+ ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
+ }
+ }
+
+ result := &TestResult{
+ testContext: testContext{ctx},
+ fixture: f,
+ Config: f.config,
+ Errs: errs,
+ NinjaDeps: ninjaDeps,
+ }
+
+ f.errorHandler.CheckErrors(f.t, result)
+
+ return result
+}
+
+// NormalizePathForTesting removes the test invocation specific build directory from the supplied
+// path.
+//
+// If the path is within the build directory (e.g. an OutputPath) then this returns the relative
+// path to avoid tests having to deal with the dynamically generated build directory.
+//
+// Otherwise, this returns the supplied path as it is almost certainly a source path that is
+// relative to the root of the source tree.
+//
+// Even though some information is removed from some paths and not others it should be possible to
+// differentiate between them by the paths themselves, e.g. output paths will likely include
+// ".intermediates" but source paths won't.
+func (r *TestResult) NormalizePathForTesting(path Path) string {
+ pathContext := PathContextForTesting(r.Config)
+ pathAsString := path.String()
+ if rel, isRel := MaybeRel(pathContext, r.Config.BuildDir(), pathAsString); isRel {
+ return rel
+ }
+ return pathAsString
+}
+
+// NormalizePathsForTesting normalizes each path in the supplied list and returns their normalized
+// forms.
+func (r *TestResult) NormalizePathsForTesting(paths Paths) []string {
+ var result []string
+ for _, path := range paths {
+ result = append(result, r.NormalizePathForTesting(path))
+ }
+ return result
+}
+
+// Preparer will return a FixturePreparer encapsulating all the preparers used to create the fixture
+// that produced this result.
+//
+// e.g. assuming that this result was created by running:
+// GroupFixturePreparers(preparer1, preparer2, preparer3).RunTest(t)
+//
+// Then this method will be equivalent to running:
+// GroupFixturePreparers(preparer1, preparer2, preparer3)
+//
+// This is intended for use by tests whose output is Android.bp files to verify that those files
+// are valid, e.g. tests of the snapshots produced by the sdk module type.
+func (r *TestResult) Preparer() FixturePreparer {
+ return newFixturePreparer(r.fixture.preparers)
+}
+
+// Module returns the module with the specific name and of the specified variant.
+func (r *TestResult) Module(name string, variant string) Module {
+ return r.ModuleForTests(name, variant).Module()
+}
diff --git a/android/fixture_test.go b/android/fixture_test.go
new file mode 100644
index 0000000..5b810e0
--- /dev/null
+++ b/android/fixture_test.go
@@ -0,0 +1,82 @@
+// Copyright 2021 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 (
+ "testing"
+)
+
+// Make sure that FixturePreparer instances are only called once per fixture and in the order in
+// which they were added.
+func TestFixtureDedup(t *testing.T) {
+ list := []string{}
+
+ appendToList := func(s string) FixturePreparer {
+ return FixtureModifyConfig(func(_ Config) {
+ list = append(list, s)
+ })
+ }
+
+ preparer1 := appendToList("preparer1")
+ preparer2 := appendToList("preparer2")
+ preparer3 := appendToList("preparer3")
+ preparer4 := OptionalFixturePreparer(appendToList("preparer4"))
+ nilPreparer := OptionalFixturePreparer(nil)
+
+ preparer1Then2 := GroupFixturePreparers(preparer1, preparer2, nilPreparer)
+
+ preparer2Then1 := GroupFixturePreparers(preparer2, preparer1)
+
+ group := GroupFixturePreparers(preparer1, preparer2, preparer1, preparer1Then2)
+
+ extension := GroupFixturePreparers(group, preparer4, preparer2)
+
+ GroupFixturePreparers(extension, preparer1, preparer2, preparer2Then1, preparer3).Fixture(t)
+
+ AssertDeepEquals(t, "preparers called in wrong order",
+ []string{"preparer1", "preparer2", "preparer4", "preparer3"}, list)
+}
+
+func TestFixtureValidateMockFS(t *testing.T) {
+ t.Run("absolute path", func(t *testing.T) {
+ AssertPanicMessageContains(t, "source path validation failed", "Path is outside directory: /abs/path/Android.bp", func() {
+ FixtureAddFile("/abs/path/Android.bp", nil).Fixture(t)
+ })
+ })
+ t.Run("not canonical", func(t *testing.T) {
+ AssertPanicMessageContains(t, "source path validation failed", `path "path/with/../in/it/Android.bp" is not a canonical path, use "path/in/it/Android.bp" instead`, func() {
+ FixtureAddFile("path/with/../in/it/Android.bp", nil).Fixture(t)
+ })
+ })
+ t.Run("FixtureAddFile", func(t *testing.T) {
+ AssertPanicMessageContains(t, "source path validation failed", `cannot add output path "out/Android.bp" to the mock file system`, func() {
+ FixtureAddFile("out/Android.bp", nil).Fixture(t)
+ })
+ })
+ t.Run("FixtureMergeMockFs", func(t *testing.T) {
+ AssertPanicMessageContains(t, "source path validation failed", `cannot add output path "out/Android.bp" to the mock file system`, func() {
+ FixtureMergeMockFs(MockFS{
+ "out/Android.bp": nil,
+ }).Fixture(t)
+ })
+ })
+ t.Run("FixtureModifyMockFS", func(t *testing.T) {
+ AssertPanicMessageContains(t, "source path validation failed", `cannot add output path "out/Android.bp" to the mock file system`, func() {
+ FixtureModifyMockFS(func(fs MockFS) {
+ fs["out/Android.bp"] = nil
+ }).Fixture(t)
+ })
+ })
+}
diff --git a/android/image.go b/android/image.go
index 1a1a423..bdb9be0 100644
--- a/android/image.go
+++ b/android/image.go
@@ -30,6 +30,11 @@
// vendor ramdisk partition).
VendorRamdiskVariantNeeded(ctx BaseModuleContext) bool
+ // DebugRamdiskVariantNeeded should return true if the module needs a debug ramdisk variant (installed on the
+ // debug ramdisk partition: $(PRODUCT_OUT)/debug_ramdisk/first_stage_ramdisk if BOARD_USES_RECOVERY_AS_ROOT is
+ // true, $(PRODUCT_OUT)/debug_ramdisk otherise).
+ DebugRamdiskVariantNeeded(ctx BaseModuleContext) bool
+
// RecoveryVariantNeeded should return true if the module needs a recovery variant (installed on the
// recovery partition).
RecoveryVariantNeeded(ctx BaseModuleContext) bool
@@ -60,6 +65,9 @@
// VendorRamdiskVariation means a module to be installed to vendor ramdisk image.
VendorRamdiskVariation string = "vendor_ramdisk"
+
+ // DebugRamdiskVariation means a module to be installed to debug ramdisk image.
+ DebugRamdiskVariation string = "debug_ramdisk"
)
// imageMutator creates variants for modules that implement the ImageInterface that
@@ -83,6 +91,9 @@
if m.VendorRamdiskVariantNeeded(ctx) {
variations = append(variations, VendorRamdiskVariation)
}
+ if m.DebugRamdiskVariantNeeded(ctx) {
+ variations = append(variations, DebugRamdiskVariation)
+ }
if m.RecoveryVariantNeeded(ctx) {
variations = append(variations, RecoveryVariation)
}
diff --git a/android/license.go b/android/license.go
new file mode 100644
index 0000000..3bc6199
--- /dev/null
+++ b/android/license.go
@@ -0,0 +1,82 @@
+// Copyright 2020 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 (
+ "github.com/google/blueprint"
+)
+
+type licenseKindDependencyTag struct {
+ blueprint.BaseDependencyTag
+}
+
+var (
+ licenseKindTag = licenseKindDependencyTag{}
+)
+
+func init() {
+ RegisterLicenseBuildComponents(InitRegistrationContext)
+}
+
+// Register the license module type.
+func RegisterLicenseBuildComponents(ctx RegistrationContext) {
+ ctx.RegisterModuleType("license", LicenseFactory)
+}
+
+type licenseProperties struct {
+ // Specifies the kinds of license that apply.
+ License_kinds []string
+ // Specifies a short copyright notice to use for the license.
+ Copyright_notice *string
+ // Specifies the path or label for the text of the license.
+ License_text []string `android:"path"`
+ // Specifies the package name to which the license applies.
+ Package_name *string
+ // Specifies where this license can be used
+ Visibility []string
+}
+
+type licenseModule struct {
+ ModuleBase
+ DefaultableModuleBase
+
+ properties licenseProperties
+}
+
+func (m *licenseModule) DepsMutator(ctx BottomUpMutatorContext) {
+ ctx.AddVariationDependencies(nil, licenseKindTag, m.properties.License_kinds...)
+}
+
+func (m *licenseModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ // Nothing to do.
+}
+
+func LicenseFactory() Module {
+ module := &licenseModule{}
+
+ base := module.base()
+ module.AddProperties(&base.nameProperties, &module.properties)
+
+ base.generalProperties = module.GetProperties()
+ base.customizableProperties = module.GetProperties()
+
+ // The visibility property needs to be checked and parsed by the visibility module.
+ setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
+
+ initAndroidModuleBase(module)
+ InitDefaultableModule(module)
+
+ return module
+}
diff --git a/android/license_kind.go b/android/license_kind.go
new file mode 100644
index 0000000..ddecd77
--- /dev/null
+++ b/android/license_kind.go
@@ -0,0 +1,66 @@
+// Copyright 2020 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
+
+func init() {
+ RegisterLicenseKindBuildComponents(InitRegistrationContext)
+}
+
+// Register the license_kind module type.
+func RegisterLicenseKindBuildComponents(ctx RegistrationContext) {
+ ctx.RegisterModuleType("license_kind", LicenseKindFactory)
+}
+
+type licenseKindProperties struct {
+ // Specifies the conditions for all licenses of the kind.
+ Conditions []string
+ // Specifies the url to the canonical license definition.
+ Url string
+ // Specifies where this license can be used
+ Visibility []string
+}
+
+type licenseKindModule struct {
+ ModuleBase
+ DefaultableModuleBase
+
+ properties licenseKindProperties
+}
+
+func (m *licenseKindModule) DepsMutator(ctx BottomUpMutatorContext) {
+ // Nothing to do.
+}
+
+func (m *licenseKindModule) GenerateAndroidBuildActions(ModuleContext) {
+ // Nothing to do.
+}
+
+func LicenseKindFactory() Module {
+ module := &licenseKindModule{}
+
+ base := module.base()
+ module.AddProperties(&base.nameProperties, &module.properties)
+
+ base.generalProperties = module.GetProperties()
+ base.customizableProperties = module.GetProperties()
+
+ // The visibility property needs to be checked and parsed by the visibility module.
+ setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
+
+ initAndroidModuleBase(module)
+ InitDefaultableModule(module)
+
+ return module
+}
diff --git a/android/license_kind_test.go b/android/license_kind_test.go
new file mode 100644
index 0000000..1f09568
--- /dev/null
+++ b/android/license_kind_test.go
@@ -0,0 +1,145 @@
+package android
+
+import (
+ "testing"
+
+ "github.com/google/blueprint"
+)
+
+var licenseKindTests = []struct {
+ name string
+ fs MockFS
+ expectedErrors []string
+}{
+ {
+ name: "license_kind must not accept licenses property",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_license",
+ licenses: ["other_license"],
+ }`),
+ },
+ expectedErrors: []string{
+ `top/Blueprints:4:14: unrecognized property "licenses"`,
+ },
+ },
+ {
+ name: "bad license_kind",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_notice",
+ conditions: ["notice"],
+ }`),
+ "other/Blueprints": []byte(`
+ mock_license {
+ name: "other_notice",
+ license_kinds: ["notice"],
+ }`),
+ },
+ expectedErrors: []string{
+ `other/Blueprints:2:5: "other_notice" depends on undefined module "notice"`,
+ },
+ },
+ {
+ name: "good license kind",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_by_exception_only",
+ conditions: ["by_exception_only"],
+ }
+
+ mock_license {
+ name: "top_proprietary",
+ license_kinds: ["top_by_exception_only"],
+ }`),
+ "other/Blueprints": []byte(`
+ mock_license {
+ name: "other_proprietary",
+ license_kinds: ["top_proprietary"],
+ }`),
+ },
+ },
+ {
+ name: "multiple license kinds",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_notice",
+ conditions: ["notice"],
+ }
+
+ license_kind {
+ name: "top_by_exception_only",
+ conditions: ["by_exception_only"],
+ }
+
+ mock_license {
+ name: "top_allowed_as_notice",
+ license_kinds: ["top_notice"],
+ }
+
+ mock_license {
+ name: "top_proprietary",
+ license_kinds: ["top_by_exception_only"],
+ }`),
+ "other/Blueprints": []byte(`
+ mock_license {
+ name: "other_rule",
+ license_kinds: ["top_by_exception_only"],
+ }`),
+ },
+ },
+}
+
+func TestLicenseKind(t *testing.T) {
+ for _, test := range licenseKindTests {
+ t.Run(test.name, func(t *testing.T) {
+ GroupFixturePreparers(
+ prepareForLicenseTest,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("mock_license", newMockLicenseModule)
+ }),
+ test.fs.AddToFixture(),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+ RunTest(t)
+ })
+ }
+}
+
+type mockLicenseProperties struct {
+ License_kinds []string
+}
+
+type mockLicenseModule struct {
+ ModuleBase
+ DefaultableModuleBase
+
+ properties mockLicenseProperties
+}
+
+func newMockLicenseModule() Module {
+ m := &mockLicenseModule{}
+ m.AddProperties(&m.properties)
+ InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon)
+ InitDefaultableModule(m)
+ return m
+}
+
+type licensekindTag struct {
+ blueprint.BaseDependencyTag
+}
+
+func (j *mockLicenseModule) DepsMutator(ctx BottomUpMutatorContext) {
+ m, ok := ctx.Module().(Module)
+ if !ok {
+ return
+ }
+ ctx.AddDependency(m, licensekindTag{}, j.properties.License_kinds...)
+}
+
+func (p *mockLicenseModule) GenerateAndroidBuildActions(ModuleContext) {
+}
diff --git a/android/license_test.go b/android/license_test.go
new file mode 100644
index 0000000..2b09a4f
--- /dev/null
+++ b/android/license_test.go
@@ -0,0 +1,211 @@
+package android
+
+import (
+ "testing"
+)
+
+// Common test set up for license tests.
+var prepareForLicenseTest = GroupFixturePreparers(
+ // General preparers in alphabetical order.
+ PrepareForTestWithDefaults,
+ prepareForTestWithLicenses,
+ PrepareForTestWithOverrides,
+ PrepareForTestWithPackageModule,
+ PrepareForTestWithPrebuilts,
+ PrepareForTestWithVisibility,
+
+ // Additional test specific stuff
+ prepareForTestWithFakePrebuiltModules,
+ FixtureMergeEnv(map[string]string{"ANDROID_REQUIRE_LICENSES": "1"}),
+)
+
+var licenseTests = []struct {
+ name string
+ fs MockFS
+ expectedErrors []string
+}{
+ {
+ name: "license must not accept licenses property",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license {
+ name: "top_license",
+ visibility: ["//visibility:private"],
+ licenses: ["other_license"],
+ }`),
+ },
+ expectedErrors: []string{
+ `top/Blueprints:5:14: unrecognized property "licenses"`,
+ },
+ },
+ {
+ name: "private license",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_notice",
+ conditions: ["notice"],
+ visibility: ["//visibility:private"],
+ }
+
+ license {
+ name: "top_allowed_as_notice",
+ license_kinds: ["top_notice"],
+ visibility: ["//visibility:private"],
+ }`),
+ "other/Blueprints": []byte(`
+ rule {
+ name: "arule",
+ licenses: ["top_allowed_as_notice"],
+ }`),
+ "yetmore/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_allowed_as_notice"],
+ }`),
+ },
+ expectedErrors: []string{
+ `other/Blueprints:2:5: module "arule": depends on //top:top_allowed_as_notice ` +
+ `which is not visible to this module`,
+ `yetmore/Blueprints:2:5: module "//yetmore": depends on //top:top_allowed_as_notice ` +
+ `which is not visible to this module`,
+ },
+ },
+ {
+ name: "must reference license_kind module",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ rule {
+ name: "top_by_exception_only",
+ }
+
+ license {
+ name: "top_proprietary",
+ license_kinds: ["top_by_exception_only"],
+ visibility: ["//visibility:public"],
+ }`),
+ },
+ expectedErrors: []string{
+ `top/Blueprints:6:5: module "top_proprietary": license_kinds property ` +
+ `"top_by_exception_only" is not a license_kind module`,
+ },
+ },
+ {
+ name: "license_kind module must exist",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license {
+ name: "top_notice_allowed",
+ license_kinds: ["top_notice"],
+ visibility: ["//visibility:public"],
+ }`),
+ },
+ expectedErrors: []string{
+ `top/Blueprints:2:5: "top_notice_allowed" depends on undefined module "top_notice"`,
+ },
+ },
+ {
+ name: "public license",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_by_exception_only",
+ conditions: ["by_exception_only"],
+ visibility: ["//visibility:private"],
+ }
+
+ license {
+ name: "top_proprietary",
+ license_kinds: ["top_by_exception_only"],
+ visibility: ["//visibility:public"],
+ }`),
+ "other/Blueprints": []byte(`
+ rule {
+ name: "arule",
+ licenses: ["top_proprietary"],
+ }`),
+ "yetmore/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_proprietary"],
+ }`),
+ },
+ },
+ {
+ name: "multiple licenses",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_proprietary"],
+ }
+
+ license_kind {
+ name: "top_notice",
+ conditions: ["notice"],
+ }
+
+ license_kind {
+ name: "top_by_exception_only",
+ conditions: ["by_exception_only"],
+ visibility: ["//visibility:public"],
+ }
+
+ license {
+ name: "top_allowed_as_notice",
+ license_kinds: ["top_notice"],
+ }
+
+ license {
+ name: "top_proprietary",
+ license_kinds: ["top_by_exception_only"],
+ visibility: ["//visibility:public"],
+ }
+ rule {
+ name: "myrule",
+ licenses: ["top_allowed_as_notice", "top_proprietary"]
+ }`),
+ "other/Blueprints": []byte(`
+ rule {
+ name: "arule",
+ licenses: ["top_proprietary"],
+ }`),
+ "yetmore/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_proprietary"],
+ }`),
+ },
+ },
+}
+
+func TestLicense(t *testing.T) {
+ for _, test := range licenseTests {
+ t.Run(test.name, func(t *testing.T) {
+ // Customize the common license text fixture factory.
+ GroupFixturePreparers(
+ prepareForLicenseTest,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("rule", newMockRuleModule)
+ }),
+ test.fs.AddToFixture(),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+ RunTest(t)
+ })
+ }
+}
+
+func testLicense(t *testing.T, fs MockFS, expectedErrors []string) {
+}
+
+type mockRuleModule struct {
+ ModuleBase
+ DefaultableModuleBase
+}
+
+func newMockRuleModule() Module {
+ m := &mockRuleModule{}
+ InitAndroidModule(m)
+ InitDefaultableModule(m)
+ return m
+}
+
+func (p *mockRuleModule) GenerateAndroidBuildActions(ModuleContext) {
+}
diff --git a/android/licenses.go b/android/licenses.go
new file mode 100644
index 0000000..2838f5d
--- /dev/null
+++ b/android/licenses.go
@@ -0,0 +1,295 @@
+// Copyright 2020 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 (
+ "reflect"
+ "sync"
+
+ "github.com/google/blueprint"
+)
+
+// Adds cross-cutting licenses dependency to propagate license metadata through the build system.
+//
+// Stage 1 - bottom-up records package-level default_applicable_licenses property mapped by package name.
+// Stage 2 - bottom-up converts licenses property or package default_applicable_licenses to dependencies.
+// Stage 3 - bottom-up type-checks every added applicable license dependency and license_kind dependency.
+// Stage 4 - GenerateBuildActions calculates properties for the union of license kinds, conditions and texts.
+
+type licensesDependencyTag struct {
+ blueprint.BaseDependencyTag
+}
+
+var (
+ licensesTag = licensesDependencyTag{}
+)
+
+// Describes the property provided by a module to reference applicable licenses.
+type applicableLicensesProperty interface {
+ // The name of the property. e.g. default_applicable_licenses or licenses
+ getName() string
+ // The values assigned to the property. (Must reference license modules.)
+ getStrings() []string
+}
+
+type applicableLicensesPropertyImpl struct {
+ name string
+ licensesProperty *[]string
+}
+
+func newApplicableLicensesProperty(name string, licensesProperty *[]string) applicableLicensesProperty {
+ return applicableLicensesPropertyImpl{
+ name: name,
+ licensesProperty: licensesProperty,
+ }
+}
+
+func (p applicableLicensesPropertyImpl) getName() string {
+ return p.name
+}
+
+func (p applicableLicensesPropertyImpl) getStrings() []string {
+ return *p.licensesProperty
+}
+
+// Set the primary applicable licenses property for a module.
+func setPrimaryLicensesProperty(module Module, name string, licensesProperty *[]string) {
+ module.base().primaryLicensesProperty = newApplicableLicensesProperty(name, licensesProperty)
+}
+
+// Storage blob for a package's default_applicable_licenses mapped by package directory.
+type licensesContainer struct {
+ licenses []string
+}
+
+func (r licensesContainer) getLicenses() []string {
+ return r.licenses
+}
+
+var packageDefaultLicensesMap = NewOnceKey("packageDefaultLicensesMap")
+
+// The map from package dir name to default applicable licenses as a licensesContainer.
+func moduleToPackageDefaultLicensesMap(config Config) *sync.Map {
+ return config.Once(packageDefaultLicensesMap, func() interface{} {
+ return &sync.Map{}
+ }).(*sync.Map)
+}
+
+// Registers the function that maps each package to its default_applicable_licenses.
+//
+// This goes before defaults expansion so the defaults can pick up the package default.
+func RegisterLicensesPackageMapper(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("licensesPackageMapper", licensesPackageMapper).Parallel()
+}
+
+// Registers the function that gathers the license dependencies for each module.
+//
+// This goes after defaults expansion so that it can pick up default licenses and before visibility enforcement.
+func RegisterLicensesPropertyGatherer(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("licensesPropertyGatherer", licensesPropertyGatherer).Parallel()
+}
+
+// Registers the function that verifies the licenses and license_kinds dependency types for each module.
+func RegisterLicensesDependencyChecker(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("licensesPropertyChecker", licensesDependencyChecker).Parallel()
+}
+
+// Maps each package to its default applicable licenses.
+func licensesPackageMapper(ctx BottomUpMutatorContext) {
+ p, ok := ctx.Module().(*packageModule)
+ if !ok {
+ return
+ }
+
+ licenses := getLicenses(ctx, p)
+
+ dir := ctx.ModuleDir()
+ c := makeLicensesContainer(licenses)
+ moduleToPackageDefaultLicensesMap(ctx.Config()).Store(dir, c)
+}
+
+// Copies the default_applicable_licenses property values for mapping by package directory.
+func makeLicensesContainer(propVals []string) licensesContainer {
+ licenses := make([]string, 0, len(propVals))
+ licenses = append(licenses, propVals...)
+
+ return licensesContainer{licenses}
+}
+
+// Gathers the applicable licenses into dependency references after defaults expansion.
+func licensesPropertyGatherer(ctx BottomUpMutatorContext) {
+ m, ok := ctx.Module().(Module)
+ if !ok {
+ return
+ }
+
+ if exemptFromRequiredApplicableLicensesProperty(m) {
+ return
+ }
+
+ licenses := getLicenses(ctx, m)
+
+ ctx.AddVariationDependencies(nil, licensesTag, licenses...)
+}
+
+// Verifies the license and license_kind dependencies are each the correct kind of module.
+func licensesDependencyChecker(ctx BottomUpMutatorContext) {
+ m, ok := ctx.Module().(Module)
+ if !ok {
+ return
+ }
+
+ // license modules have no licenses, but license_kinds must refer to license_kind modules
+ if _, ok := m.(*licenseModule); ok {
+ for _, module := range ctx.GetDirectDepsWithTag(licenseKindTag) {
+ if _, ok := module.(*licenseKindModule); !ok {
+ ctx.ModuleErrorf("license_kinds property %q is not a license_kind module", ctx.OtherModuleName(module))
+ }
+ }
+ return
+ }
+
+ if exemptFromRequiredApplicableLicensesProperty(m) {
+ return
+ }
+
+ for _, module := range ctx.GetDirectDepsWithTag(licensesTag) {
+ if _, ok := module.(*licenseModule); !ok {
+ propertyName := "licenses"
+ primaryProperty := m.base().primaryLicensesProperty
+ if primaryProperty != nil {
+ propertyName = primaryProperty.getName()
+ }
+ ctx.ModuleErrorf("%s property %q is not a license module", propertyName, ctx.OtherModuleName(module))
+ }
+ }
+}
+
+// Flattens license and license_kind dependencies into calculated properties.
+//
+// Re-validates applicable licenses properties refer only to license modules and license_kinds properties refer
+// only to license_kind modules.
+func licensesPropertyFlattener(ctx ModuleContext) {
+ m, ok := ctx.Module().(Module)
+ if !ok {
+ return
+ }
+
+ // license modules have no licenses, but license_kinds must refer to license_kind modules
+ if l, ok := m.(*licenseModule); ok {
+ mergeProps(&m.base().commonProperties.Effective_licenses, ctx.ModuleName())
+ mergeProps(&m.base().commonProperties.Effective_license_text, PathsForModuleSrc(ctx, l.properties.License_text).Strings()...)
+ for _, module := range ctx.GetDirectDepsWithTag(licenseKindTag) {
+ if lk, ok := module.(*licenseKindModule); ok {
+ mergeProps(&m.base().commonProperties.Effective_license_conditions, lk.properties.Conditions...)
+ mergeProps(&m.base().commonProperties.Effective_license_kinds, ctx.OtherModuleName(module))
+ } else {
+ ctx.ModuleErrorf("license_kinds property %q is not a license_kind module", ctx.OtherModuleName(module))
+ }
+ }
+ return
+ }
+
+ if exemptFromRequiredApplicableLicensesProperty(m) {
+ return
+ }
+
+ for _, module := range ctx.GetDirectDepsWithTag(licensesTag) {
+ if l, ok := module.(*licenseModule); ok {
+ if m.base().commonProperties.Effective_package_name == nil && l.properties.Package_name != nil {
+ m.base().commonProperties.Effective_package_name = l.properties.Package_name
+ }
+ mergeProps(&m.base().commonProperties.Effective_licenses, module.base().commonProperties.Effective_licenses...)
+ mergeProps(&m.base().commonProperties.Effective_license_text, module.base().commonProperties.Effective_license_text...)
+ mergeProps(&m.base().commonProperties.Effective_license_kinds, module.base().commonProperties.Effective_license_kinds...)
+ mergeProps(&m.base().commonProperties.Effective_license_conditions, module.base().commonProperties.Effective_license_conditions...)
+ } else {
+ propertyName := "licenses"
+ primaryProperty := m.base().primaryLicensesProperty
+ if primaryProperty != nil {
+ propertyName = primaryProperty.getName()
+ }
+ ctx.ModuleErrorf("%s property %q is not a license module", propertyName, ctx.OtherModuleName(module))
+ }
+ }
+}
+
+// Update a property string array with a distinct union of its values and a list of new values.
+func mergeProps(prop *[]string, values ...string) {
+ s := make(map[string]bool)
+ for _, v := range *prop {
+ s[v] = true
+ }
+ for _, v := range values {
+ s[v] = true
+ }
+ *prop = []string{}
+ *prop = append(*prop, SortedStringKeys(s)...)
+}
+
+// Get the licenses property falling back to the package default.
+func getLicenses(ctx BaseModuleContext, module Module) []string {
+ if exemptFromRequiredApplicableLicensesProperty(module) {
+ return nil
+ }
+
+ primaryProperty := module.base().primaryLicensesProperty
+ if primaryProperty == nil {
+ if ctx.Config().IsEnvTrue("ANDROID_REQUIRE_LICENSES") {
+ ctx.ModuleErrorf("module type %q must have an applicable licenses property", ctx.OtherModuleType(module))
+ }
+ return nil
+ }
+
+ licenses := primaryProperty.getStrings()
+ if len(licenses) > 0 {
+ s := make(map[string]bool)
+ for _, l := range licenses {
+ if _, ok := s[l]; ok {
+ ctx.ModuleErrorf("duplicate %q %s", l, primaryProperty.getName())
+ }
+ s[l] = true
+ }
+ return licenses
+ }
+
+ dir := ctx.OtherModuleDir(module)
+
+ moduleToApplicableLicenses := moduleToPackageDefaultLicensesMap(ctx.Config())
+ value, ok := moduleToApplicableLicenses.Load(dir)
+ var c licensesContainer
+ if ok {
+ c = value.(licensesContainer)
+ } else {
+ c = licensesContainer{}
+ }
+ return c.getLicenses()
+}
+
+// Returns whether a module is an allowed list of modules that do not have or need applicable licenses.
+func exemptFromRequiredApplicableLicensesProperty(module Module) bool {
+ switch reflect.TypeOf(module).String() {
+ case "*android.licenseModule": // is a license, doesn't need one
+ case "*android.licenseKindModule": // is a license, doesn't need one
+ case "*android.NamespaceModule": // just partitions things, doesn't add anything
+ case "*android.soongConfigModuleTypeModule": // creates aliases for modules with licenses
+ case "*android.soongConfigModuleTypeImport": // creates aliases for modules with licenses
+ case "*android.soongConfigStringVariableDummyModule": // used for creating aliases
+ case "*android.SoongConfigBoolVariableDummyModule": // used for creating aliases
+ default:
+ return false
+ }
+ return true
+}
diff --git a/android/licenses_test.go b/android/licenses_test.go
new file mode 100644
index 0000000..913dc88
--- /dev/null
+++ b/android/licenses_test.go
@@ -0,0 +1,849 @@
+package android
+
+import (
+ "testing"
+
+ "github.com/google/blueprint"
+)
+
+var prepareForTestWithLicenses = GroupFixturePreparers(
+ FixtureRegisterWithContext(RegisterLicenseKindBuildComponents),
+ FixtureRegisterWithContext(RegisterLicenseBuildComponents),
+ FixtureRegisterWithContext(registerLicenseMutators),
+)
+
+func registerLicenseMutators(ctx RegistrationContext) {
+ ctx.PreArchMutators(RegisterLicensesPackageMapper)
+ ctx.PreArchMutators(RegisterLicensesPropertyGatherer)
+ ctx.PostDepsMutators(RegisterLicensesDependencyChecker)
+}
+
+var licensesTests = []struct {
+ name string
+ fs MockFS
+ expectedErrors []string
+ effectiveLicenses map[string][]string
+ effectiveInheritedLicenses map[string][]string
+ effectivePackage map[string]string
+ effectiveNotices map[string][]string
+ effectiveKinds map[string][]string
+ effectiveConditions map[string][]string
+}{
+ {
+ name: "invalid module type without licenses property",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ mock_bad_module {
+ name: "libexample",
+ }`),
+ },
+ expectedErrors: []string{`module type "mock_bad_module" must have an applicable licenses property`},
+ },
+ {
+ name: "license must exist",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ mock_library {
+ name: "libexample",
+ licenses: ["notice"],
+ }`),
+ },
+ expectedErrors: []string{`"libexample" depends on undefined module "notice"`},
+ },
+ {
+ name: "all good",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "notice",
+ conditions: ["shownotice"],
+ }
+
+ license {
+ name: "top_Apache2",
+ license_kinds: ["notice"],
+ package_name: "topDog",
+ license_text: ["LICENSE", "NOTICE"],
+ }
+
+ mock_library {
+ name: "libexample1",
+ licenses: ["top_Apache2"],
+ }`),
+ "top/nested/Blueprints": []byte(`
+ mock_library {
+ name: "libnested",
+ licenses: ["top_Apache2"],
+ }`),
+ "other/Blueprints": []byte(`
+ mock_library {
+ name: "libother",
+ licenses: ["top_Apache2"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample1": []string{"top_Apache2"},
+ "libnested": []string{"top_Apache2"},
+ "libother": []string{"top_Apache2"},
+ },
+ effectiveKinds: map[string][]string{
+ "libexample1": []string{"notice"},
+ "libnested": []string{"notice"},
+ "libother": []string{"notice"},
+ },
+ effectivePackage: map[string]string{
+ "libexample1": "topDog",
+ "libnested": "topDog",
+ "libother": "topDog",
+ },
+ effectiveConditions: map[string][]string{
+ "libexample1": []string{"shownotice"},
+ "libnested": []string{"shownotice"},
+ "libother": []string{"shownotice"},
+ },
+ effectiveNotices: map[string][]string{
+ "libexample1": []string{"top/LICENSE", "top/NOTICE"},
+ "libnested": []string{"top/LICENSE", "top/NOTICE"},
+ "libother": []string{"top/LICENSE", "top/NOTICE"},
+ },
+ },
+
+ // Defaults propagation tests
+ {
+ // Check that licenses is the union of the defaults modules.
+ name: "defaults union, basic",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_notice",
+ conditions: ["notice"],
+ }
+
+ license {
+ name: "top_other",
+ license_kinds: ["top_notice"],
+ }
+
+ mock_defaults {
+ name: "libexample_defaults",
+ licenses: ["top_other"],
+ }
+ mock_library {
+ name: "libexample",
+ licenses: ["nested_other"],
+ defaults: ["libexample_defaults"],
+ }
+ mock_library {
+ name: "libsamepackage",
+ deps: ["libexample"],
+ }`),
+ "top/nested/Blueprints": []byte(`
+ license_kind {
+ name: "nested_notice",
+ conditions: ["notice"],
+ }
+
+ license {
+ name: "nested_other",
+ license_kinds: ["nested_notice"],
+ }
+
+ mock_library {
+ name: "libnested",
+ deps: ["libexample"],
+ }`),
+ "other/Blueprints": []byte(`
+ mock_library {
+ name: "libother",
+ deps: ["libexample"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample": []string{"nested_other", "top_other"},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "libexample": []string{"nested_other", "top_other"},
+ "libsamepackage": []string{"nested_other", "top_other"},
+ "libnested": []string{"nested_other", "top_other"},
+ "libother": []string{"nested_other", "top_other"},
+ },
+ effectiveKinds: map[string][]string{
+ "libexample": []string{"nested_notice", "top_notice"},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ },
+ effectiveConditions: map[string][]string{
+ "libexample": []string{"notice"},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ },
+ },
+ {
+ name: "defaults union, multiple defaults",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license {
+ name: "top",
+ }
+ mock_defaults {
+ name: "libexample_defaults_1",
+ licenses: ["other"],
+ }
+ mock_defaults {
+ name: "libexample_defaults_2",
+ licenses: ["top_nested"],
+ }
+ mock_library {
+ name: "libexample",
+ defaults: ["libexample_defaults_1", "libexample_defaults_2"],
+ }
+ mock_library {
+ name: "libsamepackage",
+ deps: ["libexample"],
+ }`),
+ "top/nested/Blueprints": []byte(`
+ license {
+ name: "top_nested",
+ license_text: ["LICENSE.txt"],
+ }
+ mock_library {
+ name: "libnested",
+ deps: ["libexample"],
+ }`),
+ "other/Blueprints": []byte(`
+ license {
+ name: "other",
+ }
+ mock_library {
+ name: "libother",
+ deps: ["libexample"],
+ }`),
+ "outsider/Blueprints": []byte(`
+ mock_library {
+ name: "liboutsider",
+ deps: ["libexample"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample": []string{"other", "top_nested"},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ "liboutsider": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "libexample": []string{"other", "top_nested"},
+ "libsamepackage": []string{"other", "top_nested"},
+ "libnested": []string{"other", "top_nested"},
+ "libother": []string{"other", "top_nested"},
+ "liboutsider": []string{"other", "top_nested"},
+ },
+ effectiveKinds: map[string][]string{
+ "libexample": []string{},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ "liboutsider": []string{},
+ },
+ effectiveNotices: map[string][]string{
+ "libexample": []string{"top/nested/LICENSE.txt"},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ "liboutsider": []string{},
+ },
+ },
+
+ // Defaults module's defaults_licenses tests
+ {
+ name: "defaults_licenses invalid",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ mock_defaults {
+ name: "top_defaults",
+ licenses: ["notice"],
+ }`),
+ },
+ expectedErrors: []string{`"top_defaults" depends on undefined module "notice"`},
+ },
+ {
+ name: "defaults_licenses overrides package default",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["by_exception_only"],
+ }
+ license {
+ name: "by_exception_only",
+ }
+ license {
+ name: "notice",
+ }
+ mock_defaults {
+ name: "top_defaults",
+ licenses: ["notice"],
+ }
+ mock_library {
+ name: "libexample",
+ }
+ mock_library {
+ name: "libdefaults",
+ defaults: ["top_defaults"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample": []string{"by_exception_only"},
+ "libdefaults": []string{"notice"},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "libexample": []string{"by_exception_only"},
+ "libdefaults": []string{"notice"},
+ },
+ },
+
+ // Package default_applicable_licenses tests
+ {
+ name: "package default_applicable_licenses must exist",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["notice"],
+ }`),
+ },
+ expectedErrors: []string{`"//top" depends on undefined module "notice"`},
+ },
+ {
+ // This test relies on the default licenses being legacy_public.
+ name: "package default_applicable_licenses property used when no licenses specified",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_notice"],
+ }
+
+ license {
+ name: "top_notice",
+ }
+ mock_library {
+ name: "libexample",
+ }`),
+ "outsider/Blueprints": []byte(`
+ mock_library {
+ name: "liboutsider",
+ deps: ["libexample"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample": []string{"top_notice"},
+ "liboutsider": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "libexample": []string{"top_notice"},
+ "liboutsider": []string{"top_notice"},
+ },
+ },
+ {
+ name: "package default_applicable_licenses not inherited to subpackages",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_notice"],
+ }
+ license {
+ name: "top_notice",
+ }
+ mock_library {
+ name: "libexample",
+ }`),
+ "top/nested/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["outsider"],
+ }
+
+ mock_library {
+ name: "libnested",
+ }`),
+ "top/other/Blueprints": []byte(`
+ mock_library {
+ name: "libother",
+ }`),
+ "outsider/Blueprints": []byte(`
+ license {
+ name: "outsider",
+ }
+ mock_library {
+ name: "liboutsider",
+ deps: ["libexample", "libother", "libnested"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample": []string{"top_notice"},
+ "libnested": []string{"outsider"},
+ "libother": []string{},
+ "liboutsider": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "libexample": []string{"top_notice"},
+ "libnested": []string{"outsider"},
+ "libother": []string{},
+ "liboutsider": []string{"top_notice", "outsider"},
+ },
+ },
+ {
+ name: "verify that prebuilt dependencies are included",
+ fs: map[string][]byte{
+ "prebuilts/Blueprints": []byte(`
+ license {
+ name: "prebuilt"
+ }
+ prebuilt {
+ name: "module",
+ licenses: ["prebuilt"],
+ }`),
+ "top/sources/source_file": nil,
+ "top/sources/Blueprints": []byte(`
+ license {
+ name: "top_sources"
+ }
+ source {
+ name: "module",
+ licenses: ["top_sources"],
+ }`),
+ "top/other/source_file": nil,
+ "top/other/Blueprints": []byte(`
+ source {
+ name: "other",
+ deps: [":module"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "other": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "other": []string{"prebuilt", "top_sources"},
+ },
+ },
+ {
+ name: "verify that prebuilt dependencies are ignored for licenses reasons (preferred)",
+ fs: map[string][]byte{
+ "prebuilts/Blueprints": []byte(`
+ license {
+ name: "prebuilt"
+ }
+ prebuilt {
+ name: "module",
+ licenses: ["prebuilt"],
+ prefer: true,
+ }`),
+ "top/sources/source_file": nil,
+ "top/sources/Blueprints": []byte(`
+ license {
+ name: "top_sources"
+ }
+ source {
+ name: "module",
+ licenses: ["top_sources"],
+ }`),
+ "top/other/source_file": nil,
+ "top/other/Blueprints": []byte(`
+ source {
+ name: "other",
+ deps: [":module"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "other": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "module": []string{"prebuilt", "top_sources"},
+ "other": []string{"prebuilt", "top_sources"},
+ },
+ },
+}
+
+func TestLicenses(t *testing.T) {
+ for _, test := range licensesTests {
+ t.Run(test.name, func(t *testing.T) {
+ // Customize the common license text fixture factory.
+ result := GroupFixturePreparers(
+ prepareForLicenseTest,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("mock_bad_module", newMockLicensesBadModule)
+ ctx.RegisterModuleType("mock_library", newMockLicensesLibraryModule)
+ ctx.RegisterModuleType("mock_defaults", defaultsLicensesFactory)
+ }),
+ test.fs.AddToFixture(),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+ RunTest(t)
+
+ if test.effectiveLicenses != nil {
+ checkEffectiveLicenses(t, result, test.effectiveLicenses)
+ }
+
+ if test.effectivePackage != nil {
+ checkEffectivePackage(t, result, test.effectivePackage)
+ }
+
+ if test.effectiveNotices != nil {
+ checkEffectiveNotices(t, result, test.effectiveNotices)
+ }
+
+ if test.effectiveKinds != nil {
+ checkEffectiveKinds(t, result, test.effectiveKinds)
+ }
+
+ if test.effectiveConditions != nil {
+ checkEffectiveConditions(t, result, test.effectiveConditions)
+ }
+
+ if test.effectiveInheritedLicenses != nil {
+ checkEffectiveInheritedLicenses(t, result, test.effectiveInheritedLicenses)
+ }
+ })
+ }
+}
+
+func checkEffectiveLicenses(t *testing.T, result *TestResult, effectiveLicenses map[string][]string) {
+ actualLicenses := make(map[string][]string)
+ result.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+ actualLicenses[m.Name()] = base.commonProperties.Effective_licenses
+ })
+
+ for moduleName, expectedLicenses := range effectiveLicenses {
+ licenses, ok := actualLicenses[moduleName]
+ if !ok {
+ licenses = []string{}
+ }
+ if !compareUnorderedStringArrays(expectedLicenses, licenses) {
+ t.Errorf("effective licenses mismatch for module %q: expected %q, found %q", moduleName, expectedLicenses, licenses)
+ }
+ }
+}
+
+func checkEffectiveInheritedLicenses(t *testing.T, result *TestResult, effectiveInheritedLicenses map[string][]string) {
+ actualLicenses := make(map[string][]string)
+ result.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+ inherited := make(map[string]bool)
+ for _, l := range base.commonProperties.Effective_licenses {
+ inherited[l] = true
+ }
+ result.Context.Context.VisitDepsDepthFirst(m, func(c blueprint.Module) {
+ if _, ok := c.(*licenseModule); ok {
+ return
+ }
+ if _, ok := c.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := c.(*packageModule); ok {
+ return
+ }
+ cmodule, ok := c.(Module)
+ if !ok {
+ t.Errorf("%q not a module", c.Name())
+ return
+ }
+ cbase := cmodule.base()
+ if cbase == nil {
+ return
+ }
+ for _, l := range cbase.commonProperties.Effective_licenses {
+ inherited[l] = true
+ }
+ })
+ actualLicenses[m.Name()] = []string{}
+ for l := range inherited {
+ actualLicenses[m.Name()] = append(actualLicenses[m.Name()], l)
+ }
+ })
+
+ for moduleName, expectedInheritedLicenses := range effectiveInheritedLicenses {
+ licenses, ok := actualLicenses[moduleName]
+ if !ok {
+ licenses = []string{}
+ }
+ if !compareUnorderedStringArrays(expectedInheritedLicenses, licenses) {
+ t.Errorf("effective inherited licenses mismatch for module %q: expected %q, found %q", moduleName, expectedInheritedLicenses, licenses)
+ }
+ }
+}
+
+func checkEffectivePackage(t *testing.T, result *TestResult, effectivePackage map[string]string) {
+ actualPackage := make(map[string]string)
+ result.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+
+ if base.commonProperties.Effective_package_name == nil {
+ actualPackage[m.Name()] = ""
+ } else {
+ actualPackage[m.Name()] = *base.commonProperties.Effective_package_name
+ }
+ })
+
+ for moduleName, expectedPackage := range effectivePackage {
+ packageName, ok := actualPackage[moduleName]
+ if !ok {
+ packageName = ""
+ }
+ if expectedPackage != packageName {
+ t.Errorf("effective package mismatch for module %q: expected %q, found %q", moduleName, expectedPackage, packageName)
+ }
+ }
+}
+
+func checkEffectiveNotices(t *testing.T, result *TestResult, effectiveNotices map[string][]string) {
+ actualNotices := make(map[string][]string)
+ result.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+ actualNotices[m.Name()] = base.commonProperties.Effective_license_text
+ })
+
+ for moduleName, expectedNotices := range effectiveNotices {
+ notices, ok := actualNotices[moduleName]
+ if !ok {
+ notices = []string{}
+ }
+ if !compareUnorderedStringArrays(expectedNotices, notices) {
+ t.Errorf("effective notice files mismatch for module %q: expected %q, found %q", moduleName, expectedNotices, notices)
+ }
+ }
+}
+
+func checkEffectiveKinds(t *testing.T, result *TestResult, effectiveKinds map[string][]string) {
+ actualKinds := make(map[string][]string)
+ result.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+ actualKinds[m.Name()] = base.commonProperties.Effective_license_kinds
+ })
+
+ for moduleName, expectedKinds := range effectiveKinds {
+ kinds, ok := actualKinds[moduleName]
+ if !ok {
+ kinds = []string{}
+ }
+ if !compareUnorderedStringArrays(expectedKinds, kinds) {
+ t.Errorf("effective license kinds mismatch for module %q: expected %q, found %q", moduleName, expectedKinds, kinds)
+ }
+ }
+}
+
+func checkEffectiveConditions(t *testing.T, result *TestResult, effectiveConditions map[string][]string) {
+ actualConditions := make(map[string][]string)
+ result.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+ actualConditions[m.Name()] = base.commonProperties.Effective_license_conditions
+ })
+
+ for moduleName, expectedConditions := range effectiveConditions {
+ conditions, ok := actualConditions[moduleName]
+ if !ok {
+ conditions = []string{}
+ }
+ if !compareUnorderedStringArrays(expectedConditions, conditions) {
+ t.Errorf("effective license conditions mismatch for module %q: expected %q, found %q", moduleName, expectedConditions, conditions)
+ }
+ }
+}
+
+func compareUnorderedStringArrays(expected, actual []string) bool {
+ if len(expected) != len(actual) {
+ return false
+ }
+ s := make(map[string]int)
+ for _, v := range expected {
+ s[v] += 1
+ }
+ for _, v := range actual {
+ c, ok := s[v]
+ if !ok {
+ return false
+ }
+ if c < 1 {
+ return false
+ }
+ s[v] -= 1
+ }
+ return true
+}
+
+type mockLicensesBadProperties struct {
+ Visibility []string
+}
+
+type mockLicensesBadModule struct {
+ ModuleBase
+ DefaultableModuleBase
+ properties mockLicensesBadProperties
+}
+
+func newMockLicensesBadModule() Module {
+ m := &mockLicensesBadModule{}
+
+ base := m.base()
+ m.AddProperties(&base.nameProperties, &m.properties)
+
+ base.generalProperties = m.GetProperties()
+ base.customizableProperties = m.GetProperties()
+
+ // The default_visibility property needs to be checked and parsed by the visibility module during
+ // its checking and parsing phases so make it the primary visibility property.
+ setPrimaryVisibilityProperty(m, "visibility", &m.properties.Visibility)
+
+ initAndroidModuleBase(m)
+ InitDefaultableModule(m)
+
+ return m
+}
+
+func (m *mockLicensesBadModule) GenerateAndroidBuildActions(ModuleContext) {
+}
+
+type mockLicensesLibraryProperties struct {
+ Deps []string
+}
+
+type mockLicensesLibraryModule struct {
+ ModuleBase
+ DefaultableModuleBase
+ properties mockLicensesLibraryProperties
+}
+
+func newMockLicensesLibraryModule() Module {
+ m := &mockLicensesLibraryModule{}
+ m.AddProperties(&m.properties)
+ InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon)
+ InitDefaultableModule(m)
+ return m
+}
+
+type dependencyLicensesTag struct {
+ blueprint.BaseDependencyTag
+ name string
+}
+
+func (j *mockLicensesLibraryModule) DepsMutator(ctx BottomUpMutatorContext) {
+ ctx.AddVariationDependencies(nil, dependencyLicensesTag{name: "mockdeps"}, j.properties.Deps...)
+}
+
+func (p *mockLicensesLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
+}
+
+type mockLicensesDefaults struct {
+ ModuleBase
+ DefaultsModuleBase
+}
+
+func defaultsLicensesFactory() Module {
+ m := &mockLicensesDefaults{}
+ InitDefaultsModule(m)
+ return m
+}
diff --git a/android/makevars.go b/android/makevars.go
index 546abcf..40c0ccd 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -134,8 +134,6 @@
// SingletonMakeVarsProvider is a Singleton with an extra method to provide extra values to be exported to Make.
type SingletonMakeVarsProvider interface {
- Singleton
-
// MakeVars uses a MakeVarsContext to provide extra values to be exported to Make.
MakeVars(ctx MakeVarsContext)
}
diff --git a/android/module.go b/android/module.go
index cfb32c1..942e071 100644
--- a/android/module.go
+++ b/android/module.go
@@ -15,6 +15,7 @@
package android
import (
+ "android/soong/bazel"
"fmt"
"os"
"path"
@@ -392,6 +393,7 @@
InstallInSanitizerDir() bool
InstallInRamdisk() bool
InstallInVendorRamdisk() bool
+ InstallInDebugRamdisk() bool
InstallInRecovery() bool
InstallInRoot() bool
InstallBypassMake() bool
@@ -442,18 +444,21 @@
Disable()
Enabled() bool
Target() Target
+ MultiTargets() []Target
Owner() string
InstallInData() bool
InstallInTestcases() bool
InstallInSanitizerDir() bool
InstallInRamdisk() bool
InstallInVendorRamdisk() bool
+ InstallInDebugRamdisk() bool
InstallInRecovery() bool
InstallInRoot() bool
InstallBypassMake() bool
InstallForceOS() (*OsType, *ArchType)
HideFromMake()
IsHideFromMake() bool
+ IsSkipInstall() bool
MakeUninstallable()
ReplacedByPrebuilt()
IsReplacedByPrebuilt() bool
@@ -490,6 +495,62 @@
TransitivePackagingSpecs() []PackagingSpec
}
+// BazelTargetModule is a lightweight wrapper interface around Module for
+// bp2build conversion purposes.
+//
+// In bp2build's bootstrap.Main execution, Soong runs an alternate pipeline of
+// mutators that creates BazelTargetModules from regular Module objects,
+// performing the mapping from Soong properties to Bazel rule attributes in the
+// process. This process may optionally create additional BazelTargetModules,
+// resulting in a 1:many mapping.
+//
+// bp2build.Codegen is then responsible for visiting all modules in the graph,
+// filtering for BazelTargetModules, and code-generating BUILD targets from
+// them.
+type BazelTargetModule interface {
+ Module
+
+ bazelTargetModuleProperties() *bazel.BazelTargetModuleProperties
+ SetBazelTargetModuleProperties(props bazel.BazelTargetModuleProperties)
+
+ RuleClass() string
+ BzlLoadLocation() string
+}
+
+// InitBazelTargetModule is a wrapper function that decorates BazelTargetModule
+// with property structs containing metadata for bp2build conversion.
+func InitBazelTargetModule(module BazelTargetModule) {
+ module.AddProperties(module.bazelTargetModuleProperties())
+ InitAndroidModule(module)
+}
+
+// BazelTargetModuleBase contains the property structs with metadata for
+// bp2build conversion.
+type BazelTargetModuleBase struct {
+ ModuleBase
+ Properties bazel.BazelTargetModuleProperties
+}
+
+// bazelTargetModuleProperties getter.
+func (btmb *BazelTargetModuleBase) bazelTargetModuleProperties() *bazel.BazelTargetModuleProperties {
+ return &btmb.Properties
+}
+
+// SetBazelTargetModuleProperties setter for BazelTargetModuleProperties
+func (btmb *BazelTargetModuleBase) SetBazelTargetModuleProperties(props bazel.BazelTargetModuleProperties) {
+ btmb.Properties = props
+}
+
+// RuleClass returns the rule class for this Bazel target
+func (b *BazelTargetModuleBase) RuleClass() string {
+ return b.bazelTargetModuleProperties().Rule_class
+}
+
+// BzlLoadLocation returns the rule class for this Bazel target
+func (b *BazelTargetModuleBase) BzlLoadLocation() string {
+ return b.bazelTargetModuleProperties().Bzl_load_location
+}
+
// Qualified id for a module
type qualifiedModuleName struct {
// The package (i.e. directory) in which the module is defined, without trailing /
@@ -619,6 +680,20 @@
// more details.
Visibility []string
+ // Describes the licenses applicable to this module. Must reference license modules.
+ Licenses []string
+
+ // Flattened from direct license dependencies. Equal to Licenses unless particular module adds more.
+ Effective_licenses []string `blueprint:"mutated"`
+ // Override of module name when reporting licenses
+ Effective_package_name *string `blueprint:"mutated"`
+ // Notice files
+ Effective_license_text []string `blueprint:"mutated"`
+ // License names
+ Effective_license_kinds []string `blueprint:"mutated"`
+ // License conditions
+ Effective_license_conditions []string `blueprint:"mutated"`
+
// control whether this module compiles for 32-bit, 64-bit, or both. Possible values
// are "32" (compile for 32-bit only), "64" (compile for 64-bit only), "both" (compile for both
// architectures), or "first" (compile for 64-bit on a 64-bit platform, and 32-bit on a 32-bit
@@ -680,7 +755,10 @@
// Whether this module is installed to vendor ramdisk
Vendor_ramdisk *bool
- // Whether this module is built for non-native architecures (also known as native bridge binary)
+ // Whether this module is installed to debug ramdisk
+ Debug_ramdisk *bool
+
+ // Whether this module is built for non-native architectures (also known as native bridge binary)
Native_bridge_supported *bool `android:"arch_variant"`
// init.rc files to be installed if this module is installed
@@ -940,6 +1018,10 @@
// The default_visibility property needs to be checked and parsed by the visibility module during
// its checking and parsing phases so make it the primary visibility property.
setPrimaryVisibilityProperty(m, "visibility", &base.commonProperties.Visibility)
+
+ // The default_applicable_licenses property needs to be checked and parsed by the licenses module during
+ // its checking and parsing phases so make it the primary licenses property.
+ setPrimaryLicensesProperty(m, "licenses", &base.commonProperties.Licenses)
}
// InitAndroidArchModule initializes the Module as an Android module that is architecture-specific.
@@ -1047,8 +1129,18 @@
variableProperties interface{}
hostAndDeviceProperties hostAndDeviceProperties
generalProperties []interface{}
- archProperties [][]interface{}
- customizableProperties []interface{}
+
+ // Arch specific versions of structs in generalProperties. The outer index
+ // has the same order as generalProperties as initialized in
+ // InitAndroidArchModule, and the inner index chooses the props specific to
+ // the architecture. The interface{} value is an archPropRoot that is
+ // filled with arch specific values by the arch mutator.
+ archProperties [][]interface{}
+
+ customizableProperties []interface{}
+
+ // Properties specific to the Blueprint to BUILD migration.
+ bazelTargetModuleProperties bazel.BazelTargetModuleProperties
// Information about all the properties on the module that contains visibility rules that need
// checking.
@@ -1057,6 +1149,9 @@
// The primary visibility property, may be nil, that controls access to the module.
primaryVisibilityProperty visibilityProperty
+ // The primary licenses property, may be nil, records license metadata for the module.
+ primaryLicensesProperty applicableLicensesProperty
+
noAddressSanitizer bool
installFiles InstallPaths
installFilesDepSet *installPathsDepSet
@@ -1086,8 +1181,6 @@
initRcPaths Paths
vintfFragmentsPaths Paths
-
- prefer32 func(ctx BaseModuleContext, base *ModuleBase, os OsType) bool
}
func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {}
@@ -1114,10 +1207,6 @@
return m.variables
}
-func (m *ModuleBase) Prefer32(prefer32 func(ctx BaseModuleContext, base *ModuleBase, os OsType) bool) {
- m.prefer32 = prefer32
-}
-
// Name returns the name of the module. It may be overridden by individual module types, for
// example prebuilts will prepend prebuilt_ to the name.
func (m *ModuleBase) Name() string {
@@ -1377,6 +1466,12 @@
m.commonProperties.SkipInstall = true
}
+// IsSkipInstall returns true if this variant is marked to not create install
+// rules when ctx.Install* are called.
+func (m *ModuleBase) IsSkipInstall() bool {
+ return m.commonProperties.SkipInstall
+}
+
// Similar to HideFromMake, but if the AndroidMk entry would set
// LOCAL_UNINSTALLABLE_MODULE then this variant may still output that entry
// rather than leaving it out altogether. That happens in cases where it would
@@ -1450,6 +1545,10 @@
return Bool(m.commonProperties.Vendor_ramdisk)
}
+func (m *ModuleBase) InstallInDebugRamdisk() bool {
+ return Bool(m.commonProperties.Debug_ramdisk)
+}
+
func (m *ModuleBase) InstallInRecovery() bool {
return Bool(m.commonProperties.Recovery)
}
@@ -1503,6 +1602,10 @@
return m.base().commonProperties.ImageVariation == VendorRamdiskVariation
}
+func (m *ModuleBase) InDebugRamdisk() bool {
+ return m.base().commonProperties.ImageVariation == DebugRamdiskVariation
+}
+
func (m *ModuleBase) InRecovery() bool {
return m.base().commonProperties.ImageVariation == RecoveryVariation
}
@@ -1732,11 +1835,28 @@
}
}
+ licensesPropertyFlattener(ctx)
+ if ctx.Failed() {
+ return
+ }
+
m.module.GenerateAndroidBuildActions(ctx)
if ctx.Failed() {
return
}
+ m.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc)
+ rcDir := PathForModuleInstall(ctx, "etc", "init")
+ for _, src := range m.initRcPaths {
+ ctx.PackageFile(rcDir, filepath.Base(src.String()), src)
+ }
+
+ m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments)
+ vintfDir := PathForModuleInstall(ctx, "etc", "vintf", "manifest")
+ for _, src := range m.vintfFragmentsPaths {
+ ctx.PackageFile(vintfDir, filepath.Base(src.String()), src)
+ }
+
// Create the set of tagged dist files after calling GenerateAndroidBuildActions
// as GenerateTaggedDistFiles() calls OutputFiles(tag) and so relies on the
// output paths being set which must be done before or during
@@ -1749,8 +1869,6 @@
m.installFiles = append(m.installFiles, ctx.installFiles...)
m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
m.packagingSpecs = append(m.packagingSpecs, ctx.packagingSpecs...)
- m.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc)
- m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments)
for k, v := range ctx.phonies {
m.phonies[k] = append(m.phonies[k], v...)
}
@@ -1811,19 +1929,11 @@
}
func (e *earlyModuleContext) Glob(globPattern string, excludes []string) Paths {
- ret, err := e.GlobWithDeps(globPattern, excludes)
- if err != nil {
- e.ModuleErrorf("glob: %s", err.Error())
- }
- return pathsForModuleSrcFromFullPath(e, ret, true)
+ return Glob(e, globPattern, excludes)
}
func (e *earlyModuleContext) GlobFiles(globPattern string, excludes []string) Paths {
- ret, err := e.GlobWithDeps(globPattern, excludes)
- if err != nil {
- e.ModuleErrorf("glob: %s", err.Error())
- }
- return pathsForModuleSrcFromFullPath(e, ret, false)
+ return GlobFiles(e, globPattern, excludes)
}
func (b *earlyModuleContext) IsSymlink(path Path) bool {
@@ -2154,10 +2264,17 @@
}
var deps []dep
b.VisitDirectDepsBlueprint(func(module blueprint.Module) {
- if aModule, _ := module.(Module); aModule != nil && aModule.base().BaseModuleName() == name {
- returnedTag := b.bp.OtherModuleDependencyTag(aModule)
+ if aModule, _ := module.(Module); aModule != nil {
+ if aModule.base().BaseModuleName() == name {
+ returnedTag := b.bp.OtherModuleDependencyTag(aModule)
+ if tag == nil || returnedTag == tag {
+ deps = append(deps, dep{aModule, returnedTag})
+ }
+ }
+ } else if b.bp.OtherModuleName(module) == name {
+ returnedTag := b.bp.OtherModuleDependencyTag(module)
if tag == nil || returnedTag == tag {
- deps = append(deps, dep{aModule, returnedTag})
+ deps = append(deps, dep{module, returnedTag})
}
}
})
@@ -2301,6 +2418,16 @@
return b.bp.FinalModule().(Module)
}
+// IsMetaDependencyTag returns true for cross-cutting metadata dependencies.
+func IsMetaDependencyTag(tag blueprint.DependencyTag) bool {
+ if tag == licenseKindTag {
+ return true
+ } else if tag == licensesTag {
+ return true
+ }
+ return false
+}
+
// A regexp for removing boilerplate from BaseDependencyTag from the string representation of
// a dependency tag.
var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:{}\E(, )?`)
@@ -2401,10 +2528,6 @@
m.commonProperties.System_ext_specific = boolPtr(false)
}
-func (m *ModuleBase) EnableNativeBridgeSupportByDefault() {
- m.commonProperties.Native_bridge_supported = boolPtr(true)
-}
-
func (m *ModuleBase) MakeAsSystemExt() {
m.commonProperties.Vendor = boolPtr(false)
m.commonProperties.Proprietary = boolPtr(false)
@@ -2438,6 +2561,10 @@
return m.module.InstallInVendorRamdisk()
}
+func (m *moduleContext) InstallInDebugRamdisk() bool {
+ return m.module.InstallInDebugRamdisk()
+}
+
func (m *moduleContext) InstallInRecovery() bool {
return m.module.InstallInRecovery()
}
diff --git a/android/module_test.go b/android/module_test.go
index e3cc613..9ac9291 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -163,6 +163,10 @@
return m
}
+var prepareForModuleTests = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("deps", depsModuleFactory)
+})
+
func TestErrorDependsOnDisabledModule(t *testing.T) {
bp := `
deps {
@@ -175,20 +179,13 @@
}
`
- config := TestConfig(buildDir, nil, bp, nil)
-
- ctx := NewTestContext(config)
- ctx.RegisterModuleType("deps", depsModuleFactory)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfNoMatchingErrors(t, `module "foo": depends on disabled module "bar"`, errs)
+ prepareForModuleTests.
+ ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo": depends on disabled module "bar"`)).
+ RunTestWithBp(t, bp)
}
func TestValidateCorrectBuildParams(t *testing.T) {
- config := TestConfig(buildDir, nil, "", nil)
+ config := TestConfig(t.TempDir(), nil, "", nil)
pathContext := PathContextForTesting(config)
bparams := convertBuildParams(BuildParams{
// Test with Output
@@ -214,7 +211,7 @@
}
func TestValidateIncorrectBuildParams(t *testing.T) {
- config := TestConfig(buildDir, nil, "", nil)
+ config := TestConfig(t.TempDir(), nil, "", nil)
pathContext := PathContextForTesting(config)
params := BuildParams{
Output: PathForOutput(pathContext, "regular_output"),
@@ -257,16 +254,6 @@
}
`
- config := TestConfig(buildDir, nil, bp, nil)
-
- ctx := NewTestContext(config)
- ctx.RegisterModuleType("deps", depsModuleFactory)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
-
expectedErrs := []string{
"\\QAndroid.bp:5:13: module \"foo\": dist.dest: Path is outside directory: ../invalid-dest\\E",
"\\QAndroid.bp:6:12: module \"foo\": dist.dir: Path is outside directory: ../invalid-dir\\E",
@@ -278,5 +265,8 @@
"\\QAndroid.bp:17:14: module \"foo\": dists[1].dir: Path is outside directory: ../invalid-dir1\\E",
"\\QAndroid.bp:18:17: module \"foo\": dists[1].suffix: Suffix may not contain a '/' character.\\E",
}
- CheckErrorsAgainstExpectations(t, errs, expectedErrs)
+
+ prepareForModuleTests.
+ ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(expectedErrs)).
+ RunTestWithBp(t, bp)
}
diff --git a/android/mutator.go b/android/mutator.go
index 31edea3..e25e2e8 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -15,7 +15,10 @@
package android
import (
+ "android/soong/bazel"
+ "fmt"
"reflect"
+ "strings"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -30,26 +33,50 @@
// run FinalDeps mutators (CreateVariations disallowed in this phase)
// continue on to GenerateAndroidBuildActions
-func registerMutatorsToContext(ctx *blueprint.Context, mutators []*mutator) {
- for _, t := range mutators {
- var handle blueprint.MutatorHandle
- if t.bottomUpMutator != nil {
- handle = ctx.RegisterBottomUpMutator(t.name, t.bottomUpMutator)
- } else if t.topDownMutator != nil {
- handle = ctx.RegisterTopDownMutator(t.name, t.topDownMutator)
- }
- if t.parallel {
- handle.Parallel()
- }
+// RegisterMutatorsForBazelConversion is a alternate registration pipeline for bp2build. Exported for testing.
+func RegisterMutatorsForBazelConversion(ctx *Context, preArchMutators, depsMutators, bp2buildMutators []RegisterMutatorFunc) {
+ mctx := ®isterMutatorsContext{
+ bazelConversionMode: true,
}
+
+ bp2buildPreArchMutators = append([]RegisterMutatorFunc{
+ RegisterNamespaceMutator,
+ RegisterDefaultsPreArchMutators,
+ // TODO(b/165114590): this is required to resolve deps that are only prebuilts, but we should
+ // evaluate the impact on conversion.
+ RegisterPrebuiltsPreArchMutators,
+ },
+ preArchMutators...)
+
+ for _, f := range bp2buildPreArchMutators {
+ f(mctx)
+ }
+
+ bp2buildDepsMutators = append([]RegisterMutatorFunc{
+ registerDepsMutatorBp2Build,
+ registerPathDepsMutator,
+ }, depsMutators...)
+
+ for _, f := range bp2buildDepsMutators {
+ f(mctx)
+ }
+
+ // Register bp2build mutators
+ for _, f := range bp2buildMutators {
+ f(mctx)
+ }
+
+ mctx.mutators.registerAll(ctx)
}
-func registerMutatorsForBazelConversion(ctx *blueprint.Context) {
- // FIXME(b/171263886): Start bringing in mutators to make the Bionic
- // module subgraph suitable for automated conversion.
+// collateGloballyRegisteredMutators constructs the list of mutators that have been registered
+// with the InitRegistrationContext and will be used at runtime.
+func collateGloballyRegisteredMutators() sortableComponents {
+ return collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps)
}
-func registerMutators(ctx *blueprint.Context, preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc) {
+// collateRegisteredMutators constructs a single list of mutators from the separate lists.
+func collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc) sortableComponents {
mctx := ®isterMutatorsContext{}
register := func(funcs []RegisterMutatorFunc) {
@@ -62,19 +89,20 @@
register(preDeps)
- mctx.BottomUp("deps", depsMutator).Parallel()
+ register([]RegisterMutatorFunc{registerDepsMutator})
register(postDeps)
mctx.finalPhase = true
register(finalDeps)
- registerMutatorsToContext(ctx, mctx.mutators)
+ return mctx.mutators
}
type registerMutatorsContext struct {
- mutators []*mutator
- finalPhase bool
+ mutators sortableComponents
+ finalPhase bool
+ bazelConversionMode bool
}
type RegisterMutatorsContext interface {
@@ -115,6 +143,11 @@
//
RegisterVisibilityRuleChecker,
+ // Record the default_applicable_licenses for each package.
+ //
+ // This must run before the defaults so that defaults modules can pick up the package default.
+ RegisterLicensesPackageMapper,
+
// Apply properties from defaults modules to the referencing modules.
//
// Any mutators that are added before this will not see any modules created by
@@ -141,6 +174,12 @@
// prebuilt.
RegisterPrebuiltsPreArchMutators,
+ // Gather the licenses properties for all modules for use during expansion and enforcement.
+ //
+ // This must come after the defaults mutators to ensure that any licenses supplied
+ // in a defaults module has been successfully applied before the rules are gathered.
+ RegisterLicensesPropertyGatherer,
+
// Gather the visibility rules for all modules for us during visibility enforcement.
//
// This must come after the defaults mutators to ensure that any visibility supplied
@@ -162,7 +201,8 @@
registerPathDepsMutator,
RegisterPrebuiltsPostDepsMutators,
RegisterVisibilityRuleEnforcer,
- RegisterNeverallowMutator,
+ RegisterLicensesDependencyChecker,
+ registerNeverallowMutator,
RegisterOverridePostDepsMutators,
}
@@ -184,6 +224,34 @@
finalDeps = append(finalDeps, f)
}
+var bp2buildPreArchMutators = []RegisterMutatorFunc{}
+var bp2buildDepsMutators = []RegisterMutatorFunc{}
+var bp2buildMutators = map[string]RegisterMutatorFunc{}
+
+// RegisterBp2BuildMutator registers specially crafted mutators for
+// converting Blueprint/Android modules into special modules that can
+// be code-generated into Bazel BUILD targets.
+//
+// TODO(b/178068862): bring this into TestContext.
+func RegisterBp2BuildMutator(moduleType string, m func(TopDownMutatorContext)) {
+ f := func(ctx RegisterMutatorsContext) {
+ ctx.TopDown(moduleType, m)
+ }
+ bp2buildMutators[moduleType] = f
+}
+
+// PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules
+// into Bazel BUILD targets that should run prior to deps and conversion.
+func PreArchBp2BuildMutators(f RegisterMutatorFunc) {
+ bp2buildPreArchMutators = append(bp2buildPreArchMutators, f)
+}
+
+// DepsBp2BuildMutators adds mutators to be register for converting Android Blueprint modules into
+// Bazel BUILD targets that should run prior to conversion to resolve dependencies.
+func DepsBp2BuildMutators(f RegisterMutatorFunc) {
+ bp2buildDepsMutators = append(bp2buildDepsMutators, f)
+}
+
type BaseMutatorContext interface {
BaseModuleContext
@@ -203,6 +271,12 @@
// CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
// the specified property structs to it as if the properties were set in a blueprint file.
CreateModule(ModuleFactory, ...interface{}) Module
+
+ // CreateBazelTargetModule creates a BazelTargetModule by calling the
+ // factory method, just like in CreateModule, but also requires
+ // BazelTargetModuleProperties containing additional metadata for the
+ // bp2build codegenerator.
+ CreateBazelTargetModule(ModuleFactory, string, bazel.BazelTargetModuleProperties, interface{}) BazelTargetModule
}
type topDownMutatorContext struct {
@@ -328,32 +402,38 @@
// variant of the current module. The value should not be modified after being passed to
// SetVariationProvider.
SetVariationProvider(module blueprint.Module, provider blueprint.ProviderKey, value interface{})
+
+ // BazelConversionMode returns whether this mutator is being run as part of Bazel Conversion.
+ BazelConversionMode() bool
}
type bottomUpMutatorContext struct {
bp blueprint.BottomUpMutatorContext
baseModuleContext
- finalPhase bool
+ finalPhase bool
+ bazelConversionMode bool
}
func bottomUpMutatorContextFactory(ctx blueprint.BottomUpMutatorContext, a Module,
- finalPhase bool) BottomUpMutatorContext {
+ finalPhase, bazelConversionMode bool) BottomUpMutatorContext {
return &bottomUpMutatorContext{
- bp: ctx,
- baseModuleContext: a.base().baseModuleContextFactory(ctx),
- finalPhase: finalPhase,
+ bp: ctx,
+ baseModuleContext: a.base().baseModuleContextFactory(ctx),
+ finalPhase: finalPhase,
+ bazelConversionMode: bazelConversionMode,
}
}
func (x *registerMutatorsContext) BottomUp(name string, m BottomUpMutator) MutatorHandle {
finalPhase := x.finalPhase
+ bazelConversionMode := x.bazelConversionMode
f := func(ctx blueprint.BottomUpMutatorContext) {
if a, ok := ctx.Module().(Module); ok {
- m(bottomUpMutatorContextFactory(ctx, a, finalPhase))
+ m(bottomUpMutatorContextFactory(ctx, a, finalPhase, bazelConversionMode))
}
}
- mutator := &mutator{name: name, bottomUpMutator: f}
+ mutator := &mutator{name: x.mutatorName(name), bottomUpMutator: f}
x.mutators = append(x.mutators, mutator)
return mutator
}
@@ -364,6 +444,13 @@
return mutator
}
+func (x *registerMutatorsContext) mutatorName(name string) string {
+ if x.bazelConversionMode {
+ return name + "_bp2build"
+ }
+ return name
+}
+
func (x *registerMutatorsContext) TopDown(name string, m TopDownMutator) MutatorHandle {
f := func(ctx blueprint.TopDownMutatorContext) {
if a, ok := ctx.Module().(Module); ok {
@@ -374,11 +461,28 @@
m(actx)
}
}
- mutator := &mutator{name: name, topDownMutator: f}
+ mutator := &mutator{name: x.mutatorName(name), topDownMutator: f}
x.mutators = append(x.mutators, mutator)
return mutator
}
+func (mutator *mutator) componentName() string {
+ return mutator.name
+}
+
+func (mutator *mutator) register(ctx *Context) {
+ blueprintCtx := ctx.Context
+ var handle blueprint.MutatorHandle
+ if mutator.bottomUpMutator != nil {
+ handle = blueprintCtx.RegisterBottomUpMutator(mutator.name, mutator.bottomUpMutator)
+ } else if mutator.topDownMutator != nil {
+ handle = blueprintCtx.RegisterTopDownMutator(mutator.name, mutator.topDownMutator)
+ }
+ if mutator.parallel {
+ handle.Parallel()
+ }
+}
+
type MutatorHandle interface {
Parallel() MutatorHandle
}
@@ -407,6 +511,39 @@
}
}
+func registerDepsMutator(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("deps", depsMutator).Parallel()
+}
+
+func registerDepsMutatorBp2Build(ctx RegisterMutatorsContext) {
+ // TODO(b/179313531): Consider a separate mutator that only runs depsMutator for modules that are
+ // being converted to build targets.
+ ctx.BottomUp("deps", depsMutator).Parallel()
+}
+
+func (t *topDownMutatorContext) CreateBazelTargetModule(
+ factory ModuleFactory,
+ name string,
+ bazelProps bazel.BazelTargetModuleProperties,
+ attrs interface{}) BazelTargetModule {
+ if strings.HasPrefix(name, bazel.BazelTargetModuleNamePrefix) {
+ panic(fmt.Errorf(
+ "The %s name prefix is added automatically, do not set it manually: %s",
+ bazel.BazelTargetModuleNamePrefix,
+ name))
+ }
+ name = bazel.BazelTargetModuleNamePrefix + name
+ nameProp := struct {
+ Name *string
+ }{
+ Name: &name,
+ }
+
+ b := t.createModuleWithoutInheritance(factory, &nameProp, attrs).(BazelTargetModule)
+ b.SetBazelTargetModuleProperties(bazelProps)
+ return b
+}
+
func (t *topDownMutatorContext) AppendProperties(props ...interface{}) {
for _, p := range props {
err := proptools.AppendMatchingProperties(t.Module().base().customizableProperties,
@@ -471,6 +608,11 @@
return module
}
+func (t *topDownMutatorContext) createModuleWithoutInheritance(factory ModuleFactory, props ...interface{}) Module {
+ module := t.bp.CreateModule(ModuleFactoryAdaptor(factory), props...).(Module)
+ return module
+}
+
func (b *bottomUpMutatorContext) MutatorName() string {
return b.bp.MutatorName()
}
@@ -534,12 +676,28 @@
func (b *bottomUpMutatorContext) AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag,
names ...string) []blueprint.Module {
+ if b.bazelConversionMode {
+ _, noSelfDeps := RemoveFromList(b.ModuleName(), names)
+ if len(noSelfDeps) == 0 {
+ return []blueprint.Module(nil)
+ }
+ // In Bazel conversion mode, mutators should not have created any variants. So, when adding a
+ // dependency, the variations would not exist and the dependency could not be added, by
+ // specifying no variations, we will allow adding the dependency to succeed.
+ return b.bp.AddFarVariationDependencies(nil, tag, noSelfDeps...)
+ }
return b.bp.AddVariationDependencies(variations, tag, names...)
}
func (b *bottomUpMutatorContext) AddFarVariationDependencies(variations []blueprint.Variation,
tag blueprint.DependencyTag, names ...string) []blueprint.Module {
+ if b.bazelConversionMode {
+ // In Bazel conversion mode, mutators should not have created any variants. So, when adding a
+ // dependency, the variations would not exist and the dependency could not be added, by
+ // specifying no variations, we will allow adding the dependency to succeed.
+ return b.bp.AddFarVariationDependencies(nil, tag, names...)
+ }
return b.bp.AddFarVariationDependencies(variations, tag, names...)
}
@@ -567,3 +725,7 @@
func (b *bottomUpMutatorContext) SetVariationProvider(module blueprint.Module, provider blueprint.ProviderKey, value interface{}) {
b.bp.SetVariationProvider(module, provider, value)
}
+
+func (b *bottomUpMutatorContext) BazelConversionMode() bool {
+ return b.bazelConversionMode
+}
diff --git a/android/mutator_test.go b/android/mutator_test.go
index 1c395c7..21eebd2 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -16,12 +16,10 @@
import (
"fmt"
- "reflect"
"strings"
"testing"
"github.com/google/blueprint"
- "github.com/google/blueprint/proptools"
)
type mutatorTestModule struct {
@@ -67,28 +65,20 @@
}
`
- config := TestConfig(buildDir, nil, bp, nil)
- config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
+ result := GroupFixturePreparers(
+ PrepareForTestWithAllowMissingDependencies,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("test", mutatorTestModuleFactory)
+ ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.TopDown("add_missing_dependencies", addMissingDependenciesMutator)
+ })
+ }),
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
- ctx := NewTestContext(config)
- ctx.SetAllowMissingDependencies(true)
+ foo := result.ModuleForTests("foo", "").Module().(*mutatorTestModule)
- ctx.RegisterModuleType("test", mutatorTestModuleFactory)
- ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
- ctx.TopDown("add_missing_dependencies", addMissingDependenciesMutator)
- })
-
- ctx.Register()
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
-
- foo := ctx.ModuleForTests("foo", "").Module().(*mutatorTestModule)
-
- if g, w := foo.missingDeps, []string{"added_missing_dep", "regular_missing_dep"}; !reflect.DeepEqual(g, w) {
- t.Errorf("want foo missing deps %q, got %q", w, g)
- }
+ AssertDeepEquals(t, "foo missing deps", []string{"added_missing_dep", "regular_missing_dep"}, foo.missingDeps)
}
func TestModuleString(t *testing.T) {
@@ -98,52 +88,47 @@
}
`
- config := TestConfig(buildDir, nil, bp, nil)
-
- ctx := NewTestContext(config)
-
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")
- })
- })
+ GroupFixturePreparers(
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
- ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
- ctx.BottomUp("pre_deps", func(ctx BottomUpMutatorContext) {
- moduleStrings = append(moduleStrings, ctx.Module().String())
- ctx.CreateVariations("c", "d")
- })
- })
+ 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.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.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("pre_deps", func(ctx BottomUpMutatorContext) {
+ moduleStrings = append(moduleStrings, ctx.Module().String())
+ ctx.CreateVariations("c", "d")
+ })
+ })
- ctx.RegisterModuleType("test", mutatorTestModuleFactory)
+ 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.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
+ ctx.RegisterModuleType("test", mutatorTestModuleFactory)
+ }),
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
want := []string{
// Initial name.
@@ -184,9 +169,7 @@
"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)
- }
+ AssertDeepEquals(t, "module String() values", want, moduleStrings)
}
func TestFinalDepsPhase(t *testing.T) {
@@ -202,52 +185,46 @@
}
`
- config := TestConfig(buildDir, nil, bp, nil)
-
- ctx := NewTestContext(config)
-
finalGot := map[string]int{}
- dep1Tag := struct {
- blueprint.BaseDependencyTag
- }{}
- dep2Tag := struct {
- blueprint.BaseDependencyTag
- }{}
+ GroupFixturePreparers(
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ dep1Tag := struct {
+ blueprint.BaseDependencyTag
+ }{}
+ dep2Tag := struct {
+ blueprint.BaseDependencyTag
+ }{}
- ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) {
- ctx.BottomUp("far_deps_1", func(ctx BottomUpMutatorContext) {
- if !strings.HasPrefix(ctx.ModuleName(), "common_dep") {
- ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep1Tag, "common_dep_1")
- }
- })
- ctx.BottomUp("variant", func(ctx BottomUpMutatorContext) {
- ctx.CreateLocalVariations("a", "b")
- })
- })
-
- ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
- ctx.BottomUp("far_deps_2", func(ctx BottomUpMutatorContext) {
- if !strings.HasPrefix(ctx.ModuleName(), "common_dep") {
- ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep2Tag, "common_dep_2")
- }
- })
- ctx.BottomUp("final", func(ctx BottomUpMutatorContext) {
- finalGot[ctx.Module().String()] += 1
- ctx.VisitDirectDeps(func(mod Module) {
- finalGot[fmt.Sprintf("%s -> %s", ctx.Module().String(), mod)] += 1
+ ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("far_deps_1", func(ctx BottomUpMutatorContext) {
+ if !strings.HasPrefix(ctx.ModuleName(), "common_dep") {
+ ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep1Tag, "common_dep_1")
+ }
+ })
+ ctx.BottomUp("variant", func(ctx BottomUpMutatorContext) {
+ ctx.CreateLocalVariations("a", "b")
+ })
})
- })
- })
- ctx.RegisterModuleType("test", mutatorTestModuleFactory)
+ ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("far_deps_2", func(ctx BottomUpMutatorContext) {
+ if !strings.HasPrefix(ctx.ModuleName(), "common_dep") {
+ ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep2Tag, "common_dep_2")
+ }
+ })
+ ctx.BottomUp("final", func(ctx BottomUpMutatorContext) {
+ finalGot[ctx.Module().String()] += 1
+ ctx.VisitDirectDeps(func(mod Module) {
+ finalGot[fmt.Sprintf("%s -> %s", ctx.Module().String(), mod)] += 1
+ })
+ })
+ })
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
+ ctx.RegisterModuleType("test", mutatorTestModuleFactory)
+ }),
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
finalWant := map[string]int{
"common_dep_1{variant:a}": 1,
@@ -262,37 +239,31 @@
"foo{variant:b} -> common_dep_2{variant:a}": 1,
}
- if !reflect.DeepEqual(finalWant, finalGot) {
- t.Errorf("want:\n%q\ngot:\n%q", finalWant, finalGot)
- }
+ AssertDeepEquals(t, "final", finalWant, finalGot)
}
func TestNoCreateVariationsInFinalDeps(t *testing.T) {
- config := TestConfig(buildDir, nil, `test {name: "foo"}`, nil)
- ctx := NewTestContext(config)
-
checkErr := func() {
if err := recover(); err == nil || !strings.Contains(fmt.Sprintf("%s", err), "not allowed in FinalDepsMutators") {
panic("Expected FinalDepsMutators consistency check to fail")
}
}
- ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
- ctx.BottomUp("vars", func(ctx BottomUpMutatorContext) {
- defer checkErr()
- ctx.CreateVariations("a", "b")
- })
- ctx.BottomUp("local_vars", func(ctx BottomUpMutatorContext) {
- defer checkErr()
- ctx.CreateLocalVariations("a", "b")
- })
- })
+ GroupFixturePreparers(
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("vars", func(ctx BottomUpMutatorContext) {
+ defer checkErr()
+ ctx.CreateVariations("a", "b")
+ })
+ ctx.BottomUp("local_vars", func(ctx BottomUpMutatorContext) {
+ defer checkErr()
+ ctx.CreateLocalVariations("a", "b")
+ })
+ })
- ctx.RegisterModuleType("test", mutatorTestModuleFactory)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
+ ctx.RegisterModuleType("test", mutatorTestModuleFactory)
+ }),
+ FixtureWithRootAndroidBp(`test {name: "foo"}`),
+ ).RunTest(t)
}
diff --git a/android/namespace_test.go b/android/namespace_test.go
index 45e2cdb..08e221a 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -143,7 +143,7 @@
}
func TestDependingOnModuleInNonImportedNamespace(t *testing.T) {
- _, errs := setupTestExpectErrs(
+ _, errs := setupTestExpectErrs(t,
map[string]string{
"dir1": `
soong_namespace {
@@ -378,7 +378,7 @@
}
func TestImportingNonexistentNamespace(t *testing.T) {
- _, errs := setupTestExpectErrs(
+ _, errs := setupTestExpectErrs(t,
map[string]string{
"dir1": `
soong_namespace {
@@ -402,7 +402,7 @@
}
func TestNamespacesDontInheritParentNamespaces(t *testing.T) {
- _, errs := setupTestExpectErrs(
+ _, errs := setupTestExpectErrs(t,
map[string]string{
"dir1": `
soong_namespace {
@@ -455,7 +455,7 @@
}
func TestNamespaceImportsNotTransitive(t *testing.T) {
- _, errs := setupTestExpectErrs(
+ _, errs := setupTestExpectErrs(t,
map[string]string{
"dir1": `
soong_namespace {
@@ -496,7 +496,7 @@
}
func TestTwoNamepacesInSameDir(t *testing.T) {
- _, errs := setupTestExpectErrs(
+ _, errs := setupTestExpectErrs(t,
map[string]string{
"dir1": `
soong_namespace {
@@ -516,7 +516,7 @@
}
func TestNamespaceNotAtTopOfFile(t *testing.T) {
- _, errs := setupTestExpectErrs(
+ _, errs := setupTestExpectErrs(t,
map[string]string{
"dir1": `
test_module {
@@ -537,7 +537,7 @@
}
func TestTwoModulesWithSameNameInSameNamespace(t *testing.T) {
- _, errs := setupTestExpectErrs(
+ _, errs := setupTestExpectErrs(t,
map[string]string{
"dir1": `
soong_namespace {
@@ -562,7 +562,7 @@
}
func TestDeclaringNamespaceInNonAndroidBpFile(t *testing.T) {
- _, errs := setupTestFromFiles(
+ _, errs := setupTestFromFiles(t,
map[string][]byte{
"Android.bp": []byte(`
build = ["include.bp"]
@@ -632,39 +632,38 @@
return files
}
-func setupTestFromFiles(bps map[string][]byte) (ctx *TestContext, errs []error) {
- config := TestConfig(buildDir, nil, "", bps)
+func setupTestFromFiles(t *testing.T, bps MockFS) (ctx *TestContext, errs []error) {
+ result := GroupFixturePreparers(
+ FixtureModifyContext(func(ctx *TestContext) {
+ ctx.RegisterModuleType("test_module", newTestModule)
+ ctx.RegisterModuleType("soong_namespace", NamespaceFactory)
+ ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
+ ctx.PreArchMutators(RegisterNamespaceMutator)
+ ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("rename", renameMutator)
+ })
+ }),
+ bps.AddToFixture(),
+ ).
+ // Ignore errors for now so tests can check them later.
+ ExtendWithErrorHandler(FixtureIgnoreErrors).
+ RunTest(t)
- ctx = NewTestContext(config)
- ctx.RegisterModuleType("test_module", newTestModule)
- ctx.RegisterModuleType("soong_namespace", NamespaceFactory)
- ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
- ctx.PreArchMutators(RegisterNamespaceMutator)
- ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
- ctx.BottomUp("rename", renameMutator)
- })
- ctx.Register()
-
- _, errs = ctx.ParseBlueprintsFiles("Android.bp")
- if len(errs) > 0 {
- return ctx, errs
- }
- _, errs = ctx.PrepareBuildActions(config)
- return ctx, errs
+ return result.TestContext, result.Errs
}
-func setupTestExpectErrs(bps map[string]string) (ctx *TestContext, errs []error) {
+func setupTestExpectErrs(t *testing.T, bps map[string]string) (ctx *TestContext, errs []error) {
files := make(map[string][]byte, len(bps))
files["Android.bp"] = []byte("")
for dir, text := range bps {
files[filepath.Join(dir, "Android.bp")] = []byte(text)
}
- return setupTestFromFiles(files)
+ return setupTestFromFiles(t, files)
}
func setupTest(t *testing.T, bps map[string]string) (ctx *TestContext) {
t.Helper()
- ctx, errs := setupTestExpectErrs(bps)
+ ctx, errs := setupTestExpectErrs(t, bps)
FailIfErrored(t, errs)
return ctx
}
@@ -698,7 +697,7 @@
testModule, ok := candidate.(*testModule)
if ok {
if testModule.properties.Id == id {
- module = TestingModule{testModule}
+ module = newTestingModule(ctx.config, testModule)
}
}
}
diff --git a/android/neverallow.go b/android/neverallow.go
index 031b3f4..a385bbc 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -42,7 +42,7 @@
// counts as a match
// - it has none of the "Without" properties matched (same rules as above)
-func RegisterNeverallowMutator(ctx RegisterMutatorsContext) {
+func registerNeverallowMutator(ctx RegisterMutatorsContext) {
ctx.BottomUp("neverallow", neverallowMutator).Parallel()
}
@@ -51,7 +51,6 @@
func init() {
AddNeverAllowRules(createIncludeDirsRules()...)
AddNeverAllowRules(createTrebleRules()...)
- AddNeverAllowRules(createMediaRules()...)
AddNeverAllowRules(createJavaDeviceForHostRules()...)
AddNeverAllowRules(createCcSdkVariantRules()...)
AddNeverAllowRules(createUncompressDexRules()...)
@@ -132,14 +131,6 @@
}
}
-func createMediaRules() []Rule {
- return []Rule{
- NeverAllow().
- With("libs", "updatable-media").
- Because("updatable-media includes private APIs. Use updatable_media_stubs instead."),
- }
-}
-
func createJavaDeviceForHostRules() []Rule {
javaDeviceForHostProjectsAllowedList := []string{
"external/guava",
@@ -670,6 +661,22 @@
// Overrides the default neverallow rules for the supplied config.
//
// For testing only.
-func SetTestNeverallowRules(config Config, testRules []Rule) {
+func setTestNeverallowRules(config Config, testRules []Rule) {
config.Once(neverallowRulesKey, func() interface{} { return testRules })
}
+
+// Prepares for a test by setting neverallow rules and enabling the mutator.
+//
+// If the supplied rules are nil then the default rules are used.
+func PrepareForTestWithNeverallowRules(testRules []Rule) FixturePreparer {
+ return GroupFixturePreparers(
+ FixtureModifyConfig(func(config Config) {
+ if testRules != nil {
+ setTestNeverallowRules(config, testRules)
+ }
+ }),
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.PostDepsMutators(registerNeverallowMutator)
+ }),
+ )
+}
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 8c7a538..268346a 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -28,7 +28,7 @@
rules []Rule
// Additional contents to add to the virtual filesystem used by the tests.
- fs map[string][]byte
+ fs MockFS
// The expected error patterns. If empty then no errors are expected, otherwise each error
// reported must be matched by at least one of these patterns. A pattern matches if the error
@@ -190,19 +190,6 @@
},
},
{
- name: "dependency on updatable-media",
- fs: map[string][]byte{
- "Android.bp": []byte(`
- java_library {
- name: "needs_updatable_media",
- libs: ["updatable-media"],
- }`),
- },
- expectedErrors: []string{
- "updatable-media includes private APIs. Use updatable_media_stubs instead.",
- },
- },
- {
name: "java_device_for_host",
fs: map[string][]byte{
"Android.bp": []byte(`
@@ -298,41 +285,30 @@
},
}
+var prepareForNeverAllowTest = GroupFixturePreparers(
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("cc_library", newMockCcLibraryModule)
+ ctx.RegisterModuleType("java_library", newMockJavaLibraryModule)
+ ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule)
+ ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule)
+ ctx.RegisterModuleType("makefile_goal", newMockMakefileGoalModule)
+ }),
+)
+
func TestNeverallow(t *testing.T) {
for _, test := range neverallowTests {
- // Create a test per config to allow for test specific config, e.g. test rules.
- config := TestConfig(buildDir, nil, "", test.fs)
-
t.Run(test.name, func(t *testing.T) {
- // If the test has its own rules then use them instead of the default ones.
- if test.rules != nil {
- SetTestNeverallowRules(config, test.rules)
- }
- _, errs := testNeverallow(config)
- CheckErrorsAgainstExpectations(t, errs, test.expectedErrors)
+ GroupFixturePreparers(
+ prepareForNeverAllowTest,
+ PrepareForTestWithNeverallowRules(test.rules),
+ test.fs.AddToFixture(),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+ RunTest(t)
})
}
}
-func testNeverallow(config Config) (*TestContext, []error) {
- ctx := NewTestContext(config)
- ctx.RegisterModuleType("cc_library", newMockCcLibraryModule)
- ctx.RegisterModuleType("java_library", newMockJavaLibraryModule)
- ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule)
- ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule)
- ctx.RegisterModuleType("makefile_goal", newMockMakefileGoalModule)
- ctx.PostDepsMutators(RegisterNeverallowMutator)
- ctx.Register()
-
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
- if len(errs) > 0 {
- return ctx, errs
- }
-
- _, errs = ctx.PrepareBuildActions(config)
- return ctx, errs
-}
-
type mockCcLibraryProperties struct {
Include_dirs []string
Vendor_available *bool
diff --git a/android/ninja_deps_test.go b/android/ninja_deps_test.go
index d3775ed..947c257 100644
--- a/android/ninja_deps_test.go
+++ b/android/ninja_deps_test.go
@@ -53,23 +53,20 @@
}
func TestNinjaDeps(t *testing.T) {
- fs := map[string][]byte{
+ fs := MockFS{
"test_ninja_deps/exists": nil,
}
- config := TestConfig(buildDir, nil, "", fs)
- ctx := NewTestContext(config)
- ctx.RegisterSingletonType("test_ninja_deps_singleton", testNinjaDepsSingletonFactory)
- ctx.RegisterSingletonType("ninja_deps_singleton", ninjaDepsSingletonFactory)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- ninjaDeps, errs := ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
+ result := GroupFixturePreparers(
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterSingletonType("test_ninja_deps_singleton", testNinjaDepsSingletonFactory)
+ ctx.RegisterSingletonType("ninja_deps_singleton", ninjaDepsSingletonFactory)
+ }),
+ fs.AddToFixture(),
+ ).RunTest(t)
// Verify that the ninja file has a dependency on the test_ninja_deps directory.
- if g, w := ninjaDeps, "test_ninja_deps"; !InList(w, g) {
+ if g, w := result.NinjaDeps, "test_ninja_deps"; !InList(w, g) {
t.Errorf("expected %q in %q", w, g)
}
}
diff --git a/android/override_module.go b/android/override_module.go
index fa08566..97acc5c 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -215,7 +215,14 @@
ctx.BottomUp("override_deps", overrideModuleDepsMutator).Parallel()
ctx.TopDown("register_override", registerOverrideMutator).Parallel()
ctx.BottomUp("perform_override", performOverrideMutator).Parallel()
+ // overridableModuleDepsMutator calls OverridablePropertiesDepsMutator so that overridable modules can
+ // add deps from overridable properties.
ctx.BottomUp("overridable_deps", overridableModuleDepsMutator).Parallel()
+ // Because overridableModuleDepsMutator is run after PrebuiltPostDepsMutator,
+ // prebuilt's ReplaceDependencies doesn't affect to those deps added by overridable properties.
+ // By running PrebuiltPostDepsMutator again after overridableModuleDepsMutator, deps via overridable properties
+ // can be replaced with prebuilts.
+ ctx.BottomUp("replace_deps_on_prebuilts_for_overridable_deps_again", PrebuiltPostDepsMutator).Parallel()
ctx.BottomUp("replace_deps_on_override", replaceDepsOnOverridingModuleMutator).Parallel()
}
diff --git a/android/package.go b/android/package.go
index 182b3ed..878e4c4 100644
--- a/android/package.go
+++ b/android/package.go
@@ -23,6 +23,8 @@
RegisterPackageBuildComponents(InitRegistrationContext)
}
+var PrepareForTestWithPackageModule = FixtureRegisterWithContext(RegisterPackageBuildComponents)
+
// Register the package module type.
func RegisterPackageBuildComponents(ctx RegistrationContext) {
ctx.RegisterModuleType("package", PackageFactory)
@@ -31,6 +33,8 @@
type packageProperties struct {
// Specifies the default visibility for all modules defined in this package.
Default_visibility []string
+ // Specifies the default license terms for all modules defined in this package.
+ Default_applicable_licenses []string
}
type packageModule struct {
@@ -68,5 +72,9 @@
// its checking and parsing phases so make it the primary visibility property.
setPrimaryVisibilityProperty(module, "default_visibility", &module.properties.Default_visibility)
+ // The default_applicable_licenses property needs to be checked and parsed by the licenses module during
+ // its checking and parsing phases so make it the primary licenses property.
+ setPrimaryLicensesProperty(module, "default_applicable_licenses", &module.properties.Default_applicable_licenses)
+
return module
}
diff --git a/android/package_ctx.go b/android/package_ctx.go
index 6d0fcb3..c19debb 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -19,6 +19,8 @@
"strings"
"github.com/google/blueprint"
+
+ "android/soong/remoteexec"
)
// PackageContext is a wrapper for blueprint.PackageContext that adds
@@ -260,3 +262,40 @@
return params, nil
}, argNames...)
}
+
+// RemoteStaticRules returns a pair of rules based on the given RuleParams, where the first rule is a
+// locally executable rule and the second rule is a remotely executable rule. commonArgs are args
+// used for both the local and remotely executable rules. reArgs are used only for remote
+// execution.
+func (p PackageContext) RemoteStaticRules(name string, ruleParams blueprint.RuleParams, reParams *remoteexec.REParams, commonArgs []string, reArgs []string) (blueprint.Rule, blueprint.Rule) {
+ ruleParamsRE := ruleParams
+ ruleParams.Command = strings.ReplaceAll(ruleParams.Command, "$reTemplate", "")
+ ruleParamsRE.Command = strings.ReplaceAll(ruleParamsRE.Command, "$reTemplate", reParams.Template())
+
+ return p.AndroidStaticRule(name, ruleParams, commonArgs...),
+ p.AndroidRemoteStaticRule(name+"RE", RemoteRuleSupports{RBE: true}, ruleParamsRE, append(commonArgs, reArgs...)...)
+}
+
+// MultiCommandStaticRules returns a pair of rules based on the given RuleParams, where the first
+// rule is a locally executable rule and the second rule is a remotely executable rule. This
+// function supports multiple remote execution wrappers placed in the template when commands are
+// chained together with &&. commonArgs are args used for both the local and remotely executable
+// rules. reArgs are args used only for remote execution.
+func (p PackageContext) MultiCommandRemoteStaticRules(name string, ruleParams blueprint.RuleParams, reParams map[string]*remoteexec.REParams, commonArgs []string, reArgs []string) (blueprint.Rule, blueprint.Rule) {
+ ruleParamsRE := ruleParams
+ for k, v := range reParams {
+ ruleParams.Command = strings.ReplaceAll(ruleParams.Command, k, "")
+ ruleParamsRE.Command = strings.ReplaceAll(ruleParamsRE.Command, k, v.Template())
+ }
+
+ return p.AndroidStaticRule(name, ruleParams, commonArgs...),
+ p.AndroidRemoteStaticRule(name+"RE", RemoteRuleSupports{RBE: true}, ruleParamsRE, append(commonArgs, reArgs...)...)
+}
+
+// StaticVariableWithEnvOverride creates a static variable that evaluates to the value of the given
+// environment variable if set, otherwise the given default.
+func (p PackageContext) StaticVariableWithEnvOverride(name, envVar, defaultVal string) blueprint.Variable {
+ return p.VariableFunc(name, func(ctx PackageVarContext) string {
+ return ctx.Config().GetenvWithDefault(envVar, defaultVal)
+ })
+}
diff --git a/android/package_test.go b/android/package_test.go
index ade95d4..3bd30cc 100644
--- a/android/package_test.go
+++ b/android/package_test.go
@@ -6,7 +6,7 @@
var packageTests = []struct {
name string
- fs map[string][]byte
+ fs MockFS
expectedErrors []string
}{
// Package default_visibility handling is tested in visibility_test.go
@@ -17,9 +17,11 @@
package {
name: "package",
visibility: ["//visibility:private"],
+ licenses: ["license"],
}`),
},
expectedErrors: []string{
+ `top/Blueprints:5:14: unrecognized property "licenses"`,
`top/Blueprints:3:10: unrecognized property "name"`,
`top/Blueprints:4:16: unrecognized property "visibility"`,
},
@@ -44,9 +46,10 @@
"top/Blueprints": []byte(`
package {
default_visibility: ["//visibility:private"],
+ default_applicable_licenses: ["license"],
}
- package {
+ package {
}`),
},
expectedErrors: []string{
@@ -58,43 +61,13 @@
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)
- }
- }
- }
+ GroupFixturePreparers(
+ PrepareForTestWithArchMutator,
+ PrepareForTestWithPackageModule,
+ test.fs.AddToFixture(),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+ RunTest(t)
})
}
}
-
-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, "", fs)
-
- ctx := NewTestArchContext(config)
- RegisterPackageBuildComponents(ctx)
- ctx.Register()
-
- _, errs := ctx.ParseBlueprintsFiles(".")
- if len(errs) > 0 {
- return ctx, errs
- }
-
- _, errs = ctx.PrepareBuildActions(config)
- return ctx, errs
-}
diff --git a/android/packaging.go b/android/packaging.go
index da745ff..72c0c17 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -49,19 +49,25 @@
return ""
}
+// Path relative to the root of the package
+func (p *PackagingSpec) RelPathInPackage() string {
+ return p.relPathInPackage
+}
+
type PackageModule interface {
Module
packagingBase() *PackagingBase
// AddDeps adds dependencies to the `deps` modules. This should be called in DepsMutator.
- // When adding the dependencies, depTag is used as the tag.
+ // When adding the dependencies, depTag is used as the tag. If `deps` modules are meant to
+ // be copied to a zip in CopyDepsToZip, `depTag` should implement PackagingItem marker interface.
AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag)
// CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and
// returns zip entries in it. This is expected to be called in GenerateAndroidBuildActions,
// followed by a build rule that unzips it and creates the final output (img, zip, tar.gz,
// etc.) from the extracted files
- CopyDepsToZip(ctx ModuleContext, zipOut OutputPath) []string
+ CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) []string
}
// PackagingBase provides basic functionality for packaging dependencies. A module is expected to
@@ -87,9 +93,17 @@
Lib64 depsProperty `android:"arch_variant"`
}
+type packagingArchProperties struct {
+ Arm64 depsProperty
+ Arm depsProperty
+ X86_64 depsProperty
+ X86 depsProperty
+}
+
type PackagingProperties struct {
Deps []string `android:"arch_variant"`
Multilib packagingMultilibProperties `android:"arch_variant"`
+ Arch packagingArchProperties
}
func InitPackageModule(p PackageModule) {
@@ -116,6 +130,7 @@
} else if arch == Common {
ret = append(ret, p.properties.Multilib.Common.Deps...)
}
+
for i, t := range ctx.MultiTargets() {
if t.Arch.ArchType == arch {
ret = append(ret, p.properties.Deps...)
@@ -124,6 +139,20 @@
}
}
}
+
+ if ctx.Arch().ArchType == Common {
+ switch arch {
+ case Arm64:
+ ret = append(ret, p.properties.Arch.Arm64.Deps...)
+ case Arm:
+ ret = append(ret, p.properties.Arch.Arm.Deps...)
+ case X86_64:
+ ret = append(ret, p.properties.Arch.X86_64.Deps...)
+ case X86:
+ ret = append(ret, p.properties.Arch.X86.Deps...)
+ }
+ }
+
return FirstUniqueStrings(ret)
}
@@ -139,6 +168,24 @@
return ret
}
+// PackagingItem is a marker interface for dependency tags.
+// Direct dependencies with a tag implementing PackagingItem are packaged in CopyDepsToZip().
+type PackagingItem interface {
+ // IsPackagingItem returns true if the dep is to be packaged
+ IsPackagingItem() bool
+}
+
+// DepTag provides default implementation of PackagingItem interface.
+// PackagingBase-derived modules can define their own dependency tag by embedding this, which
+// can be passed to AddDeps() or AddDependencies().
+type PackagingItemAlwaysDepTag struct {
+}
+
+// IsPackagingItem returns true if the dep is to be packaged
+func (PackagingItemAlwaysDepTag) IsPackagingItem() bool {
+ return true
+}
+
// See PackageModule.AddDeps
func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) {
for _, t := range p.getSupportedTargets(ctx) {
@@ -152,23 +199,22 @@
}
// See PackageModule.CopyDepsToZip
-func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut OutputPath) (entries []string) {
+func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (entries []string) {
m := make(map[string]PackagingSpec)
- ctx.WalkDeps(func(child Module, parent Module) bool {
- if !IsInstallDepNeeded(ctx.OtherModuleDependencyTag(child)) {
- return false
+ ctx.VisitDirectDeps(func(child Module) {
+ if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() {
+ return
}
- for _, ps := range child.PackagingSpecs() {
+ for _, ps := range child.TransitivePackagingSpecs() {
if _, ok := m[ps.relPathInPackage]; !ok {
m[ps.relPathInPackage] = ps
}
}
- return true
})
builder := NewRuleBuilder(pctx, ctx)
- dir := PathForModuleOut(ctx, ".zip").OutputPath
+ dir := PathForModuleOut(ctx, ".zip")
builder.Command().Text("rm").Flag("-rf").Text(dir.String())
builder.Command().Text("mkdir").Flag("-p").Text(dir.String())
diff --git a/android/packaging_test.go b/android/packaging_test.go
index 7269bfb..f91dc5d 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -15,7 +15,6 @@
package android
import (
- "reflect"
"testing"
"github.com/google/blueprint"
@@ -57,52 +56,75 @@
type packageTestModule struct {
ModuleBase
PackagingBase
-
+ properties struct {
+ Install_deps []string `android:`
+ }
entries []string
}
+func packageMultiTargetTestModuleFactory() Module {
+ module := &packageTestModule{}
+ InitPackageModule(module)
+ InitAndroidMultiTargetsArchModule(module, DeviceSupported, MultilibCommon)
+ module.AddProperties(&module.properties)
+ return module
+}
+
func packageTestModuleFactory() Module {
module := &packageTestModule{}
InitPackageModule(module)
- InitAndroidMultiTargetsArchModule(module, DeviceSupported, MultilibCommon)
+ InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
+ module.AddProperties(&module.properties)
return module
}
+type packagingDepTag struct {
+ blueprint.BaseDependencyTag
+ PackagingItemAlwaysDepTag
+}
+
func (m *packageTestModule) DepsMutator(ctx BottomUpMutatorContext) {
- m.AddDeps(ctx, installDepTag{})
+ m.AddDeps(ctx, packagingDepTag{})
+ ctx.AddDependency(ctx.Module(), installDepTag{}, m.properties.Install_deps...)
}
func (m *packageTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
- zipFile := PathForModuleOut(ctx, "myzip.zip").OutputPath
+ zipFile := PathForModuleOut(ctx, "myzip.zip")
m.entries = m.CopyDepsToZip(ctx, zipFile)
}
-func runPackagingTest(t *testing.T, bp string, expected []string) {
+func runPackagingTest(t *testing.T, multitarget bool, bp string, expected []string) {
t.Helper()
- config := TestArchConfig(buildDir, nil, bp, nil)
+ var archVariant string
+ var moduleFactory ModuleFactory
+ if multitarget {
+ archVariant = "android_common"
+ moduleFactory = packageMultiTargetTestModuleFactory
+ } else {
+ archVariant = "android_arm64_armv8-a"
+ moduleFactory = packageTestModuleFactory
+ }
- ctx := NewTestArchContext(config)
- ctx.RegisterModuleType("component", componentTestModuleFactory)
- ctx.RegisterModuleType("package_module", packageTestModuleFactory)
- ctx.Register()
+ result := GroupFixturePreparers(
+ PrepareForTestWithArchMutator,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("component", componentTestModuleFactory)
+ ctx.RegisterModuleType("package_module", moduleFactory)
+ }),
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
-
- p := ctx.ModuleForTests("package", "android_common").Module().(*packageTestModule)
+ p := result.Module("package", archVariant).(*packageTestModule)
actual := p.entries
actual = SortedUniqueStrings(actual)
expected = SortedUniqueStrings(expected)
- if !reflect.DeepEqual(actual, expected) {
- t.Errorf("\ngot: %v\nexpected: %v\n", actual, expected)
- }
+ AssertDeepEquals(t, "package entries", expected, actual)
}
-func TestPackagingBase(t *testing.T) {
- runPackagingTest(t,
+func TestPackagingBaseMultiTarget(t *testing.T) {
+ multiTarget := true
+ runPackagingTest(t, multiTarget,
`
component {
name: "foo",
@@ -114,7 +136,7 @@
}
`, []string{"lib64/foo"})
- runPackagingTest(t,
+ runPackagingTest(t, multiTarget,
`
component {
name: "foo",
@@ -131,7 +153,7 @@
}
`, []string{"lib64/foo", "lib64/bar"})
- runPackagingTest(t,
+ runPackagingTest(t, multiTarget,
`
component {
name: "foo",
@@ -149,7 +171,7 @@
}
`, []string{"lib32/foo", "lib32/bar", "lib64/foo", "lib64/bar"})
- runPackagingTest(t,
+ runPackagingTest(t, multiTarget,
`
component {
name: "foo",
@@ -172,7 +194,7 @@
}
`, []string{"lib32/foo", "lib32/bar", "lib64/foo"})
- runPackagingTest(t,
+ runPackagingTest(t, multiTarget,
`
component {
name: "foo",
@@ -193,4 +215,153 @@
compile_multilib: "both",
}
`, []string{"lib32/foo", "lib64/foo", "lib64/bar"})
+
+ runPackagingTest(t, multiTarget,
+ `
+ component {
+ name: "foo",
+ }
+
+ component {
+ name: "bar",
+ }
+
+ component {
+ name: "baz",
+ }
+
+ package_module {
+ name: "package",
+ deps: ["foo"],
+ arch: {
+ arm64: {
+ deps: ["bar"],
+ },
+ x86_64: {
+ deps: ["baz"],
+ },
+ },
+ compile_multilib: "both",
+ }
+ `, []string{"lib32/foo", "lib64/foo", "lib64/bar"})
+}
+
+func TestPackagingBaseSingleTarget(t *testing.T) {
+ multiTarget := false
+ runPackagingTest(t, multiTarget,
+ `
+ component {
+ name: "foo",
+ }
+
+ package_module {
+ name: "package",
+ deps: ["foo"],
+ }
+ `, []string{"lib64/foo"})
+
+ runPackagingTest(t, multiTarget,
+ `
+ component {
+ name: "foo",
+ deps: ["bar"],
+ }
+
+ component {
+ name: "bar",
+ }
+
+ package_module {
+ name: "package",
+ deps: ["foo"],
+ }
+ `, []string{"lib64/foo", "lib64/bar"})
+
+ runPackagingTest(t, multiTarget,
+ `
+ component {
+ name: "foo",
+ }
+
+ component {
+ name: "bar",
+ compile_multilib: "32",
+ }
+
+ package_module {
+ name: "package",
+ deps: ["foo"],
+ multilib: {
+ lib32: {
+ deps: ["bar"],
+ },
+ },
+ }
+ `, []string{"lib64/foo"})
+
+ runPackagingTest(t, multiTarget,
+ `
+ component {
+ name: "foo",
+ }
+
+ component {
+ name: "bar",
+ }
+
+ package_module {
+ name: "package",
+ deps: ["foo"],
+ multilib: {
+ lib64: {
+ deps: ["bar"],
+ },
+ },
+ }
+ `, []string{"lib64/foo", "lib64/bar"})
+
+ runPackagingTest(t, multiTarget,
+ `
+ component {
+ name: "foo",
+ }
+
+ component {
+ name: "bar",
+ }
+
+ component {
+ name: "baz",
+ }
+
+ package_module {
+ name: "package",
+ deps: ["foo"],
+ arch: {
+ arm64: {
+ deps: ["bar"],
+ },
+ x86_64: {
+ deps: ["baz"],
+ },
+ },
+ }
+ `, []string{"lib64/foo", "lib64/bar"})
+
+ runPackagingTest(t, multiTarget,
+ `
+ component {
+ name: "foo",
+ }
+
+ component {
+ name: "bar",
+ }
+
+ package_module {
+ name: "package",
+ deps: ["foo"],
+ install_deps: ["bar"],
+ }
+ `, []string{"lib64/foo"})
}
diff --git a/android/path_properties.go b/android/path_properties.go
index 4bb706a..2c8d27c 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -76,52 +76,80 @@
var ret []string
for _, i := range pathPropertyIndexes {
- // Turn an index into a field.
- sv := fieldByIndex(v, i)
- if !sv.IsValid() {
- // Skip properties inside a nil pointer.
- continue
- }
-
- // If the field is a non-nil pointer step into it.
- if sv.Kind() == reflect.Ptr {
- if sv.IsNil() {
+ var values []reflect.Value
+ fieldsByIndex(v, i, &values)
+ for _, sv := range values {
+ if !sv.IsValid() {
+ // Skip properties inside a nil pointer.
continue
}
- sv = sv.Elem()
- }
- // Collect paths from all strings and slices of strings.
- switch sv.Kind() {
- case reflect.String:
- ret = append(ret, sv.String())
- case reflect.Slice:
- ret = append(ret, sv.Interface().([]string)...)
- default:
- panic(fmt.Errorf(`field %s in type %s has tag android:"path" but is not a string or slice of strings, it is a %s`,
- v.Type().FieldByIndex(i).Name, v.Type(), sv.Type()))
+ // If the field is a non-nil pointer step into it.
+ if sv.Kind() == reflect.Ptr {
+ if sv.IsNil() {
+ continue
+ }
+ sv = sv.Elem()
+ }
+
+ // Collect paths from all strings and slices of strings.
+ switch sv.Kind() {
+ case reflect.String:
+ ret = append(ret, sv.String())
+ case reflect.Slice:
+ ret = append(ret, sv.Interface().([]string)...)
+ default:
+ panic(fmt.Errorf(`field %s in type %s has tag android:"path" but is not a string or slice of strings, it is a %s`,
+ v.Type().FieldByIndex(i).Name, v.Type(), sv.Type()))
+ }
}
}
return ret
}
-// fieldByIndex is like reflect.Value.FieldByIndex, but returns an invalid reflect.Value when
-// traversing a nil pointer to a struct.
-func fieldByIndex(v reflect.Value, index []int) reflect.Value {
+// fieldsByIndex is similar to reflect.Value.FieldByIndex, but is more robust: it doesn't track
+// nil pointers and it returns multiple values when there's slice of struct.
+func fieldsByIndex(v reflect.Value, index []int, values *[]reflect.Value) {
+ // leaf case
if len(index) == 1 {
- return v.Field(index[0])
- }
- for _, x := range index {
- if v.Kind() == reflect.Ptr {
- if v.IsNil() {
- return reflect.Value{}
+ if isSliceOfStruct(v) {
+ for i := 0; i < v.Len(); i++ {
+ *values = append(*values, v.Index(i).Field(index[0]))
}
- v = v.Elem()
+ } else {
+ // Dereference it if it's a pointer.
+ if v.Kind() == reflect.Ptr {
+ if v.IsNil() {
+ return
+ }
+ v = v.Elem()
+ }
+ *values = append(*values, v.Field(index[0]))
}
- v = v.Field(x)
+ return
}
- return v
+
+ // recursion
+ if v.Kind() == reflect.Ptr {
+ // don't track nil pointer
+ if v.IsNil() {
+ return
+ }
+ v = v.Elem()
+ } else if isSliceOfStruct(v) {
+ // do the recursion for all elements
+ for i := 0; i < v.Len(); i++ {
+ fieldsByIndex(v.Index(i).Field(index[0]), index[1:], values)
+ }
+ return
+ }
+ fieldsByIndex(v.Field(index[0]), index[1:], values)
+ return
+}
+
+func isSliceOfStruct(v reflect.Value) bool {
+ return v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Struct
}
var pathPropertyIndexesCache OncePer
diff --git a/android/path_properties_test.go b/android/path_properties_test.go
index f964d9f..568f868 100644
--- a/android/path_properties_test.go
+++ b/android/path_properties_test.go
@@ -15,7 +15,6 @@
package android
import (
- "reflect"
"testing"
)
@@ -26,6 +25,9 @@
Bar []string `android:"path,arch_variant"`
Baz *string `android:"path"`
Qux string
+ V *struct {
+ W string `android:"path"`
+ }
}
// A second property struct with a duplicate property name
@@ -33,12 +35,21 @@
Foo string `android:"path"`
}
+ // nested slices of struct
+ props3 struct {
+ X []struct {
+ Y []struct {
+ Z []string `android:"path"`
+ }
+ }
+ }
+
sourceDeps []string
}
func pathDepsMutatorTestModuleFactory() Module {
module := &pathDepsMutatorTestModule{}
- module.AddProperties(&module.props, &module.props2)
+ module.AddProperties(&module.props, &module.props2, &module.props3)
InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
return module
}
@@ -73,8 +84,23 @@
bar: [":b"],
baz: ":c{.bar}",
qux: ":d",
+ x: [
+ {
+ y: [
+ {
+ z: [":x", ":y"],
+ },
+ {
+ z: [":z"],
+ },
+ ],
+ },
+ ],
+ v: {
+ w: ":w",
+ },
}`,
- deps: []string{"a", "b", "c"},
+ deps: []string{"a", "b", "c", "w", "x", "y", "z"},
},
{
name: "arch variant",
@@ -113,25 +139,36 @@
filegroup {
name: "d",
}
+
+ filegroup {
+ name: "w",
+ }
+
+ filegroup {
+ name: "x",
+ }
+
+ filegroup {
+ name: "y",
+ }
+
+ filegroup {
+ name: "z",
+ }
`
- config := TestArchConfig(buildDir, nil, bp, nil)
- ctx := NewTestArchContext(config)
+ result := GroupFixturePreparers(
+ PrepareForTestWithArchMutator,
+ PrepareForTestWithFilegroup,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("test", pathDepsMutatorTestModuleFactory)
+ }),
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
- ctx.RegisterModuleType("test", pathDepsMutatorTestModuleFactory)
- ctx.RegisterModuleType("filegroup", FileGroupFactory)
+ m := result.Module("foo", "android_arm64_armv8-a").(*pathDepsMutatorTestModule)
- ctx.Register()
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
-
- m := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*pathDepsMutatorTestModule)
-
- if g, w := m.sourceDeps, test.deps; !reflect.DeepEqual(g, w) {
- t.Errorf("want deps %q, got %q", w, g)
- }
+ AssertDeepEquals(t, "deps", test.deps, m.sourceDeps)
})
}
}
diff --git a/android/paths.go b/android/paths.go
index 10d8d0d..c303c38 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -15,6 +15,7 @@
package android
import (
+ "android/soong/bazel"
"fmt"
"io/ioutil"
"os"
@@ -51,6 +52,53 @@
func (NullPathContext) AddNinjaFileDeps(...string) {}
func (ctx NullPathContext) Config() Config { return ctx.config }
+// EarlyModulePathContext is a subset of EarlyModuleContext methods required by the
+// Path methods. These path methods can be called before any mutators have run.
+type EarlyModulePathContext interface {
+ PathContext
+ PathGlobContext
+
+ ModuleDir() string
+ ModuleErrorf(fmt string, args ...interface{})
+}
+
+var _ EarlyModulePathContext = ModuleContext(nil)
+
+// Glob globs files and directories matching globPattern relative to ModuleDir(),
+// paths in the excludes parameter will be omitted.
+func Glob(ctx EarlyModulePathContext, globPattern string, excludes []string) Paths {
+ ret, err := ctx.GlobWithDeps(globPattern, excludes)
+ if err != nil {
+ ctx.ModuleErrorf("glob: %s", err.Error())
+ }
+ return pathsForModuleSrcFromFullPath(ctx, ret, true)
+}
+
+// GlobFiles globs *only* files (not directories) matching globPattern relative to ModuleDir().
+// Paths in the excludes parameter will be omitted.
+func GlobFiles(ctx EarlyModulePathContext, globPattern string, excludes []string) Paths {
+ ret, err := ctx.GlobWithDeps(globPattern, excludes)
+ if err != nil {
+ ctx.ModuleErrorf("glob: %s", err.Error())
+ }
+ return pathsForModuleSrcFromFullPath(ctx, ret, false)
+}
+
+// ModuleWithDepsPathContext is a subset of *ModuleContext methods required by
+// the Path methods that rely on module dependencies having been resolved.
+type ModuleWithDepsPathContext interface {
+ EarlyModulePathContext
+ GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module
+}
+
+// ModuleMissingDepsPathContext is a subset of *ModuleContext methods required by
+// the Path methods that rely on module dependencies having been resolved and ability to report
+// missing dependency errors.
+type ModuleMissingDepsPathContext interface {
+ ModuleWithDepsPathContext
+ AddMissingDependencies(missingDeps []string)
+}
+
type ModuleInstallPathContext interface {
BaseModuleContext
@@ -59,6 +107,7 @@
InstallInSanitizerDir() bool
InstallInRamdisk() bool
InstallInVendorRamdisk() bool
+ InstallInDebugRamdisk() bool
InstallInRecovery() bool
InstallInRoot() bool
InstallBypassMake() bool
@@ -126,14 +175,41 @@
// example, Rel on a PathsForModuleSrc would return the path relative to the module source
// directory, and OutputPath.Join("foo").Rel() would return "foo".
Rel() string
+
+ // RelativeToTop returns a new path relative to the top, it is provided solely for use in tests.
+ //
+ // It is guaranteed to always return the same type as it is called on, e.g. if called on an
+ // InstallPath then the returned value can be converted to an InstallPath.
+ //
+ // A standard build has the following structure:
+ // ../top/
+ // out/ - make install files go here.
+ // out/soong - this is the buildDir passed to NewTestConfig()
+ // ... - the source files
+ //
+ // This function converts a path so that it appears relative to the ../top/ directory, i.e.
+ // * Make install paths, which have the pattern "buildDir/../<path>" are converted into the top
+ // relative path "out/<path>"
+ // * Soong install paths and other writable paths, which have the pattern "buildDir/<path>" are
+ // converted into the top relative path "out/soong/<path>".
+ // * Source paths are already relative to the top.
+ // * Phony paths are not relative to anything.
+ // * toolDepPath have an absolute but known value in so don't need making relative to anything in
+ // order to test.
+ RelativeToTop() Path
}
+const (
+ OutDir = "out"
+ OutSoongDir = OutDir + "/soong"
+)
+
// WritablePath is a type of path that can be used as an output for build rules.
type WritablePath interface {
Path
// return the path to the build directory.
- buildDir() string
+ getBuildDir() string
// the writablePath method doesn't directly do anything,
// but it allows a struct to distinguish between whether or not it implements the WritablePath interface
@@ -143,18 +219,18 @@
}
type genPathProvider interface {
- genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath
+ genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath
}
type objPathProvider interface {
- objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath
+ objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath
}
type resPathProvider interface {
- resPathWithName(ctx ModuleContext, name string) ModuleResPath
+ resPathWithName(ctx ModuleOutPathContext, name string) ModuleResPath
}
// GenPathWithExt derives a new file path in ctx's generated sources directory
// from the current path, but with the new extension.
-func GenPathWithExt(ctx ModuleContext, subdir string, p Path, ext string) ModuleGenPath {
+func GenPathWithExt(ctx ModuleOutPathContext, subdir string, p Path, ext string) ModuleGenPath {
if path, ok := p.(genPathProvider); ok {
return path.genPathWithExt(ctx, subdir, ext)
}
@@ -164,7 +240,7 @@
// ObjPathWithExt derives a new file path in ctx's object directory from the
// current path, but with the new extension.
-func ObjPathWithExt(ctx ModuleContext, subdir string, p Path, ext string) ModuleObjPath {
+func ObjPathWithExt(ctx ModuleOutPathContext, subdir string, p Path, ext string) ModuleObjPath {
if path, ok := p.(objPathProvider); ok {
return path.objPathWithExt(ctx, subdir, ext)
}
@@ -175,7 +251,7 @@
// ResPathWithName derives a new path in ctx's output resource directory, using
// the current path to create the directory name, and the `name` argument for
// the filename.
-func ResPathWithName(ctx ModuleContext, p Path, name string) ModuleResPath {
+func ResPathWithName(ctx ModuleOutPathContext, p Path, name string) ModuleResPath {
if path, ok := p.(resPathProvider); ok {
return path.resPathWithName(ctx, name)
}
@@ -211,6 +287,16 @@
return p.path
}
+// RelativeToTop returns an OptionalPath with the path that was embedded having been replaced by the
+// result of calling Path.RelativeToTop on it.
+func (p OptionalPath) RelativeToTop() OptionalPath {
+ if !p.valid {
+ return p
+ }
+ p.path = p.path.RelativeToTop()
+ return p
+}
+
// String returns the string version of the Path, or "" if it isn't valid.
func (p OptionalPath) String() string {
if p.valid {
@@ -223,6 +309,20 @@
// Paths is a slice of Path objects, with helpers to operate on the collection.
type Paths []Path
+// RelativeToTop creates a new Paths containing the result of calling Path.RelativeToTop on each
+// item in this slice.
+func (p Paths) RelativeToTop() Paths {
+ ensureTestOnly()
+ if p == nil {
+ return p
+ }
+ ret := make(Paths, len(p))
+ for i, path := range p {
+ ret[i] = path.RelativeToTop()
+ }
+ return ret
+}
+
func (paths Paths) containsPath(path Path) bool {
for _, p := range paths {
if p == path {
@@ -232,7 +332,8 @@
return false
}
-// PathsForSource returns Paths rooted from SrcDir
+// PathsForSource returns Paths rooted from SrcDir, *not* rooted from the module's local source
+// directory
func PathsForSource(ctx PathContext, paths []string) Paths {
ret := make(Paths, len(paths))
for i, path := range paths {
@@ -241,9 +342,9 @@
return ret
}
-// ExistentPathsForSources returns a list of Paths rooted from SrcDir that are
-// found in the tree. If any are not found, they are omitted from the list,
-// and dependencies are added so that we're re-run when they are added.
+// ExistentPathsForSources returns a list of Paths rooted from SrcDir, *not* rooted from the
+// module's local source directory, that are found in the tree. If any are not found, they are
+// omitted from the list, and dependencies are added so that we're re-run when they are added.
func ExistentPathsForSources(ctx PathContext, paths []string) Paths {
ret := make(Paths, 0, len(paths))
for _, path := range paths {
@@ -261,7 +362,7 @@
// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
// path_properties mutator. If ctx.Config().AllowMissingDependencies() is true then any missing SourceFileProducer or
// OutputFileProducer dependencies will cause the module to be marked as having missing dependencies.
-func PathsForModuleSrc(ctx ModuleContext, paths []string) Paths {
+func PathsForModuleSrc(ctx ModuleMissingDepsPathContext, paths []string) Paths {
return PathsForModuleSrcExcludes(ctx, paths, nil)
}
@@ -272,7 +373,7 @@
// will have already been handled by the path_properties mutator. If ctx.Config().AllowMissingDependencies() is
// true then any missing SourceFileProducer or OutputFileProducer dependencies will cause the module to be marked as
// having missing dependencies.
-func PathsForModuleSrcExcludes(ctx ModuleContext, paths, excludes []string) Paths {
+func PathsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) Paths {
ret, missingDeps := PathsAndMissingDepsForModuleSrcExcludes(ctx, paths, excludes)
if ctx.Config().AllowMissingDependencies() {
ctx.AddMissingDependencies(missingDeps)
@@ -284,6 +385,247 @@
return ret
}
+// A subset of the ModuleContext methods which are sufficient to resolve references to paths/deps in
+// order to form a Bazel-compatible label for conversion.
+type BazelConversionPathContext interface {
+ EarlyModulePathContext
+
+ GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
+ Module() Module
+ ModuleType() string
+ OtherModuleName(m blueprint.Module) string
+ OtherModuleDir(m blueprint.Module) string
+}
+
+// BazelLabelForModuleDeps returns a Bazel-compatible label for the requested modules which
+// correspond to dependencies on the module within the given ctx.
+func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
+ var labels bazel.LabelList
+ for _, module := range modules {
+ bpText := module
+ if m := SrcIsModule(module); m == "" {
+ module = ":" + module
+ }
+ if m, t := SrcIsModuleWithTag(module); m != "" {
+ l := getOtherModuleLabel(ctx, m, t)
+ l.Bp_text = bpText
+ labels.Includes = append(labels.Includes, l)
+ } else {
+ ctx.ModuleErrorf("%q, is not a module reference", module)
+ }
+ }
+ return labels
+}
+
+// Returns true if a prefix + components[:i] + /Android.bp exists
+// TODO(b/185358476) Could check for BUILD file instead of checking for Android.bp file, or ensure BUILD is always generated?
+func directoryHasBlueprint(fs pathtools.FileSystem, prefix string, components []string, componentIndex int) bool {
+ blueprintPath := prefix
+ if blueprintPath != "" {
+ blueprintPath = blueprintPath + "/"
+ }
+ blueprintPath = blueprintPath + strings.Join(components[:componentIndex+1], "/")
+ blueprintPath = blueprintPath + "/Android.bp"
+ if exists, _, _ := fs.Exists(blueprintPath); exists {
+ return true
+ } else {
+ return false
+ }
+}
+
+// Transform a path (if necessary) to acknowledge package boundaries
+//
+// e.g. something like
+// async_safe/include/async_safe/CHECK.h
+// might become
+// //bionic/libc/async_safe:include/async_safe/CHECK.h
+// if the "async_safe" directory is actually a package and not just a directory.
+//
+// In particular, paths that extend into packages are transformed into absolute labels beginning with //.
+func transformSubpackagePath(ctx BazelConversionPathContext, path bazel.Label) bazel.Label {
+ var newPath bazel.Label
+
+ // Don't transform Bp_text
+ newPath.Bp_text = path.Bp_text
+
+ if strings.HasPrefix(path.Label, "//") {
+ // Assume absolute labels are already correct (e.g. //path/to/some/package:foo.h)
+ newPath.Label = path.Label
+ return newPath
+ }
+
+ newLabel := ""
+ pathComponents := strings.Split(path.Label, "/")
+ foundBlueprint := false
+ // Check the deepest subdirectory first and work upwards
+ for i := len(pathComponents) - 1; i >= 0; i-- {
+ pathComponent := pathComponents[i]
+ var sep string
+ if !foundBlueprint && directoryHasBlueprint(ctx.Config().fs, ctx.ModuleDir(), pathComponents, i) {
+ sep = ":"
+ foundBlueprint = true
+ } else {
+ sep = "/"
+ }
+ if newLabel == "" {
+ newLabel = pathComponent
+ } else {
+ newLabel = pathComponent + sep + newLabel
+ }
+ }
+ if foundBlueprint {
+ // Ensure paths end up looking like //bionic/... instead of //./bionic/...
+ moduleDir := ctx.ModuleDir()
+ if strings.HasPrefix(moduleDir, ".") {
+ moduleDir = moduleDir[1:]
+ }
+ // Make the path into an absolute label (e.g. //bionic/libc/foo:bar.h instead of just foo:bar.h)
+ if moduleDir == "" {
+ newLabel = "//" + newLabel
+ } else {
+ newLabel = "//" + moduleDir + "/" + newLabel
+ }
+ }
+ newPath.Label = newLabel
+
+ return newPath
+}
+
+// Transform paths to acknowledge package boundaries
+// See transformSubpackagePath() for more information
+func transformSubpackagePaths(ctx BazelConversionPathContext, paths bazel.LabelList) bazel.LabelList {
+ var newPaths bazel.LabelList
+ for _, include := range paths.Includes {
+ newPaths.Includes = append(newPaths.Includes, transformSubpackagePath(ctx, include))
+ }
+ for _, exclude := range paths.Excludes {
+ newPaths.Excludes = append(newPaths.Excludes, transformSubpackagePath(ctx, exclude))
+ }
+ return newPaths
+}
+
+// BazelLabelForModuleSrc returns bazel.LabelList with paths rooted from the module's local source
+// directory. It expands globs, and resolves references to modules using the ":name" syntax to
+// bazel-compatible labels. Properties passed as the paths or excludes argument must have been
+// annotated with struct tag `android:"path"` so that dependencies on other modules will have
+// already been handled by the path_properties mutator.
+//
+// With expanded globs, we can catch package boundaries problem instead of
+// silently failing to potentially missing files from Bazel's globs.
+func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList {
+ return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil))
+}
+
+// BazelLabelForModuleSrcExcludes returns bazel.LabelList with paths rooted from the module's local
+// source directory, excluding labels included in the excludes argument. It expands globs, and
+// resolves references to modules using the ":name" syntax to bazel-compatible labels. Properties
+// passed as the paths or excludes argument must have been annotated with struct tag
+// `android:"path"` so that dependencies on other modules will have already been handled by the
+// path_properties mutator.
+//
+// With expanded globs, we can catch package boundaries problem instead of
+// silently failing to potentially missing files from Bazel's globs.
+func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList {
+ excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil))
+ excluded := make([]string, 0, len(excludeLabels.Includes))
+ for _, e := range excludeLabels.Includes {
+ excluded = append(excluded, e.Label)
+ }
+ labels := expandSrcsForBazel(ctx, paths, excluded)
+ labels.Excludes = excludeLabels.Includes
+ labels = transformSubpackagePaths(ctx, labels)
+ return labels
+}
+
+// expandSrcsForBazel returns bazel.LabelList with paths rooted from the module's local
+// source directory, excluding labels included in the excludes argument. It expands globs, and
+// resolves references to modules using the ":name" syntax to bazel-compatible labels. Properties
+// passed as the paths or excludes argument must have been annotated with struct tag
+// `android:"path"` so that dependencies on other modules will have already been handled by the
+// path_properties mutator.
+func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList {
+ if paths == nil {
+ return bazel.LabelList{}
+ }
+ labels := bazel.LabelList{
+ Includes: []bazel.Label{},
+ }
+ for _, p := range paths {
+ if m, tag := SrcIsModuleWithTag(p); m != "" {
+ l := getOtherModuleLabel(ctx, m, tag)
+ if !InList(l.Label, expandedExcludes) {
+ l.Bp_text = fmt.Sprintf(":%s", m)
+ labels.Includes = append(labels.Includes, l)
+ }
+ } else {
+ var expandedPaths []bazel.Label
+ if pathtools.IsGlob(p) {
+ globbedPaths := GlobFiles(ctx, pathForModuleSrc(ctx, p).String(), expandedExcludes)
+ globbedPaths = PathsWithModuleSrcSubDir(ctx, globbedPaths, "")
+ for _, path := range globbedPaths {
+ s := path.Rel()
+ expandedPaths = append(expandedPaths, bazel.Label{Label: s})
+ }
+ } else {
+ if !InList(p, expandedExcludes) {
+ expandedPaths = append(expandedPaths, bazel.Label{Label: p})
+ }
+ }
+ labels.Includes = append(labels.Includes, expandedPaths...)
+ }
+ }
+ return labels
+}
+
+// getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the
+// module. The label will be relative to the current directory if appropriate. The dependency must
+// already be resolved by either deps mutator or path deps mutator.
+func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string) bazel.Label {
+ m, _ := ctx.GetDirectDep(dep)
+ if m == nil {
+ panic(fmt.Errorf("cannot get direct dep %s of %s", dep, ctx.Module().Name()))
+ }
+ otherLabel := bazelModuleLabel(ctx, m, tag)
+ label := bazelModuleLabel(ctx, ctx.Module(), "")
+ if samePackage(label, otherLabel) {
+ otherLabel = bazelShortLabel(otherLabel)
+ }
+
+ return bazel.Label{
+ Label: otherLabel,
+ }
+}
+
+func bazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module, tag string) string {
+ // TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
+ b, ok := module.(Bazelable)
+ // TODO(b/181155349): perhaps return an error here if the module can't be/isn't being converted
+ if !ok || !b.ConvertedToBazel(ctx) {
+ return bp2buildModuleLabel(ctx, module)
+ }
+ return b.GetBazelLabel(ctx, module)
+}
+
+func bazelShortLabel(label string) string {
+ i := strings.Index(label, ":")
+ return label[i:]
+}
+
+func bazelPackage(label string) string {
+ i := strings.Index(label, ":")
+ return label[0:i]
+}
+
+func samePackage(label1, label2 string) bool {
+ return bazelPackage(label1) == bazelPackage(label2)
+}
+
+func bp2buildModuleLabel(ctx BazelConversionPathContext, module blueprint.Module) string {
+ moduleName := ctx.OtherModuleName(module)
+ moduleDir := ctx.OtherModuleDir(module)
+ return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
+}
+
// OutputPaths is a slice of OutputPath objects, with helpers to operate on the collection.
type OutputPaths []OutputPath
@@ -311,6 +653,32 @@
return ret
}
+// Expands Paths to a SourceFileProducer or OutputFileProducer module dependency referenced via ":name" or ":name{.tag}" syntax.
+// If the dependency is not found, a missingErrorDependency is returned.
+// If the module dependency is not a SourceFileProducer or OutputFileProducer, appropriate errors will be returned.
+func getPathsFromModuleDep(ctx ModuleWithDepsPathContext, path, moduleName, tag string) (Paths, error) {
+ module := ctx.GetDirectDepWithTag(moduleName, sourceOrOutputDepTag(tag))
+ if module == nil {
+ return nil, missingDependencyError{[]string{moduleName}}
+ }
+ if aModule, ok := module.(Module); ok && !aModule.Enabled() {
+ return nil, missingDependencyError{[]string{moduleName}}
+ }
+ if outProducer, ok := module.(OutputFileProducer); ok {
+ outputFiles, err := outProducer.OutputFiles(tag)
+ if err != nil {
+ return nil, fmt.Errorf("path dependency %q: %s", path, err)
+ }
+ return outputFiles, nil
+ } else if tag != "" {
+ return nil, fmt.Errorf("path dependency %q is not an output file producing module", path)
+ } else if srcProducer, ok := module.(SourceFileProducer); ok {
+ return srcProducer.Srcs(), nil
+ } else {
+ return nil, fmt.Errorf("path dependency %q is not a source file producing module", path)
+ }
+}
+
// PathsAndMissingDepsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding
// paths listed in the excludes arguments, and a list of missing dependencies. It expands globs, references to
// SourceFileProducer modules using the ":name" syntax, and references to OutputFileProducer modules using the
@@ -319,7 +687,7 @@
// path_properties mutator. If ctx.Config().AllowMissingDependencies() is true then any missing SourceFileProducer or
// OutputFileProducer dependencies will be returned, and they will NOT cause the module to be marked as having missing
// dependencies.
-func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleContext, paths, excludes []string) (Paths, []string) {
+func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleWithDepsPathContext, paths, excludes []string) (Paths, []string) {
prefix := pathForModuleSrc(ctx).String()
var expandedExcludes []string
@@ -331,23 +699,13 @@
for _, e := range excludes {
if m, t := SrcIsModuleWithTag(e); m != "" {
- module := ctx.GetDirectDepWithTag(m, sourceOrOutputDepTag(t))
- if module == nil {
- missingExcludeDeps = append(missingExcludeDeps, m)
- continue
- }
- if outProducer, ok := module.(OutputFileProducer); ok {
- outputFiles, err := outProducer.OutputFiles(t)
- if err != nil {
- ctx.ModuleErrorf("path dependency %q: %s", e, err)
- }
- expandedExcludes = append(expandedExcludes, outputFiles.Strings()...)
- } else if t != "" {
- ctx.ModuleErrorf("path dependency %q is not an output file producing module", e)
- } else if srcProducer, ok := module.(SourceFileProducer); ok {
- expandedExcludes = append(expandedExcludes, srcProducer.Srcs().Strings()...)
+ modulePaths, err := getPathsFromModuleDep(ctx, e, m, t)
+ if m, ok := err.(missingDependencyError); ok {
+ missingExcludeDeps = append(missingExcludeDeps, m.missingDeps...)
+ } else if err != nil {
+ reportPathError(ctx, err)
} else {
- ctx.ModuleErrorf("path dependency %q is not a source file producing module", e)
+ expandedExcludes = append(expandedExcludes, modulePaths.Strings()...)
}
} else {
expandedExcludes = append(expandedExcludes, filepath.Join(prefix, e))
@@ -382,7 +740,10 @@
return "missing dependencies: " + strings.Join(e.missingDeps, ", ")
}
-func expandOneSrcPath(ctx ModuleContext, s string, expandedExcludes []string) (Paths, error) {
+// Expands one path string to Paths rooted from the module's local source
+// directory, excluding those listed in the expandedExcludes.
+// Expands globs, references to SourceFileProducer or OutputFileProducer modules using the ":name" and ":name{.tag}" syntax.
+func expandOneSrcPath(ctx ModuleWithDepsPathContext, sPath string, expandedExcludes []string) (Paths, error) {
excludePaths := func(paths Paths) Paths {
if len(expandedExcludes) == 0 {
return paths
@@ -395,32 +756,21 @@
}
return remainder
}
- if m, t := SrcIsModuleWithTag(s); m != "" {
- module := ctx.GetDirectDepWithTag(m, sourceOrOutputDepTag(t))
- if module == nil {
- return nil, missingDependencyError{[]string{m}}
- }
- if outProducer, ok := module.(OutputFileProducer); ok {
- outputFiles, err := outProducer.OutputFiles(t)
- if err != nil {
- return nil, fmt.Errorf("path dependency %q: %s", s, err)
- }
- return excludePaths(outputFiles), nil
- } else if t != "" {
- return nil, fmt.Errorf("path dependency %q is not an output file producing module", s)
- } else if srcProducer, ok := module.(SourceFileProducer); ok {
- return excludePaths(srcProducer.Srcs()), nil
+ if m, t := SrcIsModuleWithTag(sPath); m != "" {
+ modulePaths, err := getPathsFromModuleDep(ctx, sPath, m, t)
+ if err != nil {
+ return nil, err
} else {
- return nil, fmt.Errorf("path dependency %q is not a source file producing module", s)
+ return excludePaths(modulePaths), nil
}
- } else if pathtools.IsGlob(s) {
- paths := ctx.GlobFiles(pathForModuleSrc(ctx, s).String(), expandedExcludes)
+ } else if pathtools.IsGlob(sPath) {
+ paths := GlobFiles(ctx, pathForModuleSrc(ctx, sPath).String(), expandedExcludes)
return PathsWithModuleSrcSubDir(ctx, paths, ""), nil
} else {
- p := pathForModuleSrc(ctx, s)
+ p := pathForModuleSrc(ctx, sPath)
if exists, _, err := ctx.Config().fs.Exists(p.String()); err != nil {
ReportPathErrorf(ctx, "%s: %s", p, err.Error())
- } else if !exists && !ctx.Config().testAllowNonExistentPaths {
+ } else if !exists && !ctx.Config().TestAllowNonExistentPaths {
ReportPathErrorf(ctx, "module source path %q does not exist", p)
}
@@ -436,7 +786,7 @@
// each string. If incDirs is false, strip paths with a trailing '/' from the list.
// It intended for use in globs that only list files that exist, so it allows '$' in
// filenames.
-func pathsForModuleSrcFromFullPath(ctx EarlyModuleContext, paths []string, incDirs bool) Paths {
+func pathsForModuleSrcFromFullPath(ctx EarlyModulePathContext, paths []string, incDirs bool) Paths {
prefix := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir()) + "/"
if prefix == "./" {
prefix = ""
@@ -465,16 +815,16 @@
return ret
}
-// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's
-// local source directory. If input is nil, use the default if it exists. If input is empty, returns nil.
-func PathsWithOptionalDefaultForModuleSrc(ctx ModuleContext, input []string, def string) Paths {
+// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's local source
+// directory. If input is nil, use the default if it exists. If input is empty, returns nil.
+func PathsWithOptionalDefaultForModuleSrc(ctx ModuleMissingDepsPathContext, input []string, def string) Paths {
if input != nil {
return PathsForModuleSrc(ctx, input)
}
// Use Glob so that if the default doesn't exist, a dependency is added so that when it
// is created, we're run again.
path := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir(), def)
- return ctx.Glob(path, nil)
+ return Glob(ctx, path, nil)
}
// Strings returns the Paths in string form
@@ -709,6 +1059,20 @@
// WritablePaths is a slice of WritablePath, used for multiple outputs.
type WritablePaths []WritablePath
+// RelativeToTop creates a new WritablePaths containing the result of calling Path.RelativeToTop on
+// each item in this slice.
+func (p WritablePaths) RelativeToTop() WritablePaths {
+ ensureTestOnly()
+ if p == nil {
+ return p
+ }
+ ret := make(WritablePaths, len(p))
+ for i, path := range p {
+ ret[i] = path.RelativeToTop().(WritablePath)
+ }
+ return ret
+}
+
// Strings returns the string forms of the writable paths.
func (p WritablePaths) Strings() []string {
if p == nil {
@@ -734,9 +1098,8 @@
}
type basePath struct {
- path string
- config Config
- rel string
+ path string
+ rel string
}
func (p basePath) Ext() string {
@@ -767,6 +1130,14 @@
// SourcePath is a Path representing a file path rooted from SrcDir
type SourcePath struct {
basePath
+
+ // The sources root, i.e. Config.SrcDir()
+ srcDir string
+}
+
+func (p SourcePath) RelativeToTop() Path {
+ ensureTestOnly()
+ return p
}
var _ Path = SourcePath{}
@@ -780,7 +1151,7 @@
// code that is embedding ninja variables in paths
func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
p, err := validateSafePath(pathComponents...)
- ret := SourcePath{basePath{p, ctx.Config(), ""}}
+ ret := SourcePath{basePath{p, ""}, ctx.Config().srcDir}
if err != nil {
return ret, err
}
@@ -796,7 +1167,7 @@
// pathForSource creates a SourcePath from pathComponents, but does not check that it exists.
func pathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
p, err := validatePath(pathComponents...)
- ret := SourcePath{basePath{p, ctx.Config(), ""}}
+ ret := SourcePath{basePath{p, ""}, ctx.Config().srcDir}
if err != nil {
return ret, err
}
@@ -819,11 +1190,12 @@
// a single file.
files, err = gctx.GlobWithDeps(path.String(), nil)
} else {
- var deps []string
+ var result pathtools.GlobResult
// We cannot add build statements in this context, so we fall back to
// AddNinjaFileDeps
- files, deps, err = ctx.Config().fs.Glob(path.String(), nil, pathtools.FollowSymlinks)
- ctx.AddNinjaFileDeps(deps...)
+ result, err = ctx.Config().fs.Glob(path.String(), nil, pathtools.FollowSymlinks)
+ ctx.AddNinjaFileDeps(result.Deps...)
+ files = result.Matches
}
if err != nil {
@@ -846,7 +1218,7 @@
ReportPathErrorf(ctx, "path may not contain a glob: %s", path.String())
}
- if modCtx, ok := ctx.(ModuleContext); ok && ctx.Config().AllowMissingDependencies() {
+ if modCtx, ok := ctx.(ModuleMissingDepsPathContext); ok && ctx.Config().AllowMissingDependencies() {
exists, err := existsWithDependencies(ctx, path)
if err != nil {
reportPathError(ctx, err)
@@ -856,15 +1228,16 @@
}
} else if exists, _, err := ctx.Config().fs.Exists(path.String()); err != nil {
ReportPathErrorf(ctx, "%s: %s", path, err.Error())
- } else if !exists && !ctx.Config().testAllowNonExistentPaths {
+ } else if !exists && !ctx.Config().TestAllowNonExistentPaths {
ReportPathErrorf(ctx, "source path %q does not exist", path)
}
return path
}
-// ExistentPathForSource returns an OptionalPath with the SourcePath if the
-// path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added
-// so that the ninja file will be regenerated if the state of the path changes.
+// ExistentPathForSource returns an OptionalPath with the SourcePath, rooted from SrcDir, *not*
+// rooted from the module's local source directory, if the path exists, or an empty OptionalPath if
+// it doesn't exist. Dependencies are added so that the ninja file will be regenerated if the state
+// of the path changes.
func ExistentPathForSource(ctx PathContext, pathComponents ...string) OptionalPath {
path, err := pathForSource(ctx, pathComponents...)
if err != nil {
@@ -889,7 +1262,7 @@
}
func (p SourcePath) String() string {
- return filepath.Join(p.config.srcDir, p.path)
+ return filepath.Join(p.srcDir, p.path)
}
// Join creates a new SourcePath with paths... joined with the current path. The
@@ -913,7 +1286,7 @@
// OverlayPath returns the overlay for `path' if it exists. This assumes that the
// SourcePath is the path to a resource overlay directory.
-func (p SourcePath) OverlayPath(ctx ModuleContext, path Path) OptionalPath {
+func (p SourcePath) OverlayPath(ctx ModuleMissingDepsPathContext, path Path) OptionalPath {
var relDir string
if srcPath, ok := path.(SourcePath); ok {
relDir = srcPath.path
@@ -921,7 +1294,7 @@
ReportPathErrorf(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
return OptionalPath{}
}
- dir := filepath.Join(p.config.srcDir, p.path, relDir)
+ dir := filepath.Join(p.srcDir, p.path, relDir)
// Use Glob so that we are run again if the directory is added.
if pathtools.IsGlob(dir) {
ReportPathErrorf(ctx, "Path may not contain a glob: %s", dir)
@@ -934,13 +1307,17 @@
if len(paths) == 0 {
return OptionalPath{}
}
- relPath := Rel(ctx, p.config.srcDir, paths[0])
+ relPath := Rel(ctx, p.srcDir, paths[0])
return OptionalPathForPath(PathForSource(ctx, relPath))
}
// OutputPath is a Path representing an intermediates file path rooted from the build directory
type OutputPath struct {
basePath
+
+ // The soong build directory, i.e. Config.BuildDir()
+ buildDir string
+
fullPath string
}
@@ -955,18 +1332,38 @@
return p
}
-func (p OutputPath) buildDir() string {
- return p.config.buildDir
+func (p OutputPath) getBuildDir() string {
+ return p.buildDir
+}
+
+func (p OutputPath) RelativeToTop() Path {
+ return p.outputPathRelativeToTop()
+}
+
+func (p OutputPath) outputPathRelativeToTop() OutputPath {
+ p.fullPath = StringPathRelativeToTop(p.buildDir, p.fullPath)
+ p.buildDir = OutSoongDir
+ return p
+}
+
+func (p OutputPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
+ return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
}
var _ Path = OutputPath{}
var _ WritablePath = OutputPath{}
+var _ objPathProvider = OutputPath{}
// toolDepPath is a Path representing a dependency of the build tool.
type toolDepPath struct {
basePath
}
+func (t toolDepPath) RelativeToTop() Path {
+ ensureTestOnly()
+ return t
+}
+
var _ Path = toolDepPath{}
// pathForBuildToolDep returns a toolDepPath representing the given path string.
@@ -975,7 +1372,7 @@
// Only use this function to construct paths for dependencies of the build
// tool invocation.
func pathForBuildToolDep(ctx PathContext, path string) toolDepPath {
- return toolDepPath{basePath{path, ctx.Config(), ""}}
+ return toolDepPath{basePath{path, ""}}
}
// PathForOutput joins the provided paths and returns an OutputPath that is
@@ -988,7 +1385,7 @@
}
fullPath := filepath.Join(ctx.Config().buildDir, path)
path = fullPath[len(fullPath)-len(path):]
- return OutputPath{basePath{path, ctx.Config(), ""}, fullPath}
+ return OutputPath{basePath{path, ""}, ctx.Config().buildDir, fullPath}
}
// PathsForOutput returns Paths rooted from buildDir
@@ -1054,7 +1451,7 @@
// PathForModuleSrc returns a Path representing the paths... under the
// module's local source directory.
-func PathForModuleSrc(ctx ModuleContext, pathComponents ...string) Path {
+func PathForModuleSrc(ctx ModuleMissingDepsPathContext, pathComponents ...string) Path {
p, err := validatePath(pathComponents...)
if err != nil {
reportPathError(ctx, err)
@@ -1080,7 +1477,7 @@
return paths[0]
}
-func pathForModuleSrc(ctx ModuleContext, paths ...string) SourcePath {
+func pathForModuleSrc(ctx EarlyModulePathContext, paths ...string) SourcePath {
p, err := validatePath(paths...)
if err != nil {
reportPathError(ctx, err)
@@ -1099,7 +1496,7 @@
// PathsWithModuleSrcSubDir takes a list of Paths and returns a new list of Paths where Rel() on each path
// will return the path relative to subDir in the module's source directory. If any input paths are not located
// inside subDir then a path error will be reported.
-func PathsWithModuleSrcSubDir(ctx ModuleContext, paths Paths, subDir string) Paths {
+func PathsWithModuleSrcSubDir(ctx EarlyModulePathContext, paths Paths, subDir string) Paths {
paths = append(Paths(nil), paths...)
subDirFullPath := pathForModuleSrc(ctx, subDir)
for i, path := range paths {
@@ -1111,7 +1508,7 @@
// PathWithModuleSrcSubDir takes a Path and returns a Path where Rel() will return the path relative to subDir in the
// module's source directory. If the input path is not located inside subDir then a path error will be reported.
-func PathWithModuleSrcSubDir(ctx ModuleContext, path Path, subDir string) Path {
+func PathWithModuleSrcSubDir(ctx EarlyModulePathContext, path Path, subDir string) Path {
subDirFullPath := pathForModuleSrc(ctx, subDir)
rel := Rel(ctx, subDirFullPath.String(), path.String())
return subDirFullPath.Join(ctx, rel)
@@ -1119,22 +1516,22 @@
// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
// valid path if p is non-nil.
-func OptionalPathForModuleSrc(ctx ModuleContext, p *string) OptionalPath {
+func OptionalPathForModuleSrc(ctx ModuleMissingDepsPathContext, p *string) OptionalPath {
if p == nil {
return OptionalPath{}
}
return OptionalPathForPath(PathForModuleSrc(ctx, *p))
}
-func (p SourcePath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
+func (p SourcePath) genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath {
return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
}
-func (p SourcePath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
+func (p SourcePath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
}
-func (p SourcePath) resPathWithName(ctx ModuleContext, name string) ModuleResPath {
+func (p SourcePath) resPathWithName(ctx ModuleOutPathContext, name string) ModuleResPath {
// TODO: Use full directory if the new ctx is not the current ctx?
return PathForModuleRes(ctx, p.path, name)
}
@@ -1144,13 +1541,28 @@
OutputPath
}
-var _ Path = ModuleOutPath{}
+func (p ModuleOutPath) RelativeToTop() Path {
+ p.OutputPath = p.outputPathRelativeToTop()
+ return p
+}
-func (p ModuleOutPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
+var _ Path = ModuleOutPath{}
+var _ WritablePath = ModuleOutPath{}
+
+func (p ModuleOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
}
-func pathForModule(ctx ModuleContext) OutputPath {
+// ModuleOutPathContext Subset of ModuleContext functions necessary for output path methods.
+type ModuleOutPathContext interface {
+ PathContext
+
+ ModuleName() string
+ ModuleDir() string
+ ModuleSubDir() string
+}
+
+func pathForModuleOut(ctx ModuleOutPathContext) OutputPath {
return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
}
@@ -1161,13 +1573,13 @@
var _ Path = BazelOutPath{}
var _ objPathProvider = BazelOutPath{}
-func (p BazelOutPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
+func (p BazelOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
}
// PathForVndkRefAbiDump returns an OptionalPath representing the path of the
// reference abi dump for the given module. This is not guaranteed to be valid.
-func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string,
+func PathForVndkRefAbiDump(ctx ModuleInstallPathContext, version, fileName string,
isNdk, isLlndkOrVndk, isGzip bool) OptionalPath {
arches := ctx.DeviceConfig().Arches()
@@ -1213,7 +1625,8 @@
reportPathError(ctx, err)
}
- outputPath := OutputPath{basePath{"", ctx.Config(), ""},
+ outputPath := OutputPath{basePath{"", ""},
+ ctx.Config().buildDir,
ctx.Config().BazelContext.OutputBase()}
return BazelOutPath{
@@ -1223,13 +1636,13 @@
// PathForModuleOut returns a Path representing the paths... under the module's
// output directory.
-func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath {
+func PathForModuleOut(ctx ModuleOutPathContext, paths ...string) ModuleOutPath {
p, err := validatePath(paths...)
if err != nil {
reportPathError(ctx, err)
}
return ModuleOutPath{
- OutputPath: pathForModule(ctx).withRel(p),
+ OutputPath: pathForModuleOut(ctx).withRel(p),
}
}
@@ -1239,30 +1652,36 @@
ModuleOutPath
}
+func (p ModuleGenPath) RelativeToTop() Path {
+ p.OutputPath = p.outputPathRelativeToTop()
+ return p
+}
+
var _ Path = ModuleGenPath{}
+var _ WritablePath = ModuleGenPath{}
var _ genPathProvider = ModuleGenPath{}
var _ objPathProvider = ModuleGenPath{}
// PathForModuleGen returns a Path representing the paths... under the module's
// `gen' directory.
-func PathForModuleGen(ctx ModuleContext, paths ...string) ModuleGenPath {
+func PathForModuleGen(ctx ModuleOutPathContext, paths ...string) ModuleGenPath {
p, err := validatePath(paths...)
if err != nil {
reportPathError(ctx, err)
}
return ModuleGenPath{
ModuleOutPath: ModuleOutPath{
- OutputPath: pathForModule(ctx).withRel("gen").withRel(p),
+ OutputPath: pathForModuleOut(ctx).withRel("gen").withRel(p),
},
}
}
-func (p ModuleGenPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
+func (p ModuleGenPath) genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath {
// TODO: make a different path for local vs remote generated files?
return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
}
-func (p ModuleGenPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
+func (p ModuleGenPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
}
@@ -1272,11 +1691,17 @@
ModuleOutPath
}
+func (p ModuleObjPath) RelativeToTop() Path {
+ p.OutputPath = p.outputPathRelativeToTop()
+ return p
+}
+
var _ Path = ModuleObjPath{}
+var _ WritablePath = ModuleObjPath{}
// PathForModuleObj returns a Path representing the paths... under the module's
// 'obj' directory.
-func PathForModuleObj(ctx ModuleContext, pathComponents ...string) ModuleObjPath {
+func PathForModuleObj(ctx ModuleOutPathContext, pathComponents ...string) ModuleObjPath {
p, err := validatePath(pathComponents...)
if err != nil {
reportPathError(ctx, err)
@@ -1290,11 +1715,17 @@
ModuleOutPath
}
+func (p ModuleResPath) RelativeToTop() Path {
+ p.OutputPath = p.outputPathRelativeToTop()
+ return p
+}
+
var _ Path = ModuleResPath{}
+var _ WritablePath = ModuleResPath{}
// PathForModuleRes returns a Path representing the paths... under the module's
// 'res' directory.
-func PathForModuleRes(ctx ModuleContext, pathComponents ...string) ModuleResPath {
+func PathForModuleRes(ctx ModuleOutPathContext, pathComponents ...string) ModuleResPath {
p, err := validatePath(pathComponents...)
if err != nil {
reportPathError(ctx, err)
@@ -1307,6 +1738,9 @@
type InstallPath struct {
basePath
+ // The soong build directory, i.e. Config.BuildDir()
+ buildDir string
+
// partitionDir is the part of the InstallPath that is automatically determined according to the context.
// For example, it is host/<os>-<arch> for host modules, and target/product/<device>/<partition> for device modules.
partitionDir string
@@ -1315,8 +1749,22 @@
makePath bool
}
-func (p InstallPath) buildDir() string {
- return p.config.buildDir
+// Will panic if called from outside a test environment.
+func ensureTestOnly() {
+ if PrefixInList(os.Args, "-test.") {
+ return
+ }
+ panic(fmt.Errorf("Not in test. Command line:\n %s", strings.Join(os.Args, "\n ")))
+}
+
+func (p InstallPath) RelativeToTop() Path {
+ ensureTestOnly()
+ p.buildDir = OutSoongDir
+ return p
+}
+
+func (p InstallPath) getBuildDir() string {
+ return p.buildDir
}
func (p InstallPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
@@ -1331,9 +1779,9 @@
func (p InstallPath) String() string {
if p.makePath {
// Make path starts with out/ instead of out/soong.
- return filepath.Join(p.config.buildDir, "../", p.path)
+ return filepath.Join(p.buildDir, "../", p.path)
} else {
- return filepath.Join(p.config.buildDir, p.path)
+ return filepath.Join(p.buildDir, p.path)
}
}
@@ -1342,9 +1790,9 @@
// The ./soong is dropped if the install path is for Make.
func (p InstallPath) PartitionDir() string {
if p.makePath {
- return filepath.Join(p.config.buildDir, "../", p.partitionDir)
+ return filepath.Join(p.buildDir, "../", p.partitionDir)
} else {
- return filepath.Join(p.config.buildDir, p.partitionDir)
+ return filepath.Join(p.buildDir, p.partitionDir)
}
}
@@ -1427,7 +1875,8 @@
}
base := InstallPath{
- basePath: basePath{partionPath, ctx.Config(), ""},
+ basePath: basePath{partionPath, ""},
+ buildDir: ctx.Config().buildDir,
partitionDir: partionPath,
makePath: false,
}
@@ -1437,7 +1886,8 @@
func pathForNdkOrSdkInstall(ctx PathContext, prefix string, paths []string) InstallPath {
base := InstallPath{
- basePath: basePath{prefix, ctx.Config(), ""},
+ basePath: basePath{prefix, ""},
+ buildDir: ctx.Config().buildDir,
partitionDir: prefix,
makePath: false,
}
@@ -1481,13 +1931,23 @@
// on a device without a dedicated recovery partition, install the
// recovery variant.
if ctx.DeviceConfig().BoardMoveRecoveryResourcesToVendorBoot() {
- partition = "vendor-ramdisk/first_stage_ramdisk"
+ partition = "vendor_ramdisk/first_stage_ramdisk"
} else {
- partition = "vendor-ramdisk"
+ partition = "vendor_ramdisk"
}
if !ctx.InstallInRoot() {
partition += "/system"
}
+ } else if ctx.InstallInDebugRamdisk() {
+ // The module is only available after switching root into
+ // /first_stage_ramdisk. To expose the module before switching root
+ // on a device without a dedicated recovery partition, install the
+ // recovery variant.
+ if ctx.DeviceConfig().BoardUsesRecoveryAsBoot() {
+ partition = "debug_ramdisk/first_stage_ramdisk"
+ } else {
+ partition = "debug_ramdisk"
+ }
} else if ctx.InstallInRecovery() {
if ctx.InstallInRoot() {
partition = "recovery/root"
@@ -1572,7 +2032,7 @@
if strings.ContainsAny(phony, "$/") {
ReportPathErrorf(ctx, "Phony target contains invalid character ($ or /): %s", phony)
}
- return PhonyPath{basePath{phony, ctx.Config(), ""}}
+ return PhonyPath{basePath{phony, ""}}
}
type PhonyPath struct {
@@ -1581,8 +2041,16 @@
func (p PhonyPath) writablePath() {}
-func (p PhonyPath) buildDir() string {
- return p.config.buildDir
+func (p PhonyPath) getBuildDir() string {
+ // A phone path cannot contain any / so cannot be relative to the build directory.
+ return ""
+}
+
+func (p PhonyPath) RelativeToTop() Path {
+ ensureTestOnly()
+ // A phony path cannot contain any / so does not have a build directory so switching to a new
+ // build directory has no effect so just return this path.
+ return p
}
func (p PhonyPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
@@ -1596,10 +2064,17 @@
basePath
}
+func (p testPath) RelativeToTop() Path {
+ ensureTestOnly()
+ return p
+}
+
func (p testPath) String() string {
return p.path
}
+var _ Path = testPath{}
+
// PathForTesting returns a Path constructed from joining the elements of paths with '/'. It should only be used from
// within tests.
func PathForTesting(paths ...string) Path {
@@ -1643,6 +2118,7 @@
inSanitizerDir bool
inRamdisk bool
inVendorRamdisk bool
+ inDebugRamdisk bool
inRecovery bool
inRoot bool
forceOS *OsType
@@ -1675,6 +2151,10 @@
return m.inVendorRamdisk
}
+func (m testModuleInstallPathContext) InstallInDebugRamdisk() bool {
+ return m.inDebugRamdisk
+}
+
func (m testModuleInstallPathContext) InstallInRecovery() bool {
return m.inRecovery
}
@@ -1742,6 +2222,19 @@
return ioutil.WriteFile(absolutePath(path.String()), data, perm)
}
+func RemoveAllOutputDir(path WritablePath) error {
+ return os.RemoveAll(absolutePath(path.String()))
+}
+
+func CreateOutputDirIfNonexistent(path WritablePath, perm os.FileMode) error {
+ dir := absolutePath(path.String())
+ if _, err := os.Stat(dir); os.IsNotExist(err) {
+ return os.MkdirAll(dir, os.ModePerm)
+ } else {
+ return err
+ }
+}
+
func absolutePath(path string) string {
if filepath.IsAbs(path) {
return path
@@ -1760,3 +2253,28 @@
// The install path of the data file, relative to the install root.
RelativeInstallPath string
}
+
+// PathsIfNonNil returns a Paths containing only the non-nil input arguments.
+func PathsIfNonNil(paths ...Path) Paths {
+ if len(paths) == 0 {
+ // Fast path for empty argument list
+ return nil
+ } else if len(paths) == 1 {
+ // Fast path for a single argument
+ if paths[0] != nil {
+ return paths
+ } else {
+ return nil
+ }
+ }
+ ret := make(Paths, 0, len(paths))
+ for _, path := range paths {
+ if path != nil {
+ ret = append(ret, path)
+ }
+ }
+ if len(ret) == 0 {
+ return nil
+ }
+ return ret
+}
diff --git a/android/paths_test.go b/android/paths_test.go
index 14a4773..cb9138b 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -341,6 +341,73 @@
},
{
+ name: "ramdisk binary",
+ ctx: &testModuleInstallPathContext{
+ baseModuleContext: baseModuleContext{
+ os: deviceTarget.Os,
+ target: deviceTarget,
+ },
+ inRamdisk: true,
+ },
+ in: []string{"my_test"},
+ out: "target/product/test_device/ramdisk/system/my_test",
+ partitionDir: "target/product/test_device/ramdisk/system",
+ },
+ {
+ name: "ramdisk root binary",
+ ctx: &testModuleInstallPathContext{
+ baseModuleContext: baseModuleContext{
+ os: deviceTarget.Os,
+ target: deviceTarget,
+ },
+ inRamdisk: true,
+ inRoot: true,
+ },
+ in: []string{"my_test"},
+ out: "target/product/test_device/ramdisk/my_test",
+ partitionDir: "target/product/test_device/ramdisk",
+ },
+ {
+ name: "vendor_ramdisk binary",
+ ctx: &testModuleInstallPathContext{
+ baseModuleContext: baseModuleContext{
+ os: deviceTarget.Os,
+ target: deviceTarget,
+ },
+ inVendorRamdisk: true,
+ },
+ in: []string{"my_test"},
+ out: "target/product/test_device/vendor_ramdisk/system/my_test",
+ partitionDir: "target/product/test_device/vendor_ramdisk/system",
+ },
+ {
+ name: "vendor_ramdisk root binary",
+ ctx: &testModuleInstallPathContext{
+ baseModuleContext: baseModuleContext{
+ os: deviceTarget.Os,
+ target: deviceTarget,
+ },
+ inVendorRamdisk: true,
+ inRoot: true,
+ },
+ in: []string{"my_test"},
+ out: "target/product/test_device/vendor_ramdisk/my_test",
+ partitionDir: "target/product/test_device/vendor_ramdisk",
+ },
+ {
+ name: "debug_ramdisk binary",
+ ctx: &testModuleInstallPathContext{
+ baseModuleContext: baseModuleContext{
+ os: deviceTarget.Os,
+ target: deviceTarget,
+ },
+ inDebugRamdisk: true,
+ },
+ in: []string{"my_test"},
+ out: "target/product/test_device/debug_ramdisk/my_test",
+ partitionDir: "target/product/test_device/debug_ramdisk",
+ },
+ {
name: "system native test binary",
ctx: &testModuleInstallPathContext{
baseModuleContext: baseModuleContext{
@@ -637,6 +704,80 @@
}
}
+func TestPathForModuleInstallRecoveryAsBoot(t *testing.T) {
+ testConfig := pathTestConfig("")
+ testConfig.TestProductVariables.BoardUsesRecoveryAsBoot = proptools.BoolPtr(true)
+ testConfig.TestProductVariables.BoardMoveRecoveryResourcesToVendorBoot = proptools.BoolPtr(true)
+ deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}}
+
+ testCases := []struct {
+ name string
+ ctx *testModuleInstallPathContext
+ in []string
+ out string
+ partitionDir string
+ }{
+ {
+ name: "ramdisk binary",
+ ctx: &testModuleInstallPathContext{
+ baseModuleContext: baseModuleContext{
+ os: deviceTarget.Os,
+ target: deviceTarget,
+ },
+ inRamdisk: true,
+ inRoot: true,
+ },
+ in: []string{"my_test"},
+ out: "target/product/test_device/recovery/root/first_stage_ramdisk/my_test",
+ partitionDir: "target/product/test_device/recovery/root/first_stage_ramdisk",
+ },
+
+ {
+ name: "vendor_ramdisk binary",
+ ctx: &testModuleInstallPathContext{
+ baseModuleContext: baseModuleContext{
+ os: deviceTarget.Os,
+ target: deviceTarget,
+ },
+ inVendorRamdisk: true,
+ inRoot: true,
+ },
+ in: []string{"my_test"},
+ out: "target/product/test_device/vendor_ramdisk/first_stage_ramdisk/my_test",
+ partitionDir: "target/product/test_device/vendor_ramdisk/first_stage_ramdisk",
+ },
+ {
+ name: "debug_ramdisk binary",
+ ctx: &testModuleInstallPathContext{
+ baseModuleContext: baseModuleContext{
+ os: deviceTarget.Os,
+ target: deviceTarget,
+ },
+ inDebugRamdisk: true,
+ },
+ in: []string{"my_test"},
+ out: "target/product/test_device/debug_ramdisk/first_stage_ramdisk/my_test",
+ partitionDir: "target/product/test_device/debug_ramdisk/first_stage_ramdisk",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ tc.ctx.baseModuleContext.config = testConfig
+ output := PathForModuleInstall(tc.ctx, tc.in...)
+ if output.basePath.path != tc.out {
+ t.Errorf("unexpected path:\n got: %q\nwant: %q\n",
+ output.basePath.path,
+ tc.out)
+ }
+ if output.partitionDir != tc.partitionDir {
+ t.Errorf("unexpected partitionDir:\n got: %q\nwant: %q\n",
+ output.partitionDir, tc.partitionDir)
+ }
+ })
+ }
+}
+
func TestBaseDirForInstallPath(t *testing.T) {
testConfig := pathTestConfig("")
deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}}
@@ -977,7 +1118,7 @@
rel string
}
-func testPathForModuleSrc(t *testing.T, buildDir string, tests []pathForModuleSrcTestCase) {
+func testPathForModuleSrc(t *testing.T, tests []pathForModuleSrcTestCase) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
fgBp := `
@@ -995,7 +1136,7 @@
}
`
- mockFS := map[string][]byte{
+ mockFS := MockFS{
"fg/Android.bp": []byte(fgBp),
"foo/Android.bp": []byte(test.bp),
"ofp/Android.bp": []byte(ofpBp),
@@ -1007,37 +1148,21 @@
"foo/src_special/$": nil,
}
- config := TestConfig(buildDir, nil, "", mockFS)
+ result := GroupFixturePreparers(
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
+ ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory)
+ ctx.RegisterModuleType("filegroup", FileGroupFactory)
+ }),
+ mockFS.AddToFixture(),
+ ).RunTest(t)
- ctx := NewTestContext(config)
+ m := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
- ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
- ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory)
- ctx.RegisterModuleType("filegroup", FileGroupFactory)
-
- ctx.Register()
- _, errs := ctx.ParseFileList(".", []string{"fg/Android.bp", "foo/Android.bp", "ofp/Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
-
- m := ctx.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
-
- if g, w := m.srcs, test.srcs; !reflect.DeepEqual(g, w) {
- t.Errorf("want srcs %q, got %q", w, g)
- }
-
- if g, w := m.rels, test.rels; !reflect.DeepEqual(g, w) {
- t.Errorf("want rels %q, got %q", w, g)
- }
-
- if g, w := m.src, test.src; g != w {
- t.Errorf("want src %q, got %q", w, g)
- }
-
- if g, w := m.rel, test.rel; g != w {
- t.Errorf("want rel %q, got %q", w, g)
- }
+ AssertStringPathsRelativeToTopEquals(t, "srcs", result.Config, test.srcs, m.srcs)
+ AssertStringPathsRelativeToTopEquals(t, "rels", result.Config, test.rels, m.rels)
+ AssertStringPathRelativeToTopEquals(t, "src", result.Config, test.src, m.src)
+ AssertStringPathRelativeToTopEquals(t, "rel", result.Config, test.rel, m.rel)
})
}
}
@@ -1094,7 +1219,7 @@
name: "foo",
srcs: [":b"],
}`,
- srcs: []string{buildDir + "/.intermediates/ofp/b/gen/b"},
+ srcs: []string{"out/soong/.intermediates/ofp/b/gen/b"},
rels: []string{"gen/b"},
},
{
@@ -1104,7 +1229,7 @@
name: "foo",
srcs: [":b{.tagged}"],
}`,
- srcs: []string{buildDir + "/.intermediates/ofp/b/gen/c"},
+ srcs: []string{"out/soong/.intermediates/ofp/b/gen/c"},
rels: []string{"gen/c"},
},
{
@@ -1119,7 +1244,7 @@
name: "c",
outs: ["gen/c"],
}`,
- srcs: []string{buildDir + "/.intermediates/ofp/b/gen/b"},
+ srcs: []string{"out/soong/.intermediates/ofp/b/gen/b"},
rels: []string{"gen/b"},
},
{
@@ -1134,7 +1259,7 @@
},
}
- testPathForModuleSrc(t, buildDir, tests)
+ testPathForModuleSrc(t, tests)
}
func TestPathForModuleSrc(t *testing.T) {
@@ -1176,7 +1301,7 @@
name: "foo",
src: ":b",
}`,
- src: buildDir + "/.intermediates/ofp/b/gen/b",
+ src: "out/soong/.intermediates/ofp/b/gen/b",
rel: "gen/b",
},
{
@@ -1186,7 +1311,7 @@
name: "foo",
src: ":b{.tagged}",
}`,
- src: buildDir + "/.intermediates/ofp/b/gen/c",
+ src: "out/soong/.intermediates/ofp/b/gen/c",
rel: "gen/c",
},
{
@@ -1201,7 +1326,7 @@
},
}
- testPathForModuleSrc(t, buildDir, tests)
+ testPathForModuleSrc(t, tests)
}
func TestPathsForModuleSrc_AllowMissingDependencies(t *testing.T) {
@@ -1221,44 +1346,70 @@
}
`
- config := TestConfig(buildDir, nil, bp, nil)
- config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
+ result := GroupFixturePreparers(
+ PrepareForTestWithAllowMissingDependencies,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
+ }),
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
- ctx := NewTestContext(config)
- ctx.SetAllowMissingDependencies(true)
+ foo := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
- ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
+ AssertArrayString(t, "foo missing deps", []string{"a", "b", "c"}, foo.missingDeps)
+ AssertArrayString(t, "foo srcs", []string{}, foo.srcs)
+ AssertStringEquals(t, "foo src", "", foo.src)
- ctx.Register()
+ bar := result.ModuleForTests("bar", "").Module().(*pathForModuleSrcTestModule)
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
+ AssertArrayString(t, "bar missing deps", []string{"d", "e"}, bar.missingDeps)
+ AssertArrayString(t, "bar srcs", []string{}, bar.srcs)
+}
- foo := ctx.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
+func TestPathRelativeToTop(t *testing.T) {
+ testConfig := pathTestConfig("/tmp/build/top")
+ deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}}
- if g, w := foo.missingDeps, []string{"a", "b", "c"}; !reflect.DeepEqual(g, w) {
- t.Errorf("want foo missing deps %q, got %q", w, g)
+ ctx := &testModuleInstallPathContext{
+ baseModuleContext: baseModuleContext{
+ os: deviceTarget.Os,
+ target: deviceTarget,
+ },
}
+ ctx.baseModuleContext.config = testConfig
- if g, w := foo.srcs, []string{}; !reflect.DeepEqual(g, w) {
- t.Errorf("want foo srcs %q, got %q", w, g)
- }
+ t.Run("install for soong", func(t *testing.T) {
+ p := PathForModuleInstall(ctx, "install/path")
+ AssertPathRelativeToTopEquals(t, "install path for soong", "out/soong/target/product/test_device/system/install/path", p)
+ })
+ t.Run("install for make", func(t *testing.T) {
+ p := PathForModuleInstall(ctx, "install/path").ToMakePath()
+ AssertPathRelativeToTopEquals(t, "install path for make", "out/target/product/test_device/system/install/path", p)
+ })
+ t.Run("output", func(t *testing.T) {
+ p := PathForOutput(ctx, "output/path")
+ AssertPathRelativeToTopEquals(t, "output path", "out/soong/output/path", p)
+ })
+ t.Run("source", func(t *testing.T) {
+ p := PathForSource(ctx, "source/path")
+ AssertPathRelativeToTopEquals(t, "source path", "source/path", p)
+ })
+ t.Run("mixture", func(t *testing.T) {
+ paths := Paths{
+ PathForModuleInstall(ctx, "install/path"),
+ PathForModuleInstall(ctx, "install/path").ToMakePath(),
+ PathForOutput(ctx, "output/path"),
+ PathForSource(ctx, "source/path"),
+ }
- if g, w := foo.src, ""; g != w {
- t.Errorf("want foo src %q, got %q", w, g)
- }
-
- bar := ctx.ModuleForTests("bar", "").Module().(*pathForModuleSrcTestModule)
-
- if g, w := bar.missingDeps, []string{"d", "e"}; !reflect.DeepEqual(g, w) {
- t.Errorf("want bar missing deps %q, got %q", w, g)
- }
-
- if g, w := bar.srcs, []string{}; !reflect.DeepEqual(g, w) {
- t.Errorf("want bar srcs %q, got %q", w, g)
- }
+ expected := []string{
+ "out/soong/target/product/test_device/system/install/path",
+ "out/target/product/test_device/system/install/path",
+ "out/soong/output/path",
+ "source/path",
+ }
+ AssertPathsRelativeToTopEquals(t, "mixture", expected, paths)
+ })
}
func ExampleOutputPath_ReplaceExtension() {
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 39d30c5..40bcdfd 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -82,6 +82,12 @@
}
func (p *Prebuilt) Name(name string) string {
+ return PrebuiltNameFromSource(name)
+}
+
+// PrebuiltNameFromSource returns the result of prepending the "prebuilt_" prefix to the supplied
+// name.
+func PrebuiltNameFromSource(name string) string {
return "prebuilt_" + name
}
@@ -93,22 +99,24 @@
return proptools.Bool(p.properties.Prefer)
}
-// The below source-related functions and the srcs, src fields are based on an assumption that
-// prebuilt modules have a static source property at the moment. Currently there is only one
-// exception, android_app_import, which chooses a source file depending on the product's DPI
-// preference configs. We'll want to add native support for dynamic source cases if we end up having
-// more modules like this.
-func (p *Prebuilt) SingleSourcePath(ctx ModuleContext) Path {
- if p.srcsSupplier != nil {
- srcs := p.srcsSupplier(ctx)
+// SingleSourcePathFromSupplier invokes the supplied supplier for the current module in the
+// supplied context to retrieve a list of file paths, ensures that the returned list of file paths
+// contains a single value and then assumes that is a module relative file path and converts it to
+// a Path accordingly.
+//
+// Any issues, such as nil supplier or not exactly one file path will be reported as errors on the
+// supplied context and this will return nil.
+func SingleSourcePathFromSupplier(ctx ModuleContext, srcsSupplier PrebuiltSrcsSupplier, srcsPropertyName string) Path {
+ if srcsSupplier != nil {
+ srcs := srcsSupplier(ctx, ctx.Module())
if len(srcs) == 0 {
- ctx.PropertyErrorf(p.srcsPropertyName, "missing prebuilt source file")
+ ctx.PropertyErrorf(srcsPropertyName, "missing prebuilt source file")
return nil
}
if len(srcs) > 1 {
- ctx.PropertyErrorf(p.srcsPropertyName, "multiple prebuilt source files")
+ ctx.PropertyErrorf(srcsPropertyName, "multiple prebuilt source files")
return nil
}
@@ -122,14 +130,26 @@
}
}
+// The below source-related functions and the srcs, src fields are based on an assumption that
+// prebuilt modules have a static source property at the moment. Currently there is only one
+// exception, android_app_import, which chooses a source file depending on the product's DPI
+// preference configs. We'll want to add native support for dynamic source cases if we end up having
+// more modules like this.
+func (p *Prebuilt) SingleSourcePath(ctx ModuleContext) Path {
+ return SingleSourcePathFromSupplier(ctx, p.srcsSupplier, p.srcsPropertyName)
+}
+
func (p *Prebuilt) UsePrebuilt() bool {
return p.properties.UsePrebuilt
}
// Called to provide the srcs value for the prebuilt module.
//
+// This can be called with a context for any module not just the prebuilt one itself. It can also be
+// called concurrently.
+//
// Return the src value or nil if it is not available.
-type PrebuiltSrcsSupplier func(ctx BaseModuleContext) []string
+type PrebuiltSrcsSupplier func(ctx BaseModuleContext, prebuilt Module) []string
// Initialize the module as a prebuilt module that uses the provided supplier to access the
// prebuilt sources of the module.
@@ -163,7 +183,7 @@
panic(fmt.Errorf("srcs must not be nil"))
}
- srcsSupplier := func(ctx BaseModuleContext) []string {
+ srcsSupplier := func(ctx BaseModuleContext, _ Module) []string {
return *srcs
}
@@ -184,7 +204,7 @@
srcFieldIndex := srcStructField.Index
srcPropertyName := proptools.PropertyNameForField(srcField)
- srcsSupplier := func(ctx BaseModuleContext) []string {
+ srcsSupplier := func(ctx BaseModuleContext, _ Module) []string {
if !module.Enabled() {
return nil
}
@@ -210,6 +230,26 @@
Prebuilt() *Prebuilt
}
+// IsModulePreferred returns true if the given module is preferred.
+//
+// A source module is preferred if there is no corresponding prebuilt module or the prebuilt module
+// does not have "prefer: true".
+//
+// A prebuilt module is preferred if there is no corresponding source module or the prebuilt module
+// has "prefer: true".
+func IsModulePreferred(module Module) bool {
+ if module.IsReplacedByPrebuilt() {
+ // A source module that has been replaced by a prebuilt counterpart.
+ return false
+ }
+ if prebuilt, ok := module.(PrebuiltInterface); ok {
+ if p := prebuilt.Prebuilt(); p != nil {
+ return p.UsePrebuilt()
+ }
+ }
+ return true
+}
+
func RegisterPrebuiltsPreArchMutators(ctx RegisterMutatorsContext) {
ctx.BottomUp("prebuilt_rename", PrebuiltRenameMutator).Parallel()
}
@@ -256,12 +296,12 @@
panic(fmt.Errorf("prebuilt module did not have InitPrebuiltModule called on it"))
}
if !p.properties.SourceExists {
- p.properties.UsePrebuilt = p.usePrebuilt(ctx, nil)
+ p.properties.UsePrebuilt = p.usePrebuilt(ctx, nil, m)
}
} else if s, ok := ctx.Module().(Module); ok {
ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(m Module) {
p := m.(PrebuiltInterface).Prebuilt()
- if p.usePrebuilt(ctx, s) {
+ if p.usePrebuilt(ctx, s, m) {
p.properties.UsePrebuilt = true
s.ReplacedByPrebuilt()
}
@@ -269,11 +309,9 @@
}
}
-// PrebuiltPostDepsMutator does two operations. It replace dependencies on the
-// source module with dependencies on the prebuilt when both modules exist and
-// the prebuilt should be used. When the prebuilt should not be used, disable
-// installing it. Secondly, it also adds a sourcegroup to any filegroups found
-// in the prebuilt's 'Srcs' property.
+// PrebuiltPostDepsMutator replaces dependencies on the source module with dependencies on the
+// prebuilt when both modules exist and the prebuilt should be used. When the prebuilt should not
+// be used, disable installing it.
func PrebuiltPostDepsMutator(ctx BottomUpMutatorContext) {
if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
p := m.Prebuilt()
@@ -296,8 +334,15 @@
// usePrebuilt returns true if a prebuilt should be used instead of the source module. The prebuilt
// will be used if it is marked "prefer" or if the source module is disabled.
-func (p *Prebuilt) usePrebuilt(ctx TopDownMutatorContext, source Module) bool {
- if p.srcsSupplier != nil && len(p.srcsSupplier(ctx)) == 0 {
+func (p *Prebuilt) usePrebuilt(ctx TopDownMutatorContext, source Module, prebuilt Module) bool {
+ if p.srcsSupplier != nil && len(p.srcsSupplier(ctx, prebuilt)) == 0 {
+ return false
+ }
+
+ // Skip prebuilt modules under unexported namespaces so that we won't
+ // end up shadowing non-prebuilt module when prebuilt module under same
+ // name happens to have a `Prefer` property set to true.
+ if ctx.Config().KatiEnabled() && !prebuilt.ExportedToMake() {
return false
}
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 9ac3875..ced37fe 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -262,7 +262,7 @@
}
func TestPrebuilts(t *testing.T) {
- fs := map[string][]byte{
+ fs := MockFS{
"prebuilt_file": nil,
"source_file": nil,
}
@@ -277,32 +277,33 @@
deps: [":bar"],
}`
}
- config := TestArchConfig(buildDir, nil, bp, fs)
// Add windows to the target list to test the logic when a variant is
// disabled by default.
if !Windows.DefaultDisabled {
t.Errorf("windows is assumed to be disabled by default")
}
- config.config.Targets[Windows] = []Target{
- {Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true},
- }
- ctx := NewTestArchContext(config)
- registerTestPrebuiltBuildComponents(ctx)
- ctx.RegisterModuleType("filegroup", FileGroupFactory)
- ctx.Register()
+ result := GroupFixturePreparers(
+ PrepareForTestWithArchMutator,
+ PrepareForTestWithPrebuilts,
+ PrepareForTestWithOverrides,
+ PrepareForTestWithFilegroup,
+ // Add a Windows target to the configuration.
+ FixtureModifyConfig(func(config Config) {
+ config.Targets[Windows] = []Target{
+ {Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true},
+ }
+ }),
+ fs.AddToFixture(),
+ FixtureRegisterWithContext(registerTestPrebuiltModules),
+ ).RunTestWithBp(t, bp)
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
-
- for _, variant := range ctx.ModuleVariantsForTests("foo") {
- foo := ctx.ModuleForTests("foo", variant)
+ for _, variant := range result.ModuleVariantsForTests("foo") {
+ foo := result.ModuleForTests("foo", variant)
t.Run(foo.Module().Target().Os.String(), func(t *testing.T) {
var dependsOnSourceModule, dependsOnPrebuiltModule bool
- ctx.VisitDirectDeps(foo.Module(), func(m blueprint.Module) {
+ result.VisitDirectDeps(foo.Module(), func(m blueprint.Module) {
if _, ok := m.(*sourceModule); ok {
dependsOnSourceModule = true
}
@@ -381,14 +382,20 @@
}
func registerTestPrebuiltBuildComponents(ctx RegistrationContext) {
- ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
- ctx.RegisterModuleType("source", newSourceModule)
- ctx.RegisterModuleType("override_source", newOverrideSourceModule)
+ registerTestPrebuiltModules(ctx)
RegisterPrebuiltMutators(ctx)
ctx.PostDepsMutators(RegisterOverridePostDepsMutators)
}
+var prepareForTestWithFakePrebuiltModules = FixtureRegisterWithContext(registerTestPrebuiltModules)
+
+func registerTestPrebuiltModules(ctx RegistrationContext) {
+ ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
+ ctx.RegisterModuleType("source", newSourceModule)
+ ctx.RegisterModuleType("override_source", newOverrideSourceModule)
+}
+
type prebuiltModule struct {
ModuleBase
prebuilt Prebuilt
diff --git a/android/queryview.go b/android/queryview.go
index 1b7e77d..224652e 100644
--- a/android/queryview.go
+++ b/android/queryview.go
@@ -26,7 +26,6 @@
// for calling the soong_build primary builder in the main build.ninja file.
func init() {
RegisterSingletonType("bazel_queryview", BazelQueryViewSingleton)
- RegisterSingletonType("bazel_converter", BazelConverterSingleton)
}
// BazelQueryViewSingleton is the singleton responsible for registering the
@@ -52,13 +51,7 @@
func generateBuildActionsForBazelConversion(ctx SingletonContext, converterMode bool) {
name := "queryview"
- additionalEnvVars := ""
descriptionTemplate := "[EXPERIMENTAL, PRE-PRODUCTION] Creating the Bazel QueryView workspace with %s at $outDir"
- if converterMode {
- name = "bp2build"
- additionalEnvVars = "CONVERT_TO_BAZEL=true"
- descriptionTemplate = "[EXPERIMENTAL, PRE-PRODUCTION] Converting all Android.bp to Bazel BUILD files with %s at $outDir"
- }
// Create a build and rule statement, using the Bazel QueryView's WORKSPACE
// file as the output file marker.
@@ -73,13 +66,20 @@
bazelQueryView := ctx.Rule(pctx, "bazelQueryView",
blueprint.RuleParams{
Command: fmt.Sprintf(
- "rm -rf ${outDir}/* && "+
- "%s %s --bazel_queryview_dir ${outDir} %s && "+
- "echo WORKSPACE: `cat %s` > ${outDir}/.queryview-depfile.d",
- additionalEnvVars,
- primaryBuilder.String(),
- strings.Join(os.Args[1:], " "),
+ `rm -rf "${outDir}/"* && `+
+ `mkdir -p "${outDir}" && `+
+ `echo WORKSPACE: $$(cat "%s") > "${outDir}/.queryview-depfile.d" && `+
+ `BUILDER="%s" && `+
+ `echo BUILDER=$$BUILDER && `+
+ `cd "$$(dirname "$$BUILDER")" && `+
+ `echo PWD=$$PWD && `+
+ `ABSBUILDER="$$PWD/$$(basename "$$BUILDER")" && `+
+ `echo ABSBUILDER=$$ABSBUILDER && `+
+ `cd / && `+
+ `env -i "$$ABSBUILDER" --bazel_queryview_dir "${outDir}" "%s"`,
moduleListFilePath.String(), // Use the contents of Android.bp.list as the depfile.
+ primaryBuilder.String(),
+ strings.Join(os.Args[1:], "\" \""),
),
CommandDeps: []string{primaryBuilder.String()},
Description: fmt.Sprintf(
diff --git a/android/register.go b/android/register.go
index b26f9b9..4c8088d 100644
--- a/android/register.go
+++ b/android/register.go
@@ -16,24 +16,83 @@
import (
"fmt"
+ "reflect"
"github.com/google/blueprint"
)
+// A sortable component is one whose registration order affects the order in which it is executed
+// and so affects the behavior of the build system. As a result it is important for the order in
+// which they are registered during tests to match the order used at runtime and so the test
+// infrastructure will sort them to match.
+//
+// The sortable components are mutators, singletons and pre-singletons. Module types are not
+// sortable because their order of registration does not affect the runtime behavior.
+type sortableComponent interface {
+ // componentName returns the name of the component.
+ //
+ // Uniquely identifies the components within the set of components used at runtimr and during
+ // tests.
+ componentName() string
+
+ // register registers this component in the supplied context.
+ register(ctx *Context)
+}
+
+type sortableComponents []sortableComponent
+
+// registerAll registers all components in this slice with the supplied context.
+func (r sortableComponents) registerAll(ctx *Context) {
+ for _, c := range r {
+ c.register(ctx)
+ }
+}
+
type moduleType struct {
name string
factory ModuleFactory
}
+func (t moduleType) register(ctx *Context) {
+ ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory))
+}
+
var moduleTypes []moduleType
+var moduleTypesForDocs = map[string]reflect.Value{}
type singleton struct {
+ // True if this should be registered as a pre-singleton, false otherwise.
+ pre bool
+
name string
factory SingletonFactory
}
-var singletons []singleton
-var preSingletons []singleton
+func newSingleton(name string, factory SingletonFactory) singleton {
+ return singleton{false, name, factory}
+}
+
+func newPreSingleton(name string, factory SingletonFactory) singleton {
+ return singleton{true, name, factory}
+}
+
+func (s singleton) componentName() string {
+ return s.name
+}
+
+func (s singleton) register(ctx *Context) {
+ adaptor := SingletonFactoryAdaptor(ctx, s.factory)
+ if s.pre {
+ ctx.RegisterPreSingletonType(s.name, adaptor)
+ } else {
+ ctx.RegisterSingletonType(s.name, adaptor)
+ }
+}
+
+var _ sortableComponent = singleton{}
+
+var singletons sortableComponents
+var preSingletons sortableComponents
type mutator struct {
name string
@@ -42,6 +101,8 @@
parallel bool
}
+var _ sortableComponent = &mutator{}
+
type ModuleFactory func() Module
// ModuleFactoryAdaptor wraps a ModuleFactory into a blueprint.ModuleFactory by converting a Module
@@ -69,14 +130,24 @@
func RegisterModuleType(name string, factory ModuleFactory) {
moduleTypes = append(moduleTypes, moduleType{name, factory})
+ RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
+}
+
+// RegisterModuleTypeForDocs associates a module type name with a reflect.Value of the factory
+// function that has documentation for the module type. It is normally called automatically
+// by RegisterModuleType, but can be called manually after RegisterModuleType in order to
+// override the factory method used for documentation, for example if the method passed to
+// RegisterModuleType was a lambda.
+func RegisterModuleTypeForDocs(name string, factory reflect.Value) {
+ moduleTypesForDocs[name] = factory
}
func RegisterSingletonType(name string, factory SingletonFactory) {
- singletons = append(singletons, singleton{name, factory})
+ singletons = append(singletons, newSingleton(name, factory))
}
func RegisterPreSingletonType(name string, factory SingletonFactory) {
- preSingletons = append(preSingletons, singleton{name, factory})
+ preSingletons = append(preSingletons, newPreSingleton(name, factory))
}
type Context struct {
@@ -95,43 +166,65 @@
// files to semantically equivalent BUILD files.
func (ctx *Context) RegisterForBazelConversion() {
for _, t := range moduleTypes {
- ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory))
+ t.register(ctx)
}
- bazelConverterSingleton := singleton{"bp2build", BazelConverterSingleton}
- ctx.RegisterSingletonType(bazelConverterSingleton.name,
- SingletonFactoryAdaptor(ctx, bazelConverterSingleton.factory))
+ // Required for SingletonModule types, even though we are not using them.
+ for _, t := range singletons {
+ t.register(ctx)
+ }
- registerMutatorsForBazelConversion(ctx.Context)
+ bp2buildMutatorList := []RegisterMutatorFunc{}
+ for t, f := range bp2buildMutators {
+ ctx.config.bp2buildModuleTypeConfig[t] = true
+ bp2buildMutatorList = append(bp2buildMutatorList, f)
+ }
+
+ RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators, bp2buildDepsMutators, bp2buildMutatorList)
}
+// Register the pipeline of singletons, module types, and mutators for
+// generating build.ninja and other files for Kati, from Android.bp files.
func (ctx *Context) Register() {
- for _, t := range preSingletons {
- ctx.RegisterPreSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
- }
+ preSingletons.registerAll(ctx)
for _, t := range moduleTypes {
- ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory))
+ t.register(ctx)
}
- for _, t := range singletons {
- ctx.RegisterSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
+ if ctx.config.BazelContext.BazelEnabled() {
+ // Hydrate the configuration of bp2build-enabled module types. This is
+ // required as a signal to identify which modules should be deferred to
+ // Bazel in mixed builds, if it is enabled.
+ for t, _ := range bp2buildMutators {
+ ctx.config.bp2buildModuleTypeConfig[t] = true
+ }
}
- registerMutators(ctx.Context, preArch, preDeps, postDeps, finalDeps)
+ mutators := collateGloballyRegisteredMutators()
+ mutators.registerAll(ctx)
- ctx.RegisterSingletonType("bazeldeps", SingletonFactoryAdaptor(ctx, BazelSingleton))
+ singletons := collateGloballyRegisteredSingletons()
+ singletons.registerAll(ctx)
+}
- // Register phony just before makevars so it can write out its phony rules as Make rules
- ctx.RegisterSingletonType("phony", SingletonFactoryAdaptor(ctx, phonySingletonFactory))
+func collateGloballyRegisteredSingletons() sortableComponents {
+ allSingletons := append(sortableComponents(nil), singletons...)
+ allSingletons = append(allSingletons,
+ singleton{false, "bazeldeps", BazelSingleton},
- // Register makevars after other singletons so they can export values through makevars
- ctx.RegisterSingletonType("makevars", SingletonFactoryAdaptor(ctx, makeVarsSingletonFunc))
+ // Register phony just before makevars so it can write out its phony rules as Make rules
+ singleton{false, "phony", phonySingletonFactory},
- // Register env and ninjadeps last so that they can track all used environment variables and
- // Ninja file dependencies stored in the config.
- ctx.RegisterSingletonType("env", SingletonFactoryAdaptor(ctx, EnvSingleton))
- ctx.RegisterSingletonType("ninjadeps", SingletonFactoryAdaptor(ctx, ninjaDepsSingletonFactory))
+ // Register makevars after other singletons so they can export values through makevars
+ singleton{false, "makevars", makeVarsSingletonFunc},
+
+ // Register env and ninjadeps last so that they can track all used environment variables and
+ // Ninja file dependencies stored in the config.
+ singleton{false, "ninjadeps", ninjaDepsSingletonFactory},
+ )
+
+ return allSingletons
}
func ModuleTypeFactories() map[string]ModuleFactory {
@@ -142,12 +235,18 @@
return ret
}
+func ModuleTypeFactoriesForDocs() map[string]reflect.Value {
+ return moduleTypesForDocs
+}
+
// Interface for registering build components.
//
// Provided to allow registration of build components to be shared between the runtime
// and test environments.
type RegistrationContext interface {
RegisterModuleType(name string, factory ModuleFactory)
+ RegisterSingletonModuleType(name string, factory SingletonModuleFactory)
+ RegisterPreSingletonType(name string, factory SingletonFactory)
RegisterSingletonType(name string, factory SingletonFactory)
PreArchMutators(f RegisterMutatorFunc)
@@ -178,16 +277,19 @@
// ctx := android.NewTestContext(config)
// RegisterBuildComponents(ctx)
var InitRegistrationContext RegistrationContext = &initRegistrationContext{
- moduleTypes: make(map[string]ModuleFactory),
- singletonTypes: make(map[string]SingletonFactory),
+ moduleTypes: make(map[string]ModuleFactory),
+ singletonTypes: make(map[string]SingletonFactory),
+ preSingletonTypes: make(map[string]SingletonFactory),
}
// Make sure the TestContext implements RegistrationContext.
var _ RegistrationContext = (*TestContext)(nil)
type initRegistrationContext struct {
- moduleTypes map[string]ModuleFactory
- singletonTypes map[string]SingletonFactory
+ moduleTypes map[string]ModuleFactory
+ singletonTypes map[string]SingletonFactory
+ preSingletonTypes map[string]SingletonFactory
+ moduleTypesForDocs map[string]reflect.Value
}
func (ctx *initRegistrationContext) RegisterModuleType(name string, factory ModuleFactory) {
@@ -196,6 +298,17 @@
}
ctx.moduleTypes[name] = factory
RegisterModuleType(name, factory)
+ RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
+}
+
+func (ctx *initRegistrationContext) RegisterSingletonModuleType(name string, factory SingletonModuleFactory) {
+ s, m := SingletonModuleFactoryAdaptor(name, factory)
+ ctx.RegisterSingletonType(name, s)
+ ctx.RegisterModuleType(name, m)
+ // Overwrite moduleTypesForDocs with the original factory instead of the lambda returned by
+ // SingletonModuleFactoryAdaptor so that docs can find the module type documentation on the
+ // factory method.
+ RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
}
func (ctx *initRegistrationContext) RegisterSingletonType(name string, factory SingletonFactory) {
@@ -206,6 +319,14 @@
RegisterSingletonType(name, factory)
}
+func (ctx *initRegistrationContext) RegisterPreSingletonType(name string, factory SingletonFactory) {
+ if _, present := ctx.preSingletonTypes[name]; present {
+ panic(fmt.Sprintf("pre singleton type %q is already registered", name))
+ }
+ ctx.preSingletonTypes[name] = factory
+ RegisterPreSingletonType(name, factory)
+}
+
func (ctx *initRegistrationContext) PreArchMutators(f RegisterMutatorFunc) {
PreArchMutators(f)
}
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 84501fe..06e82c8 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -27,6 +27,8 @@
"github.com/google/blueprint/proptools"
"android/soong/cmd/sbox/sbox_proto"
+ "android/soong/remoteexec"
+ "android/soong/response"
"android/soong/shared"
)
@@ -48,8 +50,10 @@
sbox bool
highmem bool
remoteable RemoteRuleSupports
+ rbeParams *remoteexec.REParams
outDir WritablePath
sboxTools bool
+ sboxInputs bool
sboxManifestPath WritablePath
missingDeps []string
}
@@ -119,6 +123,18 @@
return r
}
+// Rewrapper marks the rule as running inside rewrapper using the given params in order to support
+// running on RBE. During RuleBuilder.Build the params will be combined with the inputs, outputs
+// and tools known to RuleBuilder to prepend an appropriate rewrapper command line to the rule's
+// command line.
+func (r *RuleBuilder) Rewrapper(params *remoteexec.REParams) *RuleBuilder {
+ if !r.sboxInputs {
+ panic(fmt.Errorf("RuleBuilder.Rewrapper must be called after RuleBuilder.SandboxInputs"))
+ }
+ r.rbeParams = params
+ return r
+}
+
// Sbox marks the rule as needing to be wrapped by sbox. The outputDir should point to the output
// directory that sbox will wipe. It should not be written to by any other rule. manifestPath should
// point to a location where sbox's manifest will be written and must be outside outputDir. sbox
@@ -155,6 +171,25 @@
return r
}
+// SandboxInputs enables input sandboxing for the rule by copying any referenced inputs into the
+// sandbox. It also implies SandboxTools().
+//
+// Sandboxing inputs requires RuleBuilder to be aware of all references to input paths. Paths
+// that are passed to RuleBuilder outside of the methods that expect inputs, for example
+// FlagWithArg, must use RuleBuilderCommand.PathForInput to translate the path to one that matches
+// the sandbox layout.
+func (r *RuleBuilder) SandboxInputs() *RuleBuilder {
+ if !r.sbox {
+ panic("SandboxInputs() must be called after Sbox()")
+ }
+ if len(r.commands) > 0 {
+ panic("SandboxInputs() may not be called after Command()")
+ }
+ r.sboxTools = true
+ r.sboxInputs = true
+ return r
+}
+
// Install associates an output of the rule with an install location, which can be retrieved later using
// RuleBuilder.Installs.
func (r *RuleBuilder) Install(from Path, to string) {
@@ -374,17 +409,23 @@
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
+ for _, rspFile := range c.rspFiles {
+ rspFileInputs = append(rspFileInputs, rspFile.paths...)
}
}
return rspFileInputs
}
+func (r *RuleBuilder) rspFiles() []rspFileAndPaths {
+ var rspFiles []rspFileAndPaths
+ for _, c := range r.commands {
+ rspFiles = append(rspFiles, c.rspFiles...)
+ }
+
+ return rspFiles
+}
+
// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
func (r *RuleBuilder) Commands() []string {
var commands []string
@@ -394,16 +435,6 @@
return commands
}
-// NinjaEscapedCommands returns a slice containing 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
-}
-
// BuilderContext is a subset of ModuleContext and SingletonContext.
type BuilderContext interface {
PathContext
@@ -458,9 +489,10 @@
}
tools := r.Tools()
- commands := r.NinjaEscapedCommands()
+ commands := r.Commands()
outputs := r.Outputs()
inputs := r.Inputs()
+ rspFiles := r.rspFiles()
if len(commands) == 0 {
return
@@ -504,6 +536,38 @@
}
}
+ // If sandboxing inputs is enabled, add copy rules to the manifest to copy each input
+ // into the sbox directory.
+ if r.sboxInputs {
+ for _, input := range inputs {
+ command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
+ From: proto.String(input.String()),
+ To: proto.String(r.sboxPathForInputRel(input)),
+ })
+ }
+
+ // If using rsp files copy them and their contents into the sbox directory with
+ // the appropriate path mappings.
+ for _, rspFile := range rspFiles {
+ command.RspFiles = append(command.RspFiles, &sbox_proto.RspFile{
+ File: proto.String(rspFile.file.String()),
+ // These have to match the logic in sboxPathForInputRel
+ PathMappings: []*sbox_proto.PathMapping{
+ {
+ From: proto.String(r.outDir.String()),
+ To: proto.String(sboxOutSubDir),
+ },
+ {
+ From: proto.String(PathForOutput(r.ctx).String()),
+ To: proto.String(sboxOutSubDir),
+ },
+ },
+ })
+ }
+
+ command.Chdir = proto.Bool(true)
+ }
+
// Add copy rules to the manifest to copy each output file from the sbox directory.
// to the output directory after running the commands.
sboxOutputs := make([]string, len(outputs))
@@ -516,6 +580,12 @@
})
}
+ // Outputs that were marked Temporary will not be checked that they are in the output
+ // directory by the loop above, check them here.
+ for path := range r.temporariesSet {
+ Rel(r.ctx, r.outDir.String(), path.String())
+ }
+
// Add a hash of the list of input files to the manifest so that the textproto file
// changes when the list of input files changes and causes the sbox rule that
// depends on it to rerun.
@@ -551,6 +621,35 @@
commandString = sboxCmd.buf.String()
tools = append(tools, sboxCmd.tools...)
inputs = append(inputs, sboxCmd.inputs...)
+
+ if r.rbeParams != nil {
+ // RBE needs a list of input files to copy to the remote builder. For inputs already
+ // listed in an rsp file, pass the rsp file directly to rewrapper. For the rest,
+ // create a new rsp file to pass to rewrapper.
+ var remoteRspFiles Paths
+ var remoteInputs Paths
+
+ remoteInputs = append(remoteInputs, inputs...)
+ remoteInputs = append(remoteInputs, tools...)
+
+ for _, rspFile := range rspFiles {
+ remoteInputs = append(remoteInputs, rspFile.file)
+ remoteRspFiles = append(remoteRspFiles, rspFile.file)
+ }
+
+ if len(remoteInputs) > 0 {
+ inputsListFile := r.sboxManifestPath.ReplaceExtension(r.ctx, "rbe_inputs.list")
+ writeRspFileRule(r.ctx, inputsListFile, remoteInputs)
+ remoteRspFiles = append(remoteRspFiles, inputsListFile)
+ // Add the new rsp file as an extra input to the rule.
+ inputs = append(inputs, inputsListFile)
+ }
+
+ r.rbeParams.OutputFiles = outputs.Strings()
+ r.rbeParams.RSPFiles = remoteRspFiles.Strings()
+ rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper())
+ commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'"
+ }
} else {
// If not using sbox the rule will run the command directly, put the hash of the
// list of input files in a comment at the end of the command line to ensure ninja
@@ -559,16 +658,30 @@
}
// Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
- // ImplicitOutputs. RuleBuilder only uses "$out" for the rsp file location, so the distinction between Outputs and
+ // ImplicitOutputs. RuleBuilder doesn't use "$out", 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"
+ var rspFileInputs Paths
+ if len(rspFiles) > 0 {
+ // The first rsp files uses Ninja's rsp file support for the rule
+ rspFile = rspFiles[0].file.String()
+ // Use "$in" for rspFileContent to avoid duplicating the list of files in the dependency
+ // list and in the contents of the rsp file. Inputs to the rule that are not in the
+ // rsp file will be listed in Implicits instead of Inputs so they don't show up in "$in".
rspFileContent = "$in"
+ rspFileInputs = append(rspFileInputs, rspFiles[0].paths...)
+
+ for _, rspFile := range rspFiles[1:] {
+ // Any additional rsp files need an extra rule to write the file.
+ writeRspFileRule(r.ctx, rspFile.file, rspFile.paths)
+ // The main rule needs to depend on the inputs listed in the extra rsp file.
+ inputs = append(inputs, rspFile.paths...)
+ // The main rule needs to depend on the extra rsp file.
+ inputs = append(inputs, rspFile.file)
+ }
}
var pool blueprint.Pool
@@ -585,10 +698,10 @@
r.ctx.Build(r.pctx, BuildParams{
Rule: r.ctx.Rule(pctx, name, blueprint.RuleParams{
- Command: commandString,
- CommandDeps: tools.Strings(),
+ Command: proptools.NinjaEscape(commandString),
+ CommandDeps: proptools.NinjaEscapeList(tools.Strings()),
Restat: r.restat,
- Rspfile: rspFile,
+ Rspfile: proptools.NinjaEscape(rspFile),
RspfileContent: rspFileContent,
Pool: pool,
}),
@@ -619,36 +732,54 @@
depFiles WritablePaths
tools Paths
packagedTools []PackagingSpec
- rspFileInputs Paths
+ rspFiles []rspFileAndPaths
+}
- // spans [start,end) of the command that should not be ninja escaped
- unescapedSpans [][2]int
+type rspFileAndPaths struct {
+ file WritablePath
+ paths Paths
}
func (c *RuleBuilderCommand) addInput(path Path) string {
- if c.rule.sbox {
- if rel, isRel, _ := maybeRelErr(c.rule.outDir.String(), path.String()); isRel {
- return filepath.Join(sboxOutDir, rel)
- }
- }
c.inputs = append(c.inputs, path)
- return path.String()
+ return c.PathForInput(path)
}
-func (c *RuleBuilderCommand) addImplicit(path Path) string {
- if c.rule.sbox {
- if rel, isRel, _ := maybeRelErr(c.rule.outDir.String(), path.String()); isRel {
- return filepath.Join(sboxOutDir, rel)
- }
- }
+func (c *RuleBuilderCommand) addImplicit(path Path) {
c.implicits = append(c.implicits, path)
- return path.String()
}
func (c *RuleBuilderCommand) addOrderOnly(path Path) {
c.orderOnlys = append(c.orderOnlys, path)
}
+// PathForInput takes an input path and returns the appropriate path to use on the command line. If
+// sbox was enabled via a call to RuleBuilder.Sbox() and the path was an output path it returns a
+// path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
+// original path.
+func (c *RuleBuilderCommand) PathForInput(path Path) string {
+ if c.rule.sbox {
+ rel, inSandbox := c.rule._sboxPathForInputRel(path)
+ if inSandbox {
+ rel = filepath.Join(sboxSandboxBaseDir, rel)
+ }
+ return rel
+ }
+ return path.String()
+}
+
+// PathsForInputs takes a list of input paths and returns the appropriate paths to use on the
+// command line. If sbox was enabled via a call to RuleBuilder.Sbox() a path was an output path, it
+// returns the path with the placeholder prefix used for outputs in sbox. If sbox is not enabled it
+// returns the original paths.
+func (c *RuleBuilderCommand) PathsForInputs(paths Paths) []string {
+ ret := make([]string, len(paths))
+ for i, path := range paths {
+ ret[i] = c.PathForInput(path)
+ }
+ return ret
+}
+
// PathForOutput takes an output path and returns the appropriate path to use on the command
// line. If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the
// placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
@@ -662,13 +793,6 @@
return path.String()
}
-// SboxPathForTool takes a path to a tool, which may be an output file or a source file, and returns
-// the corresponding path for the tool in the sbox sandbox. It assumes that sandboxing and tool
-// sandboxing are enabled.
-func SboxPathForTool(ctx BuilderContext, path Path) string {
- return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(ctx, path))
-}
-
func sboxPathForToolRel(ctx BuilderContext, path Path) string {
// Errors will be handled in RuleBuilder.Build where we have a context to report them
relOut, isRelOut, _ := maybeRelErr(PathForOutput(ctx, "host", ctx.Config().PrebuiltOS()).String(), path.String())
@@ -680,17 +804,52 @@
return filepath.Join(sboxToolsSubDir, "src", path.String())
}
-// SboxPathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
-// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to
-// reference the tool.
-func SboxPathForPackagedTool(spec PackagingSpec) string {
- return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
+func (r *RuleBuilder) _sboxPathForInputRel(path Path) (rel string, inSandbox bool) {
+ // Errors will be handled in RuleBuilder.Build where we have a context to report them
+ rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String())
+ if isRelSboxOut {
+ return filepath.Join(sboxOutSubDir, rel), true
+ }
+ if r.sboxInputs {
+ // When sandboxing inputs all inputs have to be copied into the sandbox. Input files that
+ // are outputs of other rules could be an arbitrary absolute path if OUT_DIR is set, so they
+ // will be copied to relative paths under __SBOX_OUT_DIR__/out.
+ rel, isRelOut, _ := maybeRelErr(PathForOutput(r.ctx).String(), path.String())
+ if isRelOut {
+ return filepath.Join(sboxOutSubDir, rel), true
+ }
+ }
+ return path.String(), false
+}
+
+func (r *RuleBuilder) sboxPathForInputRel(path Path) string {
+ rel, _ := r._sboxPathForInputRel(path)
+ return rel
+}
+
+func (r *RuleBuilder) sboxPathsForInputsRel(paths Paths) []string {
+ ret := make([]string, len(paths))
+ for i, path := range paths {
+ ret[i] = r.sboxPathForInputRel(path)
+ }
+ return ret
}
func sboxPathForPackagedToolRel(spec PackagingSpec) string {
return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
}
+// PathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
+// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to
+// reference the tool.
+func (c *RuleBuilderCommand) PathForPackagedTool(spec PackagingSpec) string {
+ if !c.rule.sboxTools {
+ panic("PathForPackagedTool() requires SandboxTools()")
+ }
+
+ return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
+}
+
// PathForTool takes a path to a tool, which may be an output file or a source file, and returns
// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path
// if it is not. This can be used on the RuleBuilder command line to reference the tool.
@@ -701,6 +860,20 @@
return path.String()
}
+// PathsForTools takes a list of paths to tools, which may be output files or source files, and
+// returns the corresponding paths for the tools in the sbox sandbox if sbox is enabled, or the
+// original paths if it is not. This can be used on the RuleBuilder command line to reference the tool.
+func (c *RuleBuilderCommand) PathsForTools(paths Paths) []string {
+ if c.rule.sbox && c.rule.sboxTools {
+ var ret []string
+ for _, path := range paths {
+ ret = append(ret, filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path)))
+ }
+ return ret
+ }
+ return paths.Strings()
+}
+
// PackagedTool adds the specified tool path to the command line. It can only be used with tool
// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
@@ -1019,24 +1192,28 @@
return c.Text(flag + c.PathForOutput(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")
- }
-
+// 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. If sbox is enabled, the
+// rspfile must be outside the sbox directory. The first use of FlagWithRspFileInputList in any
+// RuleBuilderCommand of a RuleBuilder will use Ninja's rsp file support for the rule, additional
+// uses will result in an auxiliary rules to write the rspFile contents.
+func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand {
// 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
+ c.rspFiles = append(c.rspFiles, rspFileAndPaths{rspFile, paths})
- rspFile := "$out.rsp"
- c.FlagWithArg(flag, rspFile)
- c.unescapedSpans = append(c.unescapedSpans, [2]int{c.buf.Len() - len(rspFile), c.buf.Len()})
+ if c.rule.sbox {
+ if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel {
+ panic(fmt.Errorf("FlagWithRspFileInputList rspfile %q must not be inside out dir %q",
+ rspFile.String(), c.rule.outDir.String()))
+ }
+ }
+
+ c.FlagWithArg(flag, c.PathForInput(rspFile))
return c
}
@@ -1045,11 +1222,6 @@
return c.buf.String()
}
-// String returns the command line.
-func (c *RuleBuilderCommand) NinjaEscapedString() string {
- return ninjaEscapeExceptForSpans(c.String(), c.unescapedSpans)
-}
-
// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
// and returns sbox testproto generated by the RuleBuilder.
func RuleBuilderSboxProtoForTests(t *testing.T, params TestingBuildParams) *sbox_proto.Manifest {
@@ -1063,25 +1235,6 @@
return &manifest
}
-func ninjaEscapeExceptForSpans(s string, spans [][2]int) string {
- if len(spans) == 0 {
- return proptools.NinjaEscape(s)
- }
-
- sb := strings.Builder{}
- sb.Grow(len(s) * 11 / 10)
-
- i := 0
- for _, span := range spans {
- sb.WriteString(proptools.NinjaEscape(s[i:span[0]]))
- sb.WriteString(s[span[0]:span[1]])
- i = span[1]
- }
- sb.WriteString(proptools.NinjaEscape(s[i:]))
-
- return sb.String()
-}
-
func ninjaNameEscape(s string) string {
b := []byte(s)
escaped := false
@@ -1129,3 +1282,13 @@
return nil
}
func (builderContextForTests) Build(PackageContext, BuildParams) {}
+
+func writeRspFileRule(ctx BuilderContext, rspFile WritablePath, paths Paths) {
+ buf := &strings.Builder{}
+ err := response.WriteRspFile(buf, paths.Strings())
+ if err != nil {
+ // There should never be I/O errors writing to a bytes.Buffer.
+ panic(err)
+ }
+ WriteFileRule(ctx, rspFile, buf.String())
+}
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index 06ea124..d2a7d8d 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -17,7 +17,6 @@
import (
"fmt"
"path/filepath"
- "reflect"
"regexp"
"strings"
"testing"
@@ -267,10 +266,10 @@
ctx := builderContext()
fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "javac")).
- FlagWithRspFileInputList("@", PathsForTesting("a.java", "b.java")).
- NinjaEscapedString())
+ FlagWithRspFileInputList("@", PathForOutput(ctx, "foo.rsp"), PathsForTesting("a.java", "b.java")).
+ String())
// Output:
- // javac @$out.rsp
+ // javac @out/foo.rsp
}
func ExampleRuleBuilderCommand_String() {
@@ -283,16 +282,6 @@
// FOO=foo echo $FOO
}
-func ExampleRuleBuilderCommand_NinjaEscapedString() {
- ctx := builderContext()
- fmt.Println(NewRuleBuilder(pctx, ctx).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,
@@ -307,35 +296,40 @@
"input3": nil,
}
- pathCtx := PathContextForTesting(TestConfig("out", nil, "", fs))
+ pathCtx := PathContextForTesting(TestConfig("out_local", nil, "", fs))
ctx := builderContextForTests{
PathContext: pathCtx,
}
addCommands := func(rule *RuleBuilder) {
cmd := rule.Command().
- DepFile(PathForOutput(ctx, "DepFile")).
+ DepFile(PathForOutput(ctx, "module/DepFile")).
Flag("Flag").
FlagWithArg("FlagWithArg=", "arg").
- FlagWithDepFile("FlagWithDepFile=", PathForOutput(ctx, "depfile")).
+ FlagWithDepFile("FlagWithDepFile=", PathForOutput(ctx, "module/depfile")).
FlagWithInput("FlagWithInput=", PathForSource(ctx, "input")).
- FlagWithOutput("FlagWithOutput=", PathForOutput(ctx, "output")).
+ FlagWithOutput("FlagWithOutput=", PathForOutput(ctx, "module/output")).
+ FlagWithRspFileInputList("FlagWithRspFileInputList=", PathForOutput(ctx, "rsp"),
+ Paths{
+ PathForSource(ctx, "RspInput"),
+ PathForOutput(ctx, "other/RspOutput2"),
+ }).
Implicit(PathForSource(ctx, "Implicit")).
- ImplicitDepFile(PathForOutput(ctx, "ImplicitDepFile")).
- ImplicitOutput(PathForOutput(ctx, "ImplicitOutput")).
+ ImplicitDepFile(PathForOutput(ctx, "module/ImplicitDepFile")).
+ ImplicitOutput(PathForOutput(ctx, "module/ImplicitOutput")).
Input(PathForSource(ctx, "Input")).
- Output(PathForOutput(ctx, "Output")).
+ Output(PathForOutput(ctx, "module/Output")).
OrderOnly(PathForSource(ctx, "OrderOnly")).
- SymlinkOutput(PathForOutput(ctx, "SymlinkOutput")).
- ImplicitSymlinkOutput(PathForOutput(ctx, "ImplicitSymlinkOutput")).
+ SymlinkOutput(PathForOutput(ctx, "module/SymlinkOutput")).
+ ImplicitSymlinkOutput(PathForOutput(ctx, "module/ImplicitSymlinkOutput")).
Text("Text").
Tool(PathForSource(ctx, "Tool"))
rule.Command().
Text("command2").
- DepFile(PathForOutput(ctx, "depfile2")).
+ DepFile(PathForOutput(ctx, "module/depfile2")).
Input(PathForSource(ctx, "input2")).
- Output(PathForOutput(ctx, "output2")).
+ Output(PathForOutput(ctx, "module/output2")).
OrderOnlys(PathsForSource(ctx, []string{"OrderOnlys"})).
Tool(PathForSource(ctx, "tool2"))
@@ -348,131 +342,138 @@
rule.Command().
Text("command3").
Input(PathForSource(ctx, "input3")).
- Input(PathForOutput(ctx, "output2")).
- Output(PathForOutput(ctx, "output3"))
+ Input(PathForOutput(ctx, "module/output2")).
+ Output(PathForOutput(ctx, "module/output3")).
+ Text(cmd.PathForInput(PathForSource(ctx, "input3"))).
+ Text(cmd.PathForOutput(PathForOutput(ctx, "module/output2")))
}
wantInputs := PathsForSource(ctx, []string{"Implicit", "Input", "input", "input2", "input3"})
- wantOutputs := PathsForOutput(ctx, []string{"ImplicitOutput", "ImplicitSymlinkOutput", "Output", "SymlinkOutput", "output", "output2", "output3"})
- wantDepFiles := PathsForOutput(ctx, []string{"DepFile", "depfile", "ImplicitDepFile", "depfile2"})
+ wantRspFileInputs := Paths{PathForSource(ctx, "RspInput"),
+ PathForOutput(ctx, "other/RspOutput2")}
+ wantOutputs := PathsForOutput(ctx, []string{
+ "module/ImplicitOutput", "module/ImplicitSymlinkOutput", "module/Output", "module/SymlinkOutput",
+ "module/output", "module/output2", "module/output3"})
+ wantDepFiles := PathsForOutput(ctx, []string{
+ "module/DepFile", "module/depfile", "module/ImplicitDepFile", "module/depfile2"})
wantTools := PathsForSource(ctx, []string{"Tool", "tool2"})
wantOrderOnlys := PathsForSource(ctx, []string{"OrderOnly", "OrderOnlys"})
- wantSymlinkOutputs := PathsForOutput(ctx, []string{"ImplicitSymlinkOutput", "SymlinkOutput"})
+ wantSymlinkOutputs := PathsForOutput(ctx, []string{
+ "module/ImplicitSymlinkOutput", "module/SymlinkOutput"})
t.Run("normal", func(t *testing.T) {
rule := NewRuleBuilder(pctx, ctx)
addCommands(rule)
wantCommands := []string{
- "out/DepFile Flag FlagWithArg=arg FlagWithDepFile=out/depfile FlagWithInput=input FlagWithOutput=out/output Input out/Output out/SymlinkOutput Text Tool after command2 old cmd",
- "command2 out/depfile2 input2 out/output2 tool2",
- "command3 input3 out/output2 out/output3",
+ "out_local/module/DepFile Flag FlagWithArg=arg FlagWithDepFile=out_local/module/depfile " +
+ "FlagWithInput=input FlagWithOutput=out_local/module/output FlagWithRspFileInputList=out_local/rsp " +
+ "Input out_local/module/Output out_local/module/SymlinkOutput Text Tool after command2 old cmd",
+ "command2 out_local/module/depfile2 input2 out_local/module/output2 tool2",
+ "command3 input3 out_local/module/output2 out_local/module/output3 input3 out_local/module/output2",
}
- wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer out/DepFile out/depfile out/ImplicitDepFile out/depfile2"
+ wantDepMergerCommand := "out_local/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer " +
+ "out_local/module/DepFile out_local/module/depfile out_local/module/ImplicitDepFile out_local/module/depfile2"
- if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
- t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
- }
+ AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
- if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
- }
- if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
- }
- if g, w := rule.SymlinkOutputs(), wantSymlinkOutputs; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.SymlinkOutputs() = %#v\n got %#v", w, g)
- }
- if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
- }
- if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
- }
- if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g)
- }
+ AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
+ AssertDeepEquals(t, "rule.RspfileInputs()", wantRspFileInputs, rule.RspFileInputs())
+ AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs())
+ AssertDeepEquals(t, "rule.SymlinkOutputs()", wantSymlinkOutputs, rule.SymlinkOutputs())
+ AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles())
+ AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools())
+ AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
- if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
- t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
- }
+ AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String())
})
t.Run("sbox", func(t *testing.T) {
- rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, ""),
+ rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, "module"),
PathForOutput(ctx, "sbox.textproto"))
addCommands(rule)
wantCommands := []string{
- "__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output Input __SBOX_SANDBOX_DIR__/out/Output __SBOX_SANDBOX_DIR__/out/SymlinkOutput Text Tool after command2 old cmd",
+ "__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile " +
+ "FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output " +
+ "FlagWithRspFileInputList=out_local/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
+ "__SBOX_SANDBOX_DIR__/out/SymlinkOutput Text Tool after command2 old cmd",
"command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 tool2",
- "command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3",
+ "command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3 input3 __SBOX_SANDBOX_DIR__/out/output2",
}
- wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
+ wantDepMergerCommand := "out_local/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
- if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
- t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
- }
+ AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
- if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
- }
- if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
- }
- if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
- }
- if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
- }
- if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g)
- }
+ AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
+ AssertDeepEquals(t, "rule.RspfileInputs()", wantRspFileInputs, rule.RspFileInputs())
+ AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs())
+ AssertDeepEquals(t, "rule.SymlinkOutputs()", wantSymlinkOutputs, rule.SymlinkOutputs())
+ AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles())
+ AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools())
+ AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
- if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
- t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
- }
+ AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String())
})
t.Run("sbox tools", func(t *testing.T) {
- rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, ""),
+ rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, "module"),
PathForOutput(ctx, "sbox.textproto")).SandboxTools()
addCommands(rule)
wantCommands := []string{
- "__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output Input __SBOX_SANDBOX_DIR__/out/Output __SBOX_SANDBOX_DIR__/out/SymlinkOutput Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
+ "__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile " +
+ "FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output " +
+ "FlagWithRspFileInputList=out_local/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
+ "__SBOX_SANDBOX_DIR__/out/SymlinkOutput Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
"command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/tools/src/tool2",
- "command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3",
+ "command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3 input3 __SBOX_SANDBOX_DIR__/out/output2",
}
wantDepMergerCommand := "__SBOX_SANDBOX_DIR__/tools/out/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
- if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
- t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
+ AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
+
+ AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
+ AssertDeepEquals(t, "rule.RspfileInputs()", wantRspFileInputs, rule.RspFileInputs())
+ AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs())
+ AssertDeepEquals(t, "rule.SymlinkOutputs()", wantSymlinkOutputs, rule.SymlinkOutputs())
+ AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles())
+ AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools())
+ AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
+
+ AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String())
+ })
+
+ t.Run("sbox inputs", func(t *testing.T) {
+ rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, "module"),
+ PathForOutput(ctx, "sbox.textproto")).SandboxInputs()
+ addCommands(rule)
+
+ wantCommands := []string{
+ "__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile " +
+ "FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output " +
+ "FlagWithRspFileInputList=__SBOX_SANDBOX_DIR__/out/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
+ "__SBOX_SANDBOX_DIR__/out/SymlinkOutput Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
+ "command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/tools/src/tool2",
+ "command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3 input3 __SBOX_SANDBOX_DIR__/out/output2",
}
- if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
- }
- if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
- }
- if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
- }
- if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
- }
- if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) {
- t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g)
- }
+ wantDepMergerCommand := "__SBOX_SANDBOX_DIR__/tools/out/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
- if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
- t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
- }
+ AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
+
+ AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
+ AssertDeepEquals(t, "rule.RspfileInputs()", wantRspFileInputs, rule.RspFileInputs())
+ AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs())
+ AssertDeepEquals(t, "rule.SymlinkOutputs()", wantSymlinkOutputs, rule.SymlinkOutputs())
+ AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles())
+ AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools())
+ AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
+
+ AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String())
})
}
@@ -488,8 +489,9 @@
properties struct {
Srcs []string
- Restat bool
- Sbox bool
+ Restat bool
+ Sbox bool
+ Sbox_inputs bool
}
}
@@ -498,9 +500,15 @@
out := PathForModuleOut(ctx, "gen", ctx.ModuleName())
outDep := PathForModuleOut(ctx, "gen", ctx.ModuleName()+".d")
outDir := PathForModuleOut(ctx, "gen")
+ rspFile := PathForModuleOut(ctx, "rsp")
+ rspFile2 := PathForModuleOut(ctx, "rsp2")
+ rspFileContents := PathsForSource(ctx, []string{"rsp_in"})
+ rspFileContents2 := PathsForSource(ctx, []string{"rsp_in2"})
manifestPath := PathForModuleOut(ctx, "sbox.textproto")
- testRuleBuilder_Build(ctx, in, out, outDep, outDir, manifestPath, t.properties.Restat, t.properties.Sbox)
+ testRuleBuilder_Build(ctx, in, out, outDep, outDir, manifestPath, t.properties.Restat,
+ t.properties.Sbox, t.properties.Sbox_inputs, rspFile, rspFileContents,
+ rspFile2, rspFileContents2)
}
type testRuleBuilderSingleton struct{}
@@ -514,18 +522,35 @@
out := PathForOutput(ctx, "singleton/gen/baz")
outDep := PathForOutput(ctx, "singleton/gen/baz.d")
outDir := PathForOutput(ctx, "singleton/gen")
+ rspFile := PathForOutput(ctx, "singleton/rsp")
+ rspFile2 := PathForOutput(ctx, "singleton/rsp2")
+ rspFileContents := PathsForSource(ctx, []string{"rsp_in"})
+ rspFileContents2 := PathsForSource(ctx, []string{"rsp_in2"})
manifestPath := PathForOutput(ctx, "singleton/sbox.textproto")
- testRuleBuilder_Build(ctx, Paths{in}, out, outDep, outDir, manifestPath, true, false)
+ testRuleBuilder_Build(ctx, Paths{in}, out, outDep, outDir, manifestPath, true, false, false,
+ rspFile, rspFileContents, rspFile2, rspFileContents2)
}
-func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir, manifestPath WritablePath, restat, sbox bool) {
+func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir, manifestPath WritablePath,
+ restat, sbox, sboxInputs bool,
+ rspFile WritablePath, rspFileContents Paths, rspFile2 WritablePath, rspFileContents2 Paths) {
+
rule := NewRuleBuilder(pctx, ctx)
if sbox {
rule.Sbox(outDir, manifestPath)
+ if sboxInputs {
+ rule.SandboxInputs()
+ }
}
- rule.Command().Tool(PathForSource(ctx, "cp")).Inputs(in).Output(out).ImplicitDepFile(outDep)
+ rule.Command().
+ Tool(PathForSource(ctx, "cp")).
+ Inputs(in).
+ Output(out).
+ ImplicitDepFile(outDep).
+ FlagWithRspFileInputList("@", rspFile, rspFileContents).
+ FlagWithRspFileInputList("@", rspFile2, rspFileContents2)
if restat {
rule.Restat()
@@ -534,8 +559,13 @@
rule.Build("rule", "desc")
}
+var prepareForRuleBuilderTest = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
+ ctx.RegisterSingletonType("rule_builder_test", testRuleBuilderSingletonFactory)
+})
+
func TestRuleBuilder_Build(t *testing.T) {
- fs := map[string][]byte{
+ fs := MockFS{
"bar": nil,
"cp": nil,
}
@@ -551,160 +581,118 @@
srcs: ["bar"],
sbox: true,
}
+ rule_builder_test {
+ name: "foo_sbox_inputs",
+ srcs: ["bar"],
+ sbox: true,
+ sbox_inputs: true,
+ }
`
- config := TestConfig(buildDir, nil, bp, fs)
- ctx := NewTestContext(config)
- ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
- ctx.RegisterSingletonType("rule_builder_test", testRuleBuilderSingletonFactory)
- ctx.Register()
+ result := GroupFixturePreparers(
+ prepareForRuleBuilderTest,
+ FixtureWithRootAndroidBp(bp),
+ fs.AddToFixture(),
+ ).RunTest(t)
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
+ check := func(t *testing.T, params TestingBuildParams, rspFile2Params TestingBuildParams,
+ wantCommand, wantOutput, wantDepfile, wantRspFile, wantRspFile2 string,
+ wantRestat bool, extraImplicits, extraCmdDeps []string) {
- check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraImplicits, extraCmdDeps []string) {
t.Helper()
command := params.RuleParams.Command
re := regexp.MustCompile(" # hash of input list: [a-z0-9]*$")
command = re.ReplaceAllLiteralString(command, "")
- if command != wantCommand {
- t.Errorf("\nwant RuleParams.Command = %q\n got %q", wantCommand, params.RuleParams.Command)
- }
+
+ AssertStringEquals(t, "RuleParams.Command", wantCommand, command)
wantDeps := append([]string{"cp"}, extraCmdDeps...)
- if !reflect.DeepEqual(params.RuleParams.CommandDeps, wantDeps) {
- t.Errorf("\nwant RuleParams.CommandDeps = %q\n got %q", wantDeps, params.RuleParams.CommandDeps)
- }
+ AssertArrayString(t, "RuleParams.CommandDeps", wantDeps, params.RuleParams.CommandDeps)
- if params.RuleParams.Restat != wantRestat {
- t.Errorf("want RuleParams.Restat = %v, got %v", wantRestat, params.RuleParams.Restat)
- }
+ AssertBoolEquals(t, "RuleParams.Restat", wantRestat, params.RuleParams.Restat)
+
+ wantInputs := []string{"rsp_in"}
+ AssertArrayString(t, "Inputs", wantInputs, params.Inputs.Strings())
wantImplicits := append([]string{"bar"}, extraImplicits...)
- if !reflect.DeepEqual(params.Implicits.Strings(), wantImplicits) {
- t.Errorf("want Implicits = [%q], got %q", "bar", params.Implicits.Strings())
- }
+ // The second rsp file and the files listed in it should be in implicits
+ wantImplicits = append(wantImplicits, "rsp_in2", wantRspFile2)
+ AssertPathsRelativeToTopEquals(t, "Implicits", wantImplicits, params.Implicits)
- if params.Output.String() != wantOutput {
- t.Errorf("want Output = %q, got %q", wantOutput, params.Output)
- }
+ wantRspFileContent := "$in"
+ AssertStringEquals(t, "RspfileContent", wantRspFileContent, params.RuleParams.RspfileContent)
+
+ AssertStringEquals(t, "Rspfile", wantRspFile, params.RuleParams.Rspfile)
+
+ AssertPathRelativeToTopEquals(t, "Output", wantOutput, params.Output)
if len(params.ImplicitOutputs) != 0 {
t.Errorf("want ImplicitOutputs = [], got %q", params.ImplicitOutputs.Strings())
}
- if params.Depfile.String() != wantDepfile {
- t.Errorf("want Depfile = %q, got %q", wantDepfile, params.Depfile)
- }
+ AssertPathRelativeToTopEquals(t, "Depfile", wantDepfile, params.Depfile)
if params.Deps != blueprint.DepsGCC {
t.Errorf("want Deps = %q, got %q", blueprint.DepsGCC, params.Deps)
}
+
+ rspFile2Content := ContentFromFileRuleForTests(t, rspFile2Params)
+ AssertStringEquals(t, "rspFile2 content", "rsp_in2\n", rspFile2Content)
}
t.Run("module", func(t *testing.T) {
- outFile := filepath.Join(buildDir, ".intermediates", "foo", "gen", "foo")
- check(t, ctx.ModuleForTests("foo", "").Rule("rule"),
- "cp bar "+outFile,
- outFile, outFile+".d", true, nil, nil)
+ outFile := "out/soong/.intermediates/foo/gen/foo"
+ rspFile := "out/soong/.intermediates/foo/rsp"
+ rspFile2 := "out/soong/.intermediates/foo/rsp2"
+ module := result.ModuleForTests("foo", "")
+ check(t, module.Rule("rule"), module.Output(rspFile2),
+ "cp bar "+outFile+" @"+rspFile+" @"+rspFile2,
+ outFile, outFile+".d", rspFile, rspFile2, true, nil, nil)
})
t.Run("sbox", func(t *testing.T) {
- outDir := filepath.Join(buildDir, ".intermediates", "foo_sbox")
+ outDir := "out/soong/.intermediates/foo_sbox"
outFile := filepath.Join(outDir, "gen/foo_sbox")
depFile := filepath.Join(outDir, "gen/foo_sbox.d")
+ rspFile := filepath.Join(outDir, "rsp")
+ rspFile2 := filepath.Join(outDir, "rsp2")
manifest := filepath.Join(outDir, "sbox.textproto")
- sbox := filepath.Join(buildDir, "host", config.PrebuiltOS(), "bin/sbox")
- sandboxPath := shared.TempDirForOutDir(buildDir)
+ sbox := filepath.Join("out", "soong", "host", result.Config.PrebuiltOS(), "bin/sbox")
+ sandboxPath := shared.TempDirForOutDir("out/soong")
+
+ cmd := `rm -rf ` + outDir + `/gen && ` +
+ sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
+ module := result.ModuleForTests("foo_sbox", "")
+ check(t, module.Output("gen/foo_sbox"), module.Output(rspFile2),
+ cmd, outFile, depFile, rspFile, rspFile2, false, []string{manifest}, []string{sbox})
+ })
+ t.Run("sbox_inputs", func(t *testing.T) {
+ outDir := "out/soong/.intermediates/foo_sbox_inputs"
+ outFile := filepath.Join(outDir, "gen/foo_sbox_inputs")
+ depFile := filepath.Join(outDir, "gen/foo_sbox_inputs.d")
+ rspFile := filepath.Join(outDir, "rsp")
+ rspFile2 := filepath.Join(outDir, "rsp2")
+ manifest := filepath.Join(outDir, "sbox.textproto")
+ sbox := filepath.Join("out", "soong", "host", result.Config.PrebuiltOS(), "bin/sbox")
+ sandboxPath := shared.TempDirForOutDir("out/soong")
cmd := `rm -rf ` + outDir + `/gen && ` +
sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
- check(t, ctx.ModuleForTests("foo_sbox", "").Output("gen/foo_sbox"),
- cmd, outFile, depFile, false, []string{manifest}, []string{sbox})
+ module := result.ModuleForTests("foo_sbox_inputs", "")
+ check(t, module.Output("gen/foo_sbox_inputs"), module.Output(rspFile2),
+ cmd, outFile, depFile, rspFile, rspFile2, false, []string{manifest}, []string{sbox})
})
t.Run("singleton", func(t *testing.T) {
- outFile := filepath.Join(buildDir, "singleton/gen/baz")
- check(t, ctx.SingletonForTests("rule_builder_test").Rule("rule"),
- "cp bar "+outFile, outFile, outFile+".d", true, nil, nil)
+ outFile := filepath.Join("out/soong/singleton/gen/baz")
+ rspFile := filepath.Join("out/soong/singleton/rsp")
+ rspFile2 := filepath.Join("out/soong/singleton/rsp2")
+ singleton := result.SingletonForTests("rule_builder_test")
+ check(t, singleton.Rule("rule"), singleton.Output(rspFile2),
+ "cp bar "+outFile+" @"+rspFile+" @"+rspFile2,
+ outFile, outFile+".d", rspFile, rspFile2, true, nil, 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)
- }
- })
- }
-}
-
func TestRuleBuilderHashInputs(t *testing.T) {
// The basic idea here is to verify that the command (in the case of a
// non-sbox rule) or the sbox textproto manifest contain a hash of the
@@ -750,29 +738,22 @@
},
}
- config := TestConfig(buildDir, nil, bp, nil)
- ctx := NewTestContext(config)
- ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
+ result := GroupFixturePreparers(
+ prepareForRuleBuilderTest,
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
for _, test := range testcases {
t.Run(test.name, func(t *testing.T) {
t.Run("sbox", func(t *testing.T) {
- gen := ctx.ModuleForTests(test.name+"_sbox", "")
+ gen := result.ModuleForTests(test.name+"_sbox", "")
manifest := RuleBuilderSboxProtoForTests(t, gen.Output("sbox.textproto"))
hash := manifest.Commands[0].GetInputHash()
- if g, w := hash, test.expectedHash; g != w {
- t.Errorf("Expected has %q, got %q", w, g)
- }
+ AssertStringEquals(t, "hash", test.expectedHash, hash)
})
t.Run("", func(t *testing.T) {
- gen := ctx.ModuleForTests(test.name+"", "")
+ gen := result.ModuleForTests(test.name+"", "")
command := gen.Output("gen/" + test.name).RuleParams.Command
if g, w := command, " # hash of input list: "+test.expectedHash; !strings.HasSuffix(g, w) {
t.Errorf("Expected command line to end with %q, got %q", w, g)
diff --git a/android/sandbox.go b/android/sandbox.go
index ed022fb..28e903a 100644
--- a/android/sandbox.go
+++ b/android/sandbox.go
@@ -14,29 +14,8 @@
package android
-import (
- "fmt"
- "os"
-)
-
-func init() {
- // Stash the working directory in a private variable and then change the working directory
- // to "/", which will prevent untracked accesses to files by Go Soong plugins. The
- // SOONG_SANDBOX_SOONG_BUILD environment variable is set by soong_ui, and is not
- // overrideable on the command line.
-
- orig, err := os.Getwd()
- if err != nil {
- panic(fmt.Errorf("failed to get working directory: %s", err))
- }
- absSrcDir = orig
-
- if getenv("SOONG_SANDBOX_SOONG_BUILD") == "true" {
- err = os.Chdir("/")
- if err != nil {
- panic(fmt.Errorf("failed to change working directory to '/': %s", err))
- }
- }
+func InitSandbox(topDir string) {
+ absSrcDir = topDir
}
// DO NOT USE THIS FUNCTION IN NEW CODE.
diff --git a/android/sdk_version.go b/android/sdk_version.go
new file mode 100644
index 0000000..98db824
--- /dev/null
+++ b/android/sdk_version.go
@@ -0,0 +1,281 @@
+// Copyright 2021 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"
+ "strconv"
+ "strings"
+)
+
+type SdkContext interface {
+ // SdkVersion returns SdkSpec that corresponds to the sdk_version property of the current module
+ SdkVersion(ctx EarlyModuleContext) SdkSpec
+ // SystemModules returns the system_modules property of the current module, or an empty string if it is not set.
+ SystemModules() string
+ // MinSdkVersion returns SdkSpec that corresponds to the min_sdk_version property of the current module,
+ // or from sdk_version if it is not set.
+ MinSdkVersion(ctx EarlyModuleContext) SdkSpec
+ // TargetSdkVersion returns the SdkSpec that corresponds to the target_sdk_version property of the current module,
+ // or from sdk_version if it is not set.
+ TargetSdkVersion(ctx EarlyModuleContext) SdkSpec
+}
+
+// SdkKind represents a particular category of an SDK spec like public, system, test, etc.
+type SdkKind int
+
+const (
+ SdkInvalid SdkKind = iota
+ SdkNone
+ SdkCore
+ SdkCorePlatform
+ SdkPublic
+ SdkSystem
+ SdkTest
+ SdkModule
+ SdkSystemServer
+ SdkPrivate
+)
+
+// String returns the string representation of this SdkKind
+func (k SdkKind) String() string {
+ switch k {
+ case SdkPrivate:
+ return "private"
+ case SdkNone:
+ return "none"
+ case SdkPublic:
+ return "public"
+ case SdkSystem:
+ return "system"
+ case SdkTest:
+ return "test"
+ case SdkCore:
+ return "core"
+ case SdkCorePlatform:
+ return "core_platform"
+ case SdkModule:
+ return "module-lib"
+ case SdkSystemServer:
+ return "system-server"
+ default:
+ return "invalid"
+ }
+}
+
+// SdkSpec represents the kind and the version of an SDK for a module to build against
+type SdkSpec struct {
+ Kind SdkKind
+ ApiLevel ApiLevel
+ Raw string
+}
+
+func (s SdkSpec) String() string {
+ return fmt.Sprintf("%s_%s", s.Kind, s.ApiLevel)
+}
+
+// Valid checks if this SdkSpec is well-formed. Note however that true doesn't mean that the
+// specified SDK actually exists.
+func (s SdkSpec) Valid() bool {
+ return s.Kind != SdkInvalid
+}
+
+// Specified checks if this SdkSpec is well-formed and is not "".
+func (s SdkSpec) Specified() bool {
+ return s.Valid() && s.Kind != SdkPrivate
+}
+
+// whether the API surface is managed and versioned, i.e. has .txt file that
+// get frozen on SDK freeze and changes get reviewed by API council.
+func (s SdkSpec) Stable() bool {
+ if !s.Specified() {
+ return false
+ }
+ switch s.Kind {
+ case SdkNone:
+ // there is nothing to manage and version in this case; de facto stable API.
+ return true
+ case SdkCore, SdkPublic, SdkSystem, SdkModule, SdkSystemServer:
+ return true
+ case SdkCorePlatform, SdkTest, SdkPrivate:
+ return false
+ default:
+ panic(fmt.Errorf("unknown SdkKind=%v", s.Kind))
+ }
+ return false
+}
+
+// PrebuiltSdkAvailableForUnbundledBuilt tells whether this SdkSpec can have a prebuilt SDK
+// that can be used for unbundled builds.
+func (s SdkSpec) PrebuiltSdkAvailableForUnbundledBuild() bool {
+ // "", "none", and "core_platform" are not available for unbundled build
+ // as we don't/can't have prebuilt stub for the versions
+ return s.Kind != SdkPrivate && s.Kind != SdkNone && s.Kind != SdkCorePlatform
+}
+
+func (s SdkSpec) ForVendorPartition(ctx EarlyModuleContext) SdkSpec {
+ // If BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES has a numeric value,
+ // use it instead of "current" for the vendor partition.
+ currentSdkVersion := ctx.DeviceConfig().CurrentApiLevelForVendorModules()
+ if currentSdkVersion == "current" {
+ return s
+ }
+
+ if s.Kind == SdkPublic || s.Kind == SdkSystem {
+ if s.ApiLevel.IsCurrent() {
+ if i, err := strconv.Atoi(currentSdkVersion); err == nil {
+ apiLevel := uncheckedFinalApiLevel(i)
+ return SdkSpec{s.Kind, apiLevel, s.Raw}
+ }
+ panic(fmt.Errorf("BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES must be either \"current\" or a number, but was %q", currentSdkVersion))
+ }
+ }
+ return s
+}
+
+// UsePrebuilt determines whether prebuilt SDK should be used for this SdkSpec with the given context.
+func (s SdkSpec) UsePrebuilt(ctx EarlyModuleContext) bool {
+ if s.ApiLevel.IsCurrent() {
+ // "current" can be built from source and be from prebuilt SDK
+ return ctx.Config().AlwaysUsePrebuiltSdks()
+ } else if !s.ApiLevel.IsPreview() {
+ // validation check
+ if s.Kind != SdkPublic && s.Kind != SdkSystem && s.Kind != SdkTest && s.Kind != SdkModule {
+ panic(fmt.Errorf("prebuilt SDK is not not available for SdkKind=%q", s.Kind))
+ return false
+ }
+ // numbered SDKs are always from prebuilt
+ return true
+ }
+ // "", "none", "core_platform" fall here
+ return false
+}
+
+// EffectiveVersion converts an SdkSpec into the concrete ApiLevel that the module should use. For
+// modules targeting an unreleased SDK (meaning it does not yet have a number) it returns
+// FutureApiLevel(10000).
+func (s SdkSpec) EffectiveVersion(ctx EarlyModuleContext) (ApiLevel, error) {
+ if !s.Valid() {
+ return s.ApiLevel, fmt.Errorf("invalid sdk version %q", s.Raw)
+ }
+
+ if ctx.DeviceSpecific() || ctx.SocSpecific() {
+ s = s.ForVendorPartition(ctx)
+ }
+ if !s.ApiLevel.IsPreview() {
+ return s.ApiLevel, nil
+ }
+ ret := ctx.Config().DefaultAppTargetSdk(ctx)
+ if ret.IsPreview() {
+ return FutureApiLevel, nil
+ }
+ return ret, nil
+}
+
+// EffectiveVersionString converts an SdkSpec into the concrete version string that the module
+// should use. For modules targeting an unreleased SDK (meaning it does not yet have a number)
+// it returns the codename (P, Q, R, etc.)
+func (s SdkSpec) EffectiveVersionString(ctx EarlyModuleContext) (string, error) {
+ if !s.Valid() {
+ return s.ApiLevel.String(), fmt.Errorf("invalid sdk version %q", s.Raw)
+ }
+
+ if ctx.DeviceSpecific() || ctx.SocSpecific() {
+ s = s.ForVendorPartition(ctx)
+ }
+ if !s.ApiLevel.IsPreview() {
+ return s.ApiLevel.String(), nil
+ }
+ return ctx.Config().DefaultAppTargetSdk(ctx).String(), nil
+}
+
+var (
+ SdkSpecNone = SdkSpec{SdkNone, NoneApiLevel, "(no version)"}
+ // TODO(b/175678607) ApiLevel of SdkSpecPrivate should be FutureApiLevel
+ SdkSpecPrivate = SdkSpec{SdkPrivate, NoneApiLevel, ""}
+ // TODO(b/175678607) ApiLevel of SdkSpecCorePlatform should be FutureApiLevel
+ SdkSpecCorePlatform = SdkSpec{SdkCorePlatform, NoneApiLevel, "core_platform"}
+)
+
+func SdkSpecFrom(ctx EarlyModuleContext, str string) SdkSpec {
+ switch str {
+ // special cases first
+ case "":
+ return SdkSpecPrivate
+ case "none":
+ return SdkSpecNone
+ case "core_platform":
+ return SdkSpecCorePlatform
+ default:
+ // the syntax is [kind_]version
+ sep := strings.LastIndex(str, "_")
+
+ var kindString string
+ if sep == 0 {
+ return SdkSpec{SdkInvalid, NoneApiLevel, str}
+ } else if sep == -1 {
+ kindString = ""
+ } else {
+ kindString = str[0:sep]
+ }
+ versionString := str[sep+1 : len(str)]
+
+ var kind SdkKind
+ switch kindString {
+ case "":
+ kind = SdkPublic
+ case "core":
+ kind = SdkCore
+ case "system":
+ kind = SdkSystem
+ case "test":
+ kind = SdkTest
+ case "module":
+ kind = SdkModule
+ case "system_server":
+ kind = SdkSystemServer
+ default:
+ return SdkSpec{SdkInvalid, NoneApiLevel, str}
+ }
+
+ apiLevel, err := ApiLevelFromUser(ctx, versionString)
+ if err != nil {
+ return SdkSpec{SdkInvalid, apiLevel, str}
+ }
+ return SdkSpec{kind, apiLevel, str}
+ }
+}
+
+func (s SdkSpec) ValidateSystemSdk(ctx EarlyModuleContext) bool {
+ // Ensures that the specified system SDK version is one of BOARD_SYSTEMSDK_VERSIONS (for vendor/product Java module)
+ // Assuming that BOARD_SYSTEMSDK_VERSIONS := 28 29,
+ // sdk_version of the modules in vendor/product that use system sdk must be either system_28, system_29 or system_current
+ if s.Kind != SdkSystem || s.ApiLevel.IsPreview() {
+ return true
+ }
+ allowedVersions := ctx.DeviceConfig().PlatformSystemSdkVersions()
+ if ctx.DeviceSpecific() || ctx.SocSpecific() || (ctx.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
+ systemSdkVersions := ctx.DeviceConfig().SystemSdkVersions()
+ if len(systemSdkVersions) > 0 {
+ allowedVersions = systemSdkVersions
+ }
+ }
+ if len(allowedVersions) > 0 && !InList(s.ApiLevel.String(), allowedVersions) {
+ ctx.PropertyErrorf("sdk_version", "incompatible sdk version %q. System SDK version should be one of %q",
+ s.Raw, allowedVersions)
+ return false
+ }
+ return true
+}
diff --git a/android/singleton_module.go b/android/singleton_module.go
new file mode 100644
index 0000000..2351738
--- /dev/null
+++ b/android/singleton_module.go
@@ -0,0 +1,146 @@
+// Copyright 2020 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"
+
+ "github.com/google/blueprint"
+)
+
+// A SingletonModule is halfway between a Singleton and a Module. It has access to visiting
+// other modules via its GenerateSingletonBuildActions method, but must be defined in an Android.bp
+// file and can also be depended on like a module. It must be used zero or one times in an
+// Android.bp file, and it can only have a single variant.
+//
+// The SingletonModule's GenerateAndroidBuildActions method will be called before any normal or
+// singleton module that depends on it, but its GenerateSingletonBuildActions method will be called
+// after all modules, in registration order with other singletons and singleton modules.
+// GenerateAndroidBuildActions and GenerateSingletonBuildActions will not be called if the
+// SingletonModule was not instantiated in an Android.bp file.
+//
+// Since the SingletonModule rules likely depend on the modules visited during
+// GenerateSingletonBuildActions, the GenerateAndroidBuildActions is unlikely to produce any
+// rules directly. Instead, it will probably set some providers to paths that will later have rules
+// generated to produce them in GenerateSingletonBuildActions.
+//
+// The expected use case for a SingletonModule is a module that produces files that depend on all
+// modules in the tree and will be used by other modules. For example it could produce a text
+// file that lists all modules that meet a certain criteria, and that text file could be an input
+// to another module. Care must be taken that the ninja rules produced by the SingletonModule
+// don't produce a cycle by referencing output files of rules of modules that depend on the
+// SingletonModule.
+//
+// A SingletonModule must embed a SingletonModuleBase struct, and its factory method must be
+// registered with RegisterSingletonModuleType from an init() function.
+//
+// A SingletonModule can also implement SingletonMakeVarsProvider to export values to Make.
+type SingletonModule interface {
+ Module
+ GenerateSingletonBuildActions(SingletonContext)
+ singletonModuleBase() *SingletonModuleBase
+}
+
+// SingletonModuleBase must be embedded into implementers of the SingletonModule interface.
+type SingletonModuleBase struct {
+ ModuleBase
+
+ lock sync.Mutex
+ bp string
+ variant string
+}
+
+// GenerateBuildActions wraps the ModuleBase GenerateBuildActions method, verifying it was only
+// called once to prevent multiple variants of a SingletonModule.
+func (smb *SingletonModuleBase) GenerateBuildActions(ctx blueprint.ModuleContext) {
+ smb.lock.Lock()
+ if smb.variant != "" {
+ ctx.ModuleErrorf("GenerateAndroidBuildActions already called for variant %q, SingletonModules can only have one variant", smb.variant)
+ }
+ smb.variant = ctx.ModuleSubDir()
+ smb.lock.Unlock()
+
+ smb.ModuleBase.GenerateBuildActions(ctx)
+}
+
+// InitAndroidSingletonModule must be called from the SingletonModule's factory function to
+// initialize SingletonModuleBase.
+func InitAndroidSingletonModule(sm SingletonModule) {
+ InitAndroidModule(sm)
+}
+
+// singletonModuleBase retrieves the embedded SingletonModuleBase from a SingletonModule.
+func (smb *SingletonModuleBase) singletonModuleBase() *SingletonModuleBase { return smb }
+
+// SingletonModuleFactory is a factory method that returns a SingletonModule.
+type SingletonModuleFactory func() SingletonModule
+
+// SingletonModuleFactoryAdaptor converts a SingletonModuleFactory into a SingletonFactory and a
+// ModuleFactory.
+func SingletonModuleFactoryAdaptor(name string, factory SingletonModuleFactory) (SingletonFactory, ModuleFactory) {
+ // The sm variable acts as a static holder of the only SingletonModule instance. Calls to the
+ // returned SingletonFactory and ModuleFactory lambdas will always return the same sm value.
+ // The SingletonFactory is only expected to be called once, but the ModuleFactory may be
+ // called multiple times if the module is replaced with a clone of itself at the end of
+ // blueprint.ResolveDependencies.
+ var sm SingletonModule
+ s := func() Singleton {
+ sm = factory()
+ return &singletonModuleSingletonAdaptor{sm}
+ }
+ m := func() Module {
+ if sm == nil {
+ panic(fmt.Errorf("Singleton %q for SingletonModule was not instantiated", name))
+ }
+
+ // Check for multiple uses of a SingletonModule in a LoadHook. Checking directly in the
+ // factory would incorrectly flag when the factory was called again when the module is
+ // replaced with a clone of itself at the end of blueprint.ResolveDependencies.
+ AddLoadHook(sm, func(ctx LoadHookContext) {
+ smb := sm.singletonModuleBase()
+ smb.lock.Lock()
+ defer smb.lock.Unlock()
+ if smb.bp != "" {
+ ctx.ModuleErrorf("Duplicate SingletonModule %q, previously used in %s", name, smb.bp)
+ }
+ smb.bp = ctx.BlueprintsFile()
+ })
+ return sm
+ }
+ return s, m
+}
+
+// singletonModuleSingletonAdaptor makes a SingletonModule into a Singleton by translating the
+// GenerateSingletonBuildActions method to Singleton.GenerateBuildActions.
+type singletonModuleSingletonAdaptor struct {
+ sm SingletonModule
+}
+
+// GenerateBuildActions calls the SingletonModule's GenerateSingletonBuildActions method, but only
+// if the module was defined in an Android.bp file.
+func (smsa *singletonModuleSingletonAdaptor) GenerateBuildActions(ctx SingletonContext) {
+ if smsa.sm.singletonModuleBase().bp != "" {
+ smsa.sm.GenerateSingletonBuildActions(ctx)
+ }
+}
+
+func (smsa *singletonModuleSingletonAdaptor) MakeVars(ctx MakeVarsContext) {
+ if smsa.sm.singletonModuleBase().bp != "" {
+ if makeVars, ok := smsa.sm.(SingletonMakeVarsProvider); ok {
+ makeVars.MakeVars(ctx)
+ }
+ }
+}
diff --git a/android/singleton_module_test.go b/android/singleton_module_test.go
new file mode 100644
index 0000000..eb5554c
--- /dev/null
+++ b/android/singleton_module_test.go
@@ -0,0 +1,124 @@
+// Copyright 2021 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 (
+ "testing"
+)
+
+type testSingletonModule struct {
+ SingletonModuleBase
+ ops []string
+}
+
+func (tsm *testSingletonModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ tsm.ops = append(tsm.ops, "GenerateAndroidBuildActions")
+}
+
+func (tsm *testSingletonModule) GenerateSingletonBuildActions(ctx SingletonContext) {
+ tsm.ops = append(tsm.ops, "GenerateSingletonBuildActions")
+}
+
+func (tsm *testSingletonModule) MakeVars(ctx MakeVarsContext) {
+ tsm.ops = append(tsm.ops, "MakeVars")
+}
+
+func testSingletonModuleFactory() SingletonModule {
+ tsm := &testSingletonModule{}
+ InitAndroidSingletonModule(tsm)
+ return tsm
+}
+
+var prepareForSingletonModuleTest = GroupFixturePreparers(
+ // Enable Kati output to test SingletonModules with MakeVars.
+ PrepareForTestWithAndroidMk,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterSingletonModuleType("test_singleton_module", testSingletonModuleFactory)
+ ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
+ }),
+)
+
+func TestSingletonModule(t *testing.T) {
+ bp := `
+ test_singleton_module {
+ name: "test_singleton_module",
+ }
+ `
+ result := GroupFixturePreparers(
+ prepareForSingletonModuleTest,
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
+
+ ops := result.ModuleForTests("test_singleton_module", "").Module().(*testSingletonModule).ops
+ wantOps := []string{"GenerateAndroidBuildActions", "GenerateSingletonBuildActions", "MakeVars"}
+ AssertDeepEquals(t, "operations", wantOps, ops)
+}
+
+func TestDuplicateSingletonModule(t *testing.T) {
+ bp := `
+ test_singleton_module {
+ name: "test_singleton_module",
+ }
+
+ test_singleton_module {
+ name: "test_singleton_module2",
+ }
+ `
+
+ prepareForSingletonModuleTest.
+ ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
+ `\QDuplicate SingletonModule "test_singleton_module", previously used in\E`,
+ })).RunTestWithBp(t, bp)
+}
+
+func TestUnusedSingletonModule(t *testing.T) {
+ result := GroupFixturePreparers(
+ prepareForSingletonModuleTest,
+ ).RunTest(t)
+
+ singleton := result.SingletonForTests("test_singleton_module").Singleton()
+ sm := singleton.(*singletonModuleSingletonAdaptor).sm
+ ops := sm.(*testSingletonModule).ops
+ if ops != nil {
+ t.Errorf("Expected no operations, got %q", ops)
+ }
+}
+
+func testVariantSingletonModuleMutator(ctx BottomUpMutatorContext) {
+ if _, ok := ctx.Module().(*testSingletonModule); ok {
+ ctx.CreateVariations("a", "b")
+ }
+}
+
+func TestVariantSingletonModule(t *testing.T) {
+ bp := `
+ test_singleton_module {
+ name: "test_singleton_module",
+ }
+ `
+
+ GroupFixturePreparers(
+ prepareForSingletonModuleTest,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("test_singleton_module_mutator", testVariantSingletonModuleMutator)
+ })
+ }),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
+ `\QGenerateAndroidBuildActions already called for variant\E`,
+ })).
+ RunTestWithBp(t, bp)
+}
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 619cf86..289e910 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -51,6 +51,16 @@
// variables from another Android.bp file. The imported module type will exist for all
// modules after the import in the Android.bp file.
//
+// Each soong_config_variable supports an additional value `conditions_default`. The properties
+// specified in `conditions_default` will only be used under the following conditions:
+// bool variable: the variable is unspecified or not set to a true value
+// value variable: the variable is unspecified
+// string variable: the variable is unspecified or the variable is set to a string unused in the
+// given module. For example, string variable `test` takes values: "a" and "b",
+// if the module contains a property `a` and `conditions_default`, when test=b,
+// the properties under `conditions_default` will be used. To specify that no
+// properties should be amended for `b`, you can set `b: {},`.
+//
// For example, an Android.bp file could have:
//
// soong_config_module_type_import {
@@ -69,12 +79,21 @@
// soc_b: {
// cflags: ["-DSOC_B"],
// },
+// conditions_default: {
+// cflags: ["-DSOC_DEFAULT"],
+// },
// },
// feature: {
// cflags: ["-DFEATURE"],
+// conditions_default: {
+// cflags: ["-DFEATURE_DEFAULT"],
+// },
// },
// width: {
// cflags: ["-DWIDTH=%s"],
+// conditions_default: {
+// cflags: ["-DWIDTH=DEFAULT"],
+// },
// },
// },
// }
@@ -99,7 +118,7 @@
//
// soong_config_string_variable {
// name: "board",
-// values: ["soc_a", "soc_b"],
+// values: ["soc_a", "soc_b", "soc_c"],
// }
//
// If an acme BoardConfig.mk file contained:
@@ -114,6 +133,31 @@
// SOONG_CONFIG_acme_width := 200
//
// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
+//
+// Alternatively, if acme BoardConfig.mk file contained:
+//
+// SOONG_CONFIG_NAMESPACES += acme
+// SOONG_CONFIG_acme += \
+// board \
+// feature \
+//
+// SOONG_CONFIG_acme_feature := false
+//
+// Then libacme_foo would build with cflags:
+// "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
+//
+// Similarly, if acme BoardConfig.mk file contained:
+//
+// SOONG_CONFIG_NAMESPACES += acme
+// SOONG_CONFIG_acme += \
+// board \
+// feature \
+//
+// SOONG_CONFIG_acme_board := soc_c
+//
+// Then libacme_foo would build with cflags:
+// "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
+
func soongConfigModuleTypeImportFactory() Module {
module := &soongConfigModuleTypeImport{}
@@ -148,6 +192,16 @@
// in an Android.bp file, and can be imported into other Android.bp files using
// soong_config_module_type_import.
//
+// Each soong_config_variable supports an additional value `conditions_default`. The properties
+// specified in `conditions_default` will only be used under the following conditions:
+// bool variable: the variable is unspecified or not set to a true value
+// value variable: the variable is unspecified
+// string variable: the variable is unspecified or the variable is set to a string unused in the
+// given module. For example, string variable `test` takes values: "a" and "b",
+// if the module contains a property `a` and `conditions_default`, when test=b,
+// the properties under `conditions_default` will be used. To specify that no
+// properties should be amended for `b`, you can set `b: {},`.
+//
// For example, an Android.bp file could have:
//
// soong_config_module_type {
@@ -176,12 +230,21 @@
// soc_b: {
// cflags: ["-DSOC_B"],
// },
+// conditions_default: {
+// cflags: ["-DSOC_DEFAULT"],
+// },
// },
// feature: {
// cflags: ["-DFEATURE"],
+// conditions_default: {
+// cflags: ["-DFEATURE_DEFAULT"],
+// },
// },
// width: {
// cflags: ["-DWIDTH=%s"],
+// conditions_default: {
+// cflags: ["-DWIDTH=DEFAULT"],
+// },
// },
// },
// }
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index b1810b3..8f252d9 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -15,7 +15,6 @@
package android
import (
- "reflect"
"testing"
)
@@ -60,15 +59,20 @@
name: "acme_test",
module_type: "test",
config_namespace: "acme",
- variables: ["board", "feature1", "FEATURE3"],
- bool_variables: ["feature2"],
- value_variables: ["size"],
+ variables: ["board", "feature1", "FEATURE3", "unused_string_var"],
+ bool_variables: ["feature2", "unused_feature"],
+ value_variables: ["size", "unused_size"],
properties: ["cflags", "srcs", "defaults"],
}
soong_config_string_variable {
name: "board",
- values: ["soc_a", "soc_b"],
+ values: ["soc_a", "soc_b", "soc_c", "soc_d"],
+ }
+
+ soong_config_string_variable {
+ name: "unused_string_var",
+ values: ["a", "b"],
}
soong_config_bool_variable {
@@ -105,15 +109,28 @@
soc_b: {
cflags: ["-DSOC_B"],
},
+ soc_c: {},
+ conditions_default: {
+ cflags: ["-DSOC_CONDITIONS_DEFAULT"],
+ },
},
size: {
cflags: ["-DSIZE=%s"],
+ conditions_default: {
+ cflags: ["-DSIZE=CONDITIONS_DEFAULT"],
+ },
},
feature1: {
+ conditions_default: {
+ cflags: ["-DF1_CONDITIONS_DEFAULT"],
+ },
cflags: ["-DFEATURE1"],
},
feature2: {
cflags: ["-DFEATURE2"],
+ conditions_default: {
+ cflags: ["-DF2_CONDITIONS_DEFAULT"],
+ },
},
FEATURE3: {
cflags: ["-DFEATURE3"],
@@ -145,6 +162,7 @@
cflags: ["-DSOC_B"],
defaults: ["foo_defaults_b"],
},
+ soc_c: {},
},
size: {
cflags: ["-DSIZE=%s"],
@@ -162,46 +180,126 @@
}
`
- run := func(t *testing.T, bp string, fs map[string][]byte) {
- config := TestConfig(buildDir, nil, bp, fs)
+ fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
+ return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+ variables.VendorVars = vars
+ })
+ }
- config.TestProductVariables.VendorVars = map[string]map[string]string{
- "acme": map[string]string{
- "board": "soc_a",
- "size": "42",
- "feature1": "true",
- "feature2": "false",
- // FEATURE3 unset
+ run := func(t *testing.T, bp string, fs MockFS) {
+ testCases := []struct {
+ name string
+ preparer FixturePreparer
+ fooExpectedFlags []string
+ fooDefaultsExpectedFlags []string
+ }{
+ {
+ name: "withValues",
+ preparer: fixtureForVendorVars(map[string]map[string]string{
+ "acme": {
+ "board": "soc_a",
+ "size": "42",
+ "feature1": "true",
+ "feature2": "false",
+ // FEATURE3 unset
+ "unused_feature": "true", // unused
+ "unused_size": "1", // unused
+ "unused_string_var": "a", // unused
+ },
+ }),
+ fooExpectedFlags: []string{
+ "DEFAULT",
+ "-DGENERIC",
+ "-DF2_CONDITIONS_DEFAULT",
+ "-DSIZE=42",
+ "-DSOC_A",
+ "-DFEATURE1",
+ },
+ fooDefaultsExpectedFlags: []string{
+ "DEFAULT_A",
+ "DEFAULT",
+ "-DGENERIC",
+ "-DSIZE=42",
+ "-DSOC_A",
+ "-DFEATURE1",
+ },
+ },
+ {
+ name: "empty_prop_for_string_var",
+ preparer: fixtureForVendorVars(map[string]map[string]string{
+ "acme": {"board": "soc_c"}}),
+ fooExpectedFlags: []string{
+ "DEFAULT",
+ "-DGENERIC",
+ "-DF2_CONDITIONS_DEFAULT",
+ "-DSIZE=CONDITIONS_DEFAULT",
+ "-DF1_CONDITIONS_DEFAULT",
+ },
+ fooDefaultsExpectedFlags: []string{
+ "DEFAULT",
+ "-DGENERIC",
+ },
+ },
+ {
+ name: "unused_string_var",
+ preparer: fixtureForVendorVars(map[string]map[string]string{
+ "acme": {"board": "soc_d"}}),
+ fooExpectedFlags: []string{
+ "DEFAULT",
+ "-DGENERIC",
+ "-DF2_CONDITIONS_DEFAULT",
+ "-DSIZE=CONDITIONS_DEFAULT",
+ "-DSOC_CONDITIONS_DEFAULT", // foo does not contain a prop "soc_d", so we use the default
+ "-DF1_CONDITIONS_DEFAULT",
+ },
+ fooDefaultsExpectedFlags: []string{
+ "DEFAULT",
+ "-DGENERIC",
+ },
+ },
+
+ {
+ name: "conditions_default",
+ preparer: fixtureForVendorVars(map[string]map[string]string{}),
+ fooExpectedFlags: []string{
+ "DEFAULT",
+ "-DGENERIC",
+ "-DF2_CONDITIONS_DEFAULT",
+ "-DSIZE=CONDITIONS_DEFAULT",
+ "-DSOC_CONDITIONS_DEFAULT",
+ "-DF1_CONDITIONS_DEFAULT",
+ },
+ fooDefaultsExpectedFlags: []string{
+ "DEFAULT",
+ "-DGENERIC",
+ },
},
}
- ctx := NewTestContext(config)
- ctx.RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
- ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
- ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
- ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
- ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
- ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
- ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
- ctx.Register()
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ result := GroupFixturePreparers(
+ tc.preparer,
+ PrepareForTestWithDefaults,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
+ ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
+ ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
+ ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
+ ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
+ ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
+ }),
+ fs.AddToFixture(),
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
+ foo := result.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
+ AssertDeepEquals(t, "foo cflags", tc.fooExpectedFlags, foo.props.Cflags)
- basicCFlags := []string{"DEFAULT", "-DGENERIC", "-DSIZE=42", "-DSOC_A", "-DFEATURE1"}
-
- foo := ctx.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
- if g, w := foo.props.Cflags, basicCFlags; !reflect.DeepEqual(g, w) {
- t.Errorf("wanted foo cflags %q, got %q", w, g)
+ fooDefaults := result.ModuleForTests("foo_with_defaults", "").Module().(*soongConfigTestModule)
+ AssertDeepEquals(t, "foo_with_defaults cflags", tc.fooDefaultsExpectedFlags, fooDefaults.props.Cflags)
+ })
}
-
- fooDefaults := ctx.ModuleForTests("foo_with_defaults", "").Module().(*soongConfigTestModule)
- if g, w := fooDefaults.props.Cflags, append([]string{"DEFAULT_A"}, basicCFlags...); !reflect.DeepEqual(g, w) {
- t.Errorf("wanted foo_with_defaults cflags %q, got %q", w, g)
- }
-
}
t.Run("single file", func(t *testing.T) {
@@ -214,3 +312,11 @@
})
})
}
+
+func testConfigWithVendorVars(buildDir, bp string, fs map[string][]byte, vendorVars map[string]map[string]string) Config {
+ config := TestConfig(buildDir, nil, bp, fs)
+
+ config.TestProductVariables.VendorVars = vendorVars
+
+ return config
+}
diff --git a/android/soongconfig/Android.bp b/android/soongconfig/Android.bp
index df912e6..e7fa5a0 100644
--- a/android/soongconfig/Android.bp
+++ b/android/soongconfig/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-android-soongconfig",
pkgPath: "android/soong/android/soongconfig",
@@ -10,4 +14,7 @@
"config.go",
"modules.go",
],
+ testSrcs: [
+ "modules_test.go",
+ ],
}
diff --git a/android/soongconfig/config.go b/android/soongconfig/config.go
index 39a776c..c72da2f 100644
--- a/android/soongconfig/config.go
+++ b/android/soongconfig/config.go
@@ -14,7 +14,10 @@
package soongconfig
-import "strings"
+import (
+ "fmt"
+ "strings"
+)
type SoongConfig interface {
// Bool interprets the variable named `name` as a boolean, returning true if, after
@@ -31,7 +34,16 @@
}
func Config(vars map[string]string) SoongConfig {
- return soongConfig(vars)
+ configVars := make(map[string]string)
+ if len(vars) > 0 {
+ for k, v := range vars {
+ configVars[k] = v
+ }
+ if _, exists := configVars[conditionsDefault]; exists {
+ panic(fmt.Sprintf("%q is a reserved soong config variable name", conditionsDefault))
+ }
+ }
+ return soongConfig(configVars)
}
type soongConfig map[string]string
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 5a6917e..34b180d 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -26,6 +26,8 @@
"github.com/google/blueprint/proptools"
)
+const conditionsDefault = "conditions_default"
+
var soongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
@@ -145,32 +147,10 @@
return errs
}
- mt := &ModuleType{
- affectableProperties: props.Properties,
- ConfigNamespace: props.Config_namespace,
- BaseModuleType: props.Module_type,
- variableNames: props.Variables,
- }
- v.ModuleTypes[props.Name] = mt
-
- for _, name := range props.Bool_variables {
- if name == "" {
- return []error{fmt.Errorf("bool_variable name must not be blank")}
- }
-
- mt.Variables = append(mt.Variables, newBoolVariable(name))
- }
-
- for _, name := range props.Value_variables {
- if name == "" {
- return []error{fmt.Errorf("value_variables entry must not be blank")}
- }
-
- mt.Variables = append(mt.Variables, &valueVariable{
- baseVariable: baseVariable{
- variable: name,
- },
- })
+ if mt, errs := newModuleType(props); len(errs) > 0 {
+ return errs
+ } else {
+ v.ModuleTypes[props.Name] = mt
}
return nil
@@ -196,6 +176,12 @@
return []error{fmt.Errorf("values property must be set")}
}
+ for _, name := range stringProps.Values {
+ if err := checkVariableName(name); err != nil {
+ return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
+ }
+ }
+
v.variables[base.variable] = &stringVariable{
baseVariable: base,
values: CanonicalizeToProperties(stringProps.Values),
@@ -309,18 +295,25 @@
recurse = func(prefix string, aps []string) ([]string, reflect.Type) {
var fields []reflect.StructField
+ // Iterate while the list is non-empty so it can be modified in the loop.
for len(affectableProperties) > 0 {
p := affectableProperties[0]
if !strings.HasPrefix(affectableProperties[0], prefix) {
+ // The properties are sorted and recurse is always called with a prefix that matches
+ // the first property in the list, so if we've reached one that doesn't match the
+ // prefix we are done with this prefix.
break
}
- affectableProperties = affectableProperties[1:]
nestedProperty := strings.TrimPrefix(p, prefix)
if i := strings.IndexRune(nestedProperty, '.'); i >= 0 {
var nestedType reflect.Type
nestedPrefix := nestedProperty[:i+1]
+ // Recurse to handle the properties with the found prefix. This will return
+ // an updated affectableProperties with the handled entries removed from the front
+ // of the list, and the type that contains the handled entries. The type may be
+ // nil if none of the entries matched factoryProps.
affectableProperties, nestedType = recurse(prefix+nestedPrefix, affectableProperties)
if nestedType != nil {
@@ -339,6 +332,8 @@
Type: typ,
})
}
+ // The first element in the list has been handled, remove it from the list.
+ affectableProperties = affectableProperties[1:]
}
}
@@ -417,8 +412,7 @@
// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
// based on SoongConfig values.
// Expects that props contains a struct field with name soong_config_variables. The fields within
-// soong_config_variables are expected to be in the same order as moduleType.Variables. In general,
-// props should be generated via CreateProperties.
+// soong_config_variables are expected to be in the same order as moduleType.Variables.
func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
var ret []interface{}
props = props.Elem().FieldByName(soongConfigProperty)
@@ -441,6 +435,46 @@
variableNames []string
}
+func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
+ mt := &ModuleType{
+ affectableProperties: props.Properties,
+ ConfigNamespace: props.Config_namespace,
+ BaseModuleType: props.Module_type,
+ variableNames: props.Variables,
+ }
+
+ for _, name := range props.Bool_variables {
+ if err := checkVariableName(name); err != nil {
+ return nil, []error{fmt.Errorf("bool_variables %s", err)}
+ }
+
+ mt.Variables = append(mt.Variables, newBoolVariable(name))
+ }
+
+ for _, name := range props.Value_variables {
+ if err := checkVariableName(name); err != nil {
+ return nil, []error{fmt.Errorf("value_variables %s", err)}
+ }
+
+ mt.Variables = append(mt.Variables, &valueVariable{
+ baseVariable: baseVariable{
+ variable: name,
+ },
+ })
+ }
+
+ return mt, nil
+}
+
+func checkVariableName(name string) error {
+ if name == "" {
+ return fmt.Errorf("name must not be blank")
+ } else if name == conditionsDefault {
+ return fmt.Errorf("%q is reserved", conditionsDefault)
+ }
+ return nil
+}
+
type soongConfigVariable interface {
// variableProperty returns the name of the variable.
variableProperty() string
@@ -474,7 +508,10 @@
func (s *stringVariable) variableValuesType() reflect.Type {
var fields []reflect.StructField
- for _, v := range s.values {
+ var values []string
+ values = append(values, s.values...)
+ values = append(values, conditionsDefault)
+ for _, v := range values {
fields = append(fields, reflect.StructField{
Name: proptools.FieldNameForProperty(v),
Type: emptyInterfaceType,
@@ -484,26 +521,36 @@
return reflect.StructOf(fields)
}
+// initializeProperties initializes properties to zero value of typ for supported values and a final
+// conditions default field.
func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
for i := range s.values {
v.Field(i).Set(reflect.Zero(typ))
}
+ v.Field(len(s.values)).Set(reflect.Zero(typ)) // conditions default is the final value
}
+// Extracts an interface from values containing the properties to apply based on config.
+// If config does not match a value with a non-nil property set, the default value will be returned.
func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
for j, v := range s.values {
- if config.String(s.variable) == v {
- return values.Field(j).Interface(), nil
+ f := values.Field(j)
+ if config.String(s.variable) == v && !f.Elem().IsNil() {
+ return f.Interface(), nil
}
}
-
- return nil, nil
+ // if we have reached this point, we have checked all valid values of string and either:
+ // * the value was not set
+ // * the value was set but that value was not specified in the Android.bp file
+ return values.Field(len(s.values)).Interface(), nil
}
+// Struct to allow conditions set based on a boolean variable
type boolVariable struct {
baseVariable
}
+// newBoolVariable constructs a boolVariable with the given name
func newBoolVariable(name string) *boolVariable {
return &boolVariable{
baseVariable{
@@ -516,18 +563,82 @@
return emptyInterfaceType
}
+// initializeProperties initializes a property to zero value of typ with an additional conditions
+// default field.
func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
- v.Set(reflect.Zero(typ))
+ initializePropertiesWithDefault(v, typ)
}
-func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
- if config.Bool(b.variable) {
- return values.Interface(), nil
+// initializePropertiesWithDefault, initialize with zero value, v to contain a field for each field
+// in typ, with an additional field for defaults of type typ. This should be used to initialize
+// boolVariable, valueVariable, or any future implementations of soongConfigVariable which support
+// one variable and a default.
+func initializePropertiesWithDefault(v reflect.Value, typ reflect.Type) {
+ sTyp := typ.Elem()
+ var fields []reflect.StructField
+ for i := 0; i < sTyp.NumField(); i++ {
+ fields = append(fields, sTyp.Field(i))
}
+ // create conditions_default field
+ nestedFieldName := proptools.FieldNameForProperty(conditionsDefault)
+ fields = append(fields, reflect.StructField{
+ Name: nestedFieldName,
+ Type: typ,
+ })
+
+ newTyp := reflect.PtrTo(reflect.StructOf(fields))
+ v.Set(reflect.Zero(newTyp))
+}
+
+// conditionsDefaultField extracts the conditions_default field from v. This is always the final
+// field if initialized with initializePropertiesWithDefault.
+func conditionsDefaultField(v reflect.Value) reflect.Value {
+ return v.Field(v.NumField() - 1)
+}
+
+// removeDefault removes the conditions_default field from values while retaining values from all
+// other fields. This allows
+func removeDefault(values reflect.Value) reflect.Value {
+ v := values.Elem().Elem()
+ s := conditionsDefaultField(v)
+ // if conditions_default field was not set, there will be no issues extending properties.
+ if !s.IsValid() {
+ return v
+ }
+
+ // If conditions_default field was set, it has the correct type for our property. Create a new
+ // reflect.Value of the conditions_default type and copy all fields (except for
+ // conditions_default) based on values to the result.
+ res := reflect.New(s.Type().Elem())
+ for i := 0; i < res.Type().Elem().NumField(); i++ {
+ val := v.Field(i)
+ res.Elem().Field(i).Set(val)
+ }
+
+ return res
+}
+
+// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
+// the module. If the value was not set, conditions_default interface will be returned; otherwise,
+// the interface in values, without conditions_default will be returned.
+func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
+ // If this variable was not referenced in the module, there are no properties to apply.
+ if values.Elem().IsZero() {
+ return nil, nil
+ }
+ if config.Bool(b.variable) {
+ values = removeDefault(values)
+ return values.Interface(), nil
+ }
+ v := values.Elem().Elem()
+ if f := conditionsDefaultField(v); f.IsValid() {
+ return f.Interface(), nil
+ }
return nil, nil
}
+// Struct to allow conditions set based on a value variable, supporting string substitution.
type valueVariable struct {
baseVariable
}
@@ -536,17 +647,31 @@
return emptyInterfaceType
}
+// initializeProperties initializes a property to zero value of typ with an additional conditions
+// default field.
func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
- v.Set(reflect.Zero(typ))
+ initializePropertiesWithDefault(v, typ)
}
+// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
+// the module. If the variable was not set, conditions_default interface will be returned;
+// otherwise, the interface in values, without conditions_default will be returned with all
+// appropriate string substitutions based on variable being set.
func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
- if !config.IsSet(s.variable) {
+ // If this variable was not referenced in the module, there are no properties to apply.
+ if !values.IsValid() || values.Elem().IsZero() {
return nil, nil
}
+ if !config.IsSet(s.variable) {
+ return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
+ }
configValue := config.String(s.variable)
- propStruct := values.Elem().Elem()
+ values = removeDefault(values)
+ propStruct := values.Elem()
+ if !propStruct.IsValid() {
+ return nil, nil
+ }
for i := 0; i < propStruct.NumField(); i++ {
field := propStruct.Field(i)
kind := field.Kind()
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index fb0e189..48cdfe7 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -235,6 +235,44 @@
}{},
want: "",
},
+ {
+ name: "nested",
+ affectableProperties: []string{"multilib.lib32.cflags"},
+ factoryProps: struct {
+ Multilib struct {
+ Lib32 struct {
+ Cflags string
+ }
+ }
+ }{},
+ want: "*struct { Multilib struct { Lib32 struct { Cflags string } } }",
+ },
+ {
+ name: "complex",
+ affectableProperties: []string{
+ "cflags",
+ "multilib.lib32.cflags",
+ "multilib.lib32.ldflags",
+ "multilib.lib64.cflags",
+ "multilib.lib64.ldflags",
+ "zflags",
+ },
+ factoryProps: struct {
+ Cflags string
+ Multilib struct {
+ Lib32 struct {
+ Cflags string
+ Ldflags string
+ }
+ Lib64 struct {
+ Cflags string
+ Ldflags string
+ }
+ }
+ Zflags string
+ }{},
+ want: "*struct { Cflags string; Multilib struct { Lib32 struct { Cflags string; Ldflags string }; Lib64 struct { Cflags string; Ldflags string } }; Zflags string }",
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -254,67 +292,75 @@
A *string
B bool
}
-type soongConfigVariables struct {
- Bool_var properties
- Other_bool_var properties
+
+type boolVarProps struct {
+ A *string
+ B bool
+ Conditions_default *properties
}
-type soongConfigProps struct {
- Soong_config_variables soongConfigVariables
+type soongConfigVars struct {
+ Bool_var interface{}
}
func Test_PropertiesToApply(t *testing.T) {
-
- mt := &ModuleType{
- BaseModuleType: "foo",
- ConfigNamespace: "bar",
- Variables: []soongConfigVariable{
- newBoolVariable("bool_var"),
- newBoolVariable("other_bool_var"),
- },
- affectableProperties: []string{
- "a",
- "b",
- },
+ mt, _ := newModuleType(&ModuleTypeProperties{
+ Module_type: "foo",
+ Config_namespace: "bar",
+ Bool_variables: []string{"bool_var"},
+ Properties: []string{"a", "b"},
+ })
+ boolVarPositive := &properties{
+ A: proptools.StringPtr("A"),
+ B: true,
}
- props := soongConfigProps{
- Soong_config_variables: soongConfigVariables{
- Bool_var: properties{
- A: proptools.StringPtr("a"),
- B: true,
- },
- Other_bool_var: properties{
- A: proptools.StringPtr("other"),
- B: false,
+ conditionsDefault := &properties{
+ A: proptools.StringPtr("default"),
+ B: false,
+ }
+ actualProps := &struct {
+ Soong_config_variables soongConfigVars
+ }{
+ Soong_config_variables: soongConfigVars{
+ Bool_var: &boolVarProps{
+ A: boolVarPositive.A,
+ B: boolVarPositive.B,
+ Conditions_default: conditionsDefault,
},
},
}
+ props := reflect.ValueOf(actualProps)
testCases := []struct {
+ name string
config SoongConfig
wantProps []interface{}
}{
{
- config: Config(map[string]string{}),
+ name: "no_vendor_config",
+ config: Config(map[string]string{}),
+ wantProps: []interface{}{conditionsDefault},
},
{
+ name: "vendor_config_false",
+ config: Config(map[string]string{"bool_var": "n"}),
+ wantProps: []interface{}{conditionsDefault},
+ },
+ {
+ name: "bool_var_true",
config: Config(map[string]string{"bool_var": "y"}),
- wantProps: []interface{}{props.Soong_config_variables.Bool_var},
- },
- {
- config: Config(map[string]string{"other_bool_var": "y"}),
- wantProps: []interface{}{props.Soong_config_variables.Other_bool_var},
+ wantProps: []interface{}{boolVarPositive},
},
}
for _, tc := range testCases {
- gotProps, err := PropertiesToApply(mt, reflect.ValueOf(&props), tc.config)
+ gotProps, err := PropertiesToApply(mt, props, tc.config)
if err != nil {
- t.Errorf("Unexpected error in PropertiesToApply: %s", err)
+ t.Errorf("%s: Unexpected error in PropertiesToApply: %s", tc.name, err)
}
if !reflect.DeepEqual(gotProps, tc.wantProps) {
- t.Errorf("Expected %s, got %s", tc.wantProps, gotProps)
+ t.Errorf("%s: Expected %s, got %s", tc.name, tc.wantProps, gotProps)
}
}
}
diff --git a/android/test_asserts.go b/android/test_asserts.go
new file mode 100644
index 0000000..bfb88ab
--- /dev/null
+++ b/android/test_asserts.go
@@ -0,0 +1,186 @@
+// Copyright 2021 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"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+// This file contains general purpose test assert functions.
+
+// AssertSame checks if the expected and actual values are equal and if they are not then
+// it reports an error prefixed with the supplied message and including a reason for why it failed.
+func AssertSame(t *testing.T, message string, expected interface{}, actual interface{}) {
+ t.Helper()
+ if actual != expected {
+ t.Errorf("%s: expected:\n%#v\nactual:\n%#v", message, expected, actual)
+ }
+}
+
+// AssertBoolEquals checks if the expected and actual values are equal and if they are not then it
+// reports an error prefixed with the supplied message and including a reason for why it failed.
+func AssertBoolEquals(t *testing.T, message string, expected bool, actual bool) {
+ t.Helper()
+ if actual != expected {
+ t.Errorf("%s: expected %t, actual %t", message, expected, actual)
+ }
+}
+
+// AssertIntEquals checks if the expected and actual values are equal and if they are not then it
+// reports an error prefixed with the supplied message and including a reason for why it failed.
+func AssertIntEquals(t *testing.T, message string, expected int, actual int) {
+ t.Helper()
+ if actual != expected {
+ t.Errorf("%s: expected %d, actual %d", message, expected, actual)
+ }
+}
+
+// AssertStringEquals checks if the expected and actual values are equal and if they are not then
+// it reports an error prefixed with the supplied message and including a reason for why it failed.
+func AssertStringEquals(t *testing.T, message string, expected string, actual string) {
+ t.Helper()
+ if actual != expected {
+ t.Errorf("%s: expected %s, actual %s", message, expected, actual)
+ }
+}
+
+// AssertPathRelativeToTopEquals checks if the expected value is equal to the result of calling
+// PathRelativeToTop on the actual Path.
+func AssertPathRelativeToTopEquals(t *testing.T, message string, expected string, actual Path) {
+ t.Helper()
+ AssertStringEquals(t, message, expected, PathRelativeToTop(actual))
+}
+
+// AssertPathsRelativeToTopEquals checks if the expected value is equal to the result of calling
+// PathsRelativeToTop on the actual Paths.
+func AssertPathsRelativeToTopEquals(t *testing.T, message string, expected []string, actual Paths) {
+ t.Helper()
+ AssertDeepEquals(t, message, expected, PathsRelativeToTop(actual))
+}
+
+// AssertStringPathRelativeToTopEquals checks if the expected value is equal to the result of calling
+// StringPathRelativeToTop on the actual string path.
+func AssertStringPathRelativeToTopEquals(t *testing.T, message string, config Config, expected string, actual string) {
+ t.Helper()
+ AssertStringEquals(t, message, expected, StringPathRelativeToTop(config.buildDir, actual))
+}
+
+// AssertStringPathsRelativeToTopEquals checks if the expected value is equal to the result of
+// calling StringPathsRelativeToTop on the actual string paths.
+func AssertStringPathsRelativeToTopEquals(t *testing.T, message string, config Config, expected []string, actual []string) {
+ t.Helper()
+ AssertDeepEquals(t, message, expected, StringPathsRelativeToTop(config.buildDir, actual))
+}
+
+// AssertErrorMessageEquals checks if the error is not nil and has the expected message. If it does
+// not then this reports an error prefixed with the supplied message and including a reason for why
+// it failed.
+func AssertErrorMessageEquals(t *testing.T, message string, expected string, actual error) {
+ t.Helper()
+ if actual == nil {
+ t.Errorf("Expected error but was nil")
+ } else if actual.Error() != expected {
+ t.Errorf("%s: expected %s, actual %s", message, expected, actual.Error())
+ }
+}
+
+// AssertTrimmedStringEquals checks if the expected and actual values are the same after trimming
+// leading and trailing spaces from them both. If they are not then it reports an error prefixed
+// with the supplied message and including a reason for why it failed.
+func AssertTrimmedStringEquals(t *testing.T, message string, expected string, actual string) {
+ t.Helper()
+ AssertStringEquals(t, message, strings.TrimSpace(expected), strings.TrimSpace(actual))
+}
+
+// AssertStringDoesContain checks if the string contains the expected substring. If it does not
+// then it reports an error prefixed with the supplied message and including a reason for why it
+// failed.
+func AssertStringDoesContain(t *testing.T, message string, s string, expectedSubstring string) {
+ t.Helper()
+ if !strings.Contains(s, expectedSubstring) {
+ t.Errorf("%s: could not find %q within %q", message, expectedSubstring, s)
+ }
+}
+
+// AssertStringDoesNotContain checks if the string contains the expected substring. If it does then
+// it reports an error prefixed with the supplied message and including a reason for why it failed.
+func AssertStringDoesNotContain(t *testing.T, message string, s string, unexpectedSubstring string) {
+ t.Helper()
+ if strings.Contains(s, unexpectedSubstring) {
+ t.Errorf("%s: unexpectedly found %q within %q", message, unexpectedSubstring, s)
+ }
+}
+
+// AssertStringListContains checks if the list of strings contains the expected string. If it does
+// not then it reports an error prefixed with the supplied message and including a reason for why it
+// failed.
+func AssertStringListContains(t *testing.T, message string, list []string, expected string) {
+ t.Helper()
+ if !InList(expected, list) {
+ t.Errorf("%s: could not find %q within %q", message, expected, list)
+ }
+}
+
+// AssertArrayString checks if the expected and actual values are equal and if they are not then it
+// reports an error prefixed with the supplied message and including a reason for why it failed.
+func AssertArrayString(t *testing.T, message string, expected, actual []string) {
+ t.Helper()
+ if len(actual) != len(expected) {
+ t.Errorf("%s: expected %d (%q), actual (%d) %q", message, len(expected), expected, len(actual), actual)
+ return
+ }
+ for i := range actual {
+ if actual[i] != expected[i] {
+ t.Errorf("%s: expected %d-th, %q (%q), actual %q (%q)",
+ message, i, expected[i], expected, actual[i], actual)
+ return
+ }
+ }
+}
+
+// AssertDeepEquals checks if the expected and actual values are equal using reflect.DeepEqual and
+// if they are not then it reports an error prefixed with the supplied message and including a
+// reason for why it failed.
+func AssertDeepEquals(t *testing.T, message string, expected interface{}, actual interface{}) {
+ t.Helper()
+ if !reflect.DeepEqual(actual, expected) {
+ t.Errorf("%s: expected:\n %#v\n got:\n %#v", message, expected, actual)
+ }
+}
+
+// AssertPanicMessageContains checks that the supplied function panics as expected and the message
+// obtained by formatting the recovered value as a string contains the expected contents.
+func AssertPanicMessageContains(t *testing.T, message, expectedMessageContents string, funcThatShouldPanic func()) {
+ t.Helper()
+ panicked := false
+ var recovered interface{}
+ func() {
+ defer func() {
+ if recovered = recover(); recovered != nil {
+ panicked = true
+ }
+ }()
+ funcThatShouldPanic()
+ }()
+ if !panicked {
+ t.Errorf("%s: did not panic", message)
+ }
+
+ panicMessage := fmt.Sprintf("%s", recovered)
+ AssertStringDoesContain(t, fmt.Sprintf("%s: panic message", message), panicMessage, expectedMessageContents)
+}
diff --git a/android/test_suites.go b/android/test_suites.go
index 7ecb8d2..6b7b909 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -68,7 +68,7 @@
FlagWithOutput("-o ", outputFile).
FlagWithArg("-P ", "host/testcases").
FlagWithArg("-C ", testCasesDir.String()).
- FlagWithRspFileInputList("-r ", installedPaths.Paths())
+ FlagWithRspFileInputList("-r ", outputFile.ReplaceExtension(ctx, "rsp"), installedPaths.Paths())
rule.Build("robolectric_tests_zip", "robolectric-tests.zip")
return outputFile
diff --git a/android/testing.go b/android/testing.go
index 6539063..ce27fca 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -20,9 +20,11 @@
"regexp"
"sort"
"strings"
+ "sync"
"testing"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
)
func NewTestContext(config Config) *TestContext {
@@ -48,6 +50,78 @@
return ctx
}
+var PrepareForTestWithArchMutator = GroupFixturePreparers(
+ // Configure architecture targets in the fixture config.
+ FixtureModifyConfig(modifyTestConfigToSupportArchMutator),
+
+ // Add the arch mutator to the context.
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.PreDepsMutators(registerArchMutator)
+ }),
+)
+
+var PrepareForTestWithDefaults = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
+})
+
+var PrepareForTestWithComponentsMutator = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.PreArchMutators(RegisterComponentsMutator)
+})
+
+var PrepareForTestWithPrebuilts = FixtureRegisterWithContext(RegisterPrebuiltMutators)
+
+var PrepareForTestWithOverrides = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.PostDepsMutators(RegisterOverridePostDepsMutators)
+})
+
+// Test fixture preparer that will register most java build components.
+//
+// Singletons and mutators should only be added here if they are needed for a majority of java
+// module types, otherwise they should be added under a separate preparer to allow them to be
+// selected only when needed to reduce test execution time.
+//
+// Module types do not have much of an overhead unless they are used so this should include as many
+// module types as possible. The exceptions are those module types that require mutators and/or
+// singletons in order to function in which case they should be kept together in a separate
+// preparer.
+//
+// The mutators in this group were chosen because they are needed by the vast majority of tests.
+var PrepareForTestWithAndroidBuildComponents = GroupFixturePreparers(
+ // Sorted alphabetically as the actual order does not matter as tests automatically enforce the
+ // correct order.
+ PrepareForTestWithArchMutator,
+ PrepareForTestWithComponentsMutator,
+ PrepareForTestWithDefaults,
+ PrepareForTestWithFilegroup,
+ PrepareForTestWithOverrides,
+ PrepareForTestWithPackageModule,
+ PrepareForTestWithPrebuilts,
+ PrepareForTestWithVisibility,
+)
+
+// Prepares an integration test with all build components from the android package.
+//
+// This should only be used by tests that want to run with as much of the build enabled as possible.
+var PrepareForIntegrationTestWithAndroid = GroupFixturePreparers(
+ PrepareForTestWithAndroidBuildComponents,
+)
+
+// Prepares a test that may be missing dependencies by setting allow_missing_dependencies to
+// true.
+var PrepareForTestWithAllowMissingDependencies = GroupFixturePreparers(
+ FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+ variables.Allow_missing_dependencies = proptools.BoolPtr(true)
+ }),
+ FixtureModifyContext(func(ctx *TestContext) {
+ ctx.SetAllowMissingDependencies(true)
+ }),
+)
+
+// Prepares a test that disallows non-existent paths.
+var PrepareForTestDisallowNonExistentPaths = FixtureModifyConfig(func(config Config) {
+ config.TestAllowNonExistentPaths = false
+})
+
func NewTestArchContext(config Config) *TestContext {
ctx := NewTestContext(config)
ctx.preDeps = append(ctx.preDeps, registerArchMutator)
@@ -56,8 +130,16 @@
type TestContext struct {
*Context
- preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc
- NameResolver *NameResolver
+ preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc
+ bp2buildPreArch, bp2buildDeps, bp2buildMutators []RegisterMutatorFunc
+ NameResolver *NameResolver
+
+ // The list of pre-singletons and singletons registered for the test.
+ preSingletons, singletons sortableComponents
+
+ // The order in which the pre-singletons, mutators and singletons will be run in this test
+ // context; for debugging.
+ preSingletonOrder, mutatorOrder, singletonOrder []string
}
func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) {
@@ -81,10 +163,267 @@
ctx.finalDeps = append(ctx.finalDeps, f)
}
-func (ctx *TestContext) Register() {
- registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps)
+func (ctx *TestContext) RegisterBp2BuildConfig(config Bp2BuildConfig) {
+ ctx.config.bp2buildPackageConfig = config
+}
- ctx.RegisterSingletonType("env", EnvSingleton)
+// RegisterBp2BuildMutator registers a BazelTargetModule mutator for converting a module
+// type to the equivalent Bazel target.
+func (ctx *TestContext) RegisterBp2BuildMutator(moduleType string, m func(TopDownMutatorContext)) {
+ f := func(ctx RegisterMutatorsContext) {
+ ctx.TopDown(moduleType, m)
+ }
+ ctx.config.bp2buildModuleTypeConfig[moduleType] = true
+ ctx.bp2buildMutators = append(ctx.bp2buildMutators, f)
+}
+
+// PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules
+// into Bazel BUILD targets that should run prior to deps and conversion.
+func (ctx *TestContext) PreArchBp2BuildMutators(f RegisterMutatorFunc) {
+ ctx.bp2buildPreArch = append(ctx.bp2buildPreArch, f)
+}
+
+// DepsBp2BuildMutators adds mutators to be register for converting Android Blueprint modules into
+// Bazel BUILD targets that should run prior to conversion to resolve dependencies.
+func (ctx *TestContext) DepsBp2BuildMutators(f RegisterMutatorFunc) {
+ ctx.bp2buildDeps = append(ctx.bp2buildDeps, f)
+}
+
+// registeredComponentOrder defines the order in which a sortableComponent type is registered at
+// runtime and provides support for reordering the components registered for a test in the same
+// way.
+type registeredComponentOrder struct {
+ // The name of the component type, used for error messages.
+ componentType string
+
+ // The names of the registered components in the order in which they were registered.
+ namesInOrder []string
+
+ // Maps from the component name to its position in the runtime ordering.
+ namesToIndex map[string]int
+
+ // A function that defines the order between two named components that can be used to sort a slice
+ // of component names into the same order as they appear in namesInOrder.
+ less func(string, string) bool
+}
+
+// registeredComponentOrderFromExistingOrder takes an existing slice of sortableComponents and
+// creates a registeredComponentOrder that contains a less function that can be used to sort a
+// subset of that list of names so it is in the same order as the original sortableComponents.
+func registeredComponentOrderFromExistingOrder(componentType string, existingOrder sortableComponents) registeredComponentOrder {
+ // Only the names from the existing order are needed for this so create a list of component names
+ // in the correct order.
+ namesInOrder := componentsToNames(existingOrder)
+
+ // Populate the map from name to position in the list.
+ nameToIndex := make(map[string]int)
+ for i, n := range namesInOrder {
+ nameToIndex[n] = i
+ }
+
+ // A function to use to map from a name to an index in the original order.
+ indexOf := func(name string) int {
+ index, ok := nameToIndex[name]
+ if !ok {
+ // Should never happen as tests that use components that are not known at runtime do not sort
+ // so should never use this function.
+ panic(fmt.Errorf("internal error: unknown %s %q should be one of %s", componentType, name, strings.Join(namesInOrder, ", ")))
+ }
+ return index
+ }
+
+ // The less function.
+ less := func(n1, n2 string) bool {
+ i1 := indexOf(n1)
+ i2 := indexOf(n2)
+ return i1 < i2
+ }
+
+ return registeredComponentOrder{
+ componentType: componentType,
+ namesInOrder: namesInOrder,
+ namesToIndex: nameToIndex,
+ less: less,
+ }
+}
+
+// componentsToNames maps from the slice of components to a slice of their names.
+func componentsToNames(components sortableComponents) []string {
+ names := make([]string, len(components))
+ for i, c := range components {
+ names[i] = c.componentName()
+ }
+ return names
+}
+
+// enforceOrdering enforces the supplied components are in the same order as is defined in this
+// object.
+//
+// If the supplied components contains any components that are not registered at runtime, i.e. test
+// specific components, then it is impossible to sort them into an order that both matches the
+// runtime and also preserves the implicit ordering defined in the test. In that case it will not
+// sort the components, instead it will just check that the components are in the correct order.
+//
+// Otherwise, this will sort the supplied components in place.
+func (o *registeredComponentOrder) enforceOrdering(components sortableComponents) {
+ // Check to see if the list of components contains any components that are
+ // not registered at runtime.
+ var unknownComponents []string
+ testOrder := componentsToNames(components)
+ for _, name := range testOrder {
+ if _, ok := o.namesToIndex[name]; !ok {
+ unknownComponents = append(unknownComponents, name)
+ break
+ }
+ }
+
+ // If the slice contains some unknown components then it is not possible to
+ // sort them into an order that matches the runtime while also preserving the
+ // order expected from the test, so in that case don't sort just check that
+ // the order of the known mutators does match.
+ if len(unknownComponents) > 0 {
+ // Check order.
+ o.checkTestOrder(testOrder, unknownComponents)
+ } else {
+ // Sort the components.
+ sort.Slice(components, func(i, j int) bool {
+ n1 := components[i].componentName()
+ n2 := components[j].componentName()
+ return o.less(n1, n2)
+ })
+ }
+}
+
+// checkTestOrder checks that the supplied testOrder matches the one defined by this object,
+// panicking if it does not.
+func (o *registeredComponentOrder) checkTestOrder(testOrder []string, unknownComponents []string) {
+ lastMatchingTest := -1
+ matchCount := 0
+ // Take a copy of the runtime order as it is modified during the comparison.
+ runtimeOrder := append([]string(nil), o.namesInOrder...)
+ componentType := o.componentType
+ for i, j := 0, 0; i < len(testOrder) && j < len(runtimeOrder); {
+ test := testOrder[i]
+ runtime := runtimeOrder[j]
+
+ if test == runtime {
+ testOrder[i] = test + fmt.Sprintf(" <-- matched with runtime %s %d", componentType, j)
+ runtimeOrder[j] = runtime + fmt.Sprintf(" <-- matched with test %s %d", componentType, i)
+ lastMatchingTest = i
+ i += 1
+ j += 1
+ matchCount += 1
+ } else if _, ok := o.namesToIndex[test]; !ok {
+ // The test component is not registered globally so assume it is the correct place, treat it
+ // as having matched and skip it.
+ i += 1
+ matchCount += 1
+ } else {
+ // Assume that the test list is in the same order as the runtime list but the runtime list
+ // contains some components that are not present in the tests. So, skip the runtime component
+ // to try and find the next one that matches the current test component.
+ j += 1
+ }
+ }
+
+ // If every item in the test order was either test specific or matched one in the runtime then
+ // it is in the correct order. Otherwise, it was not so fail.
+ if matchCount != len(testOrder) {
+ // The test component names were not all matched with a runtime component name so there must
+ // either be a component present in the test that is not present in the runtime or they must be
+ // in the wrong order.
+ testOrder[lastMatchingTest+1] = testOrder[lastMatchingTest+1] + " <--- unmatched"
+ panic(fmt.Errorf("the tests uses test specific components %q and so cannot be automatically sorted."+
+ " Unfortunately it uses %s components in the wrong order.\n"+
+ "test order:\n %s\n"+
+ "runtime order\n %s\n",
+ SortedUniqueStrings(unknownComponents),
+ componentType,
+ strings.Join(testOrder, "\n "),
+ strings.Join(runtimeOrder, "\n ")))
+ }
+}
+
+// registrationSorter encapsulates the information needed to ensure that the test mutators are
+// registered, and thereby executed, in the same order as they are at runtime.
+//
+// It MUST be populated lazily AFTER all package initialization has been done otherwise it will
+// only define the order for a subset of all the registered build components that are available for
+// the packages being tested.
+//
+// e.g if this is initialized during say the cc package initialization then any tests run in the
+// java package will not sort build components registered by the java package's init() functions.
+type registrationSorter struct {
+ // Used to ensure that this is only created once.
+ once sync.Once
+
+ // The order of pre-singletons
+ preSingletonOrder registeredComponentOrder
+
+ // The order of mutators
+ mutatorOrder registeredComponentOrder
+
+ // The order of singletons
+ singletonOrder registeredComponentOrder
+}
+
+// populate initializes this structure from globally registered build components.
+//
+// Only the first call has any effect.
+func (s *registrationSorter) populate() {
+ s.once.Do(func() {
+ // Create an ordering from the globally registered pre-singletons.
+ s.preSingletonOrder = registeredComponentOrderFromExistingOrder("pre-singleton", preSingletons)
+
+ // Created an ordering from the globally registered mutators.
+ globallyRegisteredMutators := collateGloballyRegisteredMutators()
+ s.mutatorOrder = registeredComponentOrderFromExistingOrder("mutator", globallyRegisteredMutators)
+
+ // Create an ordering from the globally registered singletons.
+ globallyRegisteredSingletons := collateGloballyRegisteredSingletons()
+ s.singletonOrder = registeredComponentOrderFromExistingOrder("singleton", globallyRegisteredSingletons)
+ })
+}
+
+// Provides support for enforcing the same order in which build components are registered globally
+// to the order in which they are registered during tests.
+//
+// MUST only be accessed via the globallyRegisteredComponentsOrder func.
+var globalRegistrationSorter registrationSorter
+
+// globallyRegisteredComponentsOrder returns the globalRegistrationSorter after ensuring it is
+// correctly populated.
+func globallyRegisteredComponentsOrder() *registrationSorter {
+ globalRegistrationSorter.populate()
+ return &globalRegistrationSorter
+}
+
+func (ctx *TestContext) Register() {
+ globalOrder := globallyRegisteredComponentsOrder()
+
+ // Ensure that the pre-singletons used in the test are in the same order as they are used at
+ // runtime.
+ globalOrder.preSingletonOrder.enforceOrdering(ctx.preSingletons)
+ ctx.preSingletons.registerAll(ctx.Context)
+
+ mutators := collateRegisteredMutators(ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps)
+ // Ensure that the mutators used in the test are in the same order as they are used at runtime.
+ globalOrder.mutatorOrder.enforceOrdering(mutators)
+ mutators.registerAll(ctx.Context)
+
+ // Ensure that the singletons used in the test are in the same order as they are used at runtime.
+ globalOrder.singletonOrder.enforceOrdering(ctx.singletons)
+ ctx.singletons.registerAll(ctx.Context)
+
+ // Save the sorted components order away to make them easy to access while debugging.
+ ctx.preSingletonOrder = componentsToNames(preSingletons)
+ ctx.mutatorOrder = componentsToNames(mutators)
+ ctx.singletonOrder = componentsToNames(singletons)
+}
+
+// RegisterForBazelConversion prepares a test context for bp2build conversion.
+func (ctx *TestContext) RegisterForBazelConversion() {
+ RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch, ctx.bp2buildDeps, ctx.bp2buildMutators)
}
func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
@@ -103,8 +442,18 @@
ctx.Context.RegisterModuleType(name, ModuleFactoryAdaptor(factory))
}
+func (ctx *TestContext) RegisterSingletonModuleType(name string, factory SingletonModuleFactory) {
+ s, m := SingletonModuleFactoryAdaptor(name, factory)
+ ctx.RegisterSingletonType(name, s)
+ ctx.RegisterModuleType(name, m)
+}
+
func (ctx *TestContext) RegisterSingletonType(name string, factory SingletonFactory) {
- ctx.Context.RegisterSingletonType(name, SingletonFactoryAdaptor(ctx.Context, factory))
+ ctx.singletons = append(ctx.singletons, newSingleton(name, factory))
+}
+
+func (ctx *TestContext) RegisterPreSingletonType(name string, factory SingletonFactory) {
+ ctx.preSingletons = append(ctx.preSingletons, newPreSingleton(name, factory))
}
func (ctx *TestContext) ModuleForTests(name, variant string) TestingModule {
@@ -137,7 +486,7 @@
}
}
- return TestingModule{module}
+ return newTestingModule(ctx.config, module)
}
func (ctx *TestContext) ModuleVariantsForTests(name string) []string {
@@ -157,8 +506,8 @@
n := ctx.SingletonName(s)
if n == name {
return TestingSingleton{
- singleton: s.(*singletonAdaptor).Singleton,
- provider: s.(testBuildProvider),
+ baseTestingComponent: newBaseTestingComponent(ctx.config, s.(testBuildProvider)),
+ singleton: s.(*singletonAdaptor).Singleton,
}
}
allSingletonNames = append(allSingletonNames, n)
@@ -168,6 +517,10 @@
"\nall singletons: %v", name, allSingletonNames))
}
+func (ctx *TestContext) Config() Config {
+ return ctx.config
+}
+
type testBuildProvider interface {
BuildParamsForTests() []BuildParams
RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
@@ -176,62 +529,199 @@
type TestingBuildParams struct {
BuildParams
RuleParams blueprint.RuleParams
+
+ config Config
}
-func newTestingBuildParams(provider testBuildProvider, bparams BuildParams) TestingBuildParams {
+// RelativeToTop creates a new instance of this which has had any usages of the current test's
+// temporary and test specific build directory replaced with a path relative to the notional top.
+//
+// The parts of this structure which are changed are:
+// * BuildParams
+// * Args
+// * All Path, Paths, WritablePath and WritablePaths fields.
+//
+// * RuleParams
+// * Command
+// * Depfile
+// * Rspfile
+// * RspfileContent
+// * SymlinkOutputs
+// * CommandDeps
+// * CommandOrderOnly
+//
+// See PathRelativeToTop for more details.
+//
+// deprecated: this is no longer needed as TestingBuildParams are created in this form.
+func (p TestingBuildParams) RelativeToTop() TestingBuildParams {
+ // If this is not a valid params then just return it back. That will make it easy to use with the
+ // Maybe...() methods.
+ if p.Rule == nil {
+ return p
+ }
+ if p.config.config == nil {
+ return p
+ }
+ // Take a copy of the build params and replace any args that contains test specific temporary
+ // paths with paths relative to the top.
+ bparams := p.BuildParams
+ bparams.Depfile = normalizeWritablePathRelativeToTop(bparams.Depfile)
+ bparams.Output = normalizeWritablePathRelativeToTop(bparams.Output)
+ bparams.Outputs = bparams.Outputs.RelativeToTop()
+ bparams.SymlinkOutput = normalizeWritablePathRelativeToTop(bparams.SymlinkOutput)
+ bparams.SymlinkOutputs = bparams.SymlinkOutputs.RelativeToTop()
+ bparams.ImplicitOutput = normalizeWritablePathRelativeToTop(bparams.ImplicitOutput)
+ bparams.ImplicitOutputs = bparams.ImplicitOutputs.RelativeToTop()
+ bparams.Input = normalizePathRelativeToTop(bparams.Input)
+ bparams.Inputs = bparams.Inputs.RelativeToTop()
+ bparams.Implicit = normalizePathRelativeToTop(bparams.Implicit)
+ bparams.Implicits = bparams.Implicits.RelativeToTop()
+ bparams.OrderOnly = bparams.OrderOnly.RelativeToTop()
+ bparams.Validation = normalizePathRelativeToTop(bparams.Validation)
+ bparams.Validations = bparams.Validations.RelativeToTop()
+ bparams.Args = normalizeStringMapRelativeToTop(p.config, bparams.Args)
+
+ // Ditto for any fields in the RuleParams.
+ rparams := p.RuleParams
+ rparams.Command = normalizeStringRelativeToTop(p.config, rparams.Command)
+ rparams.Depfile = normalizeStringRelativeToTop(p.config, rparams.Depfile)
+ rparams.Rspfile = normalizeStringRelativeToTop(p.config, rparams.Rspfile)
+ rparams.RspfileContent = normalizeStringRelativeToTop(p.config, rparams.RspfileContent)
+ rparams.SymlinkOutputs = normalizeStringArrayRelativeToTop(p.config, rparams.SymlinkOutputs)
+ rparams.CommandDeps = normalizeStringArrayRelativeToTop(p.config, rparams.CommandDeps)
+ rparams.CommandOrderOnly = normalizeStringArrayRelativeToTop(p.config, rparams.CommandOrderOnly)
+
return TestingBuildParams{
BuildParams: bparams,
- RuleParams: provider.RuleParamsForTests()[bparams.Rule],
+ RuleParams: rparams,
}
}
-func maybeBuildParamsFromRule(provider testBuildProvider, rule string) (TestingBuildParams, []string) {
+func normalizeWritablePathRelativeToTop(path WritablePath) WritablePath {
+ if path == nil {
+ return nil
+ }
+ return path.RelativeToTop().(WritablePath)
+}
+
+func normalizePathRelativeToTop(path Path) Path {
+ if path == nil {
+ return nil
+ }
+ return path.RelativeToTop()
+}
+
+// baseTestingComponent provides functionality common to both TestingModule and TestingSingleton.
+type baseTestingComponent struct {
+ config Config
+ provider testBuildProvider
+}
+
+func newBaseTestingComponent(config Config, provider testBuildProvider) baseTestingComponent {
+ return baseTestingComponent{config, provider}
+}
+
+// A function that will normalize a string containing paths, e.g. ninja command, by replacing
+// any references to the test specific temporary build directory that changes with each run to a
+// fixed path relative to a notional top directory.
+//
+// This is similar to StringPathRelativeToTop except that assumes the string is a single path
+// containing at most one instance of the temporary build directory at the start of the path while
+// this assumes that there can be any number at any position.
+func normalizeStringRelativeToTop(config Config, s string) string {
+ // The buildDir usually looks something like: /tmp/testFoo2345/001
+ //
+ // Replace any usage of the buildDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with
+ // "out/soong".
+ outSoongDir := filepath.Clean(config.buildDir)
+ re := regexp.MustCompile(`\Q` + outSoongDir + `\E\b`)
+ s = re.ReplaceAllString(s, "out/soong")
+
+ // Replace any usage of the buildDir/.. with out, e.g. replace "/tmp/testFoo2345" with
+ // "out". This must come after the previous replacement otherwise this would replace
+ // "/tmp/testFoo2345/001" with "out/001" instead of "out/soong".
+ outDir := filepath.Dir(outSoongDir)
+ re = regexp.MustCompile(`\Q` + outDir + `\E\b`)
+ s = re.ReplaceAllString(s, "out")
+
+ return s
+}
+
+// normalizeStringArrayRelativeToTop creates a new slice constructed by applying
+// normalizeStringRelativeToTop to each item in the slice.
+func normalizeStringArrayRelativeToTop(config Config, slice []string) []string {
+ newSlice := make([]string, len(slice))
+ for i, s := range slice {
+ newSlice[i] = normalizeStringRelativeToTop(config, s)
+ }
+ return newSlice
+}
+
+// normalizeStringMapRelativeToTop creates a new map constructed by applying
+// normalizeStringRelativeToTop to each value in the map.
+func normalizeStringMapRelativeToTop(config Config, m map[string]string) map[string]string {
+ newMap := map[string]string{}
+ for k, v := range m {
+ newMap[k] = normalizeStringRelativeToTop(config, v)
+ }
+ return newMap
+}
+
+func (b baseTestingComponent) newTestingBuildParams(bparams BuildParams) TestingBuildParams {
+ return TestingBuildParams{
+ config: b.config,
+ BuildParams: bparams,
+ RuleParams: b.provider.RuleParamsForTests()[bparams.Rule],
+ }.RelativeToTop()
+}
+
+func (b baseTestingComponent) maybeBuildParamsFromRule(rule string) (TestingBuildParams, []string) {
var searchedRules []string
- for _, p := range provider.BuildParamsForTests() {
+ for _, p := range b.provider.BuildParamsForTests() {
searchedRules = append(searchedRules, p.Rule.String())
if strings.Contains(p.Rule.String(), rule) {
- return newTestingBuildParams(provider, p), searchedRules
+ return b.newTestingBuildParams(p), searchedRules
}
}
return TestingBuildParams{}, searchedRules
}
-func buildParamsFromRule(provider testBuildProvider, rule string) TestingBuildParams {
- p, searchRules := maybeBuildParamsFromRule(provider, rule)
+func (b baseTestingComponent) buildParamsFromRule(rule string) TestingBuildParams {
+ p, searchRules := b.maybeBuildParamsFromRule(rule)
if p.Rule == nil {
panic(fmt.Errorf("couldn't find rule %q.\nall rules: %v", rule, searchRules))
}
return p
}
-func maybeBuildParamsFromDescription(provider testBuildProvider, desc string) TestingBuildParams {
- for _, p := range provider.BuildParamsForTests() {
+func (b baseTestingComponent) maybeBuildParamsFromDescription(desc string) TestingBuildParams {
+ for _, p := range b.provider.BuildParamsForTests() {
if strings.Contains(p.Description, desc) {
- return newTestingBuildParams(provider, p)
+ return b.newTestingBuildParams(p)
}
}
return TestingBuildParams{}
}
-func buildParamsFromDescription(provider testBuildProvider, desc string) TestingBuildParams {
- p := maybeBuildParamsFromDescription(provider, desc)
+func (b baseTestingComponent) buildParamsFromDescription(desc string) TestingBuildParams {
+ p := b.maybeBuildParamsFromDescription(desc)
if p.Rule == nil {
panic(fmt.Errorf("couldn't find description %q", desc))
}
return p
}
-func maybeBuildParamsFromOutput(provider testBuildProvider, file string) (TestingBuildParams, []string) {
+func (b baseTestingComponent) maybeBuildParamsFromOutput(file string) (TestingBuildParams, []string) {
var searchedOutputs []string
- for _, p := range provider.BuildParamsForTests() {
+ for _, p := range b.provider.BuildParamsForTests() {
outputs := append(WritablePaths(nil), p.Outputs...)
outputs = append(outputs, p.ImplicitOutputs...)
if p.Output != nil {
outputs = append(outputs, p.Output)
}
for _, f := range outputs {
- if f.String() == file || f.Rel() == file {
- return newTestingBuildParams(provider, p), nil
+ if f.String() == file || f.Rel() == file || PathRelativeToTop(f) == file {
+ return b.newTestingBuildParams(p), nil
}
searchedOutputs = append(searchedOutputs, f.Rel())
}
@@ -239,18 +729,18 @@
return TestingBuildParams{}, searchedOutputs
}
-func buildParamsFromOutput(provider testBuildProvider, file string) TestingBuildParams {
- p, searchedOutputs := maybeBuildParamsFromOutput(provider, file)
+func (b baseTestingComponent) buildParamsFromOutput(file string) TestingBuildParams {
+ p, searchedOutputs := b.maybeBuildParamsFromOutput(file)
if p.Rule == nil {
- panic(fmt.Errorf("couldn't find output %q.\nall outputs: %v",
- file, searchedOutputs))
+ panic(fmt.Errorf("couldn't find output %q.\nall outputs:\n %s\n",
+ file, strings.Join(searchedOutputs, "\n ")))
}
return p
}
-func allOutputs(provider testBuildProvider) []string {
+func (b baseTestingComponent) allOutputs() []string {
var outputFullPaths []string
- for _, p := range provider.BuildParamsForTests() {
+ for _, p := range b.provider.BuildParamsForTests() {
outputs := append(WritablePaths(nil), p.Outputs...)
outputs = append(outputs, p.ImplicitOutputs...)
if p.Output != nil {
@@ -261,64 +751,94 @@
return outputFullPaths
}
+// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Returns an empty
+// BuildParams if no rule is found.
+func (b baseTestingComponent) MaybeRule(rule string) TestingBuildParams {
+ r, _ := b.maybeBuildParamsFromRule(rule)
+ return r
+}
+
+// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Panics if no rule is found.
+func (b baseTestingComponent) Rule(rule string) TestingBuildParams {
+ return b.buildParamsFromRule(rule)
+}
+
+// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string. Returns an empty
+// BuildParams if no rule is found.
+func (b baseTestingComponent) MaybeDescription(desc string) TestingBuildParams {
+ return b.maybeBuildParamsFromDescription(desc)
+}
+
+// Description finds a call to ctx.Build with BuildParams.Description set to a the given string. Panics if no rule is
+// found.
+func (b baseTestingComponent) Description(desc string) TestingBuildParams {
+ return b.buildParamsFromDescription(desc)
+}
+
+// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
+// value matches the provided string. Returns an empty BuildParams if no rule is found.
+func (b baseTestingComponent) MaybeOutput(file string) TestingBuildParams {
+ p, _ := b.maybeBuildParamsFromOutput(file)
+ return p
+}
+
+// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
+// value matches the provided string. Panics if no rule is found.
+func (b baseTestingComponent) Output(file string) TestingBuildParams {
+ return b.buildParamsFromOutput(file)
+}
+
+// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
+func (b baseTestingComponent) AllOutputs() []string {
+ return b.allOutputs()
+}
+
// TestingModule is wrapper around an android.Module that provides methods to find information about individual
// ctx.Build parameters for verification in tests.
type TestingModule struct {
+ baseTestingComponent
module Module
}
+func newTestingModule(config Config, module Module) TestingModule {
+ return TestingModule{
+ newBaseTestingComponent(config, module),
+ module,
+ }
+}
+
// Module returns the Module wrapped by the TestingModule.
func (m TestingModule) Module() Module {
return m.module
}
-// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Returns an empty
-// BuildParams if no rule is found.
-func (m TestingModule) MaybeRule(rule string) TestingBuildParams {
- r, _ := maybeBuildParamsFromRule(m.module, rule)
- return r
+// VariablesForTestsRelativeToTop returns a copy of the Module.VariablesForTests() with every value
+// having any temporary build dir usages replaced with paths relative to a notional top.
+func (m TestingModule) VariablesForTestsRelativeToTop() map[string]string {
+ return normalizeStringMapRelativeToTop(m.config, m.module.VariablesForTests())
}
-// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Panics if no rule is found.
-func (m TestingModule) Rule(rule string) TestingBuildParams {
- return buildParamsFromRule(m.module, rule)
-}
+// OutputFiles calls OutputFileProducer.OutputFiles on the encapsulated module, exits the test
+// immediately if there is an error and otherwise returns the result of calling Paths.RelativeToTop
+// on the returned Paths.
+func (m TestingModule) OutputFiles(t *testing.T, tag string) Paths {
+ producer, ok := m.module.(OutputFileProducer)
+ if !ok {
+ t.Fatalf("%q must implement OutputFileProducer\n", m.module.Name())
+ }
+ paths, err := producer.OutputFiles(tag)
+ if err != nil {
+ t.Fatal(err)
+ }
-// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string. Returns an empty
-// BuildParams if no rule is found.
-func (m TestingModule) MaybeDescription(desc string) TestingBuildParams {
- return maybeBuildParamsFromDescription(m.module, desc)
-}
-
-// Description finds a call to ctx.Build with BuildParams.Description set to a the given string. Panics if no rule is
-// found.
-func (m TestingModule) Description(desc string) TestingBuildParams {
- return buildParamsFromDescription(m.module, desc)
-}
-
-// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
-// value matches the provided string. Returns an empty BuildParams if no rule is found.
-func (m TestingModule) MaybeOutput(file string) TestingBuildParams {
- p, _ := maybeBuildParamsFromOutput(m.module, file)
- return p
-}
-
-// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
-// value matches the provided string. Panics if no rule is found.
-func (m TestingModule) Output(file string) TestingBuildParams {
- return buildParamsFromOutput(m.module, file)
-}
-
-// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
-func (m TestingModule) AllOutputs() []string {
- return allOutputs(m.module)
+ return paths.RelativeToTop()
}
// TestingSingleton is wrapper around an android.Singleton that provides methods to find information about individual
// ctx.Build parameters for verification in tests.
type TestingSingleton struct {
+ baseTestingComponent
singleton Singleton
- provider testBuildProvider
}
// Singleton returns the Singleton wrapped by the TestingSingleton.
@@ -326,48 +846,6 @@
return s.singleton
}
-// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Returns an empty
-// BuildParams if no rule is found.
-func (s TestingSingleton) MaybeRule(rule string) TestingBuildParams {
- r, _ := maybeBuildParamsFromRule(s.provider, rule)
- return r
-}
-
-// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Panics if no rule is found.
-func (s TestingSingleton) Rule(rule string) TestingBuildParams {
- return buildParamsFromRule(s.provider, rule)
-}
-
-// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string. Returns an empty
-// BuildParams if no rule is found.
-func (s TestingSingleton) MaybeDescription(desc string) TestingBuildParams {
- return maybeBuildParamsFromDescription(s.provider, desc)
-}
-
-// Description finds a call to ctx.Build with BuildParams.Description set to a the given string. Panics if no rule is
-// found.
-func (s TestingSingleton) Description(desc string) TestingBuildParams {
- return buildParamsFromDescription(s.provider, desc)
-}
-
-// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
-// value matches the provided string. Returns an empty BuildParams if no rule is found.
-func (s TestingSingleton) MaybeOutput(file string) TestingBuildParams {
- p, _ := maybeBuildParamsFromOutput(s.provider, file)
- return p
-}
-
-// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
-// value matches the provided string. Panics if no rule is found.
-func (s TestingSingleton) Output(file string) TestingBuildParams {
- return buildParamsFromOutput(s.provider, file)
-}
-
-// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
-func (s TestingSingleton) AllOutputs() []string {
- return allOutputs(s.provider)
-}
-
func FailIfErrored(t *testing.T, errs []error) {
t.Helper()
if len(errs) > 0 {
@@ -378,12 +856,15 @@
}
}
-func FailIfNoMatchingErrors(t *testing.T, pattern string, errs []error) {
+// Fail if no errors that matched the regular expression were found.
+//
+// Returns true if a matching error was found, false otherwise.
+func FailIfNoMatchingErrors(t *testing.T, pattern string, errs []error) bool {
t.Helper()
matcher, err := regexp.Compile(pattern)
if err != nil {
- t.Errorf("failed to compile regular expression %q because %s", pattern, err)
+ t.Fatalf("failed to compile regular expression %q because %s", pattern, err)
}
found := false
@@ -399,6 +880,8 @@
t.Errorf("errs[%d] = %q", i, err)
}
}
+
+ return found
}
func CheckErrorsAgainstExpectations(t *testing.T, errs []error, expectedErrorPatterns []string) {
@@ -419,16 +902,16 @@
for i, err := range errs {
t.Errorf("errs[%d] = %s", i, err)
}
+ t.FailNow()
}
}
-
}
func SetKatiEnabledForTests(config Config) {
config.katiEnabled = true
}
-func AndroidMkEntriesForTest(t *testing.T, config Config, bpPath string, mod blueprint.Module) []AndroidMkEntries {
+func AndroidMkEntriesForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) []AndroidMkEntries {
var p AndroidMkEntriesProvider
var ok bool
if p, ok = mod.(AndroidMkEntriesProvider); !ok {
@@ -437,19 +920,19 @@
entriesList := p.AndroidMkEntries()
for i, _ := range entriesList {
- entriesList[i].fillInEntries(config, bpPath, mod)
+ entriesList[i].fillInEntries(ctx, mod)
}
return entriesList
}
-func AndroidMkDataForTest(t *testing.T, config Config, bpPath string, mod blueprint.Module) AndroidMkData {
+func AndroidMkDataForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) AndroidMkData {
var p AndroidMkDataProvider
var ok bool
if p, ok = mod.(AndroidMkDataProvider); !ok {
t.Errorf("module does not implement AndroidMkDataProvider: " + mod.Name())
}
data := p.AndroidMk()
- data.fillInData(config, bpPath, mod)
+ data.fillInData(ctx, mod)
return data
}
@@ -462,10 +945,16 @@
// that is relative to the root of the source tree.
//
// The build and source paths should be distinguishable based on their contents.
+//
+// deprecated: use PathRelativeToTop instead as it handles make install paths and differentiates
+// between output and source properly.
func NormalizePathForTesting(path Path) string {
+ if path == nil {
+ return "<nil path>"
+ }
p := path.String()
if w, ok := path.(WritablePath); ok {
- rel, err := filepath.Rel(w.buildDir(), p)
+ rel, err := filepath.Rel(w.getBuildDir(), p)
if err != nil {
panic(err)
}
@@ -474,6 +963,11 @@
return p
}
+// NormalizePathsForTesting creates a slice of strings where each string is the result of applying
+// NormalizePathForTesting to the corresponding Path in the input slice.
+//
+// deprecated: use PathsRelativeToTop instead as it handles make install paths and differentiates
+// between output and source properly.
func NormalizePathsForTesting(paths Paths) []string {
var result []string
for _, path := range paths {
@@ -482,3 +976,102 @@
}
return result
}
+
+// PathRelativeToTop returns a string representation of the path relative to a notional top
+// directory.
+//
+// It return "<nil path>" if the supplied path is nil, otherwise it returns the result of calling
+// Path.RelativeToTop to obtain a relative Path and then calling Path.String on that to get the
+// string representation.
+func PathRelativeToTop(path Path) string {
+ if path == nil {
+ return "<nil path>"
+ }
+ return path.RelativeToTop().String()
+}
+
+// PathsRelativeToTop creates a slice of strings where each string is the result of applying
+// PathRelativeToTop to the corresponding Path in the input slice.
+func PathsRelativeToTop(paths Paths) []string {
+ var result []string
+ for _, path := range paths {
+ relative := PathRelativeToTop(path)
+ result = append(result, relative)
+ }
+ return result
+}
+
+// StringPathRelativeToTop returns a string representation of the path relative to a notional top
+// directory.
+//
+// See Path.RelativeToTop for more details as to what `relative to top` means.
+//
+// This is provided for processing paths that have already been converted into a string, e.g. paths
+// in AndroidMkEntries structures. As a result it needs to be supplied the soong output dir against
+// which it can try and relativize paths. PathRelativeToTop must be used for process Path objects.
+func StringPathRelativeToTop(soongOutDir string, path string) string {
+ ensureTestOnly()
+
+ // A relative path must be a source path so leave it as it is.
+ if !filepath.IsAbs(path) {
+ return path
+ }
+
+ // Check to see if the path is relative to the soong out dir.
+ rel, isRel, err := maybeRelErr(soongOutDir, path)
+ if err != nil {
+ panic(err)
+ }
+
+ if isRel {
+ // The path is in the soong out dir so indicate that in the relative path.
+ return filepath.Join("out/soong", rel)
+ }
+
+ // Check to see if the path is relative to the top level out dir.
+ outDir := filepath.Dir(soongOutDir)
+ rel, isRel, err = maybeRelErr(outDir, path)
+ if err != nil {
+ panic(err)
+ }
+
+ if isRel {
+ // The path is in the out dir so indicate that in the relative path.
+ return filepath.Join("out", rel)
+ }
+
+ // This should never happen.
+ panic(fmt.Errorf("internal error: absolute path %s is not relative to the out dir %s", path, outDir))
+}
+
+// StringPathsRelativeToTop creates a slice of strings where each string is the result of applying
+// StringPathRelativeToTop to the corresponding string path in the input slice.
+//
+// This is provided for processing paths that have already been converted into a string, e.g. paths
+// in AndroidMkEntries structures. As a result it needs to be supplied the soong output dir against
+// which it can try and relativize paths. PathsRelativeToTop must be used for process Paths objects.
+func StringPathsRelativeToTop(soongOutDir string, paths []string) []string {
+ var result []string
+ for _, path := range paths {
+ relative := StringPathRelativeToTop(soongOutDir, path)
+ result = append(result, relative)
+ }
+ return result
+}
+
+// StringRelativeToTop will normalize a string containing paths, e.g. ninja command, by replacing
+// any references to the test specific temporary build directory that changes with each run to a
+// fixed path relative to a notional top directory.
+//
+// This is similar to StringPathRelativeToTop except that assumes the string is a single path
+// containing at most one instance of the temporary build directory at the start of the path while
+// this assumes that there can be any number at any position.
+func StringRelativeToTop(config Config, command string) string {
+ return normalizeStringRelativeToTop(config, command)
+}
+
+// StringsRelativeToTop will return a new slice such that each item in the new slice is the result
+// of calling StringRelativeToTop on the corresponding item in the input slice.
+func StringsRelativeToTop(config Config, command []string) []string {
+ return normalizeStringArrayRelativeToTop(config, command)
+}
diff --git a/android/util.go b/android/util.go
index 0f940fa..a0394f6 100644
--- a/android/util.go
+++ b/android/util.go
@@ -137,6 +137,16 @@
return false
}
+// Returns true if any string in the given list has the given substring.
+func SubstringInList(list []string, substr string) bool {
+ for _, s := range list {
+ if strings.Contains(s, substr) {
+ return true
+ }
+ }
+ return false
+}
+
// Returns true if any string in the given list has the given prefix.
func PrefixInList(list []string, prefix string) bool {
for _, s := range list {
@@ -183,6 +193,17 @@
return
}
+// FilterListPred returns the elements of the given list for which the predicate
+// returns true. Order is kept.
+func FilterListPred(list []string, pred func(s string) bool) (filtered []string) {
+ for _, l := range list {
+ if pred(l) {
+ filtered = append(filtered, l)
+ }
+ }
+ return
+}
+
// RemoveListFromList removes the strings belonging to the filter list from the
// given list and returns the result
func RemoveListFromList(list []string, filter_out []string) (result []string) {
diff --git a/android/util_test.go b/android/util_test.go
index fa26c77..09bec01 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -18,6 +18,7 @@
"fmt"
"reflect"
"strconv"
+ "strings"
"testing"
)
@@ -299,6 +300,14 @@
}
}
+func TestFilterListPred(t *testing.T) {
+ pred := func(s string) bool { return strings.HasPrefix(s, "a/") }
+ AssertArrayString(t, "filter", FilterListPred([]string{"a/c", "b/a", "a/b"}, pred), []string{"a/c", "a/b"})
+ AssertArrayString(t, "filter", FilterListPred([]string{"b/c", "a/a", "b/b"}, pred), []string{"a/a"})
+ AssertArrayString(t, "filter", FilterListPred([]string{"c/c", "b/a", "c/b"}, pred), []string{})
+ AssertArrayString(t, "filter", FilterListPred([]string{"a/c", "a/a", "a/b"}, pred), []string{"a/c", "a/a", "a/b"})
+}
+
func TestRemoveListFromList(t *testing.T) {
input := []string{"a", "b", "c", "d", "a", "c", "d"}
filter := []string{"a", "c"}
diff --git a/android/variable.go b/android/variable.go
index 753ddd7..e830845 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -24,11 +24,17 @@
)
func init() {
- PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ registerVariableBuildComponents(InitRegistrationContext)
+}
+
+func registerVariableBuildComponents(ctx RegistrationContext) {
+ ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
ctx.BottomUp("variable", VariableMutator).Parallel()
})
}
+var PrepareForTestWithVariables = FixtureRegisterWithContext(registerVariableBuildComponents)
+
type variableProperties struct {
Product_variables struct {
Platform_sdk_version struct {
@@ -121,10 +127,6 @@
Cppflags []string
}
- Use_lmkd_stats_log struct {
- Cflags []string
- }
-
Arc struct {
Cflags []string `android:"arch_variant"`
Exclude_srcs []string `android:"arch_variant"`
@@ -139,10 +141,6 @@
Enabled *bool
}
- Experimental_mte struct {
- Cflags []string `android:"arch_variant"`
- } `android:"arch_variant"`
-
Native_coverage struct {
Src *string `android:"arch_variant"`
Srcs []string `android:"arch_variant"`
@@ -181,6 +179,8 @@
DeviceCurrentApiLevelForVendorModules *string `json:",omitempty"`
DeviceSystemSdkVersions []string `json:",omitempty"`
+ RecoverySnapshotVersion *string `json:",omitempty"`
+
DeviceSecondaryArch *string `json:",omitempty"`
DeviceSecondaryArchVariant *string `json:",omitempty"`
DeviceSecondaryCpuVariant *string `json:",omitempty"`
@@ -205,11 +205,9 @@
CrossHostArch *string `json:",omitempty"`
CrossHostSecondaryArch *string `json:",omitempty"`
- DeviceResourceOverlays []string `json:",omitempty"`
- ProductResourceOverlays []string `json:",omitempty"`
- EnforceRROTargets []string `json:",omitempty"`
- // TODO(b/150820813) Some modules depend on static overlay, remove this after eliminating the dependency.
- EnforceRROExemptedTargets []string `json:",omitempty"`
+ DeviceResourceOverlays []string `json:",omitempty"`
+ ProductResourceOverlays []string `json:",omitempty"`
+ EnforceRROTargets []string `json:",omitempty"`
EnforceRROExcludedOverlays []string `json:",omitempty"`
AAPTCharacteristics *string `json:",omitempty"`
@@ -242,7 +240,6 @@
Treble_linker_namespaces *bool `json:",omitempty"`
Enforce_vintf_manifest *bool `json:",omitempty"`
Uml *bool `json:",omitempty"`
- Use_lmkd_stats_log *bool `json:",omitempty"`
Arc *bool `json:",omitempty"`
MinimizeJavaDebugInfo *bool `json:",omitempty"`
@@ -262,7 +259,9 @@
DisableScudo *bool `json:",omitempty"`
- Experimental_mte *bool `json:",omitempty"`
+ MemtagHeapExcludePaths []string `json:",omitempty"`
+ MemtagHeapAsyncIncludePaths []string `json:",omitempty"`
+ MemtagHeapSyncIncludePaths []string `json:",omitempty"`
VendorPath *string `json:",omitempty"`
OdmPath *string `json:",omitempty"`
@@ -309,23 +308,40 @@
VndkUseCoreVariant *bool `json:",omitempty"`
VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
+ DirectedVendorSnapshot bool `json:",omitempty"`
+ VendorSnapshotModules map[string]bool `json:",omitempty"`
+
+ DirectedRecoverySnapshot bool `json:",omitempty"`
+ RecoverySnapshotModules map[string]bool `json:",omitempty"`
+
+ VendorSnapshotDirsIncluded []string `json:",omitempty"`
+ VendorSnapshotDirsExcluded []string `json:",omitempty"`
+ RecoverySnapshotDirsExcluded []string `json:",omitempty"`
+ RecoverySnapshotDirsIncluded []string `json:",omitempty"`
+
BoardVendorSepolicyDirs []string `json:",omitempty"`
BoardOdmSepolicyDirs []string `json:",omitempty"`
+ BoardReqdMaskPolicy []string `json:",omitempty"`
SystemExtPublicSepolicyDirs []string `json:",omitempty"`
SystemExtPrivateSepolicyDirs []string `json:",omitempty"`
BoardSepolicyM4Defs []string `json:",omitempty"`
+ BoardSepolicyVers *string `json:",omitempty"`
+ PlatformSepolicyVersion *string `json:",omitempty"`
+
VendorVars map[string]map[string]string `json:",omitempty"`
- Ndk_abis *bool `json:",omitempty"`
- Exclude_draft_ndk_apis *bool `json:",omitempty"`
+ Ndk_abis *bool `json:",omitempty"`
- Flatten_apex *bool `json:",omitempty"`
- CompressedApex *bool `json:",omitempty"`
- Aml_abis *bool `json:",omitempty"`
+ Flatten_apex *bool `json:",omitempty"`
+ ForceApexSymlinkOptimization *bool `json:",omitempty"`
+ CompressedApex *bool `json:",omitempty"`
+ Aml_abis *bool `json:",omitempty"`
DexpreoptGlobalConfig *string `json:",omitempty"`
+ WithDexpreopt bool `json:",omitempty"`
+
ManifestPackageNameOverrides []string `json:",omitempty"`
CertificateOverrides []string `json:",omitempty"`
PackageNameOverrides []string `json:",omitempty"`
@@ -359,6 +375,22 @@
BoardKernelModuleInterfaceVersions []string `json:",omitempty"`
BoardMoveRecoveryResourcesToVendorBoot *bool `json:",omitempty"`
+
+ PrebuiltHiddenApiDir *string `json:",omitempty"`
+
+ ShippingApiLevel *string `json:",omitempty"`
+
+ BuildBrokenEnforceSyspropOwner bool `json:",omitempty"`
+ BuildBrokenTrebleSyspropNeverallow bool `json:",omitempty"`
+ BuildBrokenVendorPropertyNamespace bool `json:",omitempty"`
+
+ BuildDebugfsRestrictionsEnabled bool `json:",omitempty"`
+
+ RequiresInsecureExecmemForSwiftshader bool `json:",omitempty"`
+
+ SelinuxIgnoreNeverallows bool `json:",omitempty"`
+
+ SepolicySplit bool `json:",omitempty"`
}
func boolPtr(v bool) *bool {
@@ -405,6 +437,9 @@
Malloc_zero_contents: boolPtr(true),
Malloc_pattern_fill_contents: boolPtr(false),
Safestack: boolPtr(false),
+
+ BootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}},
+ UpdatableBootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}},
}
if runtime.GOOS == "linux" {
@@ -414,6 +449,63 @@
}
}
+// ProductConfigContext requires the access to the Module to get product config properties.
+type ProductConfigContext interface {
+ Module() Module
+}
+
+// ProductConfigProperty contains the information for a single property (may be a struct) paired
+// with the appropriate ProductConfigVariable.
+type ProductConfigProperty struct {
+ ProductConfigVariable string
+ Property interface{}
+}
+
+// ProductConfigProperties is a map of property name to a slice of ProductConfigProperty such that
+// all it all product variable-specific versions of a property are easily accessed together
+type ProductConfigProperties map[string][]ProductConfigProperty
+
+// ProductVariableProperties returns a ProductConfigProperties containing only the properties which
+// have been set for the module in the given context.
+func ProductVariableProperties(ctx ProductConfigContext) ProductConfigProperties {
+ module := ctx.Module()
+ moduleBase := module.base()
+
+ productConfigProperties := ProductConfigProperties{}
+
+ if moduleBase.variableProperties == nil {
+ return productConfigProperties
+ }
+
+ variableValues := reflect.ValueOf(moduleBase.variableProperties).Elem().FieldByName("Product_variables")
+ for i := 0; i < variableValues.NumField(); i++ {
+ variableValue := variableValues.Field(i)
+ // Check if any properties were set for the module
+ if variableValue.IsZero() {
+ continue
+ }
+ // e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc.
+ productVariableName := variableValues.Type().Field(i).Name
+ for j := 0; j < variableValue.NumField(); j++ {
+ property := variableValue.Field(j)
+ // If the property wasn't set, no need to pass it along
+ if property.IsZero() {
+ continue
+ }
+
+ // e.g. Asflags, Cflags, Enabled, etc.
+ propertyName := variableValue.Type().Field(j).Name
+ productConfigProperties[propertyName] = append(productConfigProperties[propertyName],
+ ProductConfigProperty{
+ ProductConfigVariable: productVariableName,
+ Property: property.Interface(),
+ })
+ }
+ }
+
+ return productConfigProperties
+}
+
func VariableMutator(mctx BottomUpMutatorContext) {
var module Module
var ok bool
@@ -430,13 +522,15 @@
variableValues := reflect.ValueOf(a.variableProperties).Elem().FieldByName("Product_variables")
+ productVariables := reflect.ValueOf(mctx.Config().productVariables)
+
for i := 0; i < variableValues.NumField(); i++ {
variableValue := variableValues.Field(i)
name := variableValues.Type().Field(i).Name
property := "product_variables." + proptools.PropertyNameForField(name)
// Check that the variable was set for the product
- val := reflect.ValueOf(mctx.Config().productVariables).FieldByName(name)
+ val := productVariables.FieldByName(name)
if !val.IsValid() || val.Kind() != reflect.Ptr || val.IsNil() {
continue
}
diff --git a/android/variable_test.go b/android/variable_test.go
index 393fe01..928bca6 100644
--- a/android/variable_test.go
+++ b/android/variable_test.go
@@ -181,32 +181,30 @@
name: "baz",
}
`
- config := TestConfig(buildDir, nil, bp, nil)
- config.TestProductVariables.Eng = proptools.BoolPtr(true)
- ctx := NewTestContext(config)
- // A module type that has a srcs property but not a cflags property.
- ctx.RegisterModuleType("module1", testProductVariableModuleFactoryFactory(&struct {
- Srcs []string
- }{}))
- // A module type that has a cflags property but not a srcs property.
- ctx.RegisterModuleType("module2", testProductVariableModuleFactoryFactory(&struct {
- Cflags []string
- }{}))
- // A module type that does not have any properties that match product_variables.
- ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(&struct {
- Foo []string
- }{}))
- ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
- ctx.BottomUp("variable", VariableMutator).Parallel()
- })
-
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
+ GroupFixturePreparers(
+ FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+ variables.Eng = proptools.BoolPtr(true)
+ }),
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ // A module type that has a srcs property but not a cflags property.
+ ctx.RegisterModuleType("module1", testProductVariableModuleFactoryFactory(&struct {
+ Srcs []string
+ }{}))
+ // A module type that has a cflags property but not a srcs property.
+ ctx.RegisterModuleType("module2", testProductVariableModuleFactoryFactory(&struct {
+ Cflags []string
+ }{}))
+ // A module type that does not have any properties that match product_variables.
+ ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(&struct {
+ Foo []string
+ }{}))
+ ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("variable", VariableMutator).Parallel()
+ })
+ }),
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
}
var testProductVariableDefaultsProperties = struct {
@@ -290,32 +288,23 @@
}
`
- config := TestConfig(buildDir, nil, bp, nil)
- config.TestProductVariables.Eng = boolPtr(true)
+ result := GroupFixturePreparers(
+ FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+ variables.Eng = boolPtr(true)
+ }),
+ PrepareForTestWithDefaults,
+ PrepareForTestWithVariables,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("test", productVariablesDefaultsTestModuleFactory)
+ ctx.RegisterModuleType("defaults", productVariablesDefaultsTestDefaultsFactory)
+ }),
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
- ctx := NewTestContext(config)
-
- ctx.RegisterModuleType("test", productVariablesDefaultsTestModuleFactory)
- ctx.RegisterModuleType("defaults", productVariablesDefaultsTestDefaultsFactory)
-
- ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
- ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
- ctx.BottomUp("variable", VariableMutator).Parallel()
- })
-
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
-
- foo := ctx.ModuleForTests("foo", "").Module().(*productVariablesDefaultsTestModule)
+ foo := result.ModuleForTests("foo", "").Module().(*productVariablesDefaultsTestModule)
want := []string{"defaults", "module", "product_variable_defaults", "product_variable_module"}
- if g, w := foo.properties.Foo, want; !reflect.DeepEqual(g, w) {
- t.Errorf("expected foo %q, got %q", w, g)
- }
+ AssertDeepEquals(t, "foo", want, foo.properties.Foo)
}
func BenchmarkSliceToTypeArray(b *testing.B) {
diff --git a/android/visibility.go b/android/visibility.go
index 7eac471..5d1be6b 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -202,6 +202,15 @@
ExcludeFromVisibilityEnforcement()
}
+// The visibility mutators.
+var PrepareForTestWithVisibility = FixtureRegisterWithContext(registerVisibilityMutators)
+
+func registerVisibilityMutators(ctx RegistrationContext) {
+ ctx.PreArchMutators(RegisterVisibilityRuleChecker)
+ ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
+ ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
+}
+
// The rule checker needs to be registered before defaults expansion to correctly check that
// //visibility:xxx isn't combined with other packages in the same list in any one module.
func RegisterVisibilityRuleChecker(ctx RegisterMutatorsContext) {
diff --git a/android/visibility_test.go b/android/visibility_test.go
index 87a295e..ffd7909 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -9,13 +9,13 @@
var visibilityTests = []struct {
name string
- fs map[string][]byte
+ fs MockFS
expectedErrors []string
effectiveVisibility map[qualifiedModuleName][]string
}{
{
name: "invalid visibility: empty list",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -26,7 +26,7 @@
},
{
name: "invalid visibility: empty rule",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -37,7 +37,7 @@
},
{
name: "invalid visibility: unqualified",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -48,7 +48,7 @@
},
{
name: "invalid visibility: empty namespace",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -59,7 +59,7 @@
},
{
name: "invalid visibility: empty module",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -70,7 +70,7 @@
},
{
name: "invalid visibility: empty namespace and module",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -81,7 +81,7 @@
},
{
name: "//visibility:unknown",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -92,7 +92,7 @@
},
{
name: "//visibility:xxx mixed",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -113,7 +113,7 @@
},
{
name: "//visibility:legacy_public",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -129,7 +129,7 @@
// Verify that //visibility:public will allow the module to be referenced from anywhere, e.g.
// the current directory, a nested directory and a directory in a separate tree.
name: "//visibility:public",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -156,7 +156,7 @@
// Verify that //visibility:private allows the module to be referenced from the current
// directory only.
name: "//visibility:private",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -188,7 +188,7 @@
{
// Verify that :__pkg__ allows the module to be referenced from the current directory only.
name: ":__pkg__",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -221,7 +221,7 @@
// Verify that //top/nested allows the module to be referenced from the current directory and
// the top/nested directory only, not a subdirectory of top/nested and not peak directory.
name: "//top/nested",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -259,7 +259,7 @@
// Verify that :__subpackages__ allows the module to be referenced from the current directory
// and sub directories but nowhere else.
name: ":__subpackages__",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -290,7 +290,7 @@
// Verify that //top/nested:__subpackages__ allows the module to be referenced from the current
// directory and sub directories but nowhere else.
name: "//top/nested:__subpackages__",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -321,7 +321,7 @@
// Verify that ["//top/nested", "//peak:__subpackages"] allows the module to be referenced from
// the current directory, top/nested and peak and all its subpackages.
name: `["//top/nested", "//peak:__subpackages__"]`,
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -347,7 +347,7 @@
{
// Verify that //vendor... cannot be used outside vendor apart from //vendor:__subpackages__
name: `//vendor`,
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -381,7 +381,7 @@
{
// Check that visibility is the union of the defaults modules.
name: "defaults union, basic",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults",
@@ -419,7 +419,7 @@
},
{
name: "defaults union, multiple defaults",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults_1",
@@ -460,7 +460,7 @@
},
{
name: "//visibility:public mixed with other in defaults",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults",
@@ -478,7 +478,7 @@
},
{
name: "//visibility:public overriding defaults",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults",
@@ -501,7 +501,7 @@
},
{
name: "//visibility:public mixed with other from different defaults 1",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults_1",
@@ -524,7 +524,7 @@
},
{
name: "//visibility:public mixed with other from different defaults 2",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults_1",
@@ -547,7 +547,7 @@
},
{
name: "//visibility:private in defaults",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults",
@@ -581,7 +581,7 @@
},
{
name: "//visibility:private mixed with other in defaults",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults",
@@ -599,7 +599,7 @@
},
{
name: "//visibility:private overriding defaults",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults",
@@ -618,7 +618,7 @@
},
{
name: "//visibility:private in defaults overridden",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults",
@@ -637,7 +637,7 @@
},
{
name: "//visibility:private override //visibility:public",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults",
@@ -655,7 +655,7 @@
},
{
name: "//visibility:public override //visibility:private",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults",
@@ -673,7 +673,7 @@
},
{
name: "//visibility:override must be first in the list",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
@@ -686,7 +686,7 @@
},
{
name: "//visibility:override discards //visibility:private",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults",
@@ -707,7 +707,7 @@
},
{
name: "//visibility:override discards //visibility:public",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults",
@@ -736,7 +736,7 @@
},
{
name: "//visibility:override discards defaults supplied rules",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults",
@@ -765,7 +765,7 @@
},
{
name: "//visibility:override can override //visibility:public with //visibility:private",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults",
@@ -788,7 +788,7 @@
},
{
name: "//visibility:override can override //visibility:private with //visibility:public",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults",
@@ -808,7 +808,7 @@
},
{
name: "//visibility:private mixed with itself",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "libexample_defaults_1",
@@ -838,7 +838,7 @@
// Defaults module's defaults_visibility tests
{
name: "defaults_visibility invalid",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_defaults {
name: "top_defaults",
@@ -851,7 +851,7 @@
},
{
name: "defaults_visibility overrides package default",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
package {
default_visibility: ["//visibility:private"],
@@ -871,7 +871,7 @@
// Package default_visibility tests
{
name: "package default_visibility property is checked",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
package {
default_visibility: ["//visibility:invalid"],
@@ -882,7 +882,7 @@
{
// This test relies on the default visibility being legacy_public.
name: "package default_visibility property used when no visibility specified",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
package {
default_visibility: ["//visibility:private"],
@@ -904,7 +904,7 @@
},
{
name: "package default_visibility public does not override visibility private",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
package {
default_visibility: ["//visibility:public"],
@@ -927,7 +927,7 @@
},
{
name: "package default_visibility private does not override visibility public",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
package {
default_visibility: ["//visibility:private"],
@@ -946,7 +946,7 @@
},
{
name: "package default_visibility :__subpackages__",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
package {
default_visibility: [":__subpackages__"],
@@ -973,7 +973,7 @@
},
{
name: "package default_visibility inherited to subpackages",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
package {
default_visibility: ["//outsider"],
@@ -1001,7 +1001,7 @@
},
{
name: "package default_visibility inherited to subpackages",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
package {
default_visibility: ["//visibility:private"],
@@ -1031,7 +1031,7 @@
},
{
name: "verify that prebuilt dependencies are ignored for visibility reasons (not preferred)",
- fs: map[string][]byte{
+ fs: MockFS{
"prebuilts/Blueprints": []byte(`
prebuilt {
name: "module",
@@ -1053,7 +1053,7 @@
},
{
name: "verify that prebuilt dependencies are ignored for visibility reasons (preferred)",
- fs: map[string][]byte{
+ fs: MockFS{
"prebuilts/Blueprints": []byte(`
prebuilt {
name: "module",
@@ -1076,7 +1076,7 @@
},
{
name: "ensure visibility properties are checked for correctness",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_parent {
name: "parent",
@@ -1093,7 +1093,7 @@
},
{
name: "invalid visibility added to child detected during gather phase",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_parent {
name: "parent",
@@ -1115,7 +1115,7 @@
},
{
name: "automatic visibility inheritance enabled",
- fs: map[string][]byte{
+ fs: MockFS{
"top/Blueprints": []byte(`
mock_parent {
name: "parent",
@@ -1142,55 +1142,44 @@
func TestVisibility(t *testing.T) {
for _, test := range visibilityTests {
t.Run(test.name, func(t *testing.T) {
- ctx, errs := testVisibility(buildDir, test.fs)
+ result := GroupFixturePreparers(
+ // General preparers in alphabetical order as test infrastructure will enforce correct
+ // registration order.
+ PrepareForTestWithArchMutator,
+ PrepareForTestWithDefaults,
+ PrepareForTestWithOverrides,
+ PrepareForTestWithPackageModule,
+ PrepareForTestWithPrebuilts,
+ PrepareForTestWithVisibility,
- CheckErrorsAgainstExpectations(t, errs, test.expectedErrors)
+ // Additional test specific preparers.
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("mock_library", newMockLibraryModule)
+ ctx.RegisterModuleType("mock_parent", newMockParentFactory)
+ ctx.RegisterModuleType("mock_defaults", defaultsFactory)
+ }),
+ prepareForTestWithFakePrebuiltModules,
+ // Add additional files to the mock filesystem
+ test.fs.AddToFixture(),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+ RunTest(t)
if test.effectiveVisibility != nil {
- checkEffectiveVisibility(t, ctx, test.effectiveVisibility)
+ checkEffectiveVisibility(t, result, test.effectiveVisibility)
}
})
}
}
-func checkEffectiveVisibility(t *testing.T, ctx *TestContext, effectiveVisibility map[qualifiedModuleName][]string) {
+func checkEffectiveVisibility(t *testing.T, result *TestResult, effectiveVisibility map[qualifiedModuleName][]string) {
for moduleName, expectedRules := range effectiveVisibility {
- rule := effectiveVisibilityRules(ctx.config, moduleName)
+ rule := effectiveVisibilityRules(result.Config, moduleName)
stringRules := rule.Strings()
- if !reflect.DeepEqual(expectedRules, stringRules) {
- t.Errorf("effective rules mismatch: expected %q, found %q", expectedRules, stringRules)
- }
+ AssertDeepEquals(t, "effective rules mismatch", expectedRules, stringRules)
}
}
-func testVisibility(buildDir string, fs map[string][]byte) (*TestContext, []error) {
-
- // Create a new config per test as visibility information is stored in the config.
- config := TestArchConfig(buildDir, nil, "", fs)
-
- ctx := NewTestArchContext(config)
- ctx.RegisterModuleType("mock_library", newMockLibraryModule)
- ctx.RegisterModuleType("mock_parent", newMockParentFactory)
- ctx.RegisterModuleType("mock_defaults", defaultsFactory)
-
- // Order of the following method calls is significant.
- RegisterPackageBuildComponents(ctx)
- registerTestPrebuiltBuildComponents(ctx)
- ctx.PreArchMutators(RegisterVisibilityRuleChecker)
- ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
- ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
- ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
- ctx.Register()
-
- _, errs := ctx.ParseBlueprintsFiles(".")
- if len(errs) > 0 {
- return ctx, errs
- }
-
- _, errs = ctx.PrepareBuildActions(config)
- return ctx, errs
-}
-
type mockLibraryProperties struct {
Deps []string
}
diff --git a/android/writedocs.go b/android/writedocs.go
index 91c2318..67b9aa3 100644
--- a/android/writedocs.go
+++ b/android/writedocs.go
@@ -34,10 +34,12 @@
type docsSingleton struct{}
func primaryBuilderPath(ctx SingletonContext) Path {
- primaryBuilder, err := filepath.Rel(ctx.Config().BuildDir(), os.Args[0])
+ buildDir := absolutePath(ctx.Config().BuildDir())
+ binary := absolutePath(os.Args[0])
+ primaryBuilder, err := filepath.Rel(buildDir, binary)
if err != nil {
- ctx.Errorf("path to primary builder %q is not in build dir %q",
- os.Args[0], ctx.Config().BuildDir())
+ ctx.Errorf("path to primary builder %q is not in build dir %q (%q)",
+ os.Args[0], ctx.Config().BuildDir(), err)
}
return PathForOutput(ctx, primaryBuilder)
@@ -65,7 +67,9 @@
soongDocs := ctx.Rule(pctx, "soongDocs",
blueprint.RuleParams{
Command: fmt.Sprintf("rm -f ${outDir}/* && %s --soong_docs %s %s",
- primaryBuilder.String(), docsFile.String(), strings.Join(os.Args[1:], " ")),
+ primaryBuilder.String(),
+ docsFile.String(),
+ "\""+strings.Join(os.Args[1:], "\" \"")+"\""),
CommandDeps: []string{primaryBuilder.String()},
Description: fmt.Sprintf("%s docs $out", primaryBuilder.Base()),
},
diff --git a/androidmk/Android.bp b/androidmk/Android.bp
index 70fc1f7..f04d01c 100644
--- a/androidmk/Android.bp
+++ b/androidmk/Android.bp
@@ -16,6 +16,10 @@
// androidmk Android.mk to Blueprints translator
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "androidmk",
srcs: [
diff --git a/androidmk/androidmk/androidmk.go b/androidmk/androidmk/androidmk.go
index 2d1bbb4..b8316a3 100644
--- a/androidmk/androidmk/androidmk.go
+++ b/androidmk/androidmk/androidmk.go
@@ -48,6 +48,22 @@
"-": "_dash_",
}
+// Fix steps that should only run in the androidmk tool, i.e. should only be applied to
+// newly-converted Android.bp files.
+var fixSteps = bpfix.FixStepsExtension{
+ Name: "androidmk",
+ Steps: []bpfix.FixStep{
+ {
+ Name: "RewriteRuntimeResourceOverlay",
+ Fix: bpfix.RewriteRuntimeResourceOverlay,
+ },
+ },
+}
+
+func init() {
+ bpfix.RegisterFixStepExtension(&fixSteps)
+}
+
func (f *bpFile) insertComment(s string) {
f.comments = append(f.comments, &bpparser.CommentGroup{
Comments: []*bpparser.Comment{
diff --git a/apex/Android.bp b/apex/Android.bp
index 9e8c30d..7b52402 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-apex",
pkgPath: "android/soong/apex",
@@ -7,6 +11,7 @@
"soong-android",
"soong-bpf",
"soong-cc",
+ "soong-filesystem",
"soong-java",
"soong-python",
"soong-rust",
@@ -17,12 +22,16 @@
"apex.go",
"apex_singleton.go",
"builder.go",
+ "deapexer.go",
"key.go",
"prebuilt.go",
+ "testing.go",
"vndk.go",
],
testSrcs: [
"apex_test.go",
+ "boot_image_test.go",
+ "platform_bootclasspath_test.go",
"vndk_test.go",
],
pluginFor: ["soong_build"],
diff --git a/apex/OWNERS b/apex/OWNERS
index 793f3ed..8e4ba5c 100644
--- a/apex/OWNERS
+++ b/apex/OWNERS
@@ -1,4 +1 @@
per-file * = jiyong@google.com
-
-per-file allowed_deps.txt = set noparent
-per-file allowed_deps.txt = dariofreni@google.com,hansson@google.com,harpin@google.com,jiyong@google.com,narayan@google.com,omakoto@google.com,jham@google.com
diff --git a/apex/allowed_deps.txt b/apex/allowed_deps.txt
deleted file mode 100644
index c5f2bf8..0000000
--- a/apex/allowed_deps.txt
+++ /dev/null
@@ -1,590 +0,0 @@
-# A list of allowed dependencies for all updatable modules.
-#
-# The list tracks all direct and transitive dependencies that end up within any
-# of the updatable binaries; specifically excluding external dependencies
-# required to compile those binaries. This prevents potential regressions in
-# case a new dependency is not aware of the different functional and
-# non-functional requirements being part of an updatable module, for example
-# setting correct min_sdk_version.
-#
-# To update the list, run:
-# repo-root$ build/soong/scripts/update-apex-allowed-deps.sh
-#
-# See go/apex-allowed-deps-error for more details.
-# TODO(b/157465465): introduce automated quality signals and remove this list.
-
-adbd(minSdkVersion:(no version))
-android.hardware.cas.native@1.0(minSdkVersion:29)
-android.hardware.cas@1.0(minSdkVersion:29)
-android.hardware.common-ndk_platform(minSdkVersion:29)
-android.hardware.common-unstable-ndk_platform(minSdkVersion:29)
-android.hardware.graphics.allocator@2.0(minSdkVersion:29)
-android.hardware.graphics.allocator@3.0(minSdkVersion:29)
-android.hardware.graphics.allocator@4.0(minSdkVersion:29)
-android.hardware.graphics.bufferqueue@1.0(minSdkVersion:29)
-android.hardware.graphics.bufferqueue@2.0(minSdkVersion:29)
-android.hardware.graphics.common-ndk_platform(minSdkVersion:29)
-android.hardware.graphics.common-unstable-ndk_platform(minSdkVersion:29)
-android.hardware.graphics.common@1.0(minSdkVersion:29)
-android.hardware.graphics.common@1.1(minSdkVersion:29)
-android.hardware.graphics.common@1.2(minSdkVersion:29)
-android.hardware.graphics.mapper@2.0(minSdkVersion:29)
-android.hardware.graphics.mapper@2.1(minSdkVersion:29)
-android.hardware.graphics.mapper@3.0(minSdkVersion:29)
-android.hardware.graphics.mapper@4.0(minSdkVersion:29)
-android.hardware.media.bufferpool@2.0(minSdkVersion:29)
-android.hardware.media.c2@1.0(minSdkVersion:29)
-android.hardware.media.c2@1.1(minSdkVersion:29)
-android.hardware.media.omx@1.0(minSdkVersion:29)
-android.hardware.media@1.0(minSdkVersion:29)
-android.hardware.neuralnetworks@1.0(minSdkVersion:30)
-android.hardware.neuralnetworks@1.1(minSdkVersion:30)
-android.hardware.neuralnetworks@1.2(minSdkVersion:30)
-android.hardware.neuralnetworks@1.3(minSdkVersion:30)
-android.hardware.tetheroffload.config-V1.0-java(minSdkVersion:current)
-android.hardware.tetheroffload.control-V1.0-java(minSdkVersion:current)
-android.hidl.allocator@1.0(minSdkVersion:29)
-android.hidl.base-V1.0-java(minSdkVersion:current)
-android.hidl.memory.token@1.0(minSdkVersion:29)
-android.hidl.memory@1.0(minSdkVersion:29)
-android.hidl.safe_union@1.0(minSdkVersion:29)
-android.hidl.token@1.0(minSdkVersion:29)
-android.hidl.token@1.0-utils(minSdkVersion:29)
-android.net.ipsec.ike(minSdkVersion:current)
-android.net.ipsec.ike.xml(minSdkVersion:(no version))
-androidx-constraintlayout_constraintlayout(minSdkVersion:14)
-androidx-constraintlayout_constraintlayout-solver(minSdkVersion:24)
-androidx.activity_activity(minSdkVersion:14)
-androidx.activity_activity-ktx(minSdkVersion:14)
-androidx.annotation_annotation(minSdkVersion:24)
-androidx.annotation_annotation(minSdkVersion:current)
-androidx.appcompat_appcompat(minSdkVersion:14)
-androidx.appcompat_appcompat-resources(minSdkVersion:14)
-androidx.arch.core_core-common(minSdkVersion:24)
-androidx.arch.core_core-common(minSdkVersion:current)
-androidx.arch.core_core-runtime(minSdkVersion:14)
-androidx.asynclayoutinflater_asynclayoutinflater(minSdkVersion:14)
-androidx.autofill_autofill(minSdkVersion:14)
-androidx.cardview_cardview(minSdkVersion:14)
-androidx.collection_collection(minSdkVersion:24)
-androidx.collection_collection(minSdkVersion:current)
-androidx.collection_collection-ktx(minSdkVersion:24)
-androidx.coordinatorlayout_coordinatorlayout(minSdkVersion:14)
-androidx.core_core(minSdkVersion:14)
-androidx.core_core-ktx(minSdkVersion:14)
-androidx.cursoradapter_cursoradapter(minSdkVersion:14)
-androidx.customview_customview(minSdkVersion:14)
-androidx.documentfile_documentfile(minSdkVersion:14)
-androidx.drawerlayout_drawerlayout(minSdkVersion:14)
-androidx.fragment_fragment(minSdkVersion:14)
-androidx.fragment_fragment-ktx(minSdkVersion:14)
-androidx.interpolator_interpolator(minSdkVersion:14)
-androidx.leanback_leanback(minSdkVersion:17)
-androidx.leanback_leanback-preference(minSdkVersion:21)
-androidx.legacy_legacy-preference-v14(minSdkVersion:14)
-androidx.legacy_legacy-support-core-ui(minSdkVersion:14)
-androidx.legacy_legacy-support-core-utils(minSdkVersion:14)
-androidx.legacy_legacy-support-v13(minSdkVersion:14)
-androidx.legacy_legacy-support-v4(minSdkVersion:14)
-androidx.lifecycle_lifecycle-common(minSdkVersion:24)
-androidx.lifecycle_lifecycle-common(minSdkVersion:current)
-androidx.lifecycle_lifecycle-common-java8(minSdkVersion:24)
-androidx.lifecycle_lifecycle-extensions(minSdkVersion:14)
-androidx.lifecycle_lifecycle-livedata(minSdkVersion:14)
-androidx.lifecycle_lifecycle-livedata-core(minSdkVersion:14)
-androidx.lifecycle_lifecycle-livedata-core-ktx(minSdkVersion:14)
-androidx.lifecycle_lifecycle-process(minSdkVersion:14)
-androidx.lifecycle_lifecycle-runtime(minSdkVersion:14)
-androidx.lifecycle_lifecycle-runtime-ktx(minSdkVersion:14)
-androidx.lifecycle_lifecycle-service(minSdkVersion:14)
-androidx.lifecycle_lifecycle-viewmodel(minSdkVersion:14)
-androidx.lifecycle_lifecycle-viewmodel-ktx(minSdkVersion:14)
-androidx.lifecycle_lifecycle-viewmodel-savedstate(minSdkVersion:14)
-androidx.loader_loader(minSdkVersion:14)
-androidx.localbroadcastmanager_localbroadcastmanager(minSdkVersion:14)
-androidx.media_media(minSdkVersion:14)
-androidx.navigation_navigation-common(minSdkVersion:14)
-androidx.navigation_navigation-common-ktx(minSdkVersion:14)
-androidx.navigation_navigation-fragment(minSdkVersion:14)
-androidx.navigation_navigation-fragment-ktx(minSdkVersion:14)
-androidx.navigation_navigation-runtime(minSdkVersion:14)
-androidx.navigation_navigation-runtime-ktx(minSdkVersion:14)
-androidx.navigation_navigation-ui(minSdkVersion:14)
-androidx.navigation_navigation-ui-ktx(minSdkVersion:14)
-androidx.preference_preference(minSdkVersion:14)
-androidx.print_print(minSdkVersion:14)
-androidx.recyclerview_recyclerview(minSdkVersion:14)
-androidx.recyclerview_recyclerview-selection(minSdkVersion:14)
-androidx.savedstate_savedstate(minSdkVersion:14)
-androidx.slidingpanelayout_slidingpanelayout(minSdkVersion:14)
-androidx.swiperefreshlayout_swiperefreshlayout(minSdkVersion:14)
-androidx.transition_transition(minSdkVersion:14)
-androidx.vectordrawable_vectordrawable(minSdkVersion:14)
-androidx.vectordrawable_vectordrawable-animated(minSdkVersion:14)
-androidx.versionedparcelable_versionedparcelable(minSdkVersion:14)
-androidx.viewpager_viewpager(minSdkVersion:14)
-apache-commons-compress(minSdkVersion:current)
-art.module.public.api.stubs(minSdkVersion:(no version))
-bcm_object(minSdkVersion:29)
-bionic_libc_platform_headers(minSdkVersion:29)
-boringssl_self_test(minSdkVersion:29)
-bouncycastle_ike_digests(minSdkVersion:current)
-brotli-java(minSdkVersion:current)
-captiveportal-lib(minSdkVersion:29)
-car-ui-lib(minSdkVersion:28)
-car-ui-lib-overlayable(minSdkVersion:28)
-CellBroadcastApp(minSdkVersion:29)
-CellBroadcastServiceModule(minSdkVersion:29)
-codecs_g711dec(minSdkVersion:29)
-com.google.android.material_material(minSdkVersion:14)
-conscrypt(minSdkVersion:29)
-conscrypt.module.platform.api.stubs(minSdkVersion:(no version))
-conscrypt.module.public.api.stubs(minSdkVersion:(no version))
-core-lambda-stubs(minSdkVersion:(no version))
-core.current.stubs(minSdkVersion:(no version))
-crtbegin_dynamic(minSdkVersion:16)
-crtbegin_dynamic(minSdkVersion:apex_inherit)
-crtbegin_dynamic1(minSdkVersion:16)
-crtbegin_dynamic1(minSdkVersion:apex_inherit)
-crtbegin_so(minSdkVersion:16)
-crtbegin_so(minSdkVersion:apex_inherit)
-crtbegin_so1(minSdkVersion:16)
-crtbegin_so1(minSdkVersion:apex_inherit)
-crtbrand(minSdkVersion:16)
-crtbrand(minSdkVersion:apex_inherit)
-crtend_android(minSdkVersion:16)
-crtend_android(minSdkVersion:apex_inherit)
-crtend_so(minSdkVersion:16)
-crtend_so(minSdkVersion:apex_inherit)
-datastallprotosnano(minSdkVersion:29)
-derive_sdk(minSdkVersion:30)
-derive_sdk(minSdkVersion:current)
-derive_sdk_prefer32(minSdkVersion:30)
-derive_sdk_prefer32(minSdkVersion:current)
-dnsresolver_aidl_interface-unstable-ndk_platform(minSdkVersion:29)
-DocumentsUI-res-lib(minSdkVersion:29)
-exoplayer2-extractor(minSdkVersion:16)
-exoplayer2-extractor-annotation-stubs(minSdkVersion:16)
-ExtServices(minSdkVersion:current)
-ExtServices-core(minSdkVersion:current)
-flatbuffer_headers(minSdkVersion:(no version))
-fmtlib(minSdkVersion:29)
-framework-permission(minSdkVersion:current)
-framework-sdkextensions(minSdkVersion:30)
-framework-sdkextensions(minSdkVersion:current)
-framework-statsd(minSdkVersion:current)
-framework-tethering(minSdkVersion:30)
-framework-tethering(minSdkVersion:current)
-gemmlowp_headers(minSdkVersion:(no version))
-GoogleCellBroadcastApp(minSdkVersion:29)
-GoogleCellBroadcastServiceModule(minSdkVersion:29)
-GoogleExtServices(minSdkVersion:current)
-GooglePermissionController(minSdkVersion:28)
-guava(minSdkVersion:current)
-gwp_asan_headers(minSdkVersion:(no version))
-i18n.module.public.api.stubs(minSdkVersion:(no version))
-iconloader(minSdkVersion:21)
-ike-internals(minSdkVersion:current)
-InProcessTethering(minSdkVersion:30)
-InProcessTethering(minSdkVersion:current)
-ipmemorystore-aidl-interfaces-java(minSdkVersion:29)
-ipmemorystore-aidl-interfaces-unstable-java(minSdkVersion:29)
-jni_headers(minSdkVersion:29)
-jsr305(minSdkVersion:14)
-kotlinx-coroutines-android(minSdkVersion:current)
-kotlinx-coroutines-core(minSdkVersion:current)
-legacy.art.module.platform.api.stubs(minSdkVersion:(no version))
-legacy.core.platform.api.stubs(minSdkVersion:(no version))
-legacy.i18n.module.platform.api.stubs(minSdkVersion:(no version))
-libaacextractor(minSdkVersion:29)
-libadb_crypto(minSdkVersion:(no version))
-libadb_pairing_auth(minSdkVersion:(no version))
-libadb_pairing_connection(minSdkVersion:(no version))
-libadb_pairing_server(minSdkVersion:(no version))
-libadb_protos(minSdkVersion:(no version))
-libadb_sysdeps(minSdkVersion:apex_inherit)
-libadb_tls_connection(minSdkVersion:(no version))
-libadbconnection_client(minSdkVersion:(no version))
-libadbconnection_server(minSdkVersion:(no version))
-libadbd(minSdkVersion:(no version))
-libadbd_core(minSdkVersion:(no version))
-libadbd_services(minSdkVersion:(no version))
-liballoc.rust_sysroot(minSdkVersion:29)
-libamrextractor(minSdkVersion:29)
-libapp_processes_protos_lite(minSdkVersion:(no version))
-libarect(minSdkVersion:29)
-libasyncio(minSdkVersion:(no version))
-libatomic(minSdkVersion:(no version))
-libaudio_system_headers(minSdkVersion:29)
-libaudioclient_headers(minSdkVersion:29)
-libaudiofoundation_headers(minSdkVersion:29)
-libaudioutils(minSdkVersion:29)
-libaudioutils_fixedfft(minSdkVersion:29)
-libavcdec(minSdkVersion:29)
-libavcenc(minSdkVersion:29)
-libavservices_minijail(minSdkVersion:29)
-libbacktrace_headers(minSdkVersion:apex_inherit)
-libbacktrace_rs.rust_sysroot(minSdkVersion:29)
-libbacktrace_sys.rust_sysroot(minSdkVersion:29)
-libbase(minSdkVersion:29)
-libbase_headers(minSdkVersion:29)
-libbinder_headers(minSdkVersion:29)
-libbinderthreadstateutils(minSdkVersion:29)
-libbluetooth-types-header(minSdkVersion:29)
-libbrotli(minSdkVersion:(no version))
-libbuildversion(minSdkVersion:(no version))
-libc(minSdkVersion:(no version))
-libc++(minSdkVersion:apex_inherit)
-libc++_static(minSdkVersion:apex_inherit)
-libc++abi(minSdkVersion:apex_inherit)
-libc++demangle(minSdkVersion:apex_inherit)
-libc_headers(minSdkVersion:apex_inherit)
-libc_headers_arch(minSdkVersion:apex_inherit)
-libcap(minSdkVersion:29)
-libcfg_if(minSdkVersion:29)
-libcfg_if.rust_sysroot(minSdkVersion:29)
-libclang_rt.hwasan-aarch64-android.llndk(minSdkVersion:(no version))
-libcodec2(minSdkVersion:29)
-libcodec2_headers(minSdkVersion:29)
-libcodec2_hidl@1.0(minSdkVersion:29)
-libcodec2_hidl@1.1(minSdkVersion:29)
-libcodec2_internal(minSdkVersion:29)
-libcodec2_soft_aacdec(minSdkVersion:29)
-libcodec2_soft_aacenc(minSdkVersion:29)
-libcodec2_soft_amrnbdec(minSdkVersion:29)
-libcodec2_soft_amrnbenc(minSdkVersion:29)
-libcodec2_soft_amrwbdec(minSdkVersion:29)
-libcodec2_soft_amrwbenc(minSdkVersion:29)
-libcodec2_soft_av1dec_gav1(minSdkVersion:29)
-libcodec2_soft_avcdec(minSdkVersion:29)
-libcodec2_soft_avcenc(minSdkVersion:29)
-libcodec2_soft_common(minSdkVersion:29)
-libcodec2_soft_flacdec(minSdkVersion:29)
-libcodec2_soft_flacenc(minSdkVersion:29)
-libcodec2_soft_g711alawdec(minSdkVersion:29)
-libcodec2_soft_g711mlawdec(minSdkVersion:29)
-libcodec2_soft_gsmdec(minSdkVersion:29)
-libcodec2_soft_h263dec(minSdkVersion:29)
-libcodec2_soft_h263enc(minSdkVersion:29)
-libcodec2_soft_hevcdec(minSdkVersion:29)
-libcodec2_soft_hevcenc(minSdkVersion:29)
-libcodec2_soft_mp3dec(minSdkVersion:29)
-libcodec2_soft_mpeg2dec(minSdkVersion:29)
-libcodec2_soft_mpeg4dec(minSdkVersion:29)
-libcodec2_soft_mpeg4enc(minSdkVersion:29)
-libcodec2_soft_opusdec(minSdkVersion:29)
-libcodec2_soft_opusenc(minSdkVersion:29)
-libcodec2_soft_rawdec(minSdkVersion:29)
-libcodec2_soft_vorbisdec(minSdkVersion:29)
-libcodec2_soft_vp8dec(minSdkVersion:29)
-libcodec2_soft_vp8enc(minSdkVersion:29)
-libcodec2_soft_vp9dec(minSdkVersion:29)
-libcodec2_soft_vp9enc(minSdkVersion:29)
-libcodec2_vndk(minSdkVersion:29)
-libcompiler_builtins.rust_sysroot(minSdkVersion:29)
-libcore.rust_sysroot(minSdkVersion:29)
-libcrypto(minSdkVersion:29)
-libcrypto_static(minSdkVersion:(no version))
-libcrypto_utils(minSdkVersion:(no version))
-libcutils(minSdkVersion:29)
-libcutils_headers(minSdkVersion:29)
-libcutils_sockets(minSdkVersion:29)
-libdiagnose_usb(minSdkVersion:(no version))
-libdl(minSdkVersion:(no version))
-libdmabufheap(minSdkVersion:29)
-libeigen(minSdkVersion:(no version))
-libfifo(minSdkVersion:29)
-libFLAC(minSdkVersion:29)
-libFLAC-config(minSdkVersion:29)
-libFLAC-headers(minSdkVersion:29)
-libflacextractor(minSdkVersion:29)
-libfmq(minSdkVersion:29)
-libfmq-base(minSdkVersion:29)
-libFraunhoferAAC(minSdkVersion:29)
-libgav1(minSdkVersion:29)
-libgcc(minSdkVersion:(no version))
-libgcc_stripped(minSdkVersion:(no version))
-libgetopts(minSdkVersion:29)
-libgralloctypes(minSdkVersion:29)
-libgrallocusage(minSdkVersion:29)
-libgsm(minSdkVersion:apex_inherit)
-libgtest_prod(minSdkVersion:apex_inherit)
-libgui_bufferqueue_static(minSdkVersion:29)
-libgui_headers(minSdkVersion:29)
-libhardware(minSdkVersion:29)
-libhardware_headers(minSdkVersion:29)
-libhashbrown.rust_sysroot(minSdkVersion:29)
-libhevcdec(minSdkVersion:29)
-libhevcenc(minSdkVersion:29)
-libhidlbase(minSdkVersion:29)
-libhidlmemory(minSdkVersion:29)
-libhwbinder-impl-internal(minSdkVersion:29)
-libhwbinder_headers(minSdkVersion:29)
-libion(minSdkVersion:29)
-libjavacrypto(minSdkVersion:29)
-libjsoncpp(minSdkVersion:29)
-liblazy_static(minSdkVersion:29)
-liblibc(minSdkVersion:29)
-liblibc.rust_sysroot(minSdkVersion:29)
-libLibGuiProperties(minSdkVersion:29)
-liblibm(minSdkVersion:29)
-liblog(minSdkVersion:(no version))
-liblog_headers(minSdkVersion:29)
-liblog_rust(minSdkVersion:29)
-liblua(minSdkVersion:(no version))
-liblz4(minSdkVersion:(no version))
-libm(minSdkVersion:(no version))
-libmath(minSdkVersion:29)
-libmdnssd(minSdkVersion:(no version))
-libmedia_codecserviceregistrant(minSdkVersion:29)
-libmedia_datasource_headers(minSdkVersion:29)
-libmedia_headers(minSdkVersion:29)
-libmedia_helper_headers(minSdkVersion:29)
-libmedia_midiiowrapper(minSdkVersion:29)
-libmidiextractor(minSdkVersion:29)
-libminijail(minSdkVersion:29)
-libminijail_gen_constants(minSdkVersion:(no version))
-libminijail_gen_constants_obj(minSdkVersion:29)
-libminijail_gen_syscall(minSdkVersion:(no version))
-libminijail_gen_syscall_obj(minSdkVersion:29)
-libminijail_generated(minSdkVersion:29)
-libmkvextractor(minSdkVersion:29)
-libmodules-utils-build(minSdkVersion:29)
-libmp3extractor(minSdkVersion:29)
-libmp4extractor(minSdkVersion:29)
-libmpeg2dec(minSdkVersion:29)
-libmpeg2extractor(minSdkVersion:29)
-libnativebase_headers(minSdkVersion:29)
-libnativehelper_compat_libc++(minSdkVersion:(no version))
-libnativehelper_header_only(minSdkVersion:29)
-libnativewindow_headers(minSdkVersion:29)
-libnetd_resolv(minSdkVersion:29)
-libnetdbinder_utils_headers(minSdkVersion:29)
-libnetdutils(minSdkVersion:29)
-libnetjniutils(minSdkVersion:29)
-libnetworkstackutilsjni(minSdkVersion:29)
-libneuralnetworks(minSdkVersion:(no version))
-libneuralnetworks_common(minSdkVersion:(no version))
-libneuralnetworks_headers(minSdkVersion:(no version))
-liboggextractor(minSdkVersion:29)
-libonce_cell(minSdkVersion:29)
-libopus(minSdkVersion:29)
-libpanic_unwind.rust_sysroot(minSdkVersion:29)
-libprocessgroup(minSdkVersion:29)
-libprocessgroup_headers(minSdkVersion:29)
-libprocpartition(minSdkVersion:(no version))
-libprofiler_builtins.rust_sysroot(minSdkVersion:29)
-libprotobuf-cpp-lite(minSdkVersion:29)
-libprotobuf-java-lite(minSdkVersion:current)
-libprotobuf-java-nano(minSdkVersion:9)
-libprotoutil(minSdkVersion:(no version))
-libqemu_pipe(minSdkVersion:(no version))
-libquiche_ffi(minSdkVersion:29)
-libring(minSdkVersion:29)
-libring-core(minSdkVersion:29)
-librustc_demangle.rust_sysroot(minSdkVersion:29)
-libsfplugin_ccodec_utils(minSdkVersion:29)
-libsonivoxwithoutjet(minSdkVersion:29)
-libspeexresampler(minSdkVersion:29)
-libspin(minSdkVersion:29)
-libssl(minSdkVersion:29)
-libstagefright_amrnb_common(minSdkVersion:29)
-libstagefright_amrnbdec(minSdkVersion:29)
-libstagefright_amrnbenc(minSdkVersion:29)
-libstagefright_amrwbdec(minSdkVersion:29)
-libstagefright_amrwbenc(minSdkVersion:29)
-libstagefright_bufferpool@2.0.1(minSdkVersion:29)
-libstagefright_bufferqueue_helper(minSdkVersion:29)
-libstagefright_enc_common(minSdkVersion:29)
-libstagefright_esds(minSdkVersion:29)
-libstagefright_flacdec(minSdkVersion:29)
-libstagefright_foundation(minSdkVersion:29)
-libstagefright_foundation_headers(minSdkVersion:29)
-libstagefright_foundation_without_imemory(minSdkVersion:29)
-libstagefright_headers(minSdkVersion:29)
-libstagefright_id3(minSdkVersion:29)
-libstagefright_m4vh263dec(minSdkVersion:29)
-libstagefright_m4vh263enc(minSdkVersion:29)
-libstagefright_metadatautils(minSdkVersion:29)
-libstagefright_mp3dec(minSdkVersion:29)
-libstagefright_mp3dec_headers(minSdkVersion:29)
-libstagefright_mpeg2extractor(minSdkVersion:29)
-libstagefright_mpeg2support_nocrypto(minSdkVersion:29)
-libstats_jni(minSdkVersion:(no version))
-libstatslog_resolv(minSdkVersion:29)
-libstatslog_statsd(minSdkVersion:(no version))
-libstatspull(minSdkVersion:(no version))
-libstatspush_compat(minSdkVersion:29)
-libstatssocket(minSdkVersion:(no version))
-libstatssocket_headers(minSdkVersion:29)
-libstd(minSdkVersion:29)
-libsystem_headers(minSdkVersion:apex_inherit)
-libsysutils(minSdkVersion:apex_inherit)
-libterm(minSdkVersion:29)
-libtest(minSdkVersion:29)
-libtetherutilsjni(minSdkVersion:30)
-libtetherutilsjni(minSdkVersion:current)
-libtextclassifier(minSdkVersion:(no version))
-libtextclassifier-java(minSdkVersion:current)
-libtextclassifier_hash_headers(minSdkVersion:(no version))
-libtextclassifier_hash_static(minSdkVersion:(no version))
-libtflite_kernel_utils(minSdkVersion:(no version))
-libtflite_static(minSdkVersion:(no version))
-libui(minSdkVersion:29)
-libui_headers(minSdkVersion:29)
-libunicode_width.rust_sysroot(minSdkVersion:29)
-libuntrusted(minSdkVersion:29)
-libunwind.rust_sysroot(minSdkVersion:29)
-libunwind_llvm(minSdkVersion:apex_inherit)
-libutf(minSdkVersion:(no version))
-libutils(minSdkVersion:apex_inherit)
-libutils_headers(minSdkVersion:apex_inherit)
-libvorbisidec(minSdkVersion:29)
-libvpx(minSdkVersion:29)
-libwatchdog(minSdkVersion:29)
-libwavextractor(minSdkVersion:29)
-libwebm(minSdkVersion:29)
-libyuv(minSdkVersion:29)
-libyuv_static(minSdkVersion:29)
-libzstd(minSdkVersion:(no version))
-media_ndk_headers(minSdkVersion:29)
-media_plugin_headers(minSdkVersion:29)
-mediaswcodec(minSdkVersion:29)
-metrics-constants-protos(minSdkVersion:29)
-modules-utils-build(minSdkVersion:29)
-ndk_crtbegin_so.19(minSdkVersion:(no version))
-ndk_crtbegin_so.21(minSdkVersion:(no version))
-ndk_crtbegin_so.27(minSdkVersion:(no version))
-ndk_crtend_so.19(minSdkVersion:(no version))
-ndk_crtend_so.21(minSdkVersion:(no version))
-ndk_crtend_so.27(minSdkVersion:(no version))
-ndk_libc++_static(minSdkVersion:(no version))
-ndk_libc++_static(minSdkVersion:16)
-ndk_libc++abi(minSdkVersion:(no version))
-ndk_libc++abi(minSdkVersion:16)
-ndk_libunwind(minSdkVersion:16)
-net-utils-device-common(minSdkVersion:29)
-net-utils-framework-common(minSdkVersion:current)
-netd_aidl_interface-unstable-java(minSdkVersion:29)
-netd_event_listener_interface-ndk_platform(minSdkVersion:29)
-netd_event_listener_interface-unstable-ndk_platform(minSdkVersion:29)
-netlink-client(minSdkVersion:29)
-networkstack-aidl-interfaces-unstable-java(minSdkVersion:29)
-networkstack-client(minSdkVersion:29)
-NetworkStackApiStableDependencies(minSdkVersion:29)
-NetworkStackApiStableLib(minSdkVersion:29)
-networkstackprotos(minSdkVersion:29)
-neuralnetworks_types(minSdkVersion:30)
-neuralnetworks_utils_hal_1_0(minSdkVersion:30)
-neuralnetworks_utils_hal_1_1(minSdkVersion:30)
-neuralnetworks_utils_hal_1_2(minSdkVersion:30)
-neuralnetworks_utils_hal_1_3(minSdkVersion:30)
-neuralnetworks_utils_hal_common(minSdkVersion:30)
-neuralnetworks_utils_hal_service(minSdkVersion:30)
-PermissionController(minSdkVersion:28)
-permissioncontroller-statsd(minSdkVersion:current)
-philox_random(minSdkVersion:(no version))
-philox_random_headers(minSdkVersion:(no version))
-prebuilt_androidx-constraintlayout_constraintlayout-nodeps(minSdkVersion:(no version))
-prebuilt_androidx-constraintlayout_constraintlayout-solver-nodeps(minSdkVersion:current)
-prebuilt_androidx.activity_activity-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.activity_activity-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.annotation_annotation-nodeps(minSdkVersion:current)
-prebuilt_androidx.appcompat_appcompat-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.appcompat_appcompat-resources-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.arch.core_core-common-nodeps(minSdkVersion:current)
-prebuilt_androidx.arch.core_core-runtime-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.asynclayoutinflater_asynclayoutinflater-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.autofill_autofill-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.cardview_cardview-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.collection_collection-ktx-nodeps(minSdkVersion:current)
-prebuilt_androidx.collection_collection-nodeps(minSdkVersion:current)
-prebuilt_androidx.coordinatorlayout_coordinatorlayout-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.core_core-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.core_core-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.cursoradapter_cursoradapter-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.customview_customview-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.documentfile_documentfile-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.drawerlayout_drawerlayout-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.fragment_fragment-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.fragment_fragment-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.interpolator_interpolator-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.leanback_leanback-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.leanback_leanback-preference-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.legacy_legacy-support-core-ui-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.legacy_legacy-support-core-utils-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.legacy_legacy-support-v13-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-common-java8-nodeps(minSdkVersion:current)
-prebuilt_androidx.lifecycle_lifecycle-common-nodeps(minSdkVersion:current)
-prebuilt_androidx.lifecycle_lifecycle-extensions-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-livedata-core-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-livedata-core-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-livedata-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-process-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-runtime-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-runtime-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-service-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-viewmodel-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-viewmodel-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.lifecycle_lifecycle-viewmodel-savedstate-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.loader_loader-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.localbroadcastmanager_localbroadcastmanager-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.media_media-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.navigation_navigation-common-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.navigation_navigation-common-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.navigation_navigation-fragment-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.navigation_navigation-fragment-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.navigation_navigation-runtime-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.navigation_navigation-runtime-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.navigation_navigation-ui-ktx-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.navigation_navigation-ui-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.preference_preference-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.print_print-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.recyclerview_recyclerview-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.recyclerview_recyclerview-selection-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.savedstate_savedstate-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.slidingpanelayout_slidingpanelayout-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.swiperefreshlayout_swiperefreshlayout-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.transition_transition-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.vectordrawable_vectordrawable-animated-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.vectordrawable_vectordrawable-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.versionedparcelable_versionedparcelable-nodeps(minSdkVersion:(no version))
-prebuilt_androidx.viewpager_viewpager-nodeps(minSdkVersion:(no version))
-prebuilt_com.google.android.material_material-nodeps(minSdkVersion:(no version))
-prebuilt_error_prone_annotations(minSdkVersion:(no version))
-prebuilt_kotlin-stdlib(minSdkVersion:current)
-prebuilt_kotlinx-coroutines-android-nodeps(minSdkVersion:(no version))
-prebuilt_kotlinx-coroutines-core-nodeps(minSdkVersion:(no version))
-prebuilt_libclang_rt.builtins-aarch64-android(minSdkVersion:(no version))
-prebuilt_libclang_rt.builtins-arm-android(minSdkVersion:(no version))
-prebuilt_libclang_rt.builtins-i686-android(minSdkVersion:(no version))
-prebuilt_libclang_rt.builtins-x86_64-android(minSdkVersion:(no version))
-prebuilt_test_framework-sdkextensions(minSdkVersion:(no version))
-server_configurable_flags(minSdkVersion:29)
-service-permission(minSdkVersion:current)
-service-statsd(minSdkVersion:current)
-SettingsLibActionBarShadow(minSdkVersion:21)
-SettingsLibAppPreference(minSdkVersion:21)
-SettingsLibBarChartPreference(minSdkVersion:21)
-SettingsLibHelpUtils(minSdkVersion:21)
-SettingsLibLayoutPreference(minSdkVersion:21)
-SettingsLibProgressBar(minSdkVersion:21)
-SettingsLibRestrictedLockUtils(minSdkVersion:21)
-SettingsLibSearchWidget(minSdkVersion:21)
-SettingsLibSettingsTheme(minSdkVersion:21)
-SettingsLibUtils(minSdkVersion:21)
-stats_proto(minSdkVersion:29)
-statsd(minSdkVersion:(no version))
-statsd-aidl-ndk_platform(minSdkVersion:(no version))
-statsprotos(minSdkVersion:29)
-tensorflow_headers(minSdkVersion:(no version))
-Tethering(minSdkVersion:30)
-Tethering(minSdkVersion:current)
-TetheringApiCurrentLib(minSdkVersion:30)
-TetheringApiCurrentLib(minSdkVersion:current)
-TetheringGoogle(minSdkVersion:current)
-textclassifier-statsd(minSdkVersion:current)
-TextClassifierNotificationLibNoManifest(minSdkVersion:29)
-TextClassifierServiceLibNoManifest(minSdkVersion:28)
-updatable-media(minSdkVersion:29)
-xz-java(minSdkVersion:current)
diff --git a/apex/androidmk.go b/apex/androidmk.go
index e7f8b7f..9fc701d 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -63,6 +63,16 @@
}
}
+// Return the full module name for a dependency module, which appends the apex module name unless re-using a system lib.
+func (a *apexBundle) fullModuleName(apexBundleName string, fi *apexFile) string {
+ linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
+
+ if linkToSystemLib {
+ return fi.androidMkModuleName
+ }
+ return fi.androidMkModuleName + "." + apexBundleName + a.suffix
+}
+
func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, apexName, moduleDir string,
apexAndroidMkData android.AndroidMkData) []string {
@@ -114,15 +124,20 @@
linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
- var moduleName string
- if linkToSystemLib {
- moduleName = fi.androidMkModuleName
- } else {
- moduleName = fi.androidMkModuleName + "." + apexBundleName + a.suffix
- }
+ moduleName := a.fullModuleName(apexBundleName, &fi)
- if !android.InList(moduleName, moduleNames) {
- moduleNames = append(moduleNames, moduleName)
+ // This name will be added to LOCAL_REQUIRED_MODULES of the APEX. We need to be
+ // arch-specific otherwise we will end up installing both ABIs even when only
+ // either of the ABI is requested.
+ aName := moduleName
+ switch fi.multilib {
+ case "lib32":
+ aName = aName + ":32"
+ case "lib64":
+ aName = aName + ":64"
+ }
+ if !android.InList(aName, moduleNames) {
+ moduleNames = append(moduleNames, aName)
}
if linkToSystemLib {
@@ -269,7 +284,7 @@
// To install companion files (init_rc, vintf_fragments)
// Copy some common properties of apexBundle to apex_manifest
commonProperties := []string{
- "LOCAL_INIT_RC", "LOCAL_VINTF_FRAGMENTS",
+ "LOCAL_FULL_INIT_RC", "LOCAL_VINTF_FRAGMENTS",
}
for _, name := range commonProperties {
if value, ok := apexAndroidMkData.Entries.EntryMap[name]; ok {
@@ -311,14 +326,16 @@
return moduleNames
}
-func (a *apexBundle) writeRequiredModules(w io.Writer) {
+func (a *apexBundle) writeRequiredModules(w io.Writer, apexBundleName string) {
var required []string
var targetRequired []string
var hostRequired []string
+ installMapSet := make(map[string]bool) // set of dependency module:location mappings
for _, fi := range a.filesInfo {
required = append(required, fi.requiredModuleNames...)
targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
+ installMapSet[a.fullModuleName(apexBundleName, &fi)+":"+fi.installDir+"/"+fi.builtFile.Base()] = true
}
if len(required) > 0 {
@@ -330,6 +347,11 @@
if len(hostRequired) > 0 {
fmt.Fprintln(w, "LOCAL_HOST_REQUIRED_MODULES +=", strings.Join(hostRequired, " "))
}
+ if len(installMapSet) > 0 {
+ var installs []string
+ installs = append(installs, android.SortedStringKeys(installMapSet)...)
+ fmt.Fprintln(w, "LOCAL_LICENSE_INSTALL_MAP +=", strings.Join(installs, " "))
+ }
}
func (a *apexBundle) androidMkForType() android.AndroidMkData {
@@ -347,16 +369,18 @@
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
+ data.Entries.WriteLicenseVariables(w)
if len(moduleNames) > 0 {
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
}
- a.writeRequiredModules(w)
+ a.writeRequiredModules(w, name)
fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
} else {
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
+ data.Entries.WriteLicenseVariables(w)
fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") // do we need a new class?
fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFile.String())
fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", a.installDir.ToMakePath().String())
@@ -370,7 +394,7 @@
// Because apex writes .mk with Custom(), we need to write manually some common properties
// which are available via data.Entries
commonProperties := []string{
- "LOCAL_INIT_RC", "LOCAL_VINTF_FRAGMENTS",
+ "LOCAL_FULL_INIT_RC", "LOCAL_VINTF_FRAGMENTS",
"LOCAL_PROPRIETARY_MODULE", "LOCAL_VENDOR_MODULE", "LOCAL_ODM_MODULE", "LOCAL_PRODUCT_MODULE", "LOCAL_SYSTEM_EXT_MODULE",
"LOCAL_MODULE_OWNER",
}
@@ -389,7 +413,7 @@
if len(a.requiredDeps) > 0 {
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.requiredDeps, " "))
}
- a.writeRequiredModules(w)
+ a.writeRequiredModules(w, name)
var postInstallCommands []string
if a.prebuiltFileToDelete != "" {
postInstallCommands = append(postInstallCommands, "rm -rf "+
@@ -426,12 +450,21 @@
fmt.Fprintf(w, dist)
}
- if a.coverageOutputPath.String() != "" {
+ if a.apisUsedByModuleFile.String() != "" {
goal := "apps_only"
- distFile := a.coverageOutputPath.String()
+ distFile := a.apisUsedByModuleFile.String()
fmt.Fprintf(w, "ifneq (,$(filter $(my_register_name),$(TARGET_BUILD_APPS)))\n"+
" $(call dist-for-goals,%s,%s:ndk_apis_usedby_apex/$(notdir %s))\n"+
- "endif",
+ "endif\n",
+ goal, distFile, distFile)
+ }
+
+ if a.apisBackedByModuleFile.String() != "" {
+ goal := "apps_only"
+ distFile := a.apisBackedByModuleFile.String()
+ fmt.Fprintf(w, "ifneq (,$(filter $(my_register_name),$(TARGET_BUILD_APPS)))\n"+
+ " $(call dist-for-goals,%s,%s:ndk_apis_backedby_apex/$(notdir %s))\n"+
+ "endif\n",
goal, distFile, distFile)
}
}
diff --git a/apex/apex.go b/apex/apex.go
index 9fb616d..f5e6fa9 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -30,6 +30,7 @@
"android/soong/bpf"
"android/soong/cc"
prebuilt_etc "android/soong/etc"
+ "android/soong/filesystem"
"android/soong/java"
"android/soong/python"
"android/soong/rust"
@@ -37,16 +38,20 @@
)
func init() {
- android.RegisterModuleType("apex", BundleFactory)
- android.RegisterModuleType("apex_test", testApexBundleFactory)
- android.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
- android.RegisterModuleType("apex_defaults", defaultsFactory)
- android.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
- android.RegisterModuleType("override_apex", overrideApexFactory)
- android.RegisterModuleType("apex_set", apexSetFactory)
+ registerApexBuildComponents(android.InitRegistrationContext)
+}
- android.PreDepsMutators(RegisterPreDepsMutators)
- android.PostDepsMutators(RegisterPostDepsMutators)
+func registerApexBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("apex", BundleFactory)
+ ctx.RegisterModuleType("apex_test", testApexBundleFactory)
+ ctx.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
+ ctx.RegisterModuleType("apex_defaults", defaultsFactory)
+ ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
+ ctx.RegisterModuleType("override_apex", overrideApexFactory)
+ ctx.RegisterModuleType("apex_set", apexSetFactory)
+
+ ctx.PreDepsMutators(RegisterPreDepsMutators)
+ ctx.PostDepsMutators(RegisterPostDepsMutators)
}
func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
@@ -87,15 +92,30 @@
Multilib apexMultilibProperties
+ // List of boot images that are embedded inside this APEX bundle.
+ //
+ // deprecated: Use Bootclasspath_fragments
+ // TODO(b/177892522): Remove after has been replaced by Bootclasspath_fragments
+ Boot_images []string
+
+ // List of bootclasspath fragments that are embedded inside this APEX bundle.
+ Bootclasspath_fragments []string
+
// List of java libraries that are embedded inside this APEX bundle.
Java_libs []string
// List of prebuilt files that are embedded inside this APEX bundle.
Prebuilts []string
+ // List of platform_compat_config files that are embedded inside this APEX bundle.
+ Compat_configs []string
+
// List of BPF programs inside this APEX bundle.
Bpfs []string
+ // List of filesystem images that are embedded inside this APEX bundle.
+ Filesystems []string
+
// Name of the apex_key module that provides the private key to sign this APEX bundle.
Key *string
@@ -113,7 +133,7 @@
// Whether this APEX is considered updatable or not. When set to true, this will enforce
// additional rules for making sure that the APEX is truly updatable. To be updatable,
// min_sdk_version should be set as well. This will also disable the size optimizations like
- // symlinking to the system libs. Default is false.
+ // symlinking to the system libs. Default is true.
Updatable *bool
// Whether this APEX is installable to one of the partitions like system, vendor, etc.
@@ -164,6 +184,10 @@
// used in tests.
Test_only_unsigned_payload *bool
+ // Whenever apex should be compressed, regardless of product flag used. Should be only
+ // used in tests.
+ Test_only_force_compression *bool
+
IsCoverageVariant bool `blueprint:"mutated"`
// List of sanitizer names that this APEX is enabled for
@@ -194,6 +218,9 @@
// List of native tests that are embedded inside this APEX.
Tests []string
+
+ // List of filesystem images that are embedded inside this APEX bundle.
+ Filesystems []string
}
type apexMultilibProperties struct {
@@ -381,7 +408,8 @@
isCompressed bool
// Path of API coverage generate file
- coverageOutputPath android.ModuleOutPath
+ apisUsedByModuleFile android.ModuleOutPath
+ apisBackedByModuleFile android.ModuleOutPath
}
// apexFileClass represents a type of file that can be included in APEX.
@@ -432,6 +460,8 @@
transitiveDep bool
isJniLib bool
+ multilib string
+
// TODO(jiyong): remove this
module android.Module
}
@@ -451,6 +481,7 @@
ret.requiredModuleNames = module.RequiredModuleNames()
ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
ret.hostRequiredModuleNames = module.HostRequiredModuleNames()
+ ret.multilib = module.Target().Arch.ArchType.Multilib
}
return ret
}
@@ -523,21 +554,35 @@
// Determines if the dependent will be part of the APEX payload. Can be false for the
// dependencies to the signing key module, etc.
payload bool
+
+ // True if the dependent can only be a source module, false if a prebuilt module is a suitable
+ // replacement. This is needed because some prebuilt modules do not provide all the information
+ // needed by the apex.
+ sourceOnly bool
}
+func (d dependencyTag) ReplaceSourceWithPrebuilt() bool {
+ return !d.sourceOnly
+}
+
+var _ android.ReplaceSourceWithPrebuilt = &dependencyTag{}
+
var (
- androidAppTag = dependencyTag{name: "androidApp", payload: true}
- bpfTag = dependencyTag{name: "bpf", payload: true}
- certificateTag = dependencyTag{name: "certificate"}
- executableTag = dependencyTag{name: "executable", payload: true}
- javaLibTag = dependencyTag{name: "javaLib", payload: true}
- jniLibTag = dependencyTag{name: "jniLib", payload: true}
- keyTag = dependencyTag{name: "key"}
- prebuiltTag = dependencyTag{name: "prebuilt", payload: true}
- rroTag = dependencyTag{name: "rro", payload: true}
- sharedLibTag = dependencyTag{name: "sharedLib", payload: true}
- testForTag = dependencyTag{name: "test for"}
- testTag = dependencyTag{name: "test", payload: true}
+ androidAppTag = dependencyTag{name: "androidApp", payload: true}
+ bpfTag = dependencyTag{name: "bpf", payload: true}
+ certificateTag = dependencyTag{name: "certificate"}
+ executableTag = dependencyTag{name: "executable", payload: true}
+ fsTag = dependencyTag{name: "filesystem", payload: true}
+ bootImageTag = dependencyTag{name: "bootImage", payload: true, sourceOnly: true}
+ compatConfigTag = dependencyTag{name: "compatConfig", payload: true, sourceOnly: true}
+ javaLibTag = dependencyTag{name: "javaLib", payload: true}
+ jniLibTag = dependencyTag{name: "jniLib", payload: true}
+ keyTag = dependencyTag{name: "key"}
+ prebuiltTag = dependencyTag{name: "prebuilt", payload: true}
+ rroTag = dependencyTag{name: "rro", payload: true}
+ sharedLibTag = dependencyTag{name: "sharedLib", payload: true}
+ testForTag = dependencyTag{name: "test for"}
+ testTag = dependencyTag{name: "test", payload: true}
)
// TODO(jiyong): shorten this function signature
@@ -560,6 +605,7 @@
ctx.AddFarVariationDependencies(libVariations, jniLibTag, nativeModules.Jni_libs...)
ctx.AddFarVariationDependencies(libVariations, sharedLibTag, nativeModules.Native_shared_libs...)
ctx.AddFarVariationDependencies(rustLibVariations, sharedLibTag, nativeModules.Rust_dyn_libs...)
+ ctx.AddFarVariationDependencies(target.Variations(), fsTag, nativeModules.Filesystems...)
}
func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) {
@@ -707,12 +753,18 @@
// Common-arch dependencies come next
commonVariation := ctx.Config().AndroidCommonTarget.Variations()
+ ctx.AddFarVariationDependencies(commonVariation, bootImageTag, a.properties.Boot_images...)
+ ctx.AddFarVariationDependencies(commonVariation, bootImageTag, a.properties.Bootclasspath_fragments...)
ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.properties.Bpfs...)
+ ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
+ ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...)
- // With EMMA_INSTRUMENT_FRAMEWORK=true the ART boot image includes jacoco library.
- if a.artApex && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
- ctx.AddFarVariationDependencies(commonVariation, javaLibTag, "jacocoagent")
+ if a.artApex {
+ // With EMMA_INSTRUMENT_FRAMEWORK=true the ART boot image includes jacoco library.
+ if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
+ ctx.AddFarVariationDependencies(commonVariation, javaLibTag, "jacocoagent")
+ }
}
// Dependencies for signing
@@ -800,7 +852,15 @@
if !ok || !am.CanHaveApexVariants() {
return false
}
- if !parent.(android.DepIsInSameApex).DepIsInSameApex(mctx, child) {
+ depTag := mctx.OtherModuleDependencyTag(child)
+
+ // Check to see if the tag always requires that the child module has an apex variant for every
+ // apex variant of the parent module. If it does not then it is still possible for something
+ // else, e.g. the DepIsInSameApex(...) method to decide that a variant is required.
+ if required, ok := depTag.(android.AlwaysRequireApexVariantTag); ok && required.AlwaysRequireApexVariant() {
+ return true
+ }
+ if !android.IsDepInSameApex(mctx, parent, child) {
return false
}
if excludeVndkLibs {
@@ -833,11 +893,17 @@
Contents: apexContents,
})
+ minSdkVersion := a.minSdkVersion(mctx)
+ // When min_sdk_version is not set, the apex is built against FutureApiLevel.
+ if minSdkVersion.IsNone() {
+ minSdkVersion = android.FutureApiLevel
+ }
+
// This is the main part of this mutator. Mark the collected dependencies that they need to
// be built for this apexBundle.
apexInfo := android.ApexInfo{
ApexVariationName: mctx.ModuleName(),
- MinSdkVersionStr: a.minSdkVersion(mctx).String(),
+ MinSdkVersion: minSdkVersion,
RequiredSdks: a.RequiredSdks(),
Updatable: a.Updatable(),
InApexes: []string{mctx.ModuleName()},
@@ -938,7 +1004,7 @@
// If any of the dep is not available to platform, this module is also considered as being
// not available to platform even if it has "//apex_available:platform"
mctx.VisitDirectDeps(func(child android.Module) {
- if !am.DepIsInSameApex(mctx, child) {
+ if !android.IsDepInSameApex(mctx, am, child) {
// if the dependency crosses apex boundary, don't consider it
return
}
@@ -983,6 +1049,16 @@
if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex {
apexBundleName := mctx.ModuleName()
mctx.CreateVariations(apexBundleName)
+ if strings.HasPrefix(apexBundleName, "com.android.art") {
+ // Create an alias from the platform variant. This is done to make
+ // test_for dependencies work for modules that are split by the APEX
+ // mutator, since test_for dependencies always go to the platform variant.
+ // This doesn't happen for normal APEXes that are disjunct, so only do
+ // this for the overlapping ART APEXes.
+ // TODO(b/183882457): Remove this if the test_for functionality is
+ // refactored to depend on the proper APEX variants instead of platform.
+ mctx.CreateAliasVariation("", apexBundleName)
+ }
} else if o, ok := mctx.Module().(*OverrideApex); ok {
apexBundleName := o.GetOverriddenModuleName()
if apexBundleName == "" {
@@ -990,6 +1066,10 @@
return
}
mctx.CreateVariations(apexBundleName)
+ if strings.HasPrefix(apexBundleName, "com.android.art") {
+ // TODO(b/183882457): See note for CreateAliasVariation above.
+ mctx.CreateAliasVariation("", apexBundleName)
+ }
}
}
@@ -1138,11 +1218,13 @@
}).([]string)
}
-// setUseVendorAllowListForTest overrides useVendorAllowList and must be called before the first
-// call to useVendorAllowList()
-func setUseVendorAllowListForTest(config android.Config, allowList []string) {
- config.Once(useVendorAllowListKey, func() interface{} {
- return allowList
+// setUseVendorAllowListForTest returns a FixturePreparer that overrides useVendorAllowList and
+// must be called before the first call to useVendorAllowList()
+func setUseVendorAllowListForTest(allowList []string) android.FixturePreparer {
+ return android.FixtureModifyConfig(func(config android.Config) {
+ config.Once(useVendorAllowListKey, func() interface{} {
+ return allowList
+ })
})
}
@@ -1200,7 +1282,7 @@
// Implements android.ApexBudleDepsInfoIntf
func (a *apexBundle) Updatable() bool {
- return proptools.Bool(a.properties.Updatable)
+ return proptools.BoolDefault(a.properties.Updatable, true)
}
// getCertString returns the name of the cert that should be used to sign this APEX. This is
@@ -1235,6 +1317,11 @@
return proptools.Bool(a.properties.Test_only_unsigned_payload)
}
+// See the test_only_force_compression property
+func (a *apexBundle) testOnlyShouldForceCompression() bool {
+ return proptools.Bool(a.properties.Test_only_force_compression)
+}
+
// These functions are interfacing with cc/sanitizer.go. The entire APEX (along with all of its
// members) can be sanitized, either forcibly, or by the global configuration. For some of the
// sanitizers, extra dependencies can be forcibly added as well.
@@ -1413,6 +1500,7 @@
}
var _ javaModule = (*java.Library)(nil)
+var _ javaModule = (*java.Import)(nil)
var _ javaModule = (*java.SdkLibrary)(nil)
var _ javaModule = (*java.DexImport)(nil)
var _ javaModule = (*java.SdkLibraryImport)(nil)
@@ -1481,7 +1569,12 @@
return newApexFile(ctx, builtFile, builtFile.Base(), dirInApex, etc, bpfProgram)
}
-// WalyPayloadDeps visits dependencies that contributes to the payload of this APEX. For each of the
+func apexFileForFilesystem(ctx android.BaseModuleContext, buildFile android.Path, fs filesystem.Filesystem) apexFile {
+ dirInApex := filepath.Join("etc", "fs")
+ return newApexFile(ctx, buildFile, buildFile.Base(), dirInApex, etc, fs)
+}
+
+// WalkPayloadDeps visits dependencies that contributes to the payload of this APEX. For each of the
// visited module, the `do` callback is executed. Returning true in the callback continues the visit
// to the child modules. Returning false makes the visit to continue in the sibling or the parent
// modules. This is used in check* functions below.
@@ -1607,9 +1700,29 @@
} else {
ctx.PropertyErrorf("binaries", "%q is neither cc_binary, rust_binary, (embedded) py_binary, (host) blueprint_go_binary, (host) bootstrap_go_binary, nor sh_binary", depName)
}
+ case bootImageTag:
+ {
+ if _, ok := child.(*java.BootImageModule); !ok {
+ ctx.PropertyErrorf("boot_images", "%q is not a boot_image module", depName)
+ return false
+ }
+ bootImageInfo := ctx.OtherModuleProvider(child, java.BootImageInfoProvider).(java.BootImageInfo)
+ for arch, files := range bootImageInfo.AndroidBootImageFilesByArchType() {
+ dirInApex := filepath.Join("javalib", arch.String())
+ for _, f := range files {
+ androidMkModuleName := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
+ // TODO(b/177892522) - consider passing in the boot image module here instead of nil
+ af := newApexFile(ctx, f, androidMkModuleName, dirInApex, etc, nil)
+ filesInfo = append(filesInfo, af)
+ }
+ }
+
+ // Track transitive dependencies.
+ return true
+ }
case javaLibTag:
switch child.(type) {
- case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport:
+ case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport, *java.Import:
af := apexFileForJavaModule(ctx, child.(javaModule))
if !af.ok() {
ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
@@ -1655,13 +1768,23 @@
} else {
ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
}
+ case fsTag:
+ if fs, ok := child.(filesystem.Filesystem); ok {
+ filesInfo = append(filesInfo, apexFileForFilesystem(ctx, fs.OutputPath(), fs))
+ } else {
+ ctx.PropertyErrorf("filesystems", "%q is not a filesystem module", depName)
+ }
case prebuiltTag:
if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
- } else if prebuilt, ok := child.(java.PlatformCompatConfigIntf); ok {
- filesInfo = append(filesInfo, apexFileForCompatConfig(ctx, prebuilt, depName))
} else {
- ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc and not a platform_compat_config module", depName)
+ ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
+ }
+ case compatConfigTag:
+ if compatConfig, ok := child.(java.PlatformCompatConfigIntf); ok {
+ filesInfo = append(filesInfo, apexFileForCompatConfig(ctx, compatConfig, depName))
+ } else {
+ ctx.PropertyErrorf("compat_configs", "%q is not a platform_compat_config module", depName)
}
case testTag:
if ccTest, ok := child.(*cc.Module); ok {
@@ -1760,7 +1883,10 @@
// like to record requiredNativeLibs even when
// DepIsInSameAPex is false. We also shouldn't do
// this for host.
- if !am.DepIsInSameApex(ctx, am) {
+ //
+ // TODO(jiyong): explain why the same module is passed in twice.
+ // Switching the first am to parent breaks lots of tests.
+ if !android.IsDepInSameApex(ctx, am, am) {
return false
}
@@ -1801,6 +1927,25 @@
filesInfo = append(filesInfo, af)
return true // track transitive dependencies
}
+ } else if rust.IsRlibDepTag(depTag) {
+ // Rlib is statically linked, but it might have shared lib
+ // dependencies. Track them.
+ return true
+ } else if java.IsbootImageContentDepTag(depTag) {
+ // Add the contents of the boot image to the apex.
+ switch child.(type) {
+ case *java.Library, *java.SdkLibrary:
+ af := apexFileForJavaModule(ctx, child.(javaModule))
+ if !af.ok() {
+ ctx.PropertyErrorf("boot_images", "boot image content %q is not configured to be compiled into dex", depName)
+ return false
+ }
+ filesInfo = append(filesInfo, af)
+ return true // track transitive dependencies
+ default:
+ ctx.PropertyErrorf("boot_images", "boot image content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
+ }
+
} else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
// nothing
} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
@@ -1815,20 +1960,6 @@
return
}
- // Specific to the ART apex: dexpreopt artifacts for libcore Java libraries. Build rules are
- // generated by the dexpreopt singleton, and here we access build artifacts via the global
- // boot image config.
- if a.artApex {
- for arch, files := range java.DexpreoptedArtApexJars(ctx) {
- dirInApex := filepath.Join("javalib", arch.String())
- for _, f := range files {
- localModule := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
- af := newApexFile(ctx, f, localModule, dirInApex, etc, nil)
- filesInfo = append(filesInfo, af)
- }
- }
- }
-
// Remove duplicates in filesInfo
removeDup := func(filesInfo []apexFile) []apexFile {
encountered := make(map[string]apexFile)
@@ -1913,9 +2044,11 @@
a.linkToSystemLib = false
}
+ forced := ctx.Config().ForceApexSymlinkOptimization()
+
// We don't need the optimization for updatable APEXes, as it might give false signal
- // to the system health when the APEXes are still bundled (b/149805758)
- if a.Updatable() && a.properties.ApexType == imageApex {
+ // to the system health when the APEXes are still bundled (b/149805758).
+ if !forced && a.Updatable() && a.properties.ApexType == imageApex {
a.linkToSystemLib = false
}
@@ -2068,17 +2201,13 @@
func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) android.ApiLevel {
ver := proptools.String(a.properties.Min_sdk_version)
if ver == "" {
- return android.FutureApiLevel
+ return android.NoneApiLevel
}
apiLevel, err := android.ApiLevelFromUser(ctx, ver)
if err != nil {
ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
return android.NoneApiLevel
}
- if apiLevel.IsPreview() {
- // All codenames should build against "current".
- return android.FutureApiLevel
- }
return apiLevel
}
@@ -2099,6 +2228,8 @@
// If `to` is not actually in the same APEX as `from` then it does not need
// apex_available and neither do any of its dependencies.
+ //
+ // It is ok to call DepIsInSameApex() directly from within WalkPayloadDeps().
if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
// As soon as the dependency graph crosses the APEX boundary, don't go further.
return false
@@ -2141,8 +2272,10 @@
tag := ctx.OtherModuleDependencyTag(module)
switch tag {
case javaLibTag, androidAppTag:
- if m, ok := module.(interface{ CheckStableSdkVersion() error }); ok {
- if err := m.CheckStableSdkVersion(); err != nil {
+ if m, ok := module.(interface {
+ CheckStableSdkVersion(ctx android.BaseModuleContext) error
+ }); ok {
+ if err := m.CheckStableSdkVersion(ctx); err != nil {
ctx.ModuleErrorf("cannot depend on \"%v\": %v", ctx.OtherModuleName(module), err)
}
}
@@ -2182,6 +2315,8 @@
// If `to` is not actually in the same APEX as `from` then it does not need
// apex_available and neither do any of its dependencies.
+ //
+ // It is ok to call DepIsInSameApex() directly from within WalkPayloadDeps().
if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
// As soon as the dependency graph crosses the APEX boundary, don't go
// further.
@@ -2191,8 +2326,10 @@
if to.AvailableFor(apexName) || baselineApexAvailable(apexName, toName) {
return true
}
- ctx.ModuleErrorf("%q requires %q that doesn't list the APEX under 'apex_available'. Dependency path:%s",
- fromName, toName, ctx.GetPathString(true))
+ ctx.ModuleErrorf("%q requires %q that doesn't list the APEX under 'apex_available'."+
+ "\n\nDependency path:%s\n\n"+
+ "Consider adding %q to 'apex_available' property of %q",
+ fromName, toName, ctx.GetPathString(true), apexName, toName)
// Visit this module's dependencies to check and report any issues with their availability.
return true
})
@@ -2297,7 +2434,6 @@
"libFraunhoferAAC",
"libaudio-a2dp-hw-utils",
"libaudio-hearing-aid-hw-utils",
- "libbinder_headers",
"libbluetooth",
"libbluetooth-types",
"libbluetooth-types-header",
@@ -2428,7 +2564,6 @@
"libaudiopolicy",
"libaudioutils",
"libaudioutils_fixedfft",
- "libbinder_headers",
"libbluetooth-types-header",
"libbufferhub",
"libbufferhub_headers",
@@ -2544,7 +2679,6 @@
"libavcenc",
"libavservices_minijail",
"libavservices_minijail",
- "libbinder_headers",
"libbinderthreadstateutils",
"libbluetooth-types-header",
"libbufferhub_headers",
@@ -2795,14 +2929,6 @@
//
// Module separator
//
- m["com.android.sdkext"] = []string{
- "fmtlib_ndk",
- "libbase_ndk",
- "libprotobuf-cpp-lite-ndk",
- }
- //
- // Module separator
- //
m["com.android.os.statsd"] = []string{
"libstatssocket",
}
@@ -2819,14 +2945,12 @@
"com.google.android.material_material",
"com.google.android.material_material-nodeps",
- "libatomic",
"libclang_rt",
- "libgcc_stripped",
"libprofile-clang-extras",
"libprofile-clang-extras_ndk",
"libprofile-extras",
"libprofile-extras_ndk",
- "libunwind_llvm",
+ "libunwind",
}
return m
}
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index ee9fc81..0ed94af 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -58,8 +58,8 @@
echo "ERROR: go/apex-allowed-deps-error";
echo "******************************";
echo "Detected changes to allowed dependencies in updatable modules.";
- echo "To fix and update build/soong/apex/allowed_deps.txt, please run:";
- echo "$$ (croot && build/soong/scripts/update-apex-allowed-deps.sh)";
+ echo "To fix and update packages/modules/common/build/allowed_deps.txt, please run:";
+ echo "$$ (croot && packages/modules/common/build/update-apex-allowed-deps.sh)";
echo "Members of mainline-modularization@google.com will review the changes.";
echo -e "******************************\n";
exit 1;
@@ -81,25 +81,35 @@
}
})
- allowedDeps := android.ExistentPathForSource(ctx, "build/soong/apex/allowed_deps.txt").Path()
-
+ allowedDepsSource := android.ExistentPathForSource(ctx, "packages/modules/common/build/allowed_deps.txt")
newAllowedDeps := android.PathForOutput(ctx, "apex", "depsinfo", "new-allowed-deps.txt")
- ctx.Build(pctx, android.BuildParams{
- Rule: generateApexDepsInfoFilesRule,
- Inputs: append(updatableFlatLists, allowedDeps),
- Output: newAllowedDeps,
- })
-
s.allowedApexDepsInfoCheckResult = android.PathForOutput(ctx, newAllowedDeps.Rel()+".check")
- ctx.Build(pctx, android.BuildParams{
- Rule: diffAllowedApexDepsInfoRule,
- Input: newAllowedDeps,
- Output: s.allowedApexDepsInfoCheckResult,
- Args: map[string]string{
- "allowed_deps": allowedDeps.String(),
- "new_allowed_deps": newAllowedDeps.String(),
- },
- })
+
+ if !allowedDepsSource.Valid() {
+ // Unbundled projects may not have packages/modules/common/ checked out; ignore those.
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Touch,
+ Output: s.allowedApexDepsInfoCheckResult,
+ })
+ } else {
+ allowedDeps := allowedDepsSource.Path()
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: generateApexDepsInfoFilesRule,
+ Inputs: append(updatableFlatLists, allowedDeps),
+ Output: newAllowedDeps,
+ })
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: diffAllowedApexDepsInfoRule,
+ Input: newAllowedDeps,
+ Output: s.allowedApexDepsInfoCheckResult,
+ Args: map[string]string{
+ "allowed_deps": allowedDeps.String(),
+ "new_allowed_deps": newAllowedDeps.String(),
+ },
+ })
+ }
ctx.Phony("apex-allowed-deps-check", s.allowedApexDepsInfoCheckResult)
}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 84a8356..977a954 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -16,12 +16,13 @@
import (
"fmt"
- "io/ioutil"
"os"
"path"
+ "path/filepath"
"reflect"
"regexp"
"sort"
+ "strconv"
"strings"
"testing"
@@ -37,8 +38,6 @@
"android/soong/sh"
)
-var buildDir string
-
// names returns name list from white space separated string
func names(s string) (ns []string) {
for _, n := range strings.Split(s, " ") {
@@ -49,49 +48,43 @@
return
}
-func testApexError(t *testing.T, pattern, bp string, handlers ...testCustomizer) {
+func testApexError(t *testing.T, pattern, bp string, preparers ...android.FixturePreparer) {
t.Helper()
- ctx, config := testApexContext(t, bp, handlers...)
- _, 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)
+ android.GroupFixturePreparers(
+ prepareForApexTest,
+ android.GroupFixturePreparers(preparers...),
+ ).
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
+ RunTestWithBp(t, bp)
}
-func testApex(t *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) {
+func testApex(t *testing.T, bp string, preparers ...android.FixturePreparer) *android.TestContext {
t.Helper()
- ctx, config := testApexContext(t, bp, handlers...)
- _, errs := ctx.ParseBlueprintsFiles(".")
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
- return ctx, config
-}
-type testCustomizer func(fs map[string][]byte, config android.Config)
-
-func withFiles(files map[string][]byte) testCustomizer {
- return func(fs map[string][]byte, config android.Config) {
- for k, v := range files {
- fs[k] = v
- }
+ optionalBpPreparer := android.NullFixturePreparer
+ if bp != "" {
+ optionalBpPreparer = android.FixtureWithRootAndroidBp(bp)
}
+
+ result := android.GroupFixturePreparers(
+ prepareForApexTest,
+ android.GroupFixturePreparers(preparers...),
+ optionalBpPreparer,
+ ).RunTest(t)
+
+ return result.TestContext
}
-func withTargets(targets map[android.OsType][]android.Target) testCustomizer {
- return func(fs map[string][]byte, config android.Config) {
+func withFiles(files android.MockFS) android.FixturePreparer {
+ return files.AddToFixture()
+}
+
+func withTargets(targets map[android.OsType][]android.Target) android.FixturePreparer {
+ return android.FixtureModifyConfig(func(config android.Config) {
for k, v := range targets {
config.Targets[k] = v
}
- }
+ })
}
// withNativeBridgeTargets sets configuration with targets including:
@@ -99,61 +92,82 @@
// - X86 (secondary)
// - Arm64 on X86_64 (native bridge)
// - Arm on X86 (native bridge)
-func withNativeBridgeEnabled(_ map[string][]byte, config android.Config) {
- config.Targets[android.Android] = []android.Target{
- {Os: android.Android, Arch: android.Arch{ArchType: android.X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}},
- NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
- {Os: android.Android, Arch: android.Arch{ArchType: android.X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}},
- NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
- {Os: android.Android, Arch: android.Arch{ArchType: android.Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}},
- NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "x86_64", NativeBridgeRelativePath: "arm64"},
- {Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}},
- NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "x86", NativeBridgeRelativePath: "arm"},
- }
+var withNativeBridgeEnabled = android.FixtureModifyConfig(
+ func(config android.Config) {
+ config.Targets[android.Android] = []android.Target{
+ {Os: android.Android, Arch: android.Arch{ArchType: android.X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}},
+ NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
+ {Os: android.Android, Arch: android.Arch{ArchType: android.X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}},
+ NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
+ {Os: android.Android, Arch: android.Arch{ArchType: android.Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}},
+ NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "x86_64", NativeBridgeRelativePath: "arm64"},
+ {Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}},
+ NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "x86", NativeBridgeRelativePath: "arm"},
+ }
+ },
+)
+
+func withManifestPackageNameOverrides(specs []string) android.FixturePreparer {
+ return android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.ManifestPackageNameOverrides = specs
+ })
}
-func withManifestPackageNameOverrides(specs []string) testCustomizer {
- return func(fs map[string][]byte, config android.Config) {
- config.TestProductVariables.ManifestPackageNameOverrides = specs
- }
-}
+var withBinder32bit = android.FixtureModifyProductVariables(
+ func(variables android.FixtureProductVariables) {
+ variables.Binder32bit = proptools.BoolPtr(true)
+ },
+)
-func withBinder32bit(_ map[string][]byte, config android.Config) {
- config.TestProductVariables.Binder32bit = proptools.BoolPtr(true)
-}
+var withUnbundledBuild = android.FixtureModifyProductVariables(
+ func(variables android.FixtureProductVariables) {
+ variables.Unbundled_build = proptools.BoolPtr(true)
+ },
+)
-func withUnbundledBuild(_ map[string][]byte, config android.Config) {
- config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
-}
+// Legacy preparer used for running tests within the apex package.
+//
+// This includes everything that was needed to run any test in the apex package prior to the
+// introduction of the test fixtures. Tests that are being converted to use fixtures directly
+// rather than through the testApex...() methods should avoid using this and instead use the
+// various preparers directly, using android.GroupFixturePreparers(...) to group them when
+// necessary.
+//
+// deprecated
+var prepareForApexTest = android.GroupFixturePreparers(
+ // General preparers in alphabetical order as test infrastructure will enforce correct
+ // registration order.
+ android.PrepareForTestWithAndroidBuildComponents,
+ bpf.PrepareForTestWithBpf,
+ cc.PrepareForTestWithCcBuildComponents,
+ java.PrepareForTestWithJavaDefaultModules,
+ prebuilt_etc.PrepareForTestWithPrebuiltEtc,
+ rust.PrepareForTestWithRustDefaultModules,
+ sh.PrepareForTestWithShBuildComponents,
-func testApexContext(_ *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) {
- bp = bp + `
+ PrepareForTestWithApexBuildComponents,
+
+ // Additional apex test specific preparers.
+ android.FixtureAddTextFile("system/sepolicy/Android.bp", `
filegroup {
name: "myapex-file_contexts",
srcs: [
- "system/sepolicy/apex/myapex-file_contexts",
+ "apex/myapex-file_contexts",
],
}
- `
-
- bp = bp + cc.GatherRequiredDepsForTest(android.Android)
-
- bp = bp + rust.GatherRequiredDepsForTest()
-
- bp = bp + java.GatherRequiredDepsForTest()
-
- fs := map[string][]byte{
- "a.java": nil,
- "PrebuiltAppFoo.apk": nil,
- "PrebuiltAppFooPriv.apk": nil,
- "build/make/target/product/security": nil,
- "apex_manifest.json": nil,
- "AndroidManifest.xml": nil,
- "system/sepolicy/apex/myapex-file_contexts": nil,
- "system/sepolicy/apex/myapex.updatable-file_contexts": nil,
- "system/sepolicy/apex/myapex2-file_contexts": nil,
- "system/sepolicy/apex/otherapex-file_contexts": nil,
- "system/sepolicy/apex/com.android.vndk-file_contexts": nil,
+ `),
+ prepareForTestWithMyapex,
+ android.FixtureMergeMockFs(android.MockFS{
+ "a.java": nil,
+ "PrebuiltAppFoo.apk": nil,
+ "PrebuiltAppFooPriv.apk": nil,
+ "apex_manifest.json": nil,
+ "AndroidManifest.xml": nil,
+ "system/sepolicy/apex/myapex.updatable-file_contexts": nil,
+ "system/sepolicy/apex/myapex2-file_contexts": nil,
+ "system/sepolicy/apex/otherapex-file_contexts": nil,
+ "system/sepolicy/apex/com.android.vndk-file_contexts": nil,
+ "system/sepolicy/apex/com.android.vndk.current-file_contexts": nil,
"mylib.cpp": nil,
"mytest.cpp": nil,
"mytest1.cpp": nil,
@@ -190,101 +204,25 @@
"testdata/baz": nil,
"AppSet.apks": nil,
"foo.rs": nil,
- }
+ "libfoo.jar": nil,
+ "libbar.jar": nil,
+ },
+ ),
- cc.GatherRequiredFilesForTest(fs)
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.DeviceVndkVersion = proptools.StringPtr("current")
+ variables.DefaultAppCertificate = proptools.StringPtr("vendor/foo/devkeys/test")
+ variables.CertificateOverrides = []string{"myapex_keytest:myapex.certificate.override"}
+ variables.Platform_sdk_codename = proptools.StringPtr("Q")
+ variables.Platform_sdk_final = proptools.BoolPtr(false)
+ variables.Platform_version_active_codenames = []string{"Q"}
+ variables.Platform_vndk_version = proptools.StringPtr("29")
+ }),
+)
- for _, handler := range handlers {
- // The fs now needs to be populated before creating the config, call handlers twice
- // for now, once to get any fs changes, and later after the config was created to
- // set product variables or targets.
- tempConfig := android.TestArchConfig(buildDir, nil, bp, fs)
- handler(fs, tempConfig)
- }
-
- config := android.TestArchConfig(buildDir, nil, bp, fs)
- 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)
- config.TestProductVariables.Platform_version_active_codenames = []string{"Q"}
- config.TestProductVariables.Platform_vndk_version = proptools.StringPtr("VER")
-
- for _, handler := range handlers {
- // The fs now needs to be populated before creating the config, call handlers twice
- // for now, earlier to get any fs changes, and now after the config was created to
- // set product variables or targets.
- tempFS := map[string][]byte{}
- handler(tempFS, config)
- }
-
- ctx := android.NewTestArchContext(config)
-
- // from android package
- android.RegisterPackageBuildComponents(ctx)
- ctx.PreArchMutators(android.RegisterVisibilityRuleChecker)
-
- ctx.RegisterModuleType("apex", BundleFactory)
- ctx.RegisterModuleType("apex_test", testApexBundleFactory)
- ctx.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
- ctx.RegisterModuleType("apex_key", ApexKeyFactory)
- ctx.RegisterModuleType("apex_defaults", defaultsFactory)
- ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
- ctx.RegisterModuleType("override_apex", overrideApexFactory)
- ctx.RegisterModuleType("apex_set", apexSetFactory)
-
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- ctx.PreArchMutators(android.RegisterComponentsMutator)
- ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
-
- android.RegisterPrebuiltMutators(ctx)
-
- // Register these after the prebuilt mutators have been registered to match what
- // happens at runtime.
- ctx.PreArchMutators(android.RegisterVisibilityRuleGatherer)
- ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
-
- cc.RegisterRequiredBuildComponentsForTest(ctx)
- rust.RegisterRequiredBuildComponentsForTest(ctx)
-
- ctx.RegisterModuleType("cc_test", cc.TestFactory)
- ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory)
- ctx.RegisterModuleType("vndk_libraries_txt", cc.VndkLibrariesTxtFactory)
- prebuilt_etc.RegisterPrebuiltEtcBuildComponents(ctx)
- ctx.RegisterModuleType("platform_compat_config", java.PlatformCompatConfigFactory)
- ctx.RegisterModuleType("sh_binary", sh.ShBinaryFactory)
- ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
- java.RegisterJavaBuildComponents(ctx)
- java.RegisterSystemModulesBuildComponents(ctx)
- java.RegisterAppBuildComponents(ctx)
- java.RegisterAppImportBuildComponents(ctx)
- java.RegisterAppSetBuildComponents(ctx)
- java.RegisterRuntimeResourceOverlayBuildComponents(ctx)
- java.RegisterSdkLibraryBuildComponents(ctx)
- java.RegisterPrebuiltApisBuildComponents(ctx)
- ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
- ctx.RegisterModuleType("bpf", bpf.BpfFactory)
-
- ctx.PreDepsMutators(RegisterPreDepsMutators)
- ctx.PostDepsMutators(RegisterPostDepsMutators)
-
- ctx.Register()
-
- return ctx, config
-}
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_apex_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- _ = os.RemoveAll(buildDir)
-}
+var prepareForTestWithMyapex = android.FixtureMergeMockFs(android.MockFS{
+ "system/sepolicy/apex/myapex-file_contexts": nil,
+})
// ensure that 'result' equals 'expected'
func ensureEquals(t *testing.T, result string, expected string) {
@@ -360,7 +298,7 @@
// Minimal test
func TestBasicApex(t *testing.T) {
- ctx, config := testApex(t, `
+ ctx := testApex(t, `
apex_defaults {
name: "myapex-defaults",
manifest: ":myapex.manifest",
@@ -381,6 +319,7 @@
"myjar",
"myjar_dex",
],
+ updatable: false,
}
apex {
@@ -441,7 +380,7 @@
}
rust_binary {
- name: "foo.rust",
+ name: "foo.rust",
srcs: ["foo.rs"],
rlibs: ["libfoo.rlib.rust"],
dylibs: ["libfoo.dylib.rust"],
@@ -449,14 +388,23 @@
}
rust_library_rlib {
- name: "libfoo.rlib.rust",
+ name: "libfoo.rlib.rust",
srcs: ["foo.rs"],
crate_name: "foo",
apex_available: ["myapex"],
+ shared_libs: ["libfoo.shared_from_rust"],
+ }
+
+ cc_library_shared {
+ name: "libfoo.shared_from_rust",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ apex_available: ["myapex"],
}
rust_library_dylib {
- name: "libfoo.dylib.rust",
+ name: "libfoo.dylib.rust",
srcs: ["foo.rs"],
crate_name: "foo",
apex_available: ["myapex"],
@@ -481,6 +429,7 @@
binaries: ["foo"],
key: "myapex.key",
file_contexts: ":myapex-file_contexts",
+ updatable: false,
}
cc_library_shared {
@@ -568,7 +517,7 @@
// Make sure that Android.mk is created
ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- data := android.AndroidMkDataForTest(t, config, "", ab)
+ data := android.AndroidMkDataForTest(t, ctx, ab)
var builder strings.Builder
data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
@@ -579,7 +528,7 @@
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_image/NOTICE")
+ ensureContains(t, optFlags, "--assets_dir out/soong/.intermediates/myapex/android_common_myapex_image/NOTICE")
copyCmds := apexRule.Args["copy_commands"]
@@ -599,6 +548,7 @@
ensureListContains(t, ctx.ModuleVariantsForTests("libfoo.rlib.rust"), "android_arm64_armv8-a_rlib_dylib-std_apex10000")
ensureListContains(t, ctx.ModuleVariantsForTests("libfoo.dylib.rust"), "android_arm64_armv8-a_dylib_apex10000")
ensureListContains(t, ctx.ModuleVariantsForTests("libbar.ffi"), "android_arm64_armv8-a_shared_apex10000")
+ ensureListContains(t, ctx.ModuleVariantsForTests("libfoo.shared_from_rust"), "android_arm64_armv8-a_shared_apex10000")
// Ensure that both direct and indirect deps are copied into apex
ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
@@ -608,6 +558,7 @@
ensureContains(t, copyCmds, "image.apex/lib64/libfoo.dylib.rust.dylib.so")
ensureContains(t, copyCmds, "image.apex/lib64/libfoo.ffi.so")
ensureContains(t, copyCmds, "image.apex/lib64/libbar.ffi.so")
+ ensureContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so")
// .. but not for java libs
ensureNotContains(t, copyCmds, "image.apex/javalib/myotherjar.jar")
ensureNotContains(t, copyCmds, "image.apex/javalib/msharedjar.jar")
@@ -650,21 +601,19 @@
fullDepsInfo := strings.Split(ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("depsinfo/fulllist.txt").Args["content"], "\\n")
ensureListContains(t, fullDepsInfo, " myjar(minSdkVersion:(no version)) <- myapex")
- ensureListContains(t, fullDepsInfo, " mylib(minSdkVersion:(no version)) <- myapex")
ensureListContains(t, fullDepsInfo, " mylib2(minSdkVersion:(no version)) <- mylib")
ensureListContains(t, fullDepsInfo, " myotherjar(minSdkVersion:(no version)) <- myjar")
ensureListContains(t, fullDepsInfo, " mysharedjar(minSdkVersion:(no version)) (external) <- myjar")
flatDepsInfo := strings.Split(ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("depsinfo/flatlist.txt").Args["content"], "\\n")
ensureListContains(t, flatDepsInfo, "myjar(minSdkVersion:(no version))")
- ensureListContains(t, flatDepsInfo, "mylib(minSdkVersion:(no version))")
ensureListContains(t, flatDepsInfo, "mylib2(minSdkVersion:(no version))")
ensureListContains(t, flatDepsInfo, "myotherjar(minSdkVersion:(no version))")
ensureListContains(t, flatDepsInfo, "mysharedjar(minSdkVersion:(no version)) (external)")
}
func TestDefaults(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex_defaults {
name: "myapex-defaults",
key: "myapex.key",
@@ -674,6 +623,7 @@
apps: ["AppFoo"],
rros: ["rro"],
bpfs: ["bpf"],
+ updatable: false,
}
prebuilt_etc {
@@ -738,10 +688,11 @@
}
func TestApexManifest(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
+ updatable: false,
}
apex_key {
@@ -759,12 +710,13 @@
}
func TestBasicZipApex(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
payload_type: "zip",
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -809,11 +761,12 @@
}
func TestApexWithStubs(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib", "mylib3"],
+ updatable: false,
}
apex_key {
@@ -878,7 +831,7 @@
mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
// Ensure that mylib is linking with the latest version of stubs for mylib2
- ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_3/mylib2.so")
+ ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so")
// ... and not linking to the non-stub (impl) variant of mylib2
ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
@@ -903,7 +856,7 @@
func TestApexWithStubsWithMinSdkVersion(t *testing.T) {
t.Parallel()
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -976,8 +929,8 @@
mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex29").Rule("ld").Args["libFlags"]
- // Ensure that mylib is linking with the version 29 stubs for mylib2
- ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_29/mylib2.so")
+ // Ensure that mylib is linking with the latest version of stub for mylib2
+ ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so")
// ... and not linking to the non-stub (impl) variant of mylib2
ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
@@ -1000,12 +953,89 @@
})
}
+func TestApex_PlatformUsesLatestStubFromApex(t *testing.T) {
+ t.Parallel()
+ // myapex (Z)
+ // mylib -----------------.
+ // |
+ // otherapex (29) |
+ // libstub's versions: 29 Z current
+ // |
+ // <platform> |
+ // libplatform ----------------'
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ min_sdk_version: "Z", // non-final
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["libstub"],
+ apex_available: ["myapex"],
+ min_sdk_version: "Z",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ apex {
+ name: "otherapex",
+ key: "myapex.key",
+ native_shared_libs: ["libstub"],
+ min_sdk_version: "29",
+ }
+
+ cc_library {
+ name: "libstub",
+ srcs: ["mylib.cpp"],
+ stubs: {
+ versions: ["29", "Z", "current"],
+ },
+ apex_available: ["otherapex"],
+ min_sdk_version: "29",
+ }
+
+ // platform module depending on libstub from otherapex should use the latest stub("current")
+ cc_library {
+ name: "libplatform",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["libstub"],
+ }
+ `,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Platform_sdk_codename = proptools.StringPtr("Z")
+ variables.Platform_sdk_final = proptools.BoolPtr(false)
+ variables.Platform_version_active_codenames = []string{"Z"}
+ }),
+ )
+
+ // Ensure that mylib from myapex is built against the latest stub (current)
+ mylibCflags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"]
+ ensureContains(t, mylibCflags, "-D__LIBSTUB_API__=10000 ")
+ mylibLdflags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
+ ensureContains(t, mylibLdflags, "libstub/android_arm64_armv8-a_shared_current/libstub.so ")
+
+ // Ensure that libplatform is built against latest stub ("current") of mylib3 from the apex
+ libplatformCflags := ctx.ModuleForTests("libplatform", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+ ensureContains(t, libplatformCflags, "-D__LIBSTUB_API__=10000 ") // "current" maps to 10000
+ libplatformLdflags := ctx.ModuleForTests("libplatform", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"]
+ ensureContains(t, libplatformLdflags, "libstub/android_arm64_armv8-a_shared_current/libstub.so ")
+}
+
func TestApexWithExplicitStubsDependency(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex2",
key: "myapex2.key",
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -1077,13 +1107,9 @@
ensureNotContains(t, libFooStubsLdFlags, "libbar.so")
fullDepsInfo := strings.Split(ctx.ModuleForTests("myapex2", "android_common_myapex2_image").Output("depsinfo/fulllist.txt").Args["content"], "\\n")
- ensureListContains(t, fullDepsInfo, " mylib(minSdkVersion:(no version)) <- myapex2")
- ensureListContains(t, fullDepsInfo, " libbaz(minSdkVersion:(no version)) <- mylib")
ensureListContains(t, fullDepsInfo, " libfoo(minSdkVersion:(no version)) (external) <- mylib")
flatDepsInfo := strings.Split(ctx.ModuleForTests("myapex2", "android_common_myapex2_image").Output("depsinfo/flatlist.txt").Args["content"], "\\n")
- ensureListContains(t, flatDepsInfo, "mylib(minSdkVersion:(no version))")
- ensureListContains(t, flatDepsInfo, "libbaz(minSdkVersion:(no version))")
ensureListContains(t, flatDepsInfo, "libfoo(minSdkVersion:(no version)) (external)")
}
@@ -1096,11 +1122,12 @@
|
`------> libbar
*/
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -1156,13 +1183,15 @@
}
-func TestRuntimeApexShouldInstallHwasanIfLibcDependsOnIt(t *testing.T) {
- ctx, _ := testApex(t, "", func(fs map[string][]byte, config android.Config) {
- bp := `
+var prepareForTestOfRuntimeApexWithHwasan = android.GroupFixturePreparers(
+ cc.PrepareForTestWithCcBuildComponents,
+ PrepareForTestWithApexBuildComponents,
+ android.FixtureAddTextFile("bionic/apex/Android.bp", `
apex {
name: "com.android.runtime",
key: "com.android.runtime.key",
native_shared_libs: ["libc"],
+ updatable: false,
}
apex_key {
@@ -1170,7 +1199,12 @@
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
+ `),
+ android.FixtureAddFile("system/sepolicy/apex/com.android.runtime-file_contexts", nil),
+)
+func TestRuntimeApexShouldInstallHwasanIfLibcDependsOnIt(t *testing.T) {
+ result := android.GroupFixturePreparers(prepareForTestOfRuntimeApexWithHwasan).RunTestWithBp(t, `
cc_library {
name: "libc",
no_libcrt: true,
@@ -1197,12 +1231,8 @@
sanitize: {
never: true,
},
- }
- `
- // override bp to use hard-coded names: com.android.runtime and libc
- fs["Android.bp"] = []byte(bp)
- fs["system/sepolicy/apex/com.android.runtime-file_contexts"] = nil
- })
+ } `)
+ ctx := result.TestContext
ensureExactContents(t, ctx, "com.android.runtime", "android_common_hwasan_com.android.runtime_image", []string{
"lib64/bionic/libc.so",
@@ -1220,20 +1250,12 @@
}
func TestRuntimeApexShouldInstallHwasanIfHwaddressSanitized(t *testing.T) {
- ctx, _ := testApex(t, "", func(fs map[string][]byte, config android.Config) {
- bp := `
- apex {
- name: "com.android.runtime",
- key: "com.android.runtime.key",
- native_shared_libs: ["libc"],
- }
-
- apex_key {
- name: "com.android.runtime.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
-
+ result := android.GroupFixturePreparers(
+ prepareForTestOfRuntimeApexWithHwasan,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.SanitizeDevice = []string{"hwaddress"}
+ }),
+ ).RunTestWithBp(t, `
cc_library {
name: "libc",
no_libcrt: true,
@@ -1257,13 +1279,8 @@
never: true,
},
}
- `
- // override bp to use hard-coded names: com.android.runtime and libc
- fs["Android.bp"] = []byte(bp)
- fs["system/sepolicy/apex/com.android.runtime-file_contexts"] = nil
-
- config.TestProductVariables.SanitizeDevice = []string{"hwaddress"}
- })
+ `)
+ ctx := result.TestContext
ensureExactContents(t, ctx, "com.android.runtime", "android_common_hwasan_com.android.runtime_image", []string{
"lib64/bionic/libc.so",
@@ -1289,28 +1306,29 @@
shouldNotLink []string
}{
{
- name: "should link to the latest",
+ name: "unspecified version links to the latest",
minSdkVersion: "",
apexVariant: "apex10000",
- shouldLink: "30",
- shouldNotLink: []string{"29"},
+ shouldLink: "current",
+ shouldNotLink: []string{"29", "30"},
},
{
- name: "should link to llndk#29",
+ name: "always use the latest",
minSdkVersion: "min_sdk_version: \"29\",",
apexVariant: "apex29",
- shouldLink: "29",
- shouldNotLink: []string{"30"},
+ shouldLink: "current",
+ shouldNotLink: []string{"29", "30"},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
use_vendor: true,
native_shared_libs: ["mylib"],
+ updatable: false,
`+tc.minSdkVersion+`
}
@@ -1344,9 +1362,10 @@
name: "libbar.llndk",
symbol_file: "",
}
- `, func(fs map[string][]byte, config android.Config) {
- setUseVendorAllowListForTest(config, []string{"myapex"})
- }, withUnbundledBuild)
+ `,
+ setUseVendorAllowListForTest([]string{"myapex"}),
+ withUnbundledBuild,
+ )
// Ensure that LLNDK dep is not included
ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
@@ -1358,24 +1377,29 @@
ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"]))
ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libbar.so")
- mylibLdFlags := ctx.ModuleForTests("mylib", "android_vendor.VER_arm64_armv8-a_shared_"+tc.apexVariant).Rule("ld").Args["libFlags"]
- ensureContains(t, mylibLdFlags, "libbar/android_vendor.VER_arm64_armv8-a_shared_"+tc.shouldLink+"/libbar.so")
+ mylibLdFlags := ctx.ModuleForTests("mylib", "android_vendor.29_arm64_armv8-a_shared_"+tc.apexVariant).Rule("ld").Args["libFlags"]
+ ensureContains(t, mylibLdFlags, "libbar/android_vendor.29_arm64_armv8-a_shared_"+tc.shouldLink+"/libbar.so")
for _, ver := range tc.shouldNotLink {
- ensureNotContains(t, mylibLdFlags, "libbar/android_vendor.VER_arm64_armv8-a_shared_"+ver+"/libbar.so")
+ ensureNotContains(t, mylibLdFlags, "libbar/android_vendor.29_arm64_armv8-a_shared_"+ver+"/libbar.so")
}
- mylibCFlags := ctx.ModuleForTests("mylib", "android_vendor.VER_arm64_armv8-a_static_"+tc.apexVariant).Rule("cc").Args["cFlags"]
- ensureContains(t, mylibCFlags, "__LIBBAR_API__="+tc.shouldLink)
+ mylibCFlags := ctx.ModuleForTests("mylib", "android_vendor.29_arm64_armv8-a_static_"+tc.apexVariant).Rule("cc").Args["cFlags"]
+ ver := tc.shouldLink
+ if tc.shouldLink == "current" {
+ ver = strconv.Itoa(android.FutureApiLevelInt)
+ }
+ ensureContains(t, mylibCFlags, "__LIBBAR_API__="+ver)
})
}
}
func TestApexWithSystemLibsStubs(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib", "mylib_shared", "libdl", "libm"],
+ updatable: false,
}
apex_key {
@@ -1426,12 +1450,12 @@
// For dependency to libc
// Ensure that mylib is linking with the latest version of stubs
- ensureContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared_29/libc.so")
+ ensureContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared_current/libc.so")
// ... and not linking to the non-stub (impl) variant
ensureNotContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared/libc.so")
// ... Cflags from stub is correctly exported to mylib
- ensureContains(t, mylibCFlags, "__LIBC_API__=29")
- ensureContains(t, mylibSharedCFlags, "__LIBC_API__=29")
+ ensureContains(t, mylibCFlags, "__LIBC_API__=10000")
+ ensureContains(t, mylibSharedCFlags, "__LIBC_API__=10000")
// For dependency to libm
// Ensure that mylib is linking with the non-stub (impl) variant
@@ -1462,11 +1486,11 @@
}
func TestApexMinSdkVersion_NativeModulesShouldBeBuiltAgainstStubs(t *testing.T) {
- // there are three links between liba --> libz
- // 1) myapex -> libx -> liba -> libz : this should be #29 link, but fallback to #28
+ // there are three links between liba --> libz.
+ // 1) myapex -> libx -> liba -> libz : this should be #30 link
// 2) otherapex -> liby -> liba -> libz : this should be #30 link
// 3) (platform) -> liba -> libz : this should be non-stub link
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -1537,18 +1561,20 @@
}
// platform liba is linked to non-stub version
expectLink("liba", "shared", "libz", "shared")
- // liba in myapex is linked to #28
- expectLink("liba", "shared_apex29", "libz", "shared_28")
+ // liba in myapex is linked to current
+ expectLink("liba", "shared_apex29", "libz", "shared_current")
expectNoLink("liba", "shared_apex29", "libz", "shared_30")
+ expectNoLink("liba", "shared_apex29", "libz", "shared_28")
expectNoLink("liba", "shared_apex29", "libz", "shared")
- // liba in otherapex is linked to #30
- expectLink("liba", "shared_apex30", "libz", "shared_30")
+ // liba in otherapex is linked to current
+ expectLink("liba", "shared_apex30", "libz", "shared_current")
+ expectNoLink("liba", "shared_apex30", "libz", "shared_30")
expectNoLink("liba", "shared_apex30", "libz", "shared_28")
expectNoLink("liba", "shared_apex30", "libz", "shared")
}
func TestApexMinSdkVersion_SupportsCodeNames(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -1579,9 +1605,11 @@
versions: ["29", "R"],
},
}
- `, func(fs map[string][]byte, config android.Config) {
- config.TestProductVariables.Platform_version_active_codenames = []string{"R"}
- })
+ `,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Platform_version_active_codenames = []string{"R"}
+ }),
+ )
expectLink := func(from, from_variant, to, to_variant string) {
ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
@@ -1591,17 +1619,19 @@
ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
}
- expectLink("libx", "shared_apex10000", "libz", "shared_R")
+ expectLink("libx", "shared_apex10000", "libz", "shared_current")
+ expectNoLink("libx", "shared_apex10000", "libz", "shared_R")
expectNoLink("libx", "shared_apex10000", "libz", "shared_29")
expectNoLink("libx", "shared_apex10000", "libz", "shared")
}
func TestApexMinSdkVersion_DefaultsToLatest(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libx"],
+ updatable: false,
}
apex_key {
@@ -1636,17 +1666,19 @@
ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
}
- expectLink("libx", "shared_apex10000", "libz", "shared_2")
+ expectLink("libx", "shared_apex10000", "libz", "shared_current")
expectNoLink("libx", "shared_apex10000", "libz", "shared_1")
+ expectNoLink("libx", "shared_apex10000", "libz", "shared_2")
expectNoLink("libx", "shared_apex10000", "libz", "shared")
}
func TestPlatformUsesLatestStubsFromApexes(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libx"],
+ updatable: false,
}
apex_key {
@@ -1683,13 +1715,20 @@
ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
}
- expectLink("libz", "shared", "libx", "shared_2")
+ expectLink("libz", "shared", "libx", "shared_current")
+ expectNoLink("libz", "shared", "libx", "shared_2")
expectNoLink("libz", "shared", "libz", "shared_1")
expectNoLink("libz", "shared", "libz", "shared")
}
+var prepareForTestWithSantitizeHwaddress = android.FixtureModifyProductVariables(
+ func(variables android.FixtureProductVariables) {
+ variables.SanitizeDevice = []string{"hwaddress"}
+ },
+)
+
func TestQApexesUseLatestStubsInBundledBuildsAndHWASAN(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -1716,19 +1755,19 @@
versions: ["29", "30"],
},
}
- `, func(fs map[string][]byte, config android.Config) {
- config.TestProductVariables.SanitizeDevice = []string{"hwaddress"}
- })
+ `,
+ prepareForTestWithSantitizeHwaddress,
+ )
expectLink := func(from, from_variant, to, to_variant string) {
ld := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld")
libFlags := ld.Args["libFlags"]
ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
}
- expectLink("libx", "shared_hwasan_apex29", "libbar", "shared_30")
+ expectLink("libx", "shared_hwasan_apex29", "libbar", "shared_current")
}
func TestQTargetApexUsesStaticUnwinder(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -1751,45 +1790,10 @@
// ensure apex variant of c++ is linked with static unwinder
cm := ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared_apex29").Module().(*cc.Module)
- ensureListContains(t, cm.Properties.AndroidMkStaticLibs, "libgcc_stripped")
+ ensureListContains(t, cm.Properties.AndroidMkStaticLibs, "libunwind")
// note that platform variant is not.
cm = ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared").Module().(*cc.Module)
- ensureListNotContains(t, cm.Properties.AndroidMkStaticLibs, "libgcc_stripped")
-}
-
-func TestApexMinSdkVersion_ErrorIfIncompatibleStubs(t *testing.T) {
- testApexError(t, `"libz" .*: not found a version\(<=29\)`, `
- apex {
- name: "myapex",
- key: "myapex.key",
- native_shared_libs: ["libx"],
- min_sdk_version: "29",
- }
-
- apex_key {
- name: "myapex.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
-
- cc_library {
- name: "libx",
- shared_libs: ["libz"],
- system_shared_libs: [],
- stl: "none",
- apex_available: [ "myapex" ],
- min_sdk_version: "29",
- }
-
- cc_library {
- name: "libz",
- system_shared_libs: [],
- stl: "none",
- stubs: {
- versions: ["30"],
- },
- }
- `)
+ ensureListNotContains(t, cm.Properties.AndroidMkStaticLibs, "libunwind")
}
func TestApexMinSdkVersion_ErrorIfIncompatibleVersion(t *testing.T) {
@@ -1843,6 +1847,30 @@
min_sdk_version: "30",
}
`)
+
+ testApexError(t, `module "libfoo".*: should support min_sdk_version\(29\)`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ java_libs: ["libfoo"],
+ min_sdk_version: "29",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_import {
+ name: "libfoo",
+ jars: ["libfoo.jar"],
+ apex_available: [
+ "myapex",
+ ],
+ min_sdk_version: "30",
+ }
+ `)
}
func TestApexMinSdkVersion_Okay(t *testing.T) {
@@ -1880,7 +1908,10 @@
name: "libbar",
sdk_version: "current",
srcs: ["a.java"],
- static_libs: ["libbar_dep"],
+ static_libs: [
+ "libbar_dep",
+ "libbar_import_dep",
+ ],
apex_available: ["myapex"],
min_sdk_version: "29",
}
@@ -1892,6 +1923,13 @@
apex_available: ["myapex"],
min_sdk_version: "29",
}
+
+ java_import {
+ name: "libbar_import_dep",
+ jars: ["libbar.jar"],
+ apex_available: ["myapex"],
+ min_sdk_version: "29",
+ }
`)
}
@@ -1908,6 +1946,7 @@
name: "myapex",
java_libs: ["myjar"],
key: "myapex.key",
+ updatable: false,
}
apex_key {
name: "myapex.key",
@@ -1917,7 +1956,7 @@
java_library {
name: "myjar",
srcs: ["foo/bar/MyClass.java"],
- sdk_version: "core_platform",
+ sdk_version: "test_current",
apex_available: ["myapex"],
}
`,
@@ -1964,14 +2003,17 @@
java_library {
name: "myjar",
srcs: ["foo/bar/MyClass.java"],
- sdk_version: "core_platform",
+ sdk_version: "test_current",
apex_available: ["myapex"],
}
`,
},
{
- name: "Updatable apex with non-stable transitive dep",
- expectedError: "compiles against Android API, but dependency \"transitive-jar\" is compiling against non-public Android API.",
+ name: "Updatable apex with non-stable transitive dep",
+ // This is not actually detecting that the transitive dependency is unstable, rather it is
+ // detecting that the transitive dependency is building against a wider API surface than the
+ // module that depends on it is using.
+ expectedError: "compiles against Android API, but dependency \"transitive-jar\" is compiling against private API.",
bp: `
apex {
name: "myapex",
@@ -2089,7 +2131,7 @@
}
func TestApexMinSdkVersion_OkayEvenWhenDepIsNewer_IfItSatisfiesApexMinSdkVersion(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -2103,7 +2145,7 @@
private_key: "testkey.pem",
}
- // mylib in myapex will link to mylib2#29
+ // mylib in myapex will link to mylib2#current
// mylib in otherapex will link to mylib2(non-stub) in otherapex as well
cc_library {
name: "mylib",
@@ -2137,12 +2179,82 @@
libFlags := ld.Args["libFlags"]
ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
}
- expectLink("mylib", "shared_apex29", "mylib2", "shared_29")
+ expectLink("mylib", "shared_apex29", "mylib2", "shared_current")
expectLink("mylib", "shared_apex30", "mylib2", "shared_apex30")
}
+func TestApexMinSdkVersion_WorksWithSdkCodename(t *testing.T) {
+ withSAsActiveCodeNames := android.FixtureModifyProductVariables(
+ func(variables android.FixtureProductVariables) {
+ variables.Platform_sdk_codename = proptools.StringPtr("S")
+ variables.Platform_version_active_codenames = []string{"S"}
+ },
+ )
+ testApexError(t, `libbar.*: should support min_sdk_version\(S\)`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["libfoo"],
+ min_sdk_version: "S",
+ }
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ cc_library {
+ name: "libfoo",
+ shared_libs: ["libbar"],
+ apex_available: ["myapex"],
+ min_sdk_version: "29",
+ }
+ cc_library {
+ name: "libbar",
+ apex_available: ["myapex"],
+ }
+ `, withSAsActiveCodeNames)
+}
+
+func TestApexMinSdkVersion_WorksWithActiveCodenames(t *testing.T) {
+ withSAsActiveCodeNames := android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Platform_sdk_codename = proptools.StringPtr("S")
+ variables.Platform_version_active_codenames = []string{"S", "T"}
+ })
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["libfoo"],
+ min_sdk_version: "S",
+ }
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ cc_library {
+ name: "libfoo",
+ shared_libs: ["libbar"],
+ apex_available: ["myapex"],
+ min_sdk_version: "S",
+ }
+ cc_library {
+ name: "libbar",
+ stubs: {
+ symbol_file: "libbar.map.txt",
+ versions: ["30", "S", "T"],
+ },
+ }
+ `, withSAsActiveCodeNames)
+
+ // ensure libfoo is linked with current version of libbar stub
+ libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared_apex10000")
+ libFlags := libfoo.Rule("ld").Args["libFlags"]
+ ensureContains(t, libFlags, "android_arm64_armv8-a_shared_current/libbar.so")
+}
+
func TestFilesInSubDir(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -2150,6 +2262,7 @@
binaries: ["mybin"],
prebuilts: ["myetc"],
compile_multilib: "both",
+ updatable: false,
}
apex_key {
@@ -2204,7 +2317,7 @@
}
func TestFilesInSubDirWhenNativeBridgeEnabled(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -2216,6 +2329,7 @@
},
compile_multilib: "both",
native_bridge_supported: true,
+ updatable: false,
}
apex_key {
@@ -2262,12 +2376,13 @@
}
func TestUseVendor(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib"],
use_vendor: true,
+ updatable: false,
}
apex_key {
@@ -2294,9 +2409,9 @@
stl: "none",
apex_available: [ "myapex" ],
}
- `, func(fs map[string][]byte, config android.Config) {
- setUseVendorAllowListForTest(config, []string{"myapex"})
- })
+ `,
+ setUseVendorAllowListForTest([]string{"myapex"}),
+ )
inputsList := []string{}
for _, i := range ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().BuildParamsForTests() {
@@ -2307,8 +2422,8 @@
inputsString := strings.Join(inputsList, " ")
// ensure that the apex includes vendor variants of the direct and indirect deps
- ensureContains(t, inputsString, "android_vendor.VER_arm64_armv8-a_shared_apex10000/mylib.so")
- ensureContains(t, inputsString, "android_vendor.VER_arm64_armv8-a_shared_apex10000/mylib2.so")
+ ensureContains(t, inputsString, "android_vendor.29_arm64_armv8-a_shared_apex10000/mylib.so")
+ ensureContains(t, inputsString, "android_vendor.29_arm64_armv8-a_shared_apex10000/mylib2.so")
// ensure that the apex does not include core variants
ensureNotContains(t, inputsString, "android_arm64_armv8-a_shared_apex10000/mylib.so")
@@ -2327,24 +2442,25 @@
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
- `, func(fs map[string][]byte, config android.Config) {
- setUseVendorAllowListForTest(config, []string{""})
- })
+ `,
+ setUseVendorAllowListForTest([]string{""}),
+ )
// no error with allow list
testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
use_vendor: true,
+ updatable: false,
}
apex_key {
name: "myapex.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
- `, func(fs map[string][]byte, config android.Config) {
- setUseVendorAllowListForTest(config, []string{"myapex"})
- })
+ `,
+ setUseVendorAllowListForTest([]string{"myapex"}),
+ )
}
func TestUseVendorFailsIfNotVendorAvailable(t *testing.T) {
@@ -2354,6 +2470,7 @@
key: "myapex.key",
native_shared_libs: ["mylib"],
use_vendor: true,
+ updatable: false,
}
apex_key {
@@ -2372,12 +2489,13 @@
}
func TestVendorApex(t *testing.T) {
- ctx, config := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
binaries: ["mybin"],
vendor: true,
+ updatable: false,
}
apex_key {
name: "myapex.key",
@@ -2403,13 +2521,14 @@
})
apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+ data := android.AndroidMkDataForTest(t, ctx, apexBundle)
name := apexBundle.BaseModuleName()
prefix := "TARGET_"
var builder strings.Builder
data.Custom(&builder, name, prefix, "", data)
- androidMk := builder.String()
- ensureContains(t, androidMk, `LOCAL_MODULE_PATH := /tmp/target/product/test_device/vendor/apex`)
+ androidMk := android.StringRelativeToTop(ctx.Config(), builder.String())
+ installPath := "out/target/product/test_device/vendor/apex"
+ ensureContains(t, androidMk, "LOCAL_MODULE_PATH := "+installPath)
apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"])
@@ -2417,13 +2536,14 @@
}
func TestVendorApex_use_vndk_as_stable(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
binaries: ["mybin"],
vendor: true,
use_vndk_as_stable: true,
+ updatable: false,
}
apex_key {
name: "myapex.key",
@@ -2449,15 +2569,15 @@
}
`)
- vendorVariant := "android_vendor.VER_arm64_armv8-a"
+ vendorVariant := "android_vendor.29_arm64_armv8-a"
ldRule := ctx.ModuleForTests("mybin", vendorVariant+"_apex10000").Rule("ld")
libs := names(ldRule.Args["libFlags"])
// VNDK libs(libvndk/libc++) as they are
- ensureListContains(t, libs, buildDir+"/.intermediates/libvndk/"+vendorVariant+"_shared/libvndk.so")
- ensureListContains(t, libs, buildDir+"/.intermediates/libc++/"+vendorVariant+"_shared/libc++.so")
+ ensureListContains(t, libs, "out/soong/.intermediates/libvndk/"+vendorVariant+"_shared/libvndk.so")
+ ensureListContains(t, libs, "out/soong/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"libc++/"+vendorVariant+"_shared/libc++.so")
// non-stable Vendor libs as APEX variants
- ensureListContains(t, libs, buildDir+"/.intermediates/libvendor/"+vendorVariant+"_shared_apex10000/libvendor.so")
+ ensureListContains(t, libs, "out/soong/.intermediates/libvendor/"+vendorVariant+"_shared_apex10000/libvendor.so")
// VNDK libs are not included when use_vndk_as_stable: true
ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
@@ -2470,6 +2590,41 @@
ensureListContains(t, requireNativeLibs, ":vndk")
}
+func TestProductVariant(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ updatable: false,
+ product_specific: true,
+ binaries: ["foo"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_binary {
+ name: "foo",
+ product_available: true,
+ apex_available: ["myapex"],
+ srcs: ["foo.cpp"],
+ }
+ `, android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.ProductVndkVersion = proptools.StringPtr("current")
+ }),
+ )
+
+ cflags := strings.Fields(
+ ctx.ModuleForTests("foo", "android_product.29_arm64_armv8-a_apex10000").Rule("cc").Args["cFlags"])
+ ensureListContains(t, cflags, "-D__ANDROID_VNDK__")
+ ensureListContains(t, cflags, "-D__ANDROID_APEX__")
+ ensureListContains(t, cflags, "-D__ANDROID_PRODUCT__")
+ ensureListNotContains(t, cflags, "-D__ANDROID_VENDOR__")
+}
+
func TestApex_withPrebuiltFirmware(t *testing.T) {
testCases := []struct {
name string
@@ -2480,11 +2635,12 @@
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
prebuilts: ["myfirmware"],
+ updatable: false,
`+tc.additionalProp+`
}
apex_key {
@@ -2507,12 +2663,13 @@
}
func TestAndroidMk_UseVendorRequired(t *testing.T) {
- ctx, config := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
use_vendor: true,
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -2526,12 +2683,12 @@
vendor_available: true,
apex_available: ["myapex"],
}
- `, func(fs map[string][]byte, config android.Config) {
- setUseVendorAllowListForTest(config, []string{"myapex"})
- })
+ `,
+ setUseVendorAllowListForTest([]string{"myapex"}),
+ )
apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+ data := android.AndroidMkDataForTest(t, ctx, apexBundle)
name := apexBundle.BaseModuleName()
prefix := "TARGET_"
var builder strings.Builder
@@ -2541,12 +2698,13 @@
}
func TestAndroidMk_VendorApexRequired(t *testing.T) {
- ctx, config := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
vendor: true,
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -2562,7 +2720,7 @@
`)
apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+ data := android.AndroidMkDataForTest(t, ctx, apexBundle)
name := apexBundle.BaseModuleName()
prefix := "TARGET_"
var builder strings.Builder
@@ -2572,12 +2730,13 @@
}
func TestAndroidMkWritesCommonProperties(t *testing.T) {
- ctx, config := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
vintf_fragments: ["fragment.xml"],
init_rc: ["init.rc"],
+ updatable: false,
}
apex_key {
name: "myapex.key",
@@ -2590,22 +2749,23 @@
`)
apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+ data := android.AndroidMkDataForTest(t, ctx, apexBundle)
name := apexBundle.BaseModuleName()
prefix := "TARGET_"
var builder strings.Builder
data.Custom(&builder, name, prefix, "", data)
androidMk := builder.String()
ensureContains(t, androidMk, "LOCAL_VINTF_FRAGMENTS := fragment.xml\n")
- ensureContains(t, androidMk, "LOCAL_INIT_RC := init.rc\n")
+ ensureContains(t, androidMk, "LOCAL_FULL_INIT_RC := init.rc\n")
}
func TestStaticLinking(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -2645,13 +2805,14 @@
}
func TestKeys(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex_keytest",
key: "myapex.key",
certificate: ":myapex.certificate",
native_shared_libs: ["mylib"],
file_contexts: ":myapex-file_contexts",
+ updatable: false,
}
cc_library {
@@ -2702,10 +2863,11 @@
func TestCertificate(t *testing.T) {
t.Run("if unspecified, it defaults to DefaultAppCertificate", func(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
+ updatable: false,
}
apex_key {
name: "myapex.key",
@@ -2719,11 +2881,12 @@
}
})
t.Run("override when unspecified", func(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex_keytest",
key: "myapex.key",
file_contexts: ":myapex-file_contexts",
+ updatable: false,
}
apex_key {
name: "myapex.key",
@@ -2741,11 +2904,12 @@
}
})
t.Run("if specified as :module, it respects the prop", func(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
certificate: ":myapex.certificate",
+ updatable: false,
}
apex_key {
name: "myapex.key",
@@ -2763,12 +2927,13 @@
}
})
t.Run("override when specifiec as <:module>", func(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex_keytest",
key: "myapex.key",
file_contexts: ":myapex-file_contexts",
certificate: ":myapex.certificate",
+ updatable: false,
}
apex_key {
name: "myapex.key",
@@ -2786,11 +2951,12 @@
}
})
t.Run("if specified as name, finds it from DefaultDevKeyDir", func(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
certificate: "testkey",
+ updatable: false,
}
apex_key {
name: "myapex.key",
@@ -2804,12 +2970,13 @@
}
})
t.Run("override when specified as <name>", func(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex_keytest",
key: "myapex.key",
file_contexts: ":myapex-file_contexts",
certificate: "testkey",
+ updatable: false,
}
apex_key {
name: "myapex.key",
@@ -2829,11 +2996,12 @@
}
func TestMacro(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib", "mylib2"],
+ updatable: false,
}
apex {
@@ -2956,11 +3124,12 @@
}
func TestHeaderLibsDependency(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -3099,14 +3268,15 @@
}
func TestVndkApexCurrent(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex_vndk {
- name: "myapex",
- key: "myapex.key",
+ name: "com.android.vndk.current",
+ key: "com.android.vndk.current.key",
+ updatable: false,
}
apex_key {
- name: "myapex.key",
+ name: "com.android.vndk.current.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
@@ -3121,7 +3291,7 @@
},
system_shared_libs: [],
stl: "none",
- apex_available: [ "myapex" ],
+ apex_available: [ "com.android.vndk.current" ],
}
cc_library {
@@ -3135,33 +3305,35 @@
},
system_shared_libs: [],
stl: "none",
- apex_available: [ "myapex" ],
+ apex_available: [ "com.android.vndk.current" ],
}
`+vndkLibrariesTxtFiles("current"))
- ensureExactContents(t, ctx, "myapex", "android_common_image", []string{
+ ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{
"lib/libvndk.so",
"lib/libvndksp.so",
"lib/libc++.so",
"lib64/libvndk.so",
"lib64/libvndksp.so",
"lib64/libc++.so",
- "etc/llndk.libraries.VER.txt",
- "etc/vndkcore.libraries.VER.txt",
- "etc/vndksp.libraries.VER.txt",
- "etc/vndkprivate.libraries.VER.txt",
+ "etc/llndk.libraries.29.txt",
+ "etc/vndkcore.libraries.29.txt",
+ "etc/vndksp.libraries.29.txt",
+ "etc/vndkprivate.libraries.29.txt",
+ "etc/vndkproduct.libraries.29.txt",
})
}
func TestVndkApexWithPrebuilt(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex_vndk {
- name: "myapex",
- key: "myapex.key",
+ name: "com.android.vndk.current",
+ key: "com.android.vndk.current.key",
+ updatable: false,
}
apex_key {
- name: "myapex.key",
+ name: "com.android.vndk.current.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
@@ -3176,7 +3348,7 @@
},
system_shared_libs: [],
stl: "none",
- apex_available: [ "myapex" ],
+ apex_available: [ "com.android.vndk.current" ],
}
cc_prebuilt_library_shared {
@@ -3195,15 +3367,14 @@
},
system_shared_libs: [],
stl: "none",
- apex_available: [ "myapex" ],
+ apex_available: [ "com.android.vndk.current" ],
}
`+vndkLibrariesTxtFiles("current"),
withFiles(map[string][]byte{
"libvndk.so": nil,
"libvndk.arm.so": nil,
}))
-
- ensureExactContents(t, ctx, "myapex", "android_common_image", []string{
+ ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{
"lib/libvndk.so",
"lib/libvndk.arm.so",
"lib64/libvndk.so",
@@ -3216,15 +3387,15 @@
func vndkLibrariesTxtFiles(vers ...string) (result string) {
for _, v := range vers {
if v == "current" {
- for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate"} {
+ for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate", "vndkproduct"} {
result += `
- vndk_libraries_txt {
+ ` + txt + `_libraries_txt {
name: "` + txt + `.libraries.txt",
}
`
}
} else {
- for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate"} {
+ for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate", "vndkproduct"} {
result += `
prebuilt_etc {
name: "` + txt + `.libraries.` + v + `.txt",
@@ -3238,12 +3409,13 @@
}
func TestVndkApexVersion(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex_vndk {
- name: "myapex_v27",
+ name: "com.android.vndk.v27",
key: "myapex.key",
file_contexts: ":myapex-file_contexts",
vndk_version: "27",
+ updatable: false,
}
apex_key {
@@ -3269,7 +3441,7 @@
srcs: ["libvndk27_arm64.so"],
},
},
- apex_available: [ "myapex_v27" ],
+ apex_available: [ "com.android.vndk.v27" ],
}
vndk_prebuilt_shared {
@@ -3298,73 +3470,27 @@
"libvndk27_x86_64.so": nil,
}))
- ensureExactContents(t, ctx, "myapex_v27", "android_common_image", []string{
+ ensureExactContents(t, ctx, "com.android.vndk.v27", "android_common_image", []string{
"lib/libvndk27_arm.so",
"lib64/libvndk27_arm64.so",
"etc/*",
})
}
-func TestVndkApexErrorWithDuplicateVersion(t *testing.T) {
- testApexError(t, `module "myapex_v27.*" .*: vndk_version: 27 is already defined in "myapex_v27.*"`, `
- apex_vndk {
- name: "myapex_v27",
- key: "myapex.key",
- file_contexts: ":myapex-file_contexts",
- vndk_version: "27",
- }
- apex_vndk {
- name: "myapex_v27_other",
- key: "myapex.key",
- file_contexts: ":myapex-file_contexts",
- vndk_version: "27",
- }
-
- apex_key {
- name: "myapex.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
-
- cc_library {
- name: "libvndk",
- srcs: ["mylib.cpp"],
- vendor_available: true,
- product_available: true,
- vndk: {
- enabled: true,
- },
- system_shared_libs: [],
- stl: "none",
- }
-
- vndk_prebuilt_shared {
- name: "libvndk",
- version: "27",
- vendor_available: true,
- product_available: true,
- vndk: {
- enabled: true,
- },
- srcs: ["libvndk.so"],
- }
- `, withFiles(map[string][]byte{
- "libvndk.so": nil,
- }))
-}
-
func TestVndkApexNameRule(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex_vndk {
- name: "myapex",
+ name: "com.android.vndk.current",
key: "myapex.key",
file_contexts: ":myapex-file_contexts",
+ updatable: false,
}
apex_vndk {
- name: "myapex_v28",
+ name: "com.android.vndk.v28",
key: "myapex.key",
file_contexts: ":myapex-file_contexts",
vndk_version: "28",
+ updatable: false,
}
apex_key {
name: "myapex.key",
@@ -3380,20 +3506,21 @@
}
}
- assertApexName("com.android.vndk.vVER", "myapex")
- assertApexName("com.android.vndk.v28", "myapex_v28")
+ assertApexName("com.android.vndk.v29", "com.android.vndk.current")
+ assertApexName("com.android.vndk.v28", "com.android.vndk.v28")
}
func TestVndkApexSkipsNativeBridgeSupportedModules(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex_vndk {
- name: "myapex",
- key: "myapex.key",
+ name: "com.android.vndk.current",
+ key: "com.android.vndk.current.key",
file_contexts: ":myapex-file_contexts",
+ updatable: false,
}
apex_key {
- name: "myapex.key",
+ name: "com.android.vndk.current.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
@@ -3410,11 +3537,12 @@
},
system_shared_libs: [],
stl: "none",
- apex_available: [ "myapex" ],
+ apex_available: [ "com.android.vndk.current" ],
}
- `+vndkLibrariesTxtFiles("current"), withNativeBridgeEnabled)
+ `+vndkLibrariesTxtFiles("current"),
+ withNativeBridgeEnabled)
- ensureExactContents(t, ctx, "myapex", "android_common_image", []string{
+ ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{
"lib/libvndk.so",
"lib64/libvndk.so",
"lib/libc++.so",
@@ -3424,16 +3552,16 @@
}
func TestVndkApexDoesntSupportNativeBridgeSupported(t *testing.T) {
- testApexError(t, `module "myapex" .*: native_bridge_supported: .* doesn't support native bridge binary`, `
+ testApexError(t, `module "com.android.vndk.current" .*: native_bridge_supported: .* doesn't support native bridge binary`, `
apex_vndk {
- name: "myapex",
- key: "myapex.key",
+ name: "com.android.vndk.current",
+ key: "com.android.vndk.current.key",
file_contexts: ":myapex-file_contexts",
native_bridge_supported: true,
}
apex_key {
- name: "myapex.key",
+ name: "com.android.vndk.current.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
@@ -3455,12 +3583,13 @@
}
func TestVndkApexWithBinder32(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex_vndk {
- name: "myapex_v27",
+ name: "com.android.vndk.v27",
key: "myapex.key",
file_contexts: ":myapex-file_contexts",
vndk_version: "27",
+ updatable: false,
}
apex_key {
@@ -3500,7 +3629,7 @@
srcs: ["libvndk27binder32.so"],
}
},
- apex_available: [ "myapex_v27" ],
+ apex_available: [ "com.android.vndk.v27" ],
}
`+vndkLibrariesTxtFiles("27"),
withFiles(map[string][]byte{
@@ -3516,22 +3645,23 @@
}),
)
- ensureExactContents(t, ctx, "myapex_v27", "android_common_image", []string{
+ ensureExactContents(t, ctx, "com.android.vndk.v27", "android_common_image", []string{
"lib/libvndk27binder32.so",
"etc/*",
})
}
func TestVndkApexShouldNotProvideNativeLibs(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex_vndk {
- name: "myapex",
- key: "myapex.key",
+ name: "com.android.vndk.current",
+ key: "com.android.vndk.current.key",
file_contexts: ":myapex-file_contexts",
+ updatable: false,
}
apex_key {
- name: "myapex.key",
+ name: "com.android.vndk.current.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
@@ -3552,19 +3682,20 @@
"libz.map.txt": nil,
}))
- apexManifestRule := ctx.ModuleForTests("myapex", "android_common_image").Rule("apexManifestRule")
+ apexManifestRule := ctx.ModuleForTests("com.android.vndk.current", "android_common_image").Rule("apexManifestRule")
provideNativeLibs := names(apexManifestRule.Args["provideNativeLibs"])
ensureListEmpty(t, provideNativeLibs)
}
func TestDependenciesInApexManifest(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex_nodep",
key: "myapex.key",
native_shared_libs: ["lib_nodep"],
compile_multilib: "both",
file_contexts: ":myapex-file_contexts",
+ updatable: false,
}
apex {
@@ -3573,6 +3704,7 @@
native_shared_libs: ["lib_dep"],
compile_multilib: "both",
file_contexts: ":myapex-file_contexts",
+ updatable: false,
}
apex {
@@ -3581,6 +3713,7 @@
native_shared_libs: ["libfoo"],
compile_multilib: "both",
file_contexts: ":myapex-file_contexts",
+ updatable: false,
}
apex {
@@ -3589,6 +3722,7 @@
native_shared_libs: ["lib_dep", "libfoo"],
compile_multilib: "both",
file_contexts: ":myapex-file_contexts",
+ updatable: false,
}
apex_key {
@@ -3662,12 +3796,13 @@
}
func TestApexName(t *testing.T) {
- ctx, config := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
apex_name: "com.android.myapex",
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -3695,7 +3830,7 @@
ensureContains(t, apexRule.Args["opt_flags"], "--do_not_check_keyname")
apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+ data := android.AndroidMkDataForTest(t, ctx, apexBundle)
name := apexBundle.BaseModuleName()
prefix := "TARGET_"
var builder strings.Builder
@@ -3706,11 +3841,12 @@
}
func TestNonTestApex(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib_common"],
+ updatable: false,
}
apex_key {
@@ -3758,11 +3894,12 @@
}
func TestTestApex(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex_test {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib_common_test"],
+ updatable: false,
}
apex_key {
@@ -3806,10 +3943,11 @@
}
func TestApexWithTarget(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
+ updatable: false,
multilib: {
first: {
native_shared_libs: ["mylib_common"],
@@ -3896,10 +4034,11 @@
}
func TestApexWithArch(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
+ updatable: false,
arch: {
arm64: {
native_shared_libs: ["mylib.arm64"],
@@ -3954,11 +4093,12 @@
}
func TestApexWithShBinary(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
binaries: ["myscript"],
+ updatable: false,
}
apex_key {
@@ -3994,10 +4134,11 @@
}
for _, tc := range testcases {
t.Run(tc.propName+":"+tc.parition, func(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
+ updatable: false,
`+tc.propName+`
}
@@ -4009,15 +4150,15 @@
`)
apex := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- expected := buildDir + "/target/product/test_device/" + tc.parition + "/apex"
- actual := apex.installDir.String()
+ expected := "out/soong/target/product/test_device/" + tc.parition + "/apex"
+ actual := apex.installDir.RelativeToTop().String()
if actual != expected {
t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
}
flattened := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
- expected = buildDir + "/target/product/test_device/" + tc.flattenedPartition + "/apex"
- actual = flattened.installDir.String()
+ expected = "out/soong/target/product/test_device/" + tc.flattenedPartition + "/apex"
+ actual = flattened.installDir.RelativeToTop().String()
if actual != expected {
t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
}
@@ -4026,10 +4167,11 @@
}
func TestFileContexts_FindInDefaultLocationIfNotSet(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
+ updatable: false,
}
apex_key {
@@ -4049,6 +4191,7 @@
name: "myapex",
key: "myapex.key",
file_contexts: "my_own_file_contexts",
+ updatable: false,
}
apex_key {
@@ -4068,6 +4211,7 @@
key: "myapex.key",
product_specific: true,
file_contexts: "product_specific_file_contexts",
+ updatable: false,
}
apex_key {
@@ -4077,12 +4221,13 @@
}
`)
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
product_specific: true,
file_contexts: "product_specific_file_contexts",
+ updatable: false,
}
apex_key {
@@ -4099,12 +4244,13 @@
}
func TestFileContexts_SetViaFileGroup(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
product_specific: true,
file_contexts: ":my-file-contexts",
+ updatable: false,
}
apex_key {
@@ -4126,7 +4272,7 @@
}
func TestApexKeyFromOtherModule(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex_key {
name: "myapex.key",
public_key: ":my.avbpubkey",
@@ -4159,7 +4305,7 @@
}
func TestPrebuilt(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
prebuilt_apex {
name: "myapex",
arch: {
@@ -4181,8 +4327,16 @@
}
}
+func TestPrebuiltMissingSrc(t *testing.T) {
+ testApexError(t, `module "myapex" variant "android_common".*: prebuilt_apex does not support "arm64_armv8-a"`, `
+ prebuilt_apex {
+ name: "myapex",
+ }
+ `)
+}
+
func TestPrebuiltFilenameOverride(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
prebuilt_apex {
name: "myapex",
src: "myapex-arm.apex",
@@ -4199,7 +4353,7 @@
}
func TestPrebuiltOverrides(t *testing.T) {
- ctx, config := testApex(t, `
+ ctx := testApex(t, `
prebuilt_apex {
name: "myapex.prebuilt",
src: "myapex-arm.apex",
@@ -4212,17 +4366,536 @@
p := ctx.ModuleForTests("myapex.prebuilt", "android_common").Module().(*Prebuilt)
expected := []string{"myapex"}
- actual := android.AndroidMkEntriesForTest(t, config, "", p)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
+ actual := android.AndroidMkEntriesForTest(t, ctx, p)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Incorrect LOCAL_OVERRIDES_MODULES value '%s', expected '%s'", actual, expected)
}
}
+// These tests verify that the prebuilt_apex/deapexer to java_import wiring allows for the
+// propagation of paths to dex implementation jars from the former to the latter.
+func TestPrebuiltExportDexImplementationJars(t *testing.T) {
+ transform := android.NullFixturePreparer
+
+ checkDexJarBuildPath := func(t *testing.T, ctx *android.TestContext, name string) {
+ // Make sure the import has been given the correct path to the dex jar.
+ p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency)
+ dexJarBuildPath := p.DexJarBuildPath()
+ stem := android.RemoveOptionalPrebuiltPrefix(name)
+ if expected, actual := ".intermediates/myapex.deapexer/android_common/deapexer/javalib/"+stem+".jar", android.NormalizePathForTesting(dexJarBuildPath); actual != expected {
+ t.Errorf("Incorrect DexJarBuildPath value '%s', expected '%s'", actual, expected)
+ }
+ }
+
+ ensureNoSourceVariant := func(t *testing.T, ctx *android.TestContext, name string) {
+ // Make sure that an apex variant is not created for the source module.
+ if expected, actual := []string{"android_common"}, ctx.ModuleVariantsForTests(name); !reflect.DeepEqual(expected, actual) {
+ t.Errorf("invalid set of variants for %q: expected %q, found %q", "libfoo", expected, actual)
+ }
+ }
+
+ t.Run("prebuilt only", func(t *testing.T) {
+ bp := `
+ prebuilt_apex {
+ name: "myapex",
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_java_libs: ["libfoo", "libbar"],
+ }
+
+ java_import {
+ name: "libfoo",
+ jars: ["libfoo.jar"],
+ }
+
+ java_sdk_library_import {
+ name: "libbar",
+ public: {
+ jars: ["libbar.jar"],
+ },
+ }
+ `
+
+ // Make sure that dexpreopt can access dex implementation files from the prebuilt.
+ ctx := testDexpreoptWithApexes(t, bp, "", transform)
+
+ // Make sure that the deapexer has the correct input APEX.
+ deapexer := ctx.ModuleForTests("myapex.deapexer", "android_common")
+ rule := deapexer.Rule("deapexer")
+ if expected, actual := []string{"myapex-arm64.apex"}, android.NormalizePathsForTesting(rule.Implicits); !reflect.DeepEqual(expected, actual) {
+ t.Errorf("expected: %q, found: %q", expected, actual)
+ }
+
+ // Make sure that the prebuilt_apex has the correct input APEX.
+ prebuiltApex := ctx.ModuleForTests("myapex", "android_common")
+ rule = prebuiltApex.Rule("android/soong/android.Cp")
+ if expected, actual := "myapex-arm64.apex", android.NormalizePathForTesting(rule.Input); !reflect.DeepEqual(expected, actual) {
+ t.Errorf("expected: %q, found: %q", expected, actual)
+ }
+
+ checkDexJarBuildPath(t, ctx, "libfoo")
+
+ checkDexJarBuildPath(t, ctx, "libbar")
+ })
+
+ t.Run("prebuilt with source preferred", func(t *testing.T) {
+
+ bp := `
+ prebuilt_apex {
+ name: "myapex",
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_java_libs: ["libfoo", "libbar"],
+ }
+
+ java_import {
+ name: "libfoo",
+ jars: ["libfoo.jar"],
+ }
+
+ java_library {
+ name: "libfoo",
+ }
+
+ java_sdk_library_import {
+ name: "libbar",
+ public: {
+ jars: ["libbar.jar"],
+ },
+ }
+
+ java_sdk_library {
+ name: "libbar",
+ srcs: ["foo/bar/MyClass.java"],
+ unsafe_ignore_missing_latest_api: true,
+ }
+ `
+
+ // Make sure that dexpreopt can access dex implementation files from the prebuilt.
+ ctx := testDexpreoptWithApexes(t, bp, "", transform)
+
+ checkDexJarBuildPath(t, ctx, "prebuilt_libfoo")
+ ensureNoSourceVariant(t, ctx, "libfoo")
+
+ checkDexJarBuildPath(t, ctx, "prebuilt_libbar")
+ ensureNoSourceVariant(t, ctx, "libbar")
+ })
+
+ t.Run("prebuilt preferred with source", func(t *testing.T) {
+ bp := `
+ prebuilt_apex {
+ name: "myapex",
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_java_libs: ["libfoo", "libbar"],
+ }
+
+ java_import {
+ name: "libfoo",
+ prefer: true,
+ jars: ["libfoo.jar"],
+ }
+
+ java_library {
+ name: "libfoo",
+ }
+
+ java_sdk_library_import {
+ name: "libbar",
+ prefer: true,
+ public: {
+ jars: ["libbar.jar"],
+ },
+ }
+
+ java_sdk_library {
+ name: "libbar",
+ srcs: ["foo/bar/MyClass.java"],
+ unsafe_ignore_missing_latest_api: true,
+ }
+ `
+
+ // Make sure that dexpreopt can access dex implementation files from the prebuilt.
+ ctx := testDexpreoptWithApexes(t, bp, "", transform)
+
+ checkDexJarBuildPath(t, ctx, "prebuilt_libfoo")
+ ensureNoSourceVariant(t, ctx, "libfoo")
+
+ checkDexJarBuildPath(t, ctx, "prebuilt_libbar")
+ ensureNoSourceVariant(t, ctx, "libbar")
+ })
+}
+
+func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) {
+ preparer := java.FixtureConfigureBootJars("myapex:libfoo", "myapex:libbar")
+
+ checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) {
+ t.Helper()
+ s := ctx.SingletonForTests("dex_bootjars")
+ foundLibfooJar := false
+ base := stem + ".jar"
+ for _, output := range s.AllOutputs() {
+ if filepath.Base(output) == base {
+ foundLibfooJar = true
+ buildRule := s.Output(output)
+ android.AssertStringEquals(t, "boot dex jar path", bootDexJarPath, buildRule.Input.String())
+ }
+ }
+ if !foundLibfooJar {
+ t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs %q", android.StringPathsRelativeToTop(ctx.Config().BuildDir(), s.AllOutputs()))
+ }
+ }
+
+ checkHiddenAPIIndexInputs := func(t *testing.T, ctx *android.TestContext, expectedInputs string) {
+ t.Helper()
+ platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common")
+ indexRule := platformBootclasspath.Rule("platform-bootclasspath-monolithic-hiddenapi-index")
+ java.CheckHiddenAPIRuleInputs(t, expectedInputs, indexRule)
+ }
+
+ t.Run("prebuilt only", func(t *testing.T) {
+ bp := `
+ prebuilt_apex {
+ name: "myapex",
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_java_libs: ["libfoo", "libbar"],
+ }
+
+ java_import {
+ name: "libfoo",
+ jars: ["libfoo.jar"],
+ apex_available: ["myapex"],
+ }
+
+ java_sdk_library_import {
+ name: "libbar",
+ public: {
+ jars: ["libbar.jar"],
+ },
+ apex_available: ["myapex"],
+ }
+ `
+
+ ctx := testDexpreoptWithApexes(t, bp, "", preparer)
+ checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+ checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+
+ // Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
+ checkHiddenAPIIndexInputs(t, ctx, `
+.intermediates/libbar/android_common_myapex/hiddenapi/index.csv
+.intermediates/libfoo/android_common_myapex/hiddenapi/index.csv
+`)
+ })
+
+ t.Run("apex_set only", func(t *testing.T) {
+ bp := `
+ apex_set {
+ name: "myapex",
+ set: "myapex.apks",
+ exported_java_libs: ["libfoo", "libbar"],
+ }
+
+ java_import {
+ name: "libfoo",
+ jars: ["libfoo.jar"],
+ apex_available: ["myapex"],
+ }
+
+ java_sdk_library_import {
+ name: "libbar",
+ public: {
+ jars: ["libbar.jar"],
+ },
+ apex_available: ["myapex"],
+ }
+ `
+
+ ctx := testDexpreoptWithApexes(t, bp, "", preparer)
+ checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+ checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+
+ // Make sure that the dex file from the apex_set contributes to the hiddenapi index file.
+ checkHiddenAPIIndexInputs(t, ctx, `
+.intermediates/libbar/android_common_myapex/hiddenapi/index.csv
+.intermediates/libfoo/android_common_myapex/hiddenapi/index.csv
+`)
+ })
+
+ t.Run("prebuilt with source library preferred", func(t *testing.T) {
+ bp := `
+ prebuilt_apex {
+ name: "myapex",
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_java_libs: ["libfoo", "libbar"],
+ }
+
+ java_import {
+ name: "libfoo",
+ jars: ["libfoo.jar"],
+ apex_available: ["myapex"],
+ }
+
+ java_library {
+ name: "libfoo",
+ srcs: ["foo/bar/MyClass.java"],
+ apex_available: ["myapex"],
+ }
+
+ java_sdk_library_import {
+ name: "libbar",
+ public: {
+ jars: ["libbar.jar"],
+ },
+ apex_available: ["myapex"],
+ }
+
+ java_sdk_library {
+ name: "libbar",
+ srcs: ["foo/bar/MyClass.java"],
+ unsafe_ignore_missing_latest_api: true,
+ apex_available: ["myapex"],
+ }
+ `
+
+ // In this test the source (java_library) libfoo is active since the
+ // prebuilt (java_import) defaults to prefer:false. However the
+ // prebuilt_apex module always depends on the prebuilt, and so it doesn't
+ // find the dex boot jar in it. We either need to disable the source libfoo
+ // or make the prebuilt libfoo preferred.
+ testDexpreoptWithApexes(t, bp, "failed to find a dex jar path for module 'libfoo'", preparer)
+ })
+
+ t.Run("prebuilt library preferred with source", func(t *testing.T) {
+ bp := `
+ prebuilt_apex {
+ name: "myapex",
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_java_libs: ["libfoo", "libbar"],
+ }
+
+ java_import {
+ name: "libfoo",
+ prefer: true,
+ jars: ["libfoo.jar"],
+ apex_available: ["myapex"],
+ }
+
+ java_library {
+ name: "libfoo",
+ srcs: ["foo/bar/MyClass.java"],
+ apex_available: ["myapex"],
+ }
+
+ java_sdk_library_import {
+ name: "libbar",
+ prefer: true,
+ public: {
+ jars: ["libbar.jar"],
+ },
+ apex_available: ["myapex"],
+ }
+
+ java_sdk_library {
+ name: "libbar",
+ srcs: ["foo/bar/MyClass.java"],
+ unsafe_ignore_missing_latest_api: true,
+ apex_available: ["myapex"],
+ }
+ `
+
+ ctx := testDexpreoptWithApexes(t, bp, "", preparer)
+ checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+ checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+
+ // Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
+ checkHiddenAPIIndexInputs(t, ctx, `
+.intermediates/prebuilt_libbar/android_common_myapex/hiddenapi/index.csv
+.intermediates/prebuilt_libfoo/android_common_myapex/hiddenapi/index.csv
+`)
+ })
+
+ t.Run("prebuilt with source apex preferred", func(t *testing.T) {
+ bp := `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ java_libs: ["libfoo", "libbar"],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ prebuilt_apex {
+ name: "myapex",
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_java_libs: ["libfoo", "libbar"],
+ }
+
+ java_import {
+ name: "libfoo",
+ jars: ["libfoo.jar"],
+ apex_available: ["myapex"],
+ }
+
+ java_library {
+ name: "libfoo",
+ srcs: ["foo/bar/MyClass.java"],
+ apex_available: ["myapex"],
+ }
+
+ java_sdk_library_import {
+ name: "libbar",
+ public: {
+ jars: ["libbar.jar"],
+ },
+ apex_available: ["myapex"],
+ }
+
+ java_sdk_library {
+ name: "libbar",
+ srcs: ["foo/bar/MyClass.java"],
+ unsafe_ignore_missing_latest_api: true,
+ apex_available: ["myapex"],
+ }
+ `
+
+ ctx := testDexpreoptWithApexes(t, bp, "", preparer)
+ checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar")
+ checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/libbar/android_common_myapex/hiddenapi/libbar.jar")
+
+ // Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
+ checkHiddenAPIIndexInputs(t, ctx, `
+.intermediates/libbar/android_common_myapex/hiddenapi/index.csv
+.intermediates/libfoo/android_common_apex10000/hiddenapi/index.csv
+`)
+ })
+
+ t.Run("prebuilt preferred with source apex disabled", func(t *testing.T) {
+ bp := `
+ apex {
+ name: "myapex",
+ enabled: false,
+ key: "myapex.key",
+ java_libs: ["libfoo", "libbar"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ prebuilt_apex {
+ name: "myapex",
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_java_libs: ["libfoo", "libbar"],
+ }
+
+ java_import {
+ name: "libfoo",
+ prefer: true,
+ jars: ["libfoo.jar"],
+ apex_available: ["myapex"],
+ }
+
+ java_library {
+ name: "libfoo",
+ srcs: ["foo/bar/MyClass.java"],
+ apex_available: ["myapex"],
+ }
+
+ java_sdk_library_import {
+ name: "libbar",
+ prefer: true,
+ public: {
+ jars: ["libbar.jar"],
+ },
+ apex_available: ["myapex"],
+ }
+
+ java_sdk_library {
+ name: "libbar",
+ srcs: ["foo/bar/MyClass.java"],
+ unsafe_ignore_missing_latest_api: true,
+ apex_available: ["myapex"],
+ }
+ `
+
+ ctx := testDexpreoptWithApexes(t, bp, "", preparer)
+ checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+ checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+
+ // Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
+ checkHiddenAPIIndexInputs(t, ctx, `
+.intermediates/prebuilt_libbar/android_common_myapex/hiddenapi/index.csv
+.intermediates/prebuilt_libfoo/android_common_myapex/hiddenapi/index.csv
+`)
+ })
+}
+
func TestApexWithTests(t *testing.T) {
- ctx, config := testApex(t, `
+ ctx := testApex(t, `
apex_test {
name: "myapex",
key: "myapex.key",
+ updatable: false,
tests: [
"mytest",
"mytests",
@@ -4307,7 +4980,7 @@
// Ensure the module is correctly translated.
bundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- data := android.AndroidMkDataForTest(t, config, "", bundle)
+ data := android.AndroidMkDataForTest(t, ctx, bundle)
name := bundle.BaseModuleName()
prefix := "TARGET_"
var builder strings.Builder
@@ -4322,7 +4995,7 @@
ensureContains(t, androidMk, "LOCAL_MODULE := myapex\n")
flatBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
- data = android.AndroidMkDataForTest(t, config, "", flatBundle)
+ data = android.AndroidMkDataForTest(t, ctx, flatBundle)
data.Custom(&builder, name, prefix, "", data)
flatAndroidMk := builder.String()
ensureContainsOnce(t, flatAndroidMk, "LOCAL_TEST_DATA := :baz :bar/baz\n")
@@ -4330,22 +5003,25 @@
}
func TestInstallExtraFlattenedApexes(t *testing.T) {
- ctx, config := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
+ updatable: false,
}
apex_key {
name: "myapex.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
- `, func(fs map[string][]byte, config android.Config) {
- config.TestProductVariables.InstallExtraFlattenedApexes = proptools.BoolPtr(true)
- })
+ `,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.InstallExtraFlattenedApexes = proptools.BoolPtr(true)
+ }),
+ )
ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
ensureListContains(t, ab.requiredDeps, "myapex.flattened")
- mk := android.AndroidMkDataForTest(t, config, "", ab)
+ mk := android.AndroidMkDataForTest(t, ctx, ab)
var builder strings.Builder
mk.Custom(&builder, ab.Name(), "TARGET_", "", mk)
androidMk := builder.String()
@@ -4398,8 +5074,37 @@
`)
}
+func TestApexWithJavaImport(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ java_libs: ["myjavaimport"],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_import {
+ name: "myjavaimport",
+ apex_available: ["myapex"],
+ jars: ["my.jar"],
+ compile_dex: true,
+ }
+ `)
+
+ module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+ apexRule := module.Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+ ensureContains(t, copyCmds, "image.apex/javalib/myjavaimport.jar")
+}
+
func TestApexWithApps(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -4407,6 +5112,7 @@
"AppFoo",
"AppFooPriv",
],
+ updatable: false,
}
apex_key {
@@ -4468,7 +5174,7 @@
}
// JNI libraries including transitive deps are
for _, jni := range []string{"libjni", "libfoo"} {
- jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_sdk_shared_apex10000").Module().(*cc.Module).OutputFile()
+ jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_sdk_shared_apex10000").Module().(*cc.Module).OutputFile().RelativeToTop()
// ... embedded inside APK (jnilibs.zip)
ensureListContains(t, appZipRule.Implicits.Strings(), jniOutput.String())
// ... and not directly inside the APEX
@@ -4477,7 +5183,7 @@
}
func TestApexWithAppImports(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -4485,6 +5191,7 @@
"AppFooPrebuilt",
"AppFooPrivPrebuilt",
],
+ updatable: false,
}
apex_key {
@@ -4525,13 +5232,14 @@
}
func TestApexWithAppImportsPrefer(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
apps: [
"AppFoo",
],
+ updatable: false,
}
apex_key {
@@ -4566,13 +5274,14 @@
}
func TestApexWithTestHelperApp(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
apps: [
"TesterHelpAppFoo",
],
+ updatable: false,
}
apex_key {
@@ -4603,6 +5312,7 @@
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libfoo"],
+ updatable: false,
}
apex_key {
@@ -4615,6 +5325,7 @@
name: "otherapex",
key: "myapex.key",
native_shared_libs: ["libfoo"],
+ updatable: false,
}
cc_defaults {
@@ -4637,6 +5348,7 @@
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libfoo"],
+ updatable: false,
}
apex_key {
@@ -4649,6 +5361,7 @@
name: "otherapex",
key: "otherapex.key",
native_shared_libs: ["libfoo"],
+ updatable: false,
}
apex_key {
@@ -4667,7 +5380,7 @@
func TestApexAvailable_IndirectDep(t *testing.T) {
// libbbaz is an indirect dep
- testApexError(t, `requires "libbaz" that doesn't list the APEX under 'apex_available'. Dependency path:
+ testApexError(t, `requires "libbaz" that doesn't list the APEX under 'apex_available'.\n\nDependency path:
.*via tag apex\.dependencyTag.*name:sharedLib.*
.*-> libfoo.*link:shared.*
.*via tag cc\.libraryDependencyTag.*Kind:sharedLibraryDependency.*
@@ -4678,6 +5391,7 @@
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libfoo"],
+ updatable: false,
}
apex_key {
@@ -4715,6 +5429,7 @@
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libfoo"],
+ updatable: false,
}
apex_key {
@@ -4735,6 +5450,7 @@
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libfoo", "libbar"],
+ updatable: false,
}
apex_key {
@@ -4769,11 +5485,12 @@
}
func TestApexAvailable_CheckForPlatform(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libbar", "libbaz"],
+ updatable: false,
}
apex_key {
@@ -4831,11 +5548,12 @@
}
func TestApexAvailable_CreatedForApex(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libfoo"],
+ updatable: false,
}
apex_key {
@@ -4865,12 +5583,13 @@
}
func TestOverrideApex(t *testing.T) {
- ctx, config := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
apps: ["app"],
overrides: ["oldapex"],
+ updatable: false,
}
override_apex {
@@ -4933,7 +5652,7 @@
optFlags := apexRule.Args["opt_flags"]
ensureContains(t, optFlags, "--override_apk_package_name test.overridden.package")
- data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+ data := android.AndroidMkDataForTest(t, ctx, apexBundle)
var builder strings.Builder
data.Custom(&builder, name, "TARGET_", "", data)
androidMk := builder.String()
@@ -4948,7 +5667,7 @@
}
func TestLegacyAndroid10Support(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -4990,7 +5709,7 @@
}
}
-var filesForSdkLibrary = map[string][]byte{
+var filesForSdkLibrary = android.MockFS{
"api/current.txt": nil,
"api/removed.txt": nil,
"api/system-current.txt": nil,
@@ -5008,11 +5727,12 @@
}
func TestJavaSDKLibrary(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
java_libs: ["foo"],
+ updatable: false,
}
apex_key {
@@ -5045,11 +5765,12 @@
}
func TestJavaSDKLibrary_WithinApex(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
java_libs: ["foo", "bar"],
+ updatable: false,
}
apex_key {
@@ -5091,17 +5812,18 @@
// The bar library should depend on the implementation jar.
barLibrary := ctx.ModuleForTests("bar", "android_common_myapex").Rule("javac")
- if expected, actual := `^-classpath /[^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+ if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
t.Errorf("expected %q, found %#q", expected, actual)
}
}
func TestJavaSDKLibrary_CrossBoundary(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
java_libs: ["foo"],
+ updatable: false,
}
apex_key {
@@ -5141,13 +5863,13 @@
// The bar library should depend on the stubs jar.
barLibrary := ctx.ModuleForTests("bar", "android_common").Rule("javac")
- if expected, actual := `^-classpath /[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+ if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
t.Errorf("expected %q, found %#q", expected, actual)
}
}
func TestJavaSDKLibrary_ImportPreferred(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
prebuilt_apis {
name: "sdk",
api_dirs: ["100"],
@@ -5164,6 +5886,7 @@
name: "myapex",
key: "myapex.key",
java_libs: ["foo", "bar"],
+ updatable: false,
}
apex_key {
@@ -5230,7 +5953,7 @@
// The bar library should depend on the implementation jar.
barLibrary := ctx.ModuleForTests("bar", "android_common_myapex").Rule("javac")
- if expected, actual := `^-classpath /[^:]*/turbine-combined/foo\.impl\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+ if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.impl\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
t.Errorf("expected %q, found %#q", expected, actual)
}
}
@@ -5241,6 +5964,7 @@
name: "myapex",
key: "myapex.key",
java_libs: ["foo"],
+ updatable: false,
}
apex_key {
@@ -5262,12 +5986,16 @@
}
func TestCompatConfig(t *testing.T) {
- ctx, _ := testApex(t, `
+ result := android.GroupFixturePreparers(
+ prepareForApexTest,
+ java.PrepareForTestWithPlatformCompatConfig,
+ ).RunTestWithBp(t, `
apex {
name: "myapex",
key: "myapex.key",
- prebuilts: ["myjar-platform-compat-config"],
+ compat_configs: ["myjar-platform-compat-config"],
java_libs: ["myjar"],
+ updatable: false,
}
apex_key {
@@ -5288,7 +6016,15 @@
system_modules: "none",
apex_available: [ "myapex" ],
}
+
+ // Make sure that a preferred prebuilt does not affect the apex contents.
+ prebuilt_platform_compat_config {
+ name: "myjar-platform-compat-config",
+ metadata: "compat-config/metadata.xml",
+ prefer: true,
+ }
`)
+ ctx := result.TestContext
ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
"etc/compatconfig/myjar-platform-compat-config.xml",
"javalib/myjar.jar",
@@ -5301,6 +6037,7 @@
name: "myapex",
key: "myapex.key",
java_libs: ["myjar"],
+ updatable: false,
}
apex_key {
@@ -5321,11 +6058,12 @@
}
func TestCarryRequiredModuleNames(t *testing.T) {
- ctx, config := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -5347,7 +6085,7 @@
`)
apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+ data := android.AndroidMkDataForTest(t, ctx, apexBundle)
name := apexBundle.BaseModuleName()
prefix := "TARGET_"
var builder strings.Builder
@@ -5365,6 +6103,7 @@
key: "myapex.key",
native_shared_libs: ["mylib"],
java_libs: ["myjar"],
+ updatable: false,
}
apex {
@@ -5463,7 +6202,7 @@
// For unbundled build, symlink shouldn't exist regardless of whether an APEX
// is updatable or not
- ctx, _ := testApex(t, bp, withUnbundledBuild)
+ ctx := testApex(t, bp, withUnbundledBuild)
files := getFiles(t, ctx, "myapex", "android_common_myapex_image")
ensureRealfileExists(t, files, "javalib/myjar.jar")
ensureRealfileExists(t, files, "lib64/mylib.so")
@@ -5475,7 +6214,7 @@
ensureRealfileExists(t, files, "lib64/myotherlib.so")
// For bundled build, symlink to the system for the non-updatable APEXes only
- ctx, _ = testApex(t, bp)
+ ctx = testApex(t, bp)
files = getFiles(t, ctx, "myapex", "android_common_myapex_image")
ensureRealfileExists(t, files, "javalib/myjar.jar")
ensureRealfileExists(t, files, "lib64/mylib.so")
@@ -5488,11 +6227,12 @@
}
func TestSymlinksFromApexToSystemRequiredModuleNames(t *testing.T) {
- ctx, config := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -5526,7 +6266,7 @@
`)
apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- data := android.AndroidMkDataForTest(t, config, "", apexBundle)
+ data := android.AndroidMkDataForTest(t, ctx, apexBundle)
var builder strings.Builder
data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
androidMk := builder.String()
@@ -5535,15 +6275,16 @@
ensureNotContains(t, androidMk, "LOCAL_MODULE := prebuilt_myotherlib.myapex\n")
ensureNotContains(t, androidMk, "LOCAL_MODULE := myotherlib.myapex\n")
// `myapex` should have `myotherlib` in its required line, not `prebuilt_myotherlib`
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += mylib.myapex myotherlib apex_manifest.pb.myapex apex_pubkey.myapex\n")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += mylib.myapex:64 myotherlib:64 apex_manifest.pb.myapex apex_pubkey.myapex\n")
}
func TestApexWithJniLibs(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
jni_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -5580,20 +6321,23 @@
}
func TestApexMutatorsDontRunIfDisabled(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
+ updatable: false,
}
apex_key {
name: "myapex.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
- `, func(fs map[string][]byte, config android.Config) {
- delete(config.Targets, android.Android)
- config.AndroidCommonTarget = android.Target{}
- })
+ `,
+ android.FixtureModifyConfig(func(config android.Config) {
+ delete(config.Targets, android.Android)
+ config.AndroidCommonTarget = android.Target{}
+ }),
+ )
if expected, got := []string{""}, ctx.ModuleVariantsForTests("myapex"); !reflect.DeepEqual(expected, got) {
t.Errorf("Expected variants: %v, but got: %v", expected, got)
@@ -5601,11 +6345,12 @@
}
func TestAppBundle(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
apps: ["AppFoo"],
+ updatable: false,
}
apex_key {
@@ -5631,11 +6376,12 @@
}
func TestAppSetBundle(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
apps: ["AppSet"],
+ updatable: false,
}
apex_key {
@@ -5663,8 +6409,7 @@
}
func TestAppSetBundlePrebuilt(t *testing.T) {
- ctx, _ := testApex(t, "", func(fs map[string][]byte, config android.Config) {
- bp := `
+ bp := `
apex_set {
name: "myapex",
filename: "foo_v2.apex",
@@ -5672,27 +6417,26 @@
none: { set: "myapex.apks", },
hwaddress: { set: "myapex.hwasan.apks", },
},
- }`
- fs["Android.bp"] = []byte(bp)
+ }
+ `
+ ctx := testApex(t, bp, prepareForTestWithSantitizeHwaddress)
- config.TestProductVariables.SanitizeDevice = []string{"hwaddress"}
- })
+ // Check that the extractor produces the correct output file from the correct input file.
+ extractorOutput := "out/soong/.intermediates/myapex.apex.extractor/android_common/extracted/myapex.hwasan.apks"
- m := ctx.ModuleForTests("myapex", "android_common")
- extractedApex := m.Output(buildDir + "/.intermediates/myapex/android_common/foo_v2.apex")
+ m := ctx.ModuleForTests("myapex.apex.extractor", "android_common")
+ extractedApex := m.Output(extractorOutput)
- actual := extractedApex.Inputs
- if len(actual) != 1 {
- t.Errorf("expected a single input")
- }
+ android.AssertArrayString(t, "extractor input", []string{"myapex.hwasan.apks"}, extractedApex.Inputs.Strings())
- expected := "myapex.hwasan.apks"
- if actual[0].String() != expected {
- t.Errorf("expected %s, got %s", expected, actual[0].String())
- }
+ // Ditto for the apex.
+ m = ctx.ModuleForTests("myapex", "android_common")
+ copiedApex := m.Output("out/soong/.intermediates/myapex/android_common/foo_v2.apex")
+
+ android.AssertStringEquals(t, "myapex input", extractorOutput, copiedApex.Input.String())
}
-func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) {
+func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, preparer android.FixturePreparer) {
t.Helper()
bp := `
@@ -5725,7 +6469,7 @@
srcs: ["a.java"],
sdk_version: "current",
apex_available: [
- "com.android.art.something",
+ "com.android.art.debug",
],
hostdex: true,
}
@@ -5742,6 +6486,7 @@
name: "some-non-updatable-apex",
key: "some-non-updatable-apex.key",
java_libs: ["some-non-updatable-apex-lib"],
+ updatable: false,
}
apex_key {
@@ -5753,15 +6498,15 @@
}
apex {
- name: "com.android.art.something",
- key: "com.android.art.something.key",
+ name: "com.android.art.debug",
+ key: "com.android.art.debug.key",
java_libs: ["some-art-lib"],
updatable: true,
min_sdk_version: "current",
}
apex_key {
- name: "com.android.art.something.key",
+ name: "com.android.art.debug.key",
}
filegroup {
@@ -5779,67 +6524,47 @@
}
`
- testDexpreoptWithApexes(t, bp, errmsg, transformDexpreoptConfig)
+ testDexpreoptWithApexes(t, bp, errmsg, preparer)
}
-func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) {
+func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, preparer android.FixturePreparer) *android.TestContext {
t.Helper()
- bp += cc.GatherRequiredDepsForTest(android.Android)
- bp += java.GatherRequiredDepsForTest()
- bp += dexpreopt.BpToolModulesForTest()
-
- fs := map[string][]byte{
- "a.java": nil,
- "a.jar": nil,
- "build/make/target/product/security": nil,
- "apex_manifest.json": nil,
- "AndroidManifest.xml": nil,
- "system/sepolicy/apex/some-updatable-apex-file_contexts": nil,
- "system/sepolicy/apex/some-non-updatable-apex-file_contexts": nil,
- "system/sepolicy/apex/com.android.art.something-file_contexts": nil,
- "framework/aidl/a.aidl": nil,
+ fs := android.MockFS{
+ "a.java": nil,
+ "a.jar": nil,
+ "apex_manifest.json": nil,
+ "AndroidManifest.xml": nil,
+ "system/sepolicy/apex/myapex-file_contexts": nil,
+ "system/sepolicy/apex/some-updatable-apex-file_contexts": nil,
+ "system/sepolicy/apex/some-non-updatable-apex-file_contexts": nil,
+ "system/sepolicy/apex/com.android.art.debug-file_contexts": nil,
+ "framework/aidl/a.aidl": nil,
}
- cc.GatherRequiredFilesForTest(fs)
- config := android.TestArchConfig(buildDir, nil, bp, fs)
-
- ctx := android.NewTestArchContext(config)
- ctx.RegisterModuleType("apex", BundleFactory)
- ctx.RegisterModuleType("apex_key", ApexKeyFactory)
- ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- android.RegisterPrebuiltMutators(ctx)
- cc.RegisterRequiredBuildComponentsForTest(ctx)
- java.RegisterJavaBuildComponents(ctx)
- java.RegisterSystemModulesBuildComponents(ctx)
- java.RegisterAppBuildComponents(ctx)
- java.RegisterDexpreoptBootJarsComponents(ctx)
- ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
- ctx.PreDepsMutators(RegisterPreDepsMutators)
- ctx.PostDepsMutators(RegisterPostDepsMutators)
-
- ctx.Register()
-
- _ = dexpreopt.GlobalSoongConfigForTests(config)
- dexpreopt.RegisterToolModulesForTest(ctx)
- pathCtx := android.PathContextForTesting(config)
- dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
- transformDexpreoptConfig(dexpreoptConfig)
- dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
-
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
- android.FailIfErrored(t, errs)
-
- _, errs = ctx.PrepareBuildActions(config)
- if errmsg == "" {
- android.FailIfErrored(t, errs)
- } else if len(errs) > 0 {
- android.FailIfNoMatchingErrors(t, errmsg, errs)
- return
- } else {
- t.Fatalf("missing expected error %q (0 errors are returned)", errmsg)
+ errorHandler := android.FixtureExpectsNoErrors
+ if errmsg != "" {
+ errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(errmsg)
}
+
+ result := android.GroupFixturePreparers(
+ cc.PrepareForTestWithCcDefaultModules,
+ java.PrepareForTestWithHiddenApiBuildComponents,
+ java.PrepareForTestWithJavaDefaultModules,
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ PrepareForTestWithApexBuildComponents,
+ preparer,
+ fs.AddToFixture(),
+ android.FixtureAddTextFile("frameworks/base/boot/Android.bp", `
+ platform_bootclasspath {
+ name: "platform-bootclasspath",
+ }
+ `),
+ ).
+ ExtendWithErrorHandler(errorHandler).
+ RunTestWithBp(t, bp)
+
+ return result.TestContext
}
func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
@@ -5858,85 +6583,153 @@
`)
}
+func TestUpdatableDefault_should_set_min_sdk_version(t *testing.T) {
+ testApexError(t, `"myapex" .*: updatable: updatable APEXes should set min_sdk_version`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `)
+}
+
func TestNoUpdatableJarsInBootImage(t *testing.T) {
- var err string
- var transform func(*dexpreopt.GlobalConfig)
+ // Set the BootJars in dexpreopt.GlobalConfig and productVariables to the same value. This can
+ // result in an invalid configuration as it does not set the ArtApexJars and allows art apex
+ // modules to be included in the BootJars.
+ prepareSetBootJars := func(bootJars ...string) android.FixturePreparer {
+ return android.GroupFixturePreparers(
+ dexpreopt.FixtureSetBootJars(bootJars...),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.BootJars = android.CreateTestConfiguredJarList(bootJars)
+ }),
+ )
+ }
+
+ // Set the ArtApexJars and BootJars in dexpreopt.GlobalConfig and productVariables all to the
+ // same value. This can result in an invalid configuration as it allows non art apex jars to be
+ // specified in the ArtApexJars configuration.
+ prepareSetArtJars := func(bootJars ...string) android.FixturePreparer {
+ return android.GroupFixturePreparers(
+ dexpreopt.FixtureSetArtBootJars(bootJars...),
+ dexpreopt.FixtureSetBootJars(bootJars...),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.BootJars = android.CreateTestConfiguredJarList(bootJars)
+ }),
+ )
+ }
t.Run("updatable jar from ART apex in the ART boot image => ok", func(t *testing.T) {
- transform = func(config *dexpreopt.GlobalConfig) {
- config.ArtApexJars = android.CreateTestConfiguredJarList([]string{"com.android.art.something:some-art-lib"})
- }
- testNoUpdatableJarsInBootImage(t, "", transform)
+ preparer := java.FixtureConfigureBootJars("com.android.art.debug:some-art-lib")
+ testNoUpdatableJarsInBootImage(t, "", preparer)
})
t.Run("updatable jar from ART apex in the framework boot image => error", func(t *testing.T) {
- err = `module "some-art-lib" from updatable apexes \["com.android.art.something"\] is not allowed in the framework boot image`
- transform = func(config *dexpreopt.GlobalConfig) {
- config.BootJars = android.CreateTestConfiguredJarList([]string{"com.android.art.something:some-art-lib"})
- }
- testNoUpdatableJarsInBootImage(t, err, transform)
+ err := `module "some-art-lib" from updatable apexes \["com.android.art.debug"\] is not allowed in the framework boot image`
+ // Update the dexpreopt BootJars directly.
+ preparer := prepareSetBootJars("com.android.art.debug:some-art-lib")
+ testNoUpdatableJarsInBootImage(t, err, preparer)
})
t.Run("updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
- err = `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the ART boot image`
- transform = func(config *dexpreopt.GlobalConfig) {
- config.ArtApexJars = android.CreateTestConfiguredJarList([]string{"some-updatable-apex:some-updatable-apex-lib"})
- }
- testNoUpdatableJarsInBootImage(t, err, transform)
+ err := `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the ART boot image`
+ // Update the dexpreopt ArtApexJars directly.
+ preparer := prepareSetArtJars("some-updatable-apex:some-updatable-apex-lib")
+ testNoUpdatableJarsInBootImage(t, err, preparer)
})
t.Run("non-updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
- err = `module "some-non-updatable-apex-lib" is not allowed in the ART boot image`
- transform = func(config *dexpreopt.GlobalConfig) {
- config.ArtApexJars = android.CreateTestConfiguredJarList([]string{"some-non-updatable-apex:some-non-updatable-apex-lib"})
- }
- testNoUpdatableJarsInBootImage(t, err, transform)
+ err := `module "some-non-updatable-apex-lib" is not allowed in the ART boot image`
+ // Update the dexpreopt ArtApexJars directly.
+ preparer := prepareSetArtJars("some-non-updatable-apex:some-non-updatable-apex-lib")
+ testNoUpdatableJarsInBootImage(t, err, preparer)
})
t.Run("updatable jar from some other apex in the framework boot image => error", func(t *testing.T) {
- err = `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the framework boot image`
- transform = func(config *dexpreopt.GlobalConfig) {
- config.BootJars = android.CreateTestConfiguredJarList([]string{"some-updatable-apex:some-updatable-apex-lib"})
- }
- testNoUpdatableJarsInBootImage(t, err, transform)
+ err := `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the framework boot image`
+ preparer := java.FixtureConfigureBootJars("some-updatable-apex:some-updatable-apex-lib")
+ testNoUpdatableJarsInBootImage(t, err, preparer)
})
t.Run("non-updatable jar from some other apex in the framework boot image => ok", func(t *testing.T) {
- transform = func(config *dexpreopt.GlobalConfig) {
- config.BootJars = android.CreateTestConfiguredJarList([]string{"some-non-updatable-apex:some-non-updatable-apex-lib"})
- }
- testNoUpdatableJarsInBootImage(t, "", transform)
+ preparer := java.FixtureConfigureBootJars("some-non-updatable-apex:some-non-updatable-apex-lib")
+ testNoUpdatableJarsInBootImage(t, "", preparer)
})
t.Run("nonexistent jar in the ART boot image => error", func(t *testing.T) {
- err = "failed to find a dex jar path for module 'nonexistent'"
- transform = func(config *dexpreopt.GlobalConfig) {
- config.ArtApexJars = android.CreateTestConfiguredJarList([]string{"platform:nonexistent"})
- }
- testNoUpdatableJarsInBootImage(t, err, transform)
+ err := `"platform-bootclasspath" depends on undefined module "nonexistent"`
+ preparer := java.FixtureConfigureBootJars("platform:nonexistent")
+ testNoUpdatableJarsInBootImage(t, err, preparer)
})
t.Run("nonexistent jar in the framework boot image => error", func(t *testing.T) {
- err = "failed to find a dex jar path for module 'nonexistent'"
- transform = func(config *dexpreopt.GlobalConfig) {
- config.BootJars = android.CreateTestConfiguredJarList([]string{"platform:nonexistent"})
- }
- testNoUpdatableJarsInBootImage(t, err, transform)
+ err := `"platform-bootclasspath" depends on undefined module "nonexistent"`
+ preparer := java.FixtureConfigureBootJars("platform:nonexistent")
+ testNoUpdatableJarsInBootImage(t, err, preparer)
})
t.Run("platform jar in the ART boot image => error", func(t *testing.T) {
- err = `module "some-platform-lib" is not allowed in the ART boot image`
- transform = func(config *dexpreopt.GlobalConfig) {
- config.ArtApexJars = android.CreateTestConfiguredJarList([]string{"platform:some-platform-lib"})
- }
- testNoUpdatableJarsInBootImage(t, err, transform)
+ err := `module "some-platform-lib" is not allowed in the ART boot image`
+ // Update the dexpreopt ArtApexJars directly.
+ preparer := prepareSetArtJars("platform:some-platform-lib")
+ testNoUpdatableJarsInBootImage(t, err, preparer)
})
t.Run("platform jar in the framework boot image => ok", func(t *testing.T) {
- transform = func(config *dexpreopt.GlobalConfig) {
- config.BootJars = android.CreateTestConfiguredJarList([]string{"platform:some-platform-lib"})
+ preparer := java.FixtureConfigureBootJars("platform:some-platform-lib")
+ testNoUpdatableJarsInBootImage(t, "", preparer)
+ })
+}
+
+func TestDexpreoptAccessDexFilesFromPrebuiltApex(t *testing.T) {
+ preparer := java.FixtureConfigureBootJars("myapex:libfoo")
+ t.Run("prebuilt no source", func(t *testing.T) {
+ testDexpreoptWithApexes(t, `
+ prebuilt_apex {
+ name: "myapex" ,
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_java_libs: ["libfoo"],
}
- testNoUpdatableJarsInBootImage(t, "", transform)
+
+ java_import {
+ name: "libfoo",
+ jars: ["libfoo.jar"],
+ }
+`, "", preparer)
+ })
+
+ t.Run("prebuilt no source", func(t *testing.T) {
+ testDexpreoptWithApexes(t, `
+ prebuilt_apex {
+ name: "myapex" ,
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_java_libs: ["libfoo"],
+ }
+
+ java_import {
+ name: "libfoo",
+ jars: ["libfoo.jar"],
+ }
+`, "", preparer)
})
}
@@ -5948,47 +6741,33 @@
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}`
- fs := map[string][]byte{
+ fs := android.MockFS{
"lib1/src/A.java": nil,
"lib2/src/B.java": nil,
"system/sepolicy/apex/myapex-file_contexts": nil,
}
- config := android.TestArchConfig(buildDir, nil, bp, fs)
- android.SetTestNeverallowRules(config, rules)
- updatableBootJars := make([]string, 0, len(apexBootJars))
- for _, apexBootJar := range apexBootJars {
- updatableBootJars = append(updatableBootJars, "myapex:"+apexBootJar)
+ errorHandler := android.FixtureExpectsNoErrors
+ if errmsg != "" {
+ errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(errmsg)
}
- config.TestProductVariables.UpdatableBootJars = android.CreateTestConfiguredJarList(updatableBootJars)
- ctx := android.NewTestArchContext(config)
- ctx.RegisterModuleType("apex", BundleFactory)
- ctx.RegisterModuleType("apex_key", ApexKeyFactory)
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- cc.RegisterRequiredBuildComponentsForTest(ctx)
- java.RegisterJavaBuildComponents(ctx)
- java.RegisterSystemModulesBuildComponents(ctx)
- java.RegisterDexpreoptBootJarsComponents(ctx)
- ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
- ctx.PreDepsMutators(RegisterPreDepsMutators)
- ctx.PostDepsMutators(RegisterPostDepsMutators)
- ctx.PostDepsMutators(android.RegisterNeverallowMutator)
-
- ctx.Register()
-
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
- android.FailIfErrored(t, errs)
-
- _, errs = ctx.PrepareBuildActions(config)
- if errmsg == "" {
- android.FailIfErrored(t, errs)
- } else if len(errs) > 0 {
- android.FailIfNoMatchingErrors(t, errmsg, errs)
- return
- } else {
- t.Fatalf("missing expected error %q (0 errors are returned)", errmsg)
- }
+ android.GroupFixturePreparers(
+ android.PrepareForTestWithAndroidBuildComponents,
+ java.PrepareForTestWithJavaBuildComponents,
+ PrepareForTestWithApexBuildComponents,
+ android.PrepareForTestWithNeverallowRules(rules),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ updatableBootJars := make([]string, 0, len(apexBootJars))
+ for _, apexBootJar := range apexBootJars {
+ updatableBootJars = append(updatableBootJars, "myapex:"+apexBootJar)
+ }
+ variables.UpdatableBootJars = android.CreateTestConfiguredJarList(updatableBootJars)
+ }),
+ fs.AddToFixture(),
+ ).
+ ExtendWithErrorHandler(errorHandler).
+ RunTestWithBp(t, bp)
}
func TestApexPermittedPackagesRules(t *testing.T) {
@@ -6024,6 +6803,7 @@
name: "myapex",
key: "myapex.key",
java_libs: ["bcp_lib1", "nonbcp_lib2"],
+ updatable: false,
}`,
bootJars: []string{"bcp_lib1"},
modulesPackages: map[string][]string{
@@ -6056,6 +6836,7 @@
name: "myapex",
key: "myapex.key",
java_libs: ["bcp_lib1", "bcp_lib2"],
+ updatable: false,
}
`,
bootJars: []string{"bcp_lib1", "bcp_lib2"},
@@ -6075,11 +6856,12 @@
}
func TestTestFor(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib", "myprivlib"],
+ updatable: false,
}
apex_key {
@@ -6137,21 +6919,127 @@
}
`)
- // the test 'mytest' is a test for the apex, therefore is linked to the
+ ensureLinkedLibIs := func(mod, variant, linkedLib, expectedVariant string) {
+ ldFlags := strings.Split(ctx.ModuleForTests(mod, variant).Rule("ld").Args["libFlags"], " ")
+ mylibLdFlags := android.FilterListPred(ldFlags, func(s string) bool { return strings.HasPrefix(s, linkedLib) })
+ android.AssertArrayString(t, "unexpected "+linkedLib+" link library for "+mod, []string{linkedLib + expectedVariant}, mylibLdFlags)
+ }
+
+ // These modules are tests for the apex, therefore are linked to the
// actual implementation of mylib instead of its stub.
- ldFlags := ctx.ModuleForTests("mytest", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
- ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared/mylib.so")
- ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
+ ensureLinkedLibIs("mytest", "android_arm64_armv8-a", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
+ ensureLinkedLibIs("mytestlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
+ ensureLinkedLibIs("mybench", "android_arm64_armv8-a", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
+}
- // The same should be true for cc_library
- ldFlags = ctx.ModuleForTests("mytestlib", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"]
- ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared/mylib.so")
- ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
+func TestIndirectTestFor(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib", "myprivlib"],
+ updatable: false,
+ }
- // ... and for cc_benchmark
- ldFlags = ctx.ModuleForTests("mybench", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
- ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared/mylib.so")
- ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
+ 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",
+ stubs: {
+ versions: ["1"],
+ },
+ apex_available: ["myapex"],
+ }
+
+ cc_library {
+ name: "myprivlib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ shared_libs: ["mylib"],
+ apex_available: ["myapex"],
+ }
+
+ cc_library {
+ name: "mytestlib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ shared_libs: ["myprivlib"],
+ stl: "none",
+ test_for: ["myapex"],
+ }
+ `)
+
+ ensureLinkedLibIs := func(mod, variant, linkedLib, expectedVariant string) {
+ ldFlags := strings.Split(ctx.ModuleForTests(mod, variant).Rule("ld").Args["libFlags"], " ")
+ mylibLdFlags := android.FilterListPred(ldFlags, func(s string) bool { return strings.HasPrefix(s, linkedLib) })
+ android.AssertArrayString(t, "unexpected "+linkedLib+" link library for "+mod, []string{linkedLib + expectedVariant}, mylibLdFlags)
+ }
+
+ // The platform variant of mytestlib links to the platform variant of the
+ // internal myprivlib.
+ ensureLinkedLibIs("mytestlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/myprivlib/", "android_arm64_armv8-a_shared/myprivlib.so")
+
+ // The platform variant of myprivlib links to the platform variant of mylib
+ // and bypasses its stubs.
+ ensureLinkedLibIs("myprivlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
+}
+
+func TestTestForForLibInOtherApex(t *testing.T) {
+ // This case is only allowed for known overlapping APEXes, i.e. the ART APEXes.
+ _ = testApex(t, `
+ apex {
+ name: "com.android.art",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ updatable: false,
+ }
+
+ apex {
+ name: "com.android.art.debug",
+ key: "myapex.key",
+ native_shared_libs: ["mylib", "mytestlib"],
+ updatable: false,
+ }
+
+ 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",
+ stubs: {
+ versions: ["1"],
+ },
+ apex_available: ["com.android.art", "com.android.art.debug"],
+ }
+
+ cc_library {
+ name: "mytestlib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ shared_libs: ["mylib"],
+ stl: "none",
+ apex_available: ["com.android.art.debug"],
+ test_for: ["com.android.art"],
+ }
+ `,
+ android.MockFS{
+ "system/sepolicy/apex/com.android.art-file_contexts": nil,
+ "system/sepolicy/apex/com.android.art.debug-file_contexts": nil,
+ }.AddToFixture())
}
// TODO(jungjw): Move this to proptools
@@ -6160,25 +7048,29 @@
}
func TestApexSet(t *testing.T) {
- ctx, config := testApex(t, `
+ ctx := testApex(t, `
apex_set {
name: "myapex",
set: "myapex.apks",
filename: "foo_v2.apex",
overrides: ["foo"],
}
- `, func(fs map[string][]byte, config android.Config) {
- config.TestProductVariables.Platform_sdk_version = intPtr(30)
- config.Targets[android.Android] = []android.Target{
- {Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}},
- {Os: android.Android, Arch: android.Arch{ArchType: android.Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}},
- }
- })
+ `,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Platform_sdk_version = intPtr(30)
+ }),
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.Targets[android.Android] = []android.Target{
+ {Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}},
+ {Os: android.Android, Arch: android.Arch{ArchType: android.Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}},
+ }
+ }),
+ )
- m := ctx.ModuleForTests("myapex", "android_common")
+ m := ctx.ModuleForTests("myapex.apex.extractor", "android_common")
// Check extract_apks tool parameters.
- extractedApex := m.Output(buildDir + "/.intermediates/myapex/android_common/foo_v2.apex")
+ extractedApex := m.Output("extracted/myapex.apks")
actual := extractedApex.Args["abis"]
expected := "ARMEABI_V7A,ARM64_V8A"
if actual != expected {
@@ -6190,9 +7082,10 @@
t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual)
}
+ m = ctx.ModuleForTests("myapex", "android_common")
a := m.Module().(*ApexSet)
expectedOverrides := []string{"foo"}
- actualOverrides := android.AndroidMkEntriesForTest(t, config, "", a)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
+ actualOverrides := android.AndroidMkEntriesForTest(t, ctx, a)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
if !reflect.DeepEqual(actualOverrides, expectedOverrides) {
t.Errorf("Incorrect LOCAL_OVERRIDES_MODULES - expected %q vs actual %q", expectedOverrides, actualOverrides)
}
@@ -6204,6 +7097,7 @@
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -6235,10 +7129,11 @@
}
func TestApexKeysTxt(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
+ updatable: false,
}
apex_key {
@@ -6275,12 +7170,13 @@
}
func TestAllowedFiles(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
apps: ["app"],
allowed_files: "allowed.txt",
+ updatable: false,
}
apex_key {
@@ -6330,11 +7226,12 @@
}
func TestNonPreferredPrebuiltDependency(t *testing.T) {
- _, _ = testApex(t, `
+ testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -6365,20 +7262,23 @@
}
func TestCompressedApex(t *testing.T) {
- ctx, config := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
compressible: true,
+ updatable: false,
}
apex_key {
name: "myapex.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
- `, func(fs map[string][]byte, config android.Config) {
- config.TestProductVariables.CompressedApex = proptools.BoolPtr(true)
- })
+ `,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.CompressedApex = proptools.BoolPtr(true)
+ }),
+ )
compressRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("compressRule")
ensureContains(t, compressRule.Output.String(), "myapex.capex.unsigned")
@@ -6391,7 +7291,7 @@
ensureContains(t, ab.outputFile.String(), "myapex.capex")
// Verify android.mk rules
- data := android.AndroidMkDataForTest(t, config, "", ab)
+ data := android.AndroidMkDataForTest(t, ctx, ab)
var builder strings.Builder
data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
androidMk := builder.String()
@@ -6399,11 +7299,12 @@
}
func TestPreferredPrebuiltSharedLibDep(t *testing.T) {
- ctx, config := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -6439,7 +7340,7 @@
`)
ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- data := android.AndroidMkDataForTest(t, config, "", ab)
+ data := android.AndroidMkDataForTest(t, ctx, ab)
var builder strings.Builder
data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
androidMk := builder.String()
@@ -6450,11 +7351,12 @@
}
func TestExcludeDependency(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -6504,6 +7406,7 @@
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
name: "myapex.key",
@@ -6522,6 +7425,7 @@
enabled: %s,
key: "myapex.key",
native_shared_libs: ["stublib"],
+ updatable: false,
}
`
@@ -6591,7 +7495,7 @@
t.Run(test.name, func(t *testing.T) {
for _, otherApexEnabled := range test.otherApexEnabled {
t.Run("otherapex_enabled_"+otherApexEnabled, func(t *testing.T) {
- ctx, config := testApex(t, fmt.Sprintf(bpBase, otherApexEnabled)+test.stublibBp)
+ ctx := testApex(t, fmt.Sprintf(bpBase, otherApexEnabled)+test.stublibBp)
type modAndMkEntries struct {
mod *cc.Module
@@ -6609,7 +7513,7 @@
if !mod.Enabled() || mod.IsHideFromMake() {
continue
}
- for _, ent := range android.AndroidMkEntriesForTest(t, config, "", mod) {
+ for _, ent := range android.AndroidMkEntriesForTest(t, ctx, mod) {
if ent.Disabled {
continue
}
@@ -6646,7 +7550,7 @@
t.Errorf("AndroidMk entry for \"stublib\" has LOCAL_NOT_AVAILABLE_FOR_PLATFORM set: %+v", entry.mkEntries)
}
cflags := entry.mkEntries.EntryMap["LOCAL_EXPORT_CFLAGS"]
- expected := "-D__STUBLIB_API__=1"
+ expected := "-D__STUBLIB_API__=10000"
if !android.InList(expected, cflags) {
t.Errorf("LOCAL_EXPORT_CFLAGS expected to have %q, but got %q", expected, cflags)
}
@@ -6658,12 +7562,5 @@
}
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ os.Exit(m.Run())
}
diff --git a/apex/boot_image_test.go b/apex/boot_image_test.go
new file mode 100644
index 0000000..d447d70
--- /dev/null
+++ b/apex/boot_image_test.go
@@ -0,0 +1,381 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package apex
+
+import (
+ "strings"
+ "testing"
+
+ "android/soong/android"
+ "android/soong/java"
+)
+
+// Contains tests for boot_image logic from java/boot_image.go as the ART boot image requires
+// modules from the ART apex.
+
+var prepareForTestWithBootImage = android.GroupFixturePreparers(
+ java.PrepareForTestWithDexpreopt,
+ PrepareForTestWithApexBuildComponents,
+)
+
+// Some additional files needed for the art apex.
+var prepareForTestWithArtApex = android.FixtureMergeMockFs(android.MockFS{
+ "com.android.art.avbpubkey": nil,
+ "com.android.art.pem": nil,
+ "system/sepolicy/apex/com.android.art-file_contexts": nil,
+})
+
+func TestBootImages(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithBootImage,
+ // Configure some libraries in the art and framework boot images.
+ java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "platform:foo", "platform:bar"),
+ prepareForTestWithArtApex,
+
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ java.FixtureWithLastReleaseApis("foo"),
+ ).RunTestWithBp(t, `
+ java_sdk_library {
+ name: "foo",
+ srcs: ["b.java"],
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.java"],
+ installable: true,
+ }
+
+ apex {
+ name: "com.android.art",
+ key: "com.android.art.key",
+ java_libs: [
+ "baz",
+ "quuz",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "com.android.art.key",
+ public_key: "com.android.art.avbpubkey",
+ private_key: "com.android.art.pem",
+ }
+
+ java_library {
+ name: "baz",
+ apex_available: [
+ "com.android.art",
+ ],
+ srcs: ["b.java"],
+ }
+
+ java_library {
+ name: "quuz",
+ apex_available: [
+ "com.android.art",
+ ],
+ srcs: ["b.java"],
+ }
+
+ boot_image {
+ name: "art-boot-image",
+ image_name: "art",
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ boot_image {
+ name: "framework-boot-image",
+ image_name: "boot",
+ }
+`,
+ )
+
+ // Make sure that the framework-boot-image is using the correct configuration.
+ checkBootImage(t, result, "framework-boot-image", "platform:foo,platform:bar", `
+test_device/dex_bootjars/android/system/framework/arm/boot-foo.art
+test_device/dex_bootjars/android/system/framework/arm/boot-foo.oat
+test_device/dex_bootjars/android/system/framework/arm/boot-foo.vdex
+test_device/dex_bootjars/android/system/framework/arm/boot-bar.art
+test_device/dex_bootjars/android/system/framework/arm/boot-bar.oat
+test_device/dex_bootjars/android/system/framework/arm/boot-bar.vdex
+test_device/dex_bootjars/android/system/framework/arm64/boot-foo.art
+test_device/dex_bootjars/android/system/framework/arm64/boot-foo.oat
+test_device/dex_bootjars/android/system/framework/arm64/boot-foo.vdex
+test_device/dex_bootjars/android/system/framework/arm64/boot-bar.art
+test_device/dex_bootjars/android/system/framework/arm64/boot-bar.oat
+test_device/dex_bootjars/android/system/framework/arm64/boot-bar.vdex
+`)
+
+ // Make sure that the art-boot-image is using the correct configuration.
+ checkBootImage(t, result, "art-boot-image", "com.android.art:baz,com.android.art:quuz", `
+test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art
+test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat
+test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex
+test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-quuz.art
+test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-quuz.oat
+test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-quuz.vdex
+test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art
+test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat
+test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex
+test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-quuz.art
+test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-quuz.oat
+test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-quuz.vdex
+`)
+}
+
+func checkBootImage(t *testing.T, result *android.TestResult, moduleName string, expectedConfiguredModules string, expectedBootImageFiles string) {
+ t.Helper()
+
+ bootImage := result.ModuleForTests(moduleName, "android_common").Module().(*java.BootImageModule)
+
+ bootImageInfo := result.ModuleProvider(bootImage, java.BootImageInfoProvider).(java.BootImageInfo)
+ modules := bootImageInfo.Modules()
+ android.AssertStringEquals(t, "invalid modules for "+moduleName, expectedConfiguredModules, modules.String())
+
+ // Get a list of all the paths in the boot image sorted by arch type.
+ allPaths := []string{}
+ bootImageFilesByArchType := bootImageInfo.AndroidBootImageFilesByArchType()
+ for _, archType := range android.ArchTypeList() {
+ if paths, ok := bootImageFilesByArchType[archType]; ok {
+ for _, path := range paths {
+ allPaths = append(allPaths, android.NormalizePathForTesting(path))
+ }
+ }
+ }
+
+ android.AssertTrimmedStringEquals(t, "invalid paths for "+moduleName, expectedBootImageFiles, strings.Join(allPaths, "\n"))
+}
+
+func TestBootImageInArtApex(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithBootImage,
+ prepareForTestWithArtApex,
+
+ // Configure some libraries in the art boot image.
+ java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+ ).RunTestWithBp(t, `
+ apex {
+ name: "com.android.art",
+ key: "com.android.art.key",
+ boot_images: [
+ "mybootimage",
+ ],
+ // bar (like foo) should be transitively included in this apex because it is part of the
+ // mybootimage boot_image. However, it is kept here to ensure that the apex dedups the files
+ // correctly.
+ java_libs: [
+ "bar",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "com.android.art.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "foo",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ boot_image {
+ name: "mybootimage",
+ image_name: "art",
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ // Make sure that a preferred prebuilt doesn't affect the apex.
+ prebuilt_boot_image {
+ name: "mybootimage",
+ image_name: "art",
+ prefer: true,
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+ `)
+
+ ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ "javalib/arm/boot.art",
+ "javalib/arm/boot.oat",
+ "javalib/arm/boot.vdex",
+ "javalib/arm/boot-bar.art",
+ "javalib/arm/boot-bar.oat",
+ "javalib/arm/boot-bar.vdex",
+ "javalib/arm64/boot.art",
+ "javalib/arm64/boot.oat",
+ "javalib/arm64/boot.vdex",
+ "javalib/arm64/boot-bar.art",
+ "javalib/arm64/boot-bar.oat",
+ "javalib/arm64/boot-bar.vdex",
+ "javalib/bar.jar",
+ "javalib/foo.jar",
+ })
+
+ java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ `bar`,
+ `com.android.art.key`,
+ `mybootimage`,
+ })
+}
+
+func TestBootImageInPrebuiltArtApex(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithBootImage,
+ prepareForTestWithArtApex,
+
+ android.FixtureMergeMockFs(android.MockFS{
+ "com.android.art-arm64.apex": nil,
+ "com.android.art-arm.apex": nil,
+ }),
+
+ // Configure some libraries in the art boot image.
+ java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+ ).RunTestWithBp(t, `
+ prebuilt_apex {
+ name: "com.android.art",
+ arch: {
+ arm64: {
+ src: "com.android.art-arm64.apex",
+ },
+ arm: {
+ src: "com.android.art-arm.apex",
+ },
+ },
+ exported_java_libs: ["foo", "bar"],
+ }
+
+ java_import {
+ name: "foo",
+ jars: ["foo.jar"],
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ java_import {
+ name: "bar",
+ jars: ["bar.jar"],
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ prebuilt_boot_image {
+ name: "mybootimage",
+ image_name: "art",
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+ `)
+
+ java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common", []string{
+ `com.android.art.apex.selector`,
+ `prebuilt_bar`,
+ `prebuilt_foo`,
+ })
+
+ java.CheckModuleDependencies(t, result.TestContext, "mybootimage", "android_common", []string{
+ `dex2oatd`,
+ `prebuilt_bar`,
+ `prebuilt_foo`,
+ })
+}
+
+func TestBootImageContentsNoName(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithBootImage,
+ prepareForTestWithMyapex,
+ ).RunTestWithBp(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ boot_images: [
+ "mybootimage",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "foo",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ boot_image {
+ name: "mybootimage",
+ contents: [
+ "foo",
+ "bar",
+ ],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ `)
+
+ ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+ // This does not include art, oat or vdex files as they are only included for the art boot
+ // image.
+ "javalib/bar.jar",
+ "javalib/foo.jar",
+ })
+
+ java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+ `myapex.key`,
+ `mybootimage`,
+ })
+}
+
+// TODO(b/177892522) - add test for host apex.
diff --git a/apex/builder.go b/apex/builder.go
index 106302b..e59dc96 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -517,6 +517,9 @@
outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String()
prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
+ // Figure out if need to compress apex.
+ compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.properties.Compressible, false) && !a.testApex
+
if apexType == imageApex {
////////////////////////////////////////////////////////////////////////////////////
// Step 2: create canned_fs_config which encodes filemode,uid,gid of each files
@@ -598,7 +601,7 @@
// bundletool doesn't understand what "current" is. We need to transform it to
// codename
- if moduleMinSdkVersion.IsCurrent() {
+ if moduleMinSdkVersion.IsCurrent() || moduleMinSdkVersion.IsNone() {
minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
}
// apex module doesn't have a concept of target_sdk_version, hence for the time
@@ -631,7 +634,7 @@
ctx.PropertyErrorf("test_only_no_hashtree", "not available")
return
}
- if moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) || a.testOnlyShouldSkipHashtreeGeneration() {
+ if (moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) || a.testOnlyShouldSkipHashtreeGeneration()) && !compressionEnabled {
// Apexes which are supposed to be installed in builtin dirs(/system, etc)
// don't need hashtree for activation. Therefore, by removing hashtree from
// apex bundle (filesystem image in it, to be specific), we can save storage.
@@ -687,7 +690,7 @@
implicitInputs = append(implicitInputs, unsignedOutputFile)
// Run coverage analysis
- apisUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+".txt")
+ apisUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_using.txt")
ctx.Build(pctx, android.BuildParams{
Rule: generateAPIsUsedbyApexRule,
Implicits: implicitInputs,
@@ -698,7 +701,24 @@
"readelf": "${config.ClangBin}/llvm-readelf",
},
})
- a.coverageOutputPath = apisUsedbyOutputFile
+ a.apisUsedByModuleFile = apisUsedbyOutputFile
+
+ var libNames []string
+ for _, f := range a.filesInfo {
+ if f.class == nativeSharedLib {
+ libNames = append(libNames, f.stem())
+ }
+ }
+ apisBackedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_backing.txt")
+ ndkLibraryList := android.PathForSource(ctx, "system/core/rootdir/etc/public.libraries.android.txt")
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ Tool(android.PathForSource(ctx, "build/soong/scripts/gen_ndk_backedby_apex.sh")).
+ Output(apisBackedbyOutputFile).
+ Input(ndkLibraryList).
+ Flags(libNames)
+ rule.Build("ndk_backedby_list", "Generate API libraries backed by Apex")
+ a.apisBackedByModuleFile = apisBackedbyOutputFile
bundleConfig := a.buildBundleConfig(ctx)
@@ -763,9 +783,12 @@
})
a.outputFile = signedOutputFile
- // Process APEX compression if enabled
- compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.properties.Compressible, true)
- if compressionEnabled && apexType == imageApex {
+ if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && a.testOnlyShouldForceCompression() {
+ ctx.PropertyErrorf("test_only_force_compression", "not available")
+ return
+ }
+
+ if apexType == imageApex && (compressionEnabled || a.testOnlyShouldForceCompression()) {
a.isCompressed = true
unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+".capex.unsigned")
@@ -782,6 +805,9 @@
compressRule.Build("compressRule", "Generate unsigned compressed APEX file")
signedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+".capex")
+ if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_SIGNAPK") {
+ args["outCommaList"] = signedCompressedOutputFile.String()
+ }
ctx.Build(pctx, android.BuildParams{
Rule: rule,
Description: "sign compressedApex",
@@ -902,9 +928,15 @@
return !externalDep
}
+ // Skip dependencies that are only available to APEXes; they are developed with updatability
+ // in mind and don't need manual approval.
+ if to.(android.ApexModule).NotAvailableForPlatform() {
+ return !externalDep
+ }
+
depTag := ctx.OtherModuleDependencyTag(to)
+ // Check to see if dependency been marked to skip the dependency check
if skipDepCheck, ok := depTag.(android.SkipApexAllowedDependenciesCheck); ok && skipDepCheck.SkipApexAllowedDependenciesCheck() {
- // Check to see if dependency been marked to skip the dependency check
return !externalDep
}
@@ -916,12 +948,19 @@
depInfos[to.Name()] = info
} else {
toMinSdkVersion := "(no version)"
- if m, ok := to.(interface{ MinSdkVersion() string }); ok {
+ if m, ok := to.(interface {
+ MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+ }); ok {
+ if v := m.MinSdkVersion(ctx); !v.ApiLevel.IsNone() {
+ toMinSdkVersion = v.ApiLevel.String()
+ }
+ } else if m, ok := to.(interface{ MinSdkVersion() string }); ok {
+ // TODO(b/175678607) eliminate the use of MinSdkVersion returning
+ // string
if v := m.MinSdkVersion(); v != "" {
toMinSdkVersion = v
}
}
-
depInfos[to.Name()] = android.ApexModuleDepInfo{
To: to.Name(),
From: []string{from.Name()},
diff --git a/apex/deapexer.go b/apex/deapexer.go
new file mode 100644
index 0000000..1db13f9
--- /dev/null
+++ b/apex/deapexer.go
@@ -0,0 +1,130 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package apex
+
+import (
+ "android/soong/android"
+)
+
+// Contains 'deapexer' a private module type used by 'prebuilt_apex' to make dex files contained
+// within a .apex file referenced by `prebuilt_apex` available for use by their associated
+// `java_import` modules.
+//
+// An 'apex' module references `java_library` modules from which .dex files are obtained that are
+// stored in the resulting `.apex` file. The resulting `.apex` file is then made available as a
+// prebuilt by referencing it from a `prebuilt_apex`. For each such `java_library` that is used by
+// modules outside the `.apex` file a `java_import` prebuilt is made available referencing a jar
+// that contains the Java classes.
+//
+// When building a Java module type, e.g. `java_module` or `android_app` against such prebuilts the
+// `java_import` provides the classes jar (jar containing `.class` files) against which the
+// module's `.java` files are compiled. That classes jar usually contains only stub classes. The
+// resulting classes jar is converted into a dex jar (jar containing `.dex` files). Then if
+// necessary the dex jar is further processed by `dexpreopt` to produce an optimized form of the
+// library specific to the current Android version. This process requires access to implementation
+// dex jars for each `java_import`. The `java_import` will obtain the implementation dex jar from
+// the `.apex` file in the associated `prebuilt_apex`.
+//
+// This is intentionally not registered by name as it is not intended to be used from within an
+// `Android.bp` file.
+
+// Properties that are specific to `deapexer` but which need to be provided on the `prebuilt_apex`
+// module.`
+type DeapexerProperties struct {
+ // List of java libraries that are embedded inside this prebuilt APEX bundle and for which this
+ // APEX bundle will provide dex implementation jars for use by dexpreopt and boot jars package
+ // check.
+ Exported_java_libs []string
+}
+
+type SelectedApexProperties struct {
+ // The path to the apex selected for use by this module.
+ //
+ // Is tagged as `android:"path"` because it will usually contain a string of the form ":<module>"
+ // and is tagged as "`blueprint:"mutate"` because it is only initialized in a LoadHook not an
+ // Android.bp file.
+ Selected_apex *string `android:"path" blueprint:"mutated"`
+}
+
+type Deapexer struct {
+ android.ModuleBase
+
+ properties DeapexerProperties
+ selectedApexProperties SelectedApexProperties
+
+ inputApex android.Path
+}
+
+func privateDeapexerFactory() android.Module {
+ module := &Deapexer{}
+ module.AddProperties(&module.properties, &module.selectedApexProperties)
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ return module
+}
+
+func (p *Deapexer) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // Add dependencies from the java modules to which this exports files from the `.apex` file onto
+ // this module so that they can access the `DeapexerInfo` object that this provides.
+ for _, lib := range p.properties.Exported_java_libs {
+ dep := prebuiltApexExportedModuleName(ctx, lib)
+ ctx.AddReverseDependency(ctx.Module(), android.DeapexerTag, dep)
+ }
+}
+
+func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ p.inputApex = android.OptionalPathForModuleSrc(ctx, p.selectedApexProperties.Selected_apex).Path()
+
+ // Create and remember the directory into which the .apex file's contents will be unpacked.
+ deapexerOutput := android.PathForModuleOut(ctx, "deapexer")
+
+ exports := make(map[string]android.Path)
+
+ // Create mappings from name+tag to all the required exported paths.
+ for _, l := range p.properties.Exported_java_libs {
+ // Populate the exports that this makes available. The path here must match the path of the
+ // file in the APEX created by apexFileForJavaModule(...).
+ exports[l+"{.dexjar}"] = deapexerOutput.Join(ctx, "javalib", l+".jar")
+ }
+
+ // If the prebuilt_apex exports any files then create a build rule that unpacks the apex using
+ // deapexer and verifies that all the required files were created. Also, make the mapping from
+ // name+tag to path available for other modules.
+ if len(exports) > 0 {
+ // Make the information available for other modules.
+ ctx.SetProvider(android.DeapexerProvider, android.NewDeapexerInfo(exports))
+
+ // Create a sorted list of the files that this exports.
+ exportedPaths := make(android.Paths, 0, len(exports))
+ for _, p := range exports {
+ exportedPaths = append(exportedPaths, p)
+ }
+ exportedPaths = android.SortedUniquePaths(exportedPaths)
+
+ // The apex needs to export some files so create a ninja rule to unpack the apex and check that
+ // the required files are present.
+ builder := android.NewRuleBuilder(pctx, ctx)
+ command := builder.Command()
+ command.
+ Tool(android.PathForSource(ctx, "build/soong/scripts/unpack-prebuilt-apex.sh")).
+ BuiltTool("deapexer").
+ BuiltTool("debugfs").
+ Input(p.inputApex).
+ Text(deapexerOutput.String())
+ for _, p := range exportedPaths {
+ command.Output(p.(android.WritablePath))
+ }
+ builder.Build("deapexer", "deapex "+ctx.ModuleName())
+ }
+}
diff --git a/apex/key.go b/apex/key.go
index 752888d..8b33b59 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -27,8 +27,12 @@
var String = proptools.String
func init() {
- android.RegisterModuleType("apex_key", ApexKeyFactory)
- android.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
+ registerApexKeyBuildComponents(android.InitRegistrationContext)
+}
+
+func registerApexKeyBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("apex_key", ApexKeyFactory)
+ ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
}
type apexKey struct {
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
new file mode 100644
index 0000000..74830d3
--- /dev/null
+++ b/apex/platform_bootclasspath_test.go
@@ -0,0 +1,175 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package apex
+
+import (
+ "testing"
+
+ "android/soong/android"
+ "android/soong/java"
+ "github.com/google/blueprint"
+)
+
+// Contains tests for platform_bootclasspath logic from java/platform_bootclasspath.go that requires
+// apexes.
+
+var prepareForTestWithPlatformBootclasspath = android.GroupFixturePreparers(
+ java.PrepareForTestWithDexpreopt,
+ PrepareForTestWithApexBuildComponents,
+)
+
+func TestPlatformBootclasspathDependencies(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithPlatformBootclasspath,
+ prepareForTestWithArtApex,
+ prepareForTestWithMyapex,
+ // Configure some libraries in the art and framework boot images.
+ java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "platform:foo"),
+ java.FixtureConfigureUpdatableBootJars("myapex:bar"),
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ java.FixtureWithLastReleaseApis("foo"),
+ ).RunTestWithBp(t, `
+ apex {
+ name: "com.android.art",
+ key: "com.android.art.key",
+ bootclasspath_fragments: [
+ "art-bootclasspath-fragment",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "com.android.art.key",
+ public_key: "com.android.art.avbpubkey",
+ private_key: "com.android.art.pem",
+ }
+
+ bootclasspath_fragment {
+ name: "art-bootclasspath-fragment",
+ apex_available: [
+ "com.android.art",
+ ],
+ contents: [
+ "baz",
+ "quuz",
+ ],
+ }
+
+ java_library {
+ name: "baz",
+ apex_available: [
+ "com.android.art",
+ ],
+ srcs: ["b.java"],
+ installable: true,
+ }
+
+ // Add a java_import that is not preferred and so won't have an appropriate apex variant created
+ // for it to make sure that the platform_bootclasspath doesn't try and add a dependency onto it.
+ java_import {
+ name: "baz",
+ apex_available: [
+ "com.android.art",
+ ],
+ jars: ["b.jar"],
+ }
+
+ java_library {
+ name: "quuz",
+ apex_available: [
+ "com.android.art",
+ ],
+ srcs: ["b.java"],
+ installable: true,
+ }
+
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ java_libs: [
+ "bar",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_sdk_library {
+ name: "foo",
+ srcs: ["b.java"],
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: ["myapex"],
+ permitted_packages: ["bar"],
+ }
+
+ platform_bootclasspath {
+ name: "myplatform-bootclasspath",
+
+ fragments: [
+ {
+ apex: "com.android.art",
+ module: "art-bootclasspath-fragment",
+ },
+ ],
+ }
+`,
+ )
+
+ java.CheckPlatformBootclasspathModules(t, result, "myplatform-bootclasspath", []string{
+ "com.android.art:baz",
+ "com.android.art:quuz",
+ "platform:foo",
+ "myapex:bar",
+ })
+
+ java.CheckPlatformBootclasspathFragments(t, result, "myplatform-bootclasspath", []string{
+ `com.android.art:art-bootclasspath-fragment`,
+ })
+
+ // Make sure that the myplatform-bootclasspath has the correct dependencies.
+ CheckModuleDependencies(t, result.TestContext, "myplatform-bootclasspath", "android_common", []string{
+ `platform:dex2oatd`,
+ `com.android.art:baz`,
+ `com.android.art:quuz`,
+ `platform:foo`,
+ `myapex:bar`,
+ `com.android.art:art-bootclasspath-fragment`,
+ })
+}
+
+// CheckModuleDependencies checks the dependencies of the selected module against the expected list.
+//
+// The expected list must be a list of strings of the form "<apex>:<module>", where <apex> is the
+// name of the apex, or platform is it is not part of an apex and <module> is the module name.
+func CheckModuleDependencies(t *testing.T, ctx *android.TestContext, name, variant string, expected []string) {
+ t.Helper()
+ module := ctx.ModuleForTests(name, variant).Module()
+ modules := []android.Module{}
+ ctx.VisitDirectDeps(module, func(m blueprint.Module) {
+ modules = append(modules, m.(android.Module))
+ })
+
+ pairs := java.ApexNamePairsFromModules(ctx, modules)
+ android.AssertDeepEquals(t, "module dependencies", expected, pairs)
+}
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 7931e9e..a9d24a7 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -48,6 +48,9 @@
type prebuiltCommon struct {
prebuilt android.Prebuilt
properties prebuiltCommonProperties
+
+ deapexerProperties DeapexerProperties
+ selectedApexProperties SelectedApexProperties
}
type sanitizedPrebuilt interface {
@@ -91,11 +94,130 @@
return false
}
+func (p *prebuiltCommon) deapexerDeps(ctx android.BottomUpMutatorContext) {
+ // Add dependencies onto the java modules that represent the java libraries that are provided by
+ // and exported from this prebuilt apex.
+ for _, lib := range p.deapexerProperties.Exported_java_libs {
+ dep := prebuiltApexExportedModuleName(ctx, lib)
+ ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), exportedJavaLibTag, dep)
+ }
+}
+
+// apexInfoMutator marks any modules for which this apex exports a file as requiring an apex
+// specific variant and checks that they are supported.
+//
+// The apexMutator will ensure that the ApexInfo objects passed to BuildForApex(ApexInfo) are
+// associated with the apex specific variant using the ApexInfoProvider for later retrieval.
+//
+// Unlike the source apex module type the prebuilt_apex module type cannot share compatible variants
+// across prebuilt_apex modules. That is because there is no way to determine whether two
+// prebuilt_apex modules that export files for the same module are compatible. e.g. they could have
+// been built from different source at different times or they could have been built with different
+// build options that affect the libraries.
+//
+// While it may be possible to provide sufficient information to determine whether two prebuilt_apex
+// modules were compatible it would be a lot of work and would not provide much benefit for a couple
+// of reasons:
+// * The number of prebuilt_apex modules that will be exporting files for the same module will be
+// low as the prebuilt_apex only exports files for the direct dependencies that require it and
+// very few modules are direct dependencies of multiple prebuilt_apex modules, e.g. there are a
+// few com.android.art* apex files that contain the same contents and could export files for the
+// same modules but only one of them needs to do so. Contrast that with source apex modules which
+// need apex specific variants for every module that contributes code to the apex, whether direct
+// or indirect.
+// * The build cost of a prebuilt_apex variant is generally low as at worst it will involve some
+// extra copying of files. Contrast that with source apex modules that has to build each variant
+// from source.
+func (p *prebuiltCommon) apexInfoMutator(mctx android.TopDownMutatorContext) {
+
+ // Collect direct dependencies into contents.
+ contents := make(map[string]android.ApexMembership)
+
+ // Collect the list of dependencies.
+ var dependencies []android.ApexModule
+ mctx.VisitDirectDeps(func(m android.Module) {
+ tag := mctx.OtherModuleDependencyTag(m)
+ if tag == exportedJavaLibTag {
+ depName := mctx.OtherModuleName(m)
+
+ // It is an error if the other module is not a prebuilt.
+ if _, ok := m.(android.PrebuiltInterface); !ok {
+ mctx.PropertyErrorf("exported_java_libs", "%q is not a prebuilt module", depName)
+ return
+ }
+
+ // It is an error if the other module is not an ApexModule.
+ if _, ok := m.(android.ApexModule); !ok {
+ mctx.PropertyErrorf("exported_java_libs", "%q is not usable within an apex", depName)
+ return
+ }
+
+ // Strip off the prebuilt_ prefix if present before storing content to ensure consistent
+ // behavior whether there is a corresponding source module present or not.
+ depName = android.RemoveOptionalPrebuiltPrefix(depName)
+
+ // Remember that this module was added as a direct dependency.
+ contents[depName] = contents[depName].Add(true)
+
+ // Add the module to the list of dependencies that need to have an APEX variant.
+ dependencies = append(dependencies, m.(android.ApexModule))
+ }
+ })
+
+ // Create contents for the prebuilt_apex and store it away for later use.
+ apexContents := android.NewApexContents(contents)
+ mctx.SetProvider(ApexBundleInfoProvider, ApexBundleInfo{
+ Contents: apexContents,
+ })
+
+ // Create an ApexInfo for the prebuilt_apex.
+ apexInfo := android.ApexInfo{
+ ApexVariationName: android.RemoveOptionalPrebuiltPrefix(mctx.ModuleName()),
+ InApexes: []string{mctx.ModuleName()},
+ ApexContents: []*android.ApexContents{apexContents},
+ ForPrebuiltApex: true,
+ }
+
+ // Mark the dependencies of this module as requiring a variant for this module.
+ for _, am := range dependencies {
+ am.BuildForApex(apexInfo)
+ }
+}
+
+// prebuiltApexSelectorModule is a private module type that is only created by the prebuilt_apex
+// module. It selects the apex to use and makes it available for use by prebuilt_apex and the
+// deapexer.
+type prebuiltApexSelectorModule struct {
+ android.ModuleBase
+
+ apexFileProperties ApexFileProperties
+
+ inputApex android.Path
+}
+
+func privateApexSelectorModuleFactory() android.Module {
+ module := &prebuiltApexSelectorModule{}
+ module.AddProperties(
+ &module.apexFileProperties,
+ )
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ return module
+}
+
+func (p *prebuiltApexSelectorModule) Srcs() android.Paths {
+ return android.Paths{p.inputApex}
+}
+
+func (p *prebuiltApexSelectorModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ p.inputApex = android.SingleSourcePathFromSupplier(ctx, p.apexFileProperties.prebuiltApexSelector, "src")
+}
+
type Prebuilt struct {
android.ModuleBase
prebuiltCommon
- properties PrebuiltProperties
+ properties PrebuiltProperties
+ selectedApexProperties SelectedApexProperties
inputApex android.Path
installDir android.InstallPath
@@ -107,25 +229,67 @@
compatSymlinks []string
}
-type PrebuiltProperties struct {
+type ApexFileProperties struct {
// the path to the prebuilt .apex file to import.
- Source string `blueprint:"mutated"`
-
- Src *string
+ //
+ // This cannot be marked as `android:"arch_variant"` because the `prebuilt_apex` is only mutated
+ // for android_common. That is so that it will have the same arch variant as, and so be compatible
+ // with, the source `apex` module type that it replaces.
+ Src *string `android:"path"`
Arch struct {
Arm struct {
- Src *string
+ Src *string `android:"path"`
}
Arm64 struct {
- Src *string
+ Src *string `android:"path"`
}
X86 struct {
- Src *string
+ Src *string `android:"path"`
}
X86_64 struct {
- Src *string
+ Src *string `android:"path"`
}
}
+}
+
+// prebuiltApexSelector selects the correct prebuilt APEX file for the build target.
+//
+// The ctx parameter can be for any module not just the prebuilt module so care must be taken not
+// to use methods on it that are specific to the current module.
+//
+// See the ApexFileProperties.Src property.
+func (p *ApexFileProperties) prebuiltApexSelector(ctx android.BaseModuleContext, prebuilt android.Module) []string {
+ multiTargets := prebuilt.MultiTargets()
+ if len(multiTargets) != 1 {
+ ctx.OtherModuleErrorf(prebuilt, "compile_multilib shouldn't be \"both\" for prebuilt_apex")
+ return nil
+ }
+ var src string
+ switch multiTargets[0].Arch.ArchType {
+ case android.Arm:
+ src = String(p.Arch.Arm.Src)
+ case android.Arm64:
+ src = String(p.Arch.Arm64.Src)
+ case android.X86:
+ src = String(p.Arch.X86.Src)
+ case android.X86_64:
+ src = String(p.Arch.X86_64.Src)
+ }
+ if src == "" {
+ src = String(p.Src)
+ }
+
+ if src == "" {
+ ctx.OtherModuleErrorf(prebuilt, "prebuilt_apex does not support %q", multiTargets[0].Arch.String())
+ // Drop through to return an empty string as the src (instead of nil) to avoid the prebuilt
+ // logic from reporting a more general, less useful message.
+ }
+
+ return []string{src}
+}
+
+type PrebuiltProperties struct {
+ ApexFileProperties
Installable *bool
// Optional name for the installed apex. If unspecified, name of the
@@ -166,45 +330,143 @@
}
// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
+//
+// If this needs to make files from within a `.apex` file available for use by other Soong modules,
+// e.g. make dex implementation jars available for java_import modules isted in exported_java_libs,
+// it does so as follows:
+//
+// 1. It creates a `deapexer` module that actually extracts the files from the `.apex` file and
+// makes them available for use by other modules, at both Soong and ninja levels.
+//
+// 2. It adds a dependency onto those modules and creates an apex specific variant similar to what
+// an `apex` module does. That ensures that code which looks for specific apex variant, e.g.
+// dexpreopt, will work the same way from source and prebuilt.
+//
+// 3. The `deapexer` module adds a dependency from the modules that require the exported files onto
+// itself so that they can retrieve the file paths to those files.
+//
+// It also creates a child module `selector` that is responsible for selecting the appropriate
+// input apex for both the prebuilt_apex and the deapexer. That is needed for a couple of reasons:
+// 1. To dedup the selection logic so it only runs in one module.
+// 2. To allow the deapexer to be wired up to a different source for the input apex, e.g. an
+// `apex_set`.
+//
+// prebuilt_apex
+// / | \
+// / | \
+// V | V
+// selector <--- deapexer <--- exported java lib
+//
func PrebuiltFactory() android.Module {
module := &Prebuilt{}
- module.AddProperties(&module.properties)
- android.InitSingleSourcePrebuiltModule(module, &module.properties, "Source")
+ module.AddProperties(&module.properties, &module.deapexerProperties, &module.selectedApexProperties)
+ android.InitSingleSourcePrebuiltModule(module, &module.selectedApexProperties, "Selected_apex")
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ baseModuleName := module.BaseModuleName()
+
+ apexSelectorModuleName := apexSelectorModuleName(baseModuleName)
+ createApexSelectorModule(ctx, apexSelectorModuleName, &module.properties.ApexFileProperties)
+
+ apexFileSource := ":" + apexSelectorModuleName
+ if len(module.deapexerProperties.Exported_java_libs) != 0 {
+ createDeapexerModule(ctx, deapexerModuleName(baseModuleName), apexFileSource, &module.deapexerProperties)
+ }
+
+ // Add a source reference to retrieve the selected apex from the selector module.
+ module.selectedApexProperties.Selected_apex = proptools.StringPtr(apexFileSource)
+ })
+
return module
}
+func createApexSelectorModule(ctx android.LoadHookContext, name string, apexFileProperties *ApexFileProperties) {
+ props := struct {
+ Name *string
+ }{
+ Name: proptools.StringPtr(name),
+ }
+
+ ctx.CreateModule(privateApexSelectorModuleFactory,
+ &props,
+ apexFileProperties,
+ )
+}
+
+func createDeapexerModule(ctx android.LoadHookContext, deapexerName string, apexFileSource string, deapexerProperties *DeapexerProperties) {
+ props := struct {
+ Name *string
+ Selected_apex *string
+ }{
+ Name: proptools.StringPtr(deapexerName),
+ Selected_apex: proptools.StringPtr(apexFileSource),
+ }
+ ctx.CreateModule(privateDeapexerFactory,
+ &props,
+ deapexerProperties,
+ )
+}
+
+func deapexerModuleName(baseModuleName string) string {
+ return baseModuleName + ".deapexer"
+}
+
+func apexSelectorModuleName(baseModuleName string) string {
+ return baseModuleName + ".apex.selector"
+}
+
+func prebuiltApexExportedModuleName(ctx android.BottomUpMutatorContext, name string) string {
+ // The prebuilt_apex should be depending on prebuilt modules but as this runs after
+ // prebuilt_rename the prebuilt module may or may not be using the prebuilt_ prefixed named. So,
+ // check to see if the prefixed name is in use first, if it is then use that, otherwise assume
+ // the unprefixed name is the one to use. If the unprefixed one turns out to be a source module
+ // and not a renamed prebuilt module then that will be detected and reported as an error when
+ // processing the dependency in ApexInfoMutator().
+ prebuiltName := android.PrebuiltNameFromSource(name)
+ if ctx.OtherModuleExists(prebuiltName) {
+ name = prebuiltName
+ }
+ return name
+}
+
+type exportedDependencyTag struct {
+ blueprint.BaseDependencyTag
+ name string
+}
+
+// Mark this tag so dependencies that use it are excluded from visibility enforcement.
+//
+// This does allow any prebuilt_apex to reference any module which does open up a small window for
+// restricted visibility modules to be referenced from the wrong prebuilt_apex. However, doing so
+// avoids opening up a much bigger window by widening the visibility of modules that need files
+// provided by the prebuilt_apex to include all the possible locations they may be defined, which
+// could include everything below vendor/.
+//
+// A prebuilt_apex that references a module via this tag will have to contain the appropriate files
+// corresponding to that module, otherwise it will fail when attempting to retrieve the files from
+// the .apex file. It will also have to be included in the module's apex_available property too.
+// That makes it highly unlikely that a prebuilt_apex would reference a restricted module
+// incorrectly.
+func (t exportedDependencyTag) ExcludeFromVisibilityEnforcement() {}
+
+var (
+ exportedJavaLibTag = exportedDependencyTag{name: "exported_java_lib"}
+)
+
func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
- // 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.
- if len(ctx.MultiTargets()) != 1 {
- ctx.ModuleErrorf("compile_multilib shouldn't be \"both\" for prebuilt_apex")
- return
- }
- var src string
- switch ctx.MultiTargets()[0].Arch.ArchType {
- case android.Arm:
- src = String(p.properties.Arch.Arm.Src)
- case android.Arm64:
- src = String(p.properties.Arch.Arm64.Src)
- case android.X86:
- src = String(p.properties.Arch.X86.Src)
- case android.X86_64:
- src = String(p.properties.Arch.X86_64.Src)
- default:
- ctx.ModuleErrorf("prebuilt_apex does not support %q", ctx.MultiTargets()[0].Arch.String())
- return
- }
- if src == "" {
- src = String(p.properties.Src)
- }
- p.properties.Source = src
+ p.deapexerDeps(ctx)
+}
+
+var _ ApexInfoMutator = (*Prebuilt)(nil)
+
+func (p *Prebuilt) ApexInfoMutator(mctx android.TopDownMutatorContext) {
+ p.apexInfoMutator(mctx)
}
func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// TODO(jungjw): Check the key validity.
- p.inputApex = p.Prebuilt().SingleSourcePath(ctx)
+ p.inputApex = android.OptionalPathForModuleSrc(ctx, p.selectedApexProperties.Selected_apex).Path()
p.installDir = android.PathForModuleInstall(ctx, "apex")
p.installFilename = p.InstallFilename()
if !strings.HasSuffix(p.installFilename, imageApexSuffix) {
@@ -240,7 +502,7 @@
OutputFile: android.OptionalPathForPath(p.inputApex),
Include: "$(BUILD_PREBUILT)",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
entries.SetString("LOCAL_MODULE_STEM", p.installFilename)
entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
@@ -253,6 +515,49 @@
}}
}
+// prebuiltApexExtractorModule is a private module type that is only created by the prebuilt_apex
+// module. It extracts the correct apex to use and makes it available for use by apex_set.
+type prebuiltApexExtractorModule struct {
+ android.ModuleBase
+
+ properties ApexExtractorProperties
+
+ extractedApex android.WritablePath
+}
+
+func privateApexExtractorModuleFactory() android.Module {
+ module := &prebuiltApexExtractorModule{}
+ module.AddProperties(
+ &module.properties,
+ )
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ return module
+}
+
+func (p *prebuiltApexExtractorModule) Srcs() android.Paths {
+ return android.Paths{p.extractedApex}
+}
+
+func (p *prebuiltApexExtractorModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ srcsSupplier := func(ctx android.BaseModuleContext, prebuilt android.Module) []string {
+ return p.properties.prebuiltSrcs(ctx)
+ }
+ apexSet := android.SingleSourcePathFromSupplier(ctx, srcsSupplier, "set")
+ p.extractedApex = android.PathForModuleOut(ctx, "extracted", apexSet.Base())
+ ctx.Build(pctx,
+ android.BuildParams{
+ Rule: extractMatchingApex,
+ Description: "Extract an apex from an apex set",
+ Inputs: android.Paths{apexSet},
+ Output: p.extractedApex,
+ Args: map[string]string{
+ "abis": strings.Join(java.SupportedAbis(ctx), ","),
+ "allow-prereleased": strconv.FormatBool(proptools.Bool(p.properties.Prerelease)),
+ "sdk-version": ctx.Config().PlatformSdkVersion().String(),
+ },
+ })
+}
+
type ApexSet struct {
android.ModuleBase
prebuiltCommon
@@ -271,7 +576,7 @@
postInstallCommands []string
}
-type ApexSetProperties struct {
+type ApexExtractorProperties struct {
// the .apks file path that contains prebuilt apex files to be extracted.
Set *string
@@ -287,6 +592,37 @@
}
}
+ // apexes in this set use prerelease SDK version
+ Prerelease *bool
+}
+
+func (e *ApexExtractorProperties) prebuiltSrcs(ctx android.BaseModuleContext) []string {
+ var srcs []string
+ if e.Set != nil {
+ srcs = append(srcs, *e.Set)
+ }
+
+ var sanitizers []string
+ if ctx.Host() {
+ sanitizers = ctx.Config().SanitizeHost()
+ } else {
+ sanitizers = ctx.Config().SanitizeDevice()
+ }
+
+ if android.InList("address", sanitizers) && e.Sanitized.Address.Set != nil {
+ srcs = append(srcs, *e.Sanitized.Address.Set)
+ } else if android.InList("hwaddress", sanitizers) && e.Sanitized.Hwaddress.Set != nil {
+ srcs = append(srcs, *e.Sanitized.Hwaddress.Set)
+ } else if e.Sanitized.None.Set != nil {
+ srcs = append(srcs, *e.Sanitized.None.Set)
+ }
+
+ return srcs
+}
+
+type ApexSetProperties struct {
+ ApexExtractorProperties
+
// whether the extracted apex file installable.
Installable *bool
@@ -300,33 +636,6 @@
// binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
// from PRODUCT_PACKAGES.
Overrides []string
-
- // apexes in this set use prerelease SDK version
- Prerelease *bool
-}
-
-func (a *ApexSet) prebuiltSrcs(ctx android.BaseModuleContext) []string {
- var srcs []string
- if a.properties.Set != nil {
- srcs = append(srcs, *a.properties.Set)
- }
-
- var sanitizers []string
- if ctx.Host() {
- sanitizers = ctx.Config().SanitizeHost()
- } else {
- sanitizers = ctx.Config().SanitizeDevice()
- }
-
- if android.InList("address", sanitizers) && a.properties.Sanitized.Address.Set != nil {
- srcs = append(srcs, *a.properties.Sanitized.Address.Set)
- } else if android.InList("hwaddress", sanitizers) && a.properties.Sanitized.Hwaddress.Set != nil {
- srcs = append(srcs, *a.properties.Sanitized.Hwaddress.Set)
- } else if a.properties.Sanitized.None.Set != nil {
- srcs = append(srcs, *a.properties.Sanitized.None.Set)
- }
-
- return srcs
}
func (a *ApexSet) hasSanitizedSource(sanitizer string) bool {
@@ -359,15 +668,54 @@
// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
func apexSetFactory() android.Module {
module := &ApexSet{}
- module.AddProperties(&module.properties)
+ module.AddProperties(&module.properties, &module.selectedApexProperties, &module.deapexerProperties)
- srcsSupplier := func(ctx android.BaseModuleContext) []string {
- return module.prebuiltSrcs(ctx)
+ android.InitSingleSourcePrebuiltModule(module, &module.selectedApexProperties, "Selected_apex")
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ baseModuleName := module.BaseModuleName()
+
+ apexExtractorModuleName := apexExtractorModuleName(baseModuleName)
+ createApexExtractorModule(ctx, apexExtractorModuleName, &module.properties.ApexExtractorProperties)
+
+ apexFileSource := ":" + apexExtractorModuleName
+ if len(module.deapexerProperties.Exported_java_libs) != 0 {
+ createDeapexerModule(ctx, deapexerModuleName(baseModuleName), apexFileSource, &module.deapexerProperties)
+ }
+
+ // After passing the arch specific src properties to the creating the apex selector module
+ module.selectedApexProperties.Selected_apex = proptools.StringPtr(apexFileSource)
+ })
+
+ return module
+}
+
+func createApexExtractorModule(ctx android.LoadHookContext, name string, apexExtractorProperties *ApexExtractorProperties) {
+ props := struct {
+ Name *string
+ }{
+ Name: proptools.StringPtr(name),
}
- android.InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "set")
- android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
- return module
+ ctx.CreateModule(privateApexExtractorModuleFactory,
+ &props,
+ apexExtractorProperties,
+ )
+}
+
+func apexExtractorModuleName(baseModuleName string) string {
+ return baseModuleName + ".apex.extractor"
+}
+
+func (a *ApexSet) DepsMutator(ctx android.BottomUpMutatorContext) {
+ a.deapexerDeps(ctx)
+}
+
+var _ ApexInfoMutator = (*ApexSet)(nil)
+
+func (a *ApexSet) ApexInfoMutator(mctx android.TopDownMutatorContext) {
+ a.apexInfoMutator(mctx)
}
func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -376,20 +724,13 @@
ctx.ModuleErrorf("filename should end in %s for apex_set", imageApexSuffix)
}
- apexSet := a.prebuiltCommon.prebuilt.SingleSourcePath(ctx)
+ inputApex := android.OptionalPathForModuleSrc(ctx, a.selectedApexProperties.Selected_apex).Path()
a.outputApex = android.PathForModuleOut(ctx, a.installFilename)
- ctx.Build(pctx,
- android.BuildParams{
- Rule: extractMatchingApex,
- Description: "Extract an apex from an apex set",
- Inputs: android.Paths{apexSet},
- Output: a.outputApex,
- Args: map[string]string{
- "abis": strings.Join(java.SupportedAbis(ctx), ","),
- "allow-prereleased": strconv.FormatBool(proptools.Bool(a.properties.Prerelease)),
- "sdk-version": ctx.Config().PlatformSdkVersion().String(),
- },
- })
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: inputApex,
+ Output: a.outputApex,
+ })
if a.prebuiltCommon.checkForceDisable(ctx) {
a.HideFromMake()
@@ -435,7 +776,7 @@
Include: "$(BUILD_PREBUILT)",
Host_required: a.hostRequired,
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_PATH", a.installDir.ToMakePath().String())
entries.SetString("LOCAL_MODULE_STEM", a.installFilename)
entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !a.installable())
diff --git a/apex/testing.go b/apex/testing.go
new file mode 100644
index 0000000..926125f
--- /dev/null
+++ b/apex/testing.go
@@ -0,0 +1,29 @@
+// Copyright 2021 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 apex
+
+import "android/soong/android"
+
+var PrepareForTestWithApexBuildComponents = android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(registerApexBuildComponents),
+ android.FixtureRegisterWithContext(registerApexKeyBuildComponents),
+ // Additional files needed in tests that disallow non-existent source files.
+ // This includes files that are needed by all, or at least most, instances of an apex module type.
+ android.MockFS{
+ // Needed by apex.
+ "system/core/rootdir/etc/public.libraries.android.txt": nil,
+ "build/soong/scripts/gen_ndk_backedby_apex.sh": nil,
+ }.AddToFixture(),
+)
diff --git a/apex/vndk.go b/apex/vndk.go
index f4b12b5..75c0fb0 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -17,7 +17,6 @@
import (
"path/filepath"
"strings"
- "sync"
"android/soong/android"
"android/soong/cc"
@@ -60,17 +59,6 @@
Vndk_version *string
}
-var (
- vndkApexListKey = android.NewOnceKey("vndkApexList")
- vndkApexListMutex sync.Mutex
-)
-
-func vndkApexList(config android.Config) map[string]string {
- return config.Once(vndkApexListKey, func() interface{} {
- return map[string]string{}
- }).(map[string]string)
-}
-
func apexVndkMutator(mctx android.TopDownMutatorContext) {
if ab, ok := mctx.Module().(*apexBundle); ok && ab.vndkApex {
if ab.IsNativeBridgeSupported() {
@@ -80,15 +68,6 @@
vndkVersion := ab.vndkVersion(mctx.DeviceConfig())
// Ensure VNDK APEX mount point is formatted as com.android.vndk.v###
ab.properties.Apex_name = proptools.StringPtr(vndkApexNamePrefix + vndkVersion)
-
- // vndk_version should be unique
- vndkApexListMutex.Lock()
- defer vndkApexListMutex.Unlock()
- vndkApexList := vndkApexList(mctx.Config())
- if other, ok := vndkApexList[vndkVersion]; ok {
- mctx.PropertyErrorf("vndk_version", "%v is already defined in %q", vndkVersion, other)
- }
- vndkApexList[vndkVersion] = mctx.ModuleName()
}
}
@@ -99,9 +78,16 @@
if vndkVersion == "" {
vndkVersion = mctx.DeviceConfig().PlatformVndkVersion()
}
- vndkApexList := vndkApexList(mctx.Config())
- if vndkApex, ok := vndkApexList[vndkVersion]; ok {
- mctx.AddReverseDependency(mctx.Module(), sharedLibTag, vndkApex)
+ if vndkVersion == mctx.DeviceConfig().PlatformVndkVersion() {
+ vndkVersion = "current"
+ } else {
+ vndkVersion = "v" + vndkVersion
+ }
+
+ vndkApexName := "com.android.vndk." + vndkVersion
+
+ if mctx.OtherModuleExists(vndkApexName) {
+ mctx.AddReverseDependency(mctx.Module(), sharedLibTag, vndkApexName)
}
} else if a, ok := mctx.Module().(*apexBundle); ok && a.vndkApex {
vndkVersion := proptools.StringDefault(a.vndkProperties.Vndk_version, "current")
diff --git a/apex/vndk_test.go b/apex/vndk_test.go
index 27d93ee..d580e5a 100644
--- a/apex/vndk_test.go
+++ b/apex/vndk_test.go
@@ -9,14 +9,15 @@
)
func TestVndkApexForVndkLite(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex_vndk {
- name: "myapex",
- key: "myapex.key",
+ name: "com.android.vndk.current",
+ key: "com.android.vndk.current.key",
+ updatable: false,
}
apex_key {
- name: "myapex.key",
+ name: "com.android.vndk.current.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
@@ -31,7 +32,7 @@
},
system_shared_libs: [],
stl: "none",
- apex_available: [ "myapex" ],
+ apex_available: [ "com.android.vndk.current" ],
}
cc_library {
@@ -45,29 +46,33 @@
},
system_shared_libs: [],
stl: "none",
- apex_available: [ "myapex" ],
+ apex_available: [ "com.android.vndk.current" ],
}
- `+vndkLibrariesTxtFiles("current"), func(fs map[string][]byte, config android.Config) {
- config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("")
- })
+ `+vndkLibrariesTxtFiles("current"),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.DeviceVndkVersion = proptools.StringPtr("")
+ }),
+ )
// VNDK-Lite contains only core variants of VNDK-Sp libraries
- ensureExactContents(t, ctx, "myapex", "android_common_image", []string{
+ ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{
"lib/libvndksp.so",
"lib/libc++.so",
"lib64/libvndksp.so",
"lib64/libc++.so",
- "etc/llndk.libraries.VER.txt",
- "etc/vndkcore.libraries.VER.txt",
- "etc/vndksp.libraries.VER.txt",
- "etc/vndkprivate.libraries.VER.txt",
+ "etc/llndk.libraries.29.txt",
+ "etc/vndkcore.libraries.29.txt",
+ "etc/vndksp.libraries.29.txt",
+ "etc/vndkprivate.libraries.29.txt",
+ "etc/vndkproduct.libraries.29.txt",
})
}
func TestVndkApexUsesVendorVariant(t *testing.T) {
bp := `
apex_vndk {
- name: "myapex",
+ name: "com.android.vndk.current",
key: "mykey",
+ updatable: false,
}
apex_key {
name: "mykey",
@@ -93,11 +98,11 @@
return
}
}
- t.Fail()
+ t.Errorf("expected path %q not found", path)
}
t.Run("VNDK lib doesn't have an apex variant", func(t *testing.T) {
- ctx, _ := testApex(t, bp)
+ ctx := testApex(t, bp)
// libfoo doesn't have apex variants
for _, variant := range ctx.ModuleVariantsForTests("libfoo") {
@@ -105,30 +110,34 @@
}
// VNDK APEX doesn't create apex variant
- files := getFiles(t, ctx, "myapex", "android_common_image")
- ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared/libfoo.so")
+ files := getFiles(t, ctx, "com.android.vndk.current", "android_common_image")
+ ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.29_arm_armv7-a-neon_shared/libfoo.so")
})
t.Run("VNDK APEX gathers only vendor variants even if product variants are available", func(t *testing.T) {
- ctx, _ := testApex(t, bp, func(fs map[string][]byte, config android.Config) {
- // Now product variant is available
- config.TestProductVariables.ProductVndkVersion = proptools.StringPtr("current")
- })
+ ctx := testApex(t, bp,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ // Now product variant is available
+ variables.ProductVndkVersion = proptools.StringPtr("current")
+ }),
+ )
- files := getFiles(t, ctx, "myapex", "android_common_image")
- ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared/libfoo.so")
+ files := getFiles(t, ctx, "com.android.vndk.current", "android_common_image")
+ ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.29_arm_armv7-a-neon_shared/libfoo.so")
})
t.Run("VNDK APEX supports coverage variants", func(t *testing.T) {
- ctx, _ := testApex(t, bp, func(fs map[string][]byte, config android.Config) {
- config.TestProductVariables.GcovCoverage = proptools.BoolPtr(true)
- config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
- })
+ ctx := testApex(t, bp,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.GcovCoverage = proptools.BoolPtr(true)
+ variables.Native_coverage = proptools.BoolPtr(true)
+ }),
+ )
- files := getFiles(t, ctx, "myapex", "android_common_image")
- ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared/libfoo.so")
+ files := getFiles(t, ctx, "com.android.vndk.current", "android_common_image")
+ ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.29_arm_armv7-a-neon_shared/libfoo.so")
- files = getFiles(t, ctx, "myapex", "android_common_cov_image")
- ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared_cov/libfoo.so")
+ files = getFiles(t, ctx, "com.android.vndk.current", "android_common_cov_image")
+ ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.29_arm_armv7-a-neon_shared_cov/libfoo.so")
})
}
diff --git a/bazel/Android.bp b/bazel/Android.bp
index 05eddc1..b7c185a 100644
--- a/bazel/Android.bp
+++ b/bazel/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-bazel",
pkgPath: "android/soong/bazel",
@@ -6,6 +10,10 @@
"constants.go",
"properties.go",
],
+ testSrcs: [
+ "aquery_test.go",
+ "properties_test.go",
+ ],
pluginFor: [
"soong_build",
],
diff --git a/bazel/aquery.go b/bazel/aquery.go
index 69d4fde..555f1dc 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -16,6 +16,8 @@
import (
"encoding/json"
+ "fmt"
+ "path/filepath"
"strings"
"github.com/google/blueprint/proptools"
@@ -24,8 +26,14 @@
// artifact contains relevant portions of Bazel's aquery proto, Artifact.
// Represents a single artifact, whether it's a source file or a derived output file.
type artifact struct {
- Id string
- ExecPath string
+ Id int
+ PathFragmentId int
+}
+
+type pathFragment struct {
+ Id int
+ Label string
+ ParentId int
}
// KeyValuePair represents Bazel's aquery proto, KeyValuePair.
@@ -38,9 +46,9 @@
// Represents a data structure containing one or more files. Depsets in Bazel are an efficient
// data structure for storing large numbers of file paths.
type depSetOfFiles struct {
- Id string
- // TODO(cparsons): Handle non-flat depsets.
- DirectArtifactIds []string
+ Id int
+ DirectArtifactIds []int
+ TransitiveDepSetIds []int
}
// action contains relevant portions of Bazel's aquery proto, Action.
@@ -48,9 +56,9 @@
type action struct {
Arguments []string
EnvironmentVariables []KeyValuePair
- InputDepSetIds []string
+ InputDepSetIds []int
Mnemonic string
- OutputIds []string
+ OutputIds []int
}
// actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
@@ -59,12 +67,14 @@
Artifacts []artifact
Actions []action
DepSetOfFiles []depSetOfFiles
+ PathFragments []pathFragment
}
// BuildStatement contains information to register a build statement corresponding (one to one)
// with a Bazel action from Bazel's action graph.
type BuildStatement struct {
Command string
+ Depfile *string
OutputPaths []string
InputPaths []string
Env []KeyValuePair
@@ -74,43 +84,164 @@
// AqueryBuildStatements returns an array of BuildStatements which should be registered (and output
// to a ninja file) to correspond one-to-one with the given action graph json proto (from a bazel
// aquery invocation).
-func AqueryBuildStatements(aqueryJsonProto []byte) []BuildStatement {
+func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) {
buildStatements := []BuildStatement{}
var aqueryResult actionGraphContainer
- json.Unmarshal(aqueryJsonProto, &aqueryResult)
+ err := json.Unmarshal(aqueryJsonProto, &aqueryResult)
- artifactIdToPath := map[string]string{}
- for _, artifact := range aqueryResult.Artifacts {
- artifactIdToPath[artifact.Id] = artifact.ExecPath
+ if err != nil {
+ return nil, err
}
- depsetIdToArtifactIds := map[string][]string{}
+
+ pathFragments := map[int]pathFragment{}
+ for _, pathFragment := range aqueryResult.PathFragments {
+ pathFragments[pathFragment.Id] = pathFragment
+ }
+ artifactIdToPath := map[int]string{}
+ for _, artifact := range aqueryResult.Artifacts {
+ artifactPath, err := expandPathFragment(artifact.PathFragmentId, pathFragments)
+ if err != nil {
+ return nil, err
+ }
+ artifactIdToPath[artifact.Id] = artifactPath
+ }
+
+ depsetIdToDepset := map[int]depSetOfFiles{}
for _, depset := range aqueryResult.DepSetOfFiles {
- depsetIdToArtifactIds[depset.Id] = depset.DirectArtifactIds
+ depsetIdToDepset[depset.Id] = depset
+ }
+
+ // depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening
+ // may be an expensive operation.
+ depsetIdToArtifactIdsCache := map[int][]int{}
+
+ // Do a pass through all actions to identify which artifacts are middleman artifacts.
+ // These will be omitted from the inputs of other actions.
+ // TODO(b/180945500): Handle middleman actions; without proper handling, depending on generated
+ // headers may cause build failures.
+ middlemanArtifactIds := map[int]bool{}
+ for _, actionEntry := range aqueryResult.Actions {
+ if actionEntry.Mnemonic == "Middleman" {
+ for _, outputId := range actionEntry.OutputIds {
+ middlemanArtifactIds[outputId] = true
+ }
+ }
}
for _, actionEntry := range aqueryResult.Actions {
+ if shouldSkipAction(actionEntry) {
+ continue
+ }
outputPaths := []string{}
+ var depfile *string
for _, outputId := range actionEntry.OutputIds {
- // TODO(cparsons): Validate the id is present.
- outputPaths = append(outputPaths, artifactIdToPath[outputId])
+ outputPath, exists := artifactIdToPath[outputId]
+ if !exists {
+ return nil, fmt.Errorf("undefined outputId %d", outputId)
+ }
+ ext := filepath.Ext(outputPath)
+ if ext == ".d" {
+ if depfile != nil {
+ return nil, fmt.Errorf("found multiple potential depfiles %q, %q", *depfile, outputPath)
+ } else {
+ depfile = &outputPath
+ }
+ } else {
+ outputPaths = append(outputPaths, outputPath)
+ }
}
inputPaths := []string{}
for _, inputDepSetId := range actionEntry.InputDepSetIds {
- // TODO(cparsons): Validate the id is present.
- for _, inputId := range depsetIdToArtifactIds[inputDepSetId] {
- // TODO(cparsons): Validate the id is present.
- inputPaths = append(inputPaths, artifactIdToPath[inputId])
+ inputArtifacts, err :=
+ artifactIdsFromDepsetId(depsetIdToDepset, depsetIdToArtifactIdsCache, inputDepSetId)
+ if err != nil {
+ return nil, err
+ }
+ for _, inputId := range inputArtifacts {
+ if _, isMiddlemanArtifact := middlemanArtifactIds[inputId]; isMiddlemanArtifact {
+ // Omit middleman artifacts.
+ continue
+ }
+ inputPath, exists := artifactIdToPath[inputId]
+ if !exists {
+ return nil, fmt.Errorf("undefined input artifactId %d", inputId)
+ }
+ inputPaths = append(inputPaths, inputPath)
}
}
buildStatement := BuildStatement{
Command: strings.Join(proptools.ShellEscapeList(actionEntry.Arguments), " "),
+ Depfile: depfile,
OutputPaths: outputPaths,
InputPaths: inputPaths,
Env: actionEntry.EnvironmentVariables,
Mnemonic: actionEntry.Mnemonic}
+ if len(actionEntry.Arguments) < 1 {
+ return nil, fmt.Errorf("received action with no command: [%v]", buildStatement)
+ continue
+ }
buildStatements = append(buildStatements, buildStatement)
}
- return buildStatements
+ return buildStatements, nil
+}
+
+func shouldSkipAction(a action) bool {
+ // TODO(b/180945121): Handle symlink actions.
+ if a.Mnemonic == "Symlink" || a.Mnemonic == "SourceSymlinkManifest" || a.Mnemonic == "SymlinkTree" {
+ return true
+ }
+ // TODO(b/180945500): Handle middleman actions; without proper handling, depending on generated
+ // headers may cause build failures.
+ if a.Mnemonic == "Middleman" {
+ return true
+ }
+ // Skip "Fail" actions, which are placeholder actions designed to always fail.
+ if a.Mnemonic == "Fail" {
+ return true
+ }
+ // TODO(b/180946980): Handle FileWrite. The aquery proto currently contains no information
+ // about the contents that are written.
+ if a.Mnemonic == "FileWrite" {
+ return true
+ }
+ return false
+}
+
+func artifactIdsFromDepsetId(depsetIdToDepset map[int]depSetOfFiles,
+ depsetIdToArtifactIdsCache map[int][]int, depsetId int) ([]int, error) {
+ if result, exists := depsetIdToArtifactIdsCache[depsetId]; exists {
+ return result, nil
+ }
+ if depset, exists := depsetIdToDepset[depsetId]; exists {
+ result := depset.DirectArtifactIds
+ for _, childId := range depset.TransitiveDepSetIds {
+ childArtifactIds, err :=
+ artifactIdsFromDepsetId(depsetIdToDepset, depsetIdToArtifactIdsCache, childId)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, childArtifactIds...)
+ }
+ depsetIdToArtifactIdsCache[depsetId] = result
+ return result, nil
+ } else {
+ return nil, fmt.Errorf("undefined input depsetId %d", depsetId)
+ }
+}
+
+func expandPathFragment(id int, pathFragmentsMap map[int]pathFragment) (string, error) {
+ labels := []string{}
+ currId := id
+ // Only positive IDs are valid for path fragments. An ID of zero indicates a terminal node.
+ for currId > 0 {
+ currFragment, ok := pathFragmentsMap[currId]
+ if !ok {
+ return "", fmt.Errorf("undefined path fragment id %d", currId)
+ }
+ labels = append([]string{currFragment.Label}, labels...)
+ currId = currFragment.ParentId
+ }
+ return filepath.Join(labels...), nil
}
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
new file mode 100644
index 0000000..fa8810f
--- /dev/null
+++ b/bazel/aquery_test.go
@@ -0,0 +1,777 @@
+// Copyright 2020 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 bazel
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+)
+
+func TestAqueryMultiArchGenrule(t *testing.T) {
+ // This input string is retrieved from a real build of bionic-related genrules.
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 6
+ }, {
+ "id": 3,
+ "pathFragmentId": 8
+ }, {
+ "id": 4,
+ "pathFragmentId": 12
+ }, {
+ "id": 5,
+ "pathFragmentId": 19
+ }, {
+ "id": 6,
+ "pathFragmentId": 20
+ }, {
+ "id": 7,
+ "pathFragmentId": 21
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7",
+ "mnemonic": "Genrule",
+ "configurationId": 1,
+ "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm.S"],
+ "environmentVariables": [{
+ "key": "PATH",
+ "value": "/bin:/usr/bin:/usr/local/bin"
+ }],
+ "inputDepSetIds": [1],
+ "outputIds": [4],
+ "primaryOutputId": 4
+ }, {
+ "targetId": 2,
+ "actionKey": "9f4309ce165dac458498cb92811c18b0b7919782cc37b82a42d2141b8cc90826",
+ "mnemonic": "Genrule",
+ "configurationId": 1,
+ "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86.S"],
+ "environmentVariables": [{
+ "key": "PATH",
+ "value": "/bin:/usr/bin:/usr/local/bin"
+ }],
+ "inputDepSetIds": [2],
+ "outputIds": [5],
+ "primaryOutputId": 5
+ }, {
+ "targetId": 3,
+ "actionKey": "50d6c586103ebeed3a218195540bcc30d329464eae36377eb82f8ce7c36ac342",
+ "mnemonic": "Genrule",
+ "configurationId": 1,
+ "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86_64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86_64.S"],
+ "environmentVariables": [{
+ "key": "PATH",
+ "value": "/bin:/usr/bin:/usr/local/bin"
+ }],
+ "inputDepSetIds": [3],
+ "outputIds": [6],
+ "primaryOutputId": 6
+ }, {
+ "targetId": 4,
+ "actionKey": "f30cbe442f5216f4223cf16a39112cad4ec56f31f49290d85cff587e48647ffa",
+ "mnemonic": "Genrule",
+ "configurationId": 1,
+ "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm64.S"],
+ "environmentVariables": [{
+ "key": "PATH",
+ "value": "/bin:/usr/bin:/usr/local/bin"
+ }],
+ "inputDepSetIds": [4],
+ "outputIds": [7],
+ "primaryOutputId": 7
+ }],
+ "targets": [{
+ "id": 1,
+ "label": "@sourceroot//bionic/libc:syscalls-arm",
+ "ruleClassId": 1
+ }, {
+ "id": 2,
+ "label": "@sourceroot//bionic/libc:syscalls-x86",
+ "ruleClassId": 1
+ }, {
+ "id": 3,
+ "label": "@sourceroot//bionic/libc:syscalls-x86_64",
+ "ruleClassId": 1
+ }, {
+ "id": 4,
+ "label": "@sourceroot//bionic/libc:syscalls-arm64",
+ "ruleClassId": 1
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1, 2, 3]
+ }, {
+ "id": 2,
+ "directArtifactIds": [1, 2, 3]
+ }, {
+ "id": 3,
+ "directArtifactIds": [1, 2, 3]
+ }, {
+ "id": 4,
+ "directArtifactIds": [1, 2, 3]
+ }],
+ "configuration": [{
+ "id": 1,
+ "mnemonic": "k8-fastbuild",
+ "platformName": "k8",
+ "checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046"
+ }],
+ "ruleClasses": [{
+ "id": 1,
+ "name": "genrule"
+ }],
+ "pathFragments": [{
+ "id": 5,
+ "label": ".."
+ }, {
+ "id": 4,
+ "label": "sourceroot",
+ "parentId": 5
+ }, {
+ "id": 3,
+ "label": "bionic",
+ "parentId": 4
+ }, {
+ "id": 2,
+ "label": "libc",
+ "parentId": 3
+ }, {
+ "id": 1,
+ "label": "SYSCALLS.TXT",
+ "parentId": 2
+ }, {
+ "id": 7,
+ "label": "tools",
+ "parentId": 2
+ }, {
+ "id": 6,
+ "label": "gensyscalls.py",
+ "parentId": 7
+ }, {
+ "id": 11,
+ "label": "bazel_tools",
+ "parentId": 5
+ }, {
+ "id": 10,
+ "label": "tools",
+ "parentId": 11
+ }, {
+ "id": 9,
+ "label": "genrule",
+ "parentId": 10
+ }, {
+ "id": 8,
+ "label": "genrule-setup.sh",
+ "parentId": 9
+ }, {
+ "id": 18,
+ "label": "bazel-out"
+ }, {
+ "id": 17,
+ "label": "sourceroot",
+ "parentId": 18
+ }, {
+ "id": 16,
+ "label": "k8-fastbuild",
+ "parentId": 17
+ }, {
+ "id": 15,
+ "label": "bin",
+ "parentId": 16
+ }, {
+ "id": 14,
+ "label": "bionic",
+ "parentId": 15
+ }, {
+ "id": 13,
+ "label": "libc",
+ "parentId": 14
+ }, {
+ "id": 12,
+ "label": "syscalls-arm.S",
+ "parentId": 13
+ }, {
+ "id": 19,
+ "label": "syscalls-x86.S",
+ "parentId": 13
+ }, {
+ "id": 20,
+ "label": "syscalls-x86_64.S",
+ "parentId": 13
+ }, {
+ "id": 21,
+ "label": "syscalls-arm64.S",
+ "parentId": 13
+ }]
+}`
+ actualbuildStatements, _ := AqueryBuildStatements([]byte(inputString))
+ expectedBuildStatements := []BuildStatement{}
+ for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
+ expectedBuildStatements = append(expectedBuildStatements,
+ BuildStatement{
+ Command: fmt.Sprintf(
+ "/bin/bash -c 'source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py %s ../sourceroot/bionic/libc/SYSCALLS.TXT > bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S'",
+ arch, arch),
+ OutputPaths: []string{
+ fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch),
+ },
+ InputPaths: []string{
+ "../sourceroot/bionic/libc/SYSCALLS.TXT",
+ "../sourceroot/bionic/libc/tools/gensyscalls.py",
+ "../bazel_tools/tools/genrule/genrule-setup.sh",
+ },
+ Env: []KeyValuePair{
+ KeyValuePair{Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
+ },
+ Mnemonic: "Genrule",
+ })
+ }
+ assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
+}
+
+func TestInvalidOutputId(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "inputDepSetIds": [1],
+ "outputIds": [3],
+ "primaryOutputId": 3
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1, 2]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "two"
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, "undefined outputId 3")
+}
+
+func TestInvalidInputDepsetId(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "inputDepSetIds": [2],
+ "outputIds": [1],
+ "primaryOutputId": 1
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1, 2]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "two"
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, "undefined input depsetId 2")
+}
+
+func TestInvalidInputArtifactId(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "inputDepSetIds": [1],
+ "outputIds": [1],
+ "primaryOutputId": 1
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1, 3]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "two"
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, "undefined input artifactId 3")
+}
+
+func TestInvalidPathFragmentId(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "inputDepSetIds": [1],
+ "outputIds": [1],
+ "primaryOutputId": 1
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1, 2]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "two",
+ "parentId": 3
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, "undefined path fragment id 3")
+}
+
+func TestDepfiles(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }, {
+ "id": 3,
+ "pathFragmentId": 3
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "inputDepSetIds": [1],
+ "outputIds": [2, 3],
+ "primaryOutputId": 2
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1, 2, 3]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "two"
+ }, {
+ "id": 3,
+ "label": "two.d"
+ }]
+}`
+
+ actual, err := AqueryBuildStatements([]byte(inputString))
+ if err != nil {
+ t.Errorf("Unexpected error %q", err)
+ }
+ if expected := 1; len(actual) != expected {
+ t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
+ }
+
+ bs := actual[0]
+ expectedDepfile := "two.d"
+ if bs.Depfile == nil {
+ t.Errorf("Expected depfile %q, but there was none found", expectedDepfile)
+ } else if *bs.Depfile != expectedDepfile {
+ t.Errorf("Expected depfile %q, but got %q", expectedDepfile, *bs.Depfile)
+ }
+}
+
+func TestMultipleDepfiles(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }, {
+ "id": 3,
+ "pathFragmentId": 3
+ }, {
+ "id": 4,
+ "pathFragmentId": 4
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "inputDepSetIds": [1],
+ "outputIds": [2,3,4],
+ "primaryOutputId": 2
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1, 2, 3, 4]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "two"
+ }, {
+ "id": 3,
+ "label": "two.d"
+ }, {
+ "id": 4,
+ "label": "other.d"
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`)
+}
+
+func TestTransitiveInputDepsets(t *testing.T) {
+ // The input aquery for this test comes from a proof-of-concept starlark rule which registers
+ // a single action with many inputs given via a deep depset.
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 7
+ }, {
+ "id": 3,
+ "pathFragmentId": 8
+ }, {
+ "id": 4,
+ "pathFragmentId": 9
+ }, {
+ "id": 5,
+ "pathFragmentId": 10
+ }, {
+ "id": 6,
+ "pathFragmentId": 11
+ }, {
+ "id": 7,
+ "pathFragmentId": 12
+ }, {
+ "id": 8,
+ "pathFragmentId": 13
+ }, {
+ "id": 9,
+ "pathFragmentId": 14
+ }, {
+ "id": 10,
+ "pathFragmentId": 15
+ }, {
+ "id": 11,
+ "pathFragmentId": 16
+ }, {
+ "id": 12,
+ "pathFragmentId": 17
+ }, {
+ "id": 13,
+ "pathFragmentId": 18
+ }, {
+ "id": 14,
+ "pathFragmentId": 19
+ }, {
+ "id": 15,
+ "pathFragmentId": 20
+ }, {
+ "id": 16,
+ "pathFragmentId": 21
+ }, {
+ "id": 17,
+ "pathFragmentId": 22
+ }, {
+ "id": 18,
+ "pathFragmentId": 23
+ }, {
+ "id": 19,
+ "pathFragmentId": 24
+ }, {
+ "id": 20,
+ "pathFragmentId": 25
+ }, {
+ "id": 21,
+ "pathFragmentId": 26
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "3b826d17fadbbbcd8313e456b90ec47c078c438088891dd45b4adbcd8889dc50",
+ "mnemonic": "Action",
+ "configurationId": 1,
+ "arguments": ["/bin/bash", "-c", "touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"],
+ "inputDepSetIds": [1],
+ "outputIds": [21],
+ "primaryOutputId": 21
+ }],
+ "depSetOfFiles": [{
+ "id": 3,
+ "directArtifactIds": [1, 2, 3, 4, 5]
+ }, {
+ "id": 4,
+ "directArtifactIds": [6, 7, 8, 9, 10]
+ }, {
+ "id": 2,
+ "transitiveDepSetIds": [3, 4],
+ "directArtifactIds": [11, 12, 13, 14, 15]
+ }, {
+ "id": 5,
+ "directArtifactIds": [16, 17, 18, 19]
+ }, {
+ "id": 1,
+ "transitiveDepSetIds": [2, 5],
+ "directArtifactIds": [20]
+ }],
+ "pathFragments": [{
+ "id": 6,
+ "label": "bazel-out"
+ }, {
+ "id": 5,
+ "label": "sourceroot",
+ "parentId": 6
+ }, {
+ "id": 4,
+ "label": "k8-fastbuild",
+ "parentId": 5
+ }, {
+ "id": 3,
+ "label": "bin",
+ "parentId": 4
+ }, {
+ "id": 2,
+ "label": "testpkg",
+ "parentId": 3
+ }, {
+ "id": 1,
+ "label": "test_1",
+ "parentId": 2
+ }, {
+ "id": 7,
+ "label": "test_2",
+ "parentId": 2
+ }, {
+ "id": 8,
+ "label": "test_3",
+ "parentId": 2
+ }, {
+ "id": 9,
+ "label": "test_4",
+ "parentId": 2
+ }, {
+ "id": 10,
+ "label": "test_5",
+ "parentId": 2
+ }, {
+ "id": 11,
+ "label": "test_6",
+ "parentId": 2
+ }, {
+ "id": 12,
+ "label": "test_7",
+ "parentId": 2
+ }, {
+ "id": 13,
+ "label": "test_8",
+ "parentId": 2
+ }, {
+ "id": 14,
+ "label": "test_9",
+ "parentId": 2
+ }, {
+ "id": 15,
+ "label": "test_10",
+ "parentId": 2
+ }, {
+ "id": 16,
+ "label": "test_11",
+ "parentId": 2
+ }, {
+ "id": 17,
+ "label": "test_12",
+ "parentId": 2
+ }, {
+ "id": 18,
+ "label": "test_13",
+ "parentId": 2
+ }, {
+ "id": 19,
+ "label": "test_14",
+ "parentId": 2
+ }, {
+ "id": 20,
+ "label": "test_15",
+ "parentId": 2
+ }, {
+ "id": 21,
+ "label": "test_16",
+ "parentId": 2
+ }, {
+ "id": 22,
+ "label": "test_17",
+ "parentId": 2
+ }, {
+ "id": 23,
+ "label": "test_18",
+ "parentId": 2
+ }, {
+ "id": 24,
+ "label": "test_19",
+ "parentId": 2
+ }, {
+ "id": 25,
+ "label": "test_root",
+ "parentId": 2
+ }, {
+ "id": 26,
+ "label": "test_out",
+ "parentId": 2
+ }]
+}`
+
+ actualbuildStatements, _ := AqueryBuildStatements([]byte(inputString))
+ // Inputs for the action are test_{i} from 1 to 20, and test_root. These inputs
+ // are given via a deep depset, but the depset is flattened when returned as a
+ // BuildStatement slice.
+ inputPaths := []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_root"}
+ for i := 1; i < 20; i++ {
+ inputPaths = append(inputPaths, fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_%d", i))
+ }
+ expectedBuildStatements := []BuildStatement{
+ BuildStatement{
+ Command: "/bin/bash -c touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out",
+ OutputPaths: []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
+ InputPaths: inputPaths,
+ Mnemonic: "Action",
+ },
+ }
+ assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
+}
+
+func assertError(t *testing.T, err error, expected string) {
+ if err == nil {
+ t.Errorf("expected error '%s', but got no error", expected)
+ } else if err.Error() != expected {
+ t.Errorf("expected error '%s', but got: %s", expected, err.Error())
+ }
+}
+
+// Asserts that the given actual build statements match the given expected build statements.
+// Build statement equivalence is determined using buildStatementEquals.
+func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
+ if len(expected) != len(actual) {
+ t.Errorf("expected %d build statements, but got %d,\n expected: %v,\n actual: %v",
+ len(expected), len(actual), expected, actual)
+ return
+ }
+ACTUAL_LOOP:
+ for _, actualStatement := range actual {
+ for _, expectedStatement := range expected {
+ if buildStatementEquals(actualStatement, expectedStatement) {
+ continue ACTUAL_LOOP
+ }
+ }
+ t.Errorf("unexpected build statement %v.\n expected: %v",
+ actualStatement, expected)
+ return
+ }
+}
+
+func buildStatementEquals(first BuildStatement, second BuildStatement) bool {
+ if first.Mnemonic != second.Mnemonic {
+ return false
+ }
+ if first.Command != second.Command {
+ return false
+ }
+ // Ordering is significant for environment variables.
+ if !reflect.DeepEqual(first.Env, second.Env) {
+ return false
+ }
+ // Ordering is irrelevant for input and output paths, so compare sets.
+ if !reflect.DeepEqual(stringSet(first.InputPaths), stringSet(second.InputPaths)) {
+ return false
+ }
+ if !reflect.DeepEqual(stringSet(first.OutputPaths), stringSet(second.OutputPaths)) {
+ return false
+ }
+ return true
+}
+
+func stringSet(stringSlice []string) map[string]struct{} {
+ stringMap := make(map[string]struct{})
+ for _, s := range stringSlice {
+ stringMap[s] = struct{}{}
+ }
+ return stringMap
+}
diff --git a/bazel/cquery/Android.bp b/bazel/cquery/Android.bp
new file mode 100644
index 0000000..3a71e9c
--- /dev/null
+++ b/bazel/cquery/Android.bp
@@ -0,0 +1,14 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-cquery",
+ pkgPath: "android/soong/bazel/cquery",
+ srcs: [
+ "request_type.go",
+ ],
+ pluginFor: [
+ "soong_build",
+ ],
+}
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
new file mode 100644
index 0000000..8049108
--- /dev/null
+++ b/bazel/cquery/request_type.go
@@ -0,0 +1,118 @@
+package cquery
+
+import (
+ "fmt"
+ "strings"
+)
+
+var (
+ GetOutputFiles = &getOutputFilesRequestType{}
+ GetCcInfo = &getCcInfoType{}
+)
+
+type CcInfo struct {
+ OutputFiles []string
+ CcObjectFiles []string
+ CcStaticLibraryFiles []string
+}
+
+type getOutputFilesRequestType struct{}
+
+// Name returns a string name for this request type. Such request type names must be unique,
+// and must only consist of alphanumeric characters.
+func (g getOutputFilesRequestType) Name() string {
+ return "getOutputFiles"
+}
+
+// StarlarkFunctionBody returns a starlark function body to process this request type.
+// The returned string is the body of a Starlark function which obtains
+// all request-relevant information about a target and returns a string containing
+// this information.
+// The function should have the following properties:
+// - `target` is the only parameter to this function (a configured target).
+// - The return value must be a string.
+// - The function body should not be indented outside of its own scope.
+func (g getOutputFilesRequestType) StarlarkFunctionBody() string {
+ return "return ', '.join([f.path for f in target.files.to_list()])"
+}
+
+// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
+// The given rawString must correspond to the string output which was created by evaluating the
+// Starlark given in StarlarkFunctionBody.
+func (g getOutputFilesRequestType) ParseResult(rawString string) []string {
+ return splitOrEmpty(rawString, ", ")
+}
+
+type getCcInfoType struct{}
+
+// Name returns a string name for this request type. Such request type names must be unique,
+// and must only consist of alphanumeric characters.
+func (g getCcInfoType) Name() string {
+ return "getCcInfo"
+}
+
+// StarlarkFunctionBody returns a starlark function body to process this request type.
+// The returned string is the body of a Starlark function which obtains
+// all request-relevant information about a target and returns a string containing
+// this information.
+// The function should have the following properties:
+// - `target` is the only parameter to this function (a configured target).
+// - The return value must be a string.
+// - The function body should not be indented outside of its own scope.
+func (g getCcInfoType) StarlarkFunctionBody() string {
+ return `
+outputFiles = [f.path for f in target.files.to_list()]
+
+ccObjectFiles = []
+staticLibraries = []
+linker_inputs = providers(target)["CcInfo"].linking_context.linker_inputs.to_list()
+
+for linker_input in linker_inputs:
+ for library in linker_input.libraries:
+ for object in library.objects:
+ ccObjectFiles += [object.path]
+ if library.static_library:
+ staticLibraries.append(library.static_library.path)
+
+returns = [
+ outputFiles,
+ staticLibraries,
+ ccObjectFiles,
+]
+
+return "|".join([", ".join(r) for r in returns])`
+}
+
+// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
+// The given rawString must correspond to the string output which was created by evaluating the
+// Starlark given in StarlarkFunctionBody.
+func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) {
+ var outputFiles []string
+ var ccObjects []string
+
+ splitString := strings.Split(rawString, "|")
+ if expectedLen := 3; len(splitString) != expectedLen {
+ return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString)
+ }
+ outputFilesString := splitString[0]
+ ccStaticLibrariesString := splitString[1]
+ ccObjectsString := splitString[2]
+ outputFiles = splitOrEmpty(outputFilesString, ", ")
+ ccStaticLibraries := splitOrEmpty(ccStaticLibrariesString, ", ")
+ ccObjects = splitOrEmpty(ccObjectsString, ", ")
+ return CcInfo{
+ OutputFiles: outputFiles,
+ CcObjectFiles: ccObjects,
+ CcStaticLibraryFiles: ccStaticLibraries,
+ }, nil
+}
+
+// splitOrEmpty is a modification of strings.Split() that returns an empty list
+// if the given string is empty.
+func splitOrEmpty(s string, sep string) []string {
+ if len(s) < 1 {
+ return []string{}
+ } else {
+ return strings.Split(s, sep)
+ }
+}
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
new file mode 100644
index 0000000..48edb90
--- /dev/null
+++ b/bazel/cquery/request_type_test.go
@@ -0,0 +1,95 @@
+package cquery
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+)
+
+func TestGetOutputFilesParseResults(t *testing.T) {
+ testCases := []struct {
+ description string
+ input string
+ expectedOutput []string
+ }{
+ {
+ description: "no result",
+ input: "",
+ expectedOutput: []string{},
+ },
+ {
+ description: "one result",
+ input: "test",
+ expectedOutput: []string{"test"},
+ },
+ {
+ description: "splits on comma with space",
+ input: "foo, bar",
+ expectedOutput: []string{"foo", "bar"},
+ },
+ }
+ for _, tc := range testCases {
+ actualOutput := GetOutputFiles.ParseResult(tc.input)
+ if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+ t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
+ }
+ }
+}
+
+func TestGetCcInfoParseResults(t *testing.T) {
+ testCases := []struct {
+ description string
+ input string
+ expectedOutput CcInfo
+ expectedErrorMessage string
+ }{
+ {
+ description: "no result",
+ input: "||",
+ expectedOutput: CcInfo{
+ OutputFiles: []string{},
+ CcObjectFiles: []string{},
+ CcStaticLibraryFiles: []string{},
+ },
+ },
+ {
+ description: "only output",
+ input: "test||",
+ expectedOutput: CcInfo{
+ OutputFiles: []string{"test"},
+ CcObjectFiles: []string{},
+ CcStaticLibraryFiles: []string{},
+ },
+ },
+ {
+ description: "all items set",
+ input: "out1, out2|static_lib1, static_lib2|object1, object2",
+ expectedOutput: CcInfo{
+ OutputFiles: []string{"out1", "out2"},
+ CcObjectFiles: []string{"object1", "object2"},
+ CcStaticLibraryFiles: []string{"static_lib1", "static_lib2"},
+ },
+ },
+ {
+ description: "too few result splits",
+ input: "|",
+ expectedOutput: CcInfo{},
+ expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 3, []string{"", ""}),
+ },
+ {
+ description: "too many result splits",
+ input: "|||",
+ expectedOutput: CcInfo{},
+ expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 3, []string{"", "", "", ""}),
+ },
+ }
+ for _, tc := range testCases {
+ actualOutput, err := GetCcInfo.ParseResult(tc.input)
+ if (err == nil && tc.expectedErrorMessage != "") ||
+ (err != nil && err.Error() != tc.expectedErrorMessage) {
+ t.Errorf("%q: expected Error %s, got %s", tc.description, tc.expectedErrorMessage, err)
+ } else if err == nil && !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+ t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
+ }
+ }
+}
diff --git a/bazel/properties.go b/bazel/properties.go
index 8bb1956..4bb2391 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -14,14 +14,492 @@
package bazel
-type bazelModuleProperties struct {
- // The label of the Bazel target replacing this Soong module.
- Label string
+import (
+ "fmt"
+ "path/filepath"
+ "regexp"
+ "sort"
+)
+
+// BazelTargetModuleProperties contain properties and metadata used for
+// Blueprint to BUILD file conversion.
+type BazelTargetModuleProperties struct {
+ // The Bazel rule class for this target.
+ Rule_class string `blueprint:"mutated"`
+
+ // The target label for the bzl file containing the definition of the rule class.
+ Bzl_load_location string `blueprint:"mutated"`
}
-// Properties contains common module properties for migration purposes.
-type Properties struct {
- // In USE_BAZEL_ANALYSIS=1 mode, this represents the Bazel target replacing
- // this Soong module.
- Bazel_module bazelModuleProperties
+const BazelTargetModuleNamePrefix = "__bp2build__"
+
+var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
+
+// Label is used to represent a Bazel compatible Label. Also stores the original bp text to support
+// string replacement.
+type Label struct {
+ Bp_text string
+ Label string
+}
+
+// LabelList is used to represent a list of Bazel labels.
+type LabelList struct {
+ Includes []Label
+ Excludes []Label
+}
+
+// GlobsInDir returns a list of glob expressions for a list of extensions
+// (optionally recursive) within a directory.
+func GlobsInDir(dir string, recursive bool, extensions []string) []string {
+ globs := []string{}
+
+ globInfix := ""
+ if dir == "." {
+ if recursive {
+ // e.g "**/*.h"
+ globInfix = "**/"
+ } // else e.g. "*.h"
+ for _, ext := range extensions {
+ globs = append(globs, globInfix+"*"+ext)
+ }
+ } else {
+ if recursive {
+ // e.g. "foo/bar/**/*.h"
+ dir += "/**"
+ } // else e.g. "foo/bar/*.h"
+ for _, ext := range extensions {
+ globs = append(globs, dir+"/*"+ext)
+ }
+ }
+ return globs
+}
+
+// LooseHdrsGlobs returns the list of non-recursive header globs for each parent directory of
+// each source file in this LabelList's Includes.
+func (ll *LabelList) LooseHdrsGlobs(exts []string) []string {
+ var globs []string
+ for _, parentDir := range ll.uniqueParentDirectories() {
+ globs = append(globs,
+ GlobsInDir(parentDir, false, exts)...)
+ }
+ return globs
+}
+
+// uniqueParentDirectories returns a list of the unique parent directories for
+// all files in ll.Includes.
+func (ll *LabelList) uniqueParentDirectories() []string {
+ dirMap := map[string]bool{}
+ for _, label := range ll.Includes {
+ dirMap[filepath.Dir(label.Label)] = true
+ }
+ dirs := []string{}
+ for dir := range dirMap {
+ dirs = append(dirs, dir)
+ }
+ return dirs
+}
+
+// Append appends the fields of other labelList to the corresponding fields of ll.
+func (ll *LabelList) Append(other LabelList) {
+ if len(ll.Includes) > 0 || len(other.Includes) > 0 {
+ ll.Includes = append(ll.Includes, other.Includes...)
+ }
+ if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
+ ll.Excludes = append(other.Excludes, other.Excludes...)
+ }
+}
+
+func UniqueBazelLabels(originalLabels []Label) []Label {
+ uniqueLabelsSet := make(map[Label]bool)
+ for _, l := range originalLabels {
+ uniqueLabelsSet[l] = true
+ }
+ var uniqueLabels []Label
+ for l, _ := range uniqueLabelsSet {
+ uniqueLabels = append(uniqueLabels, l)
+ }
+ sort.SliceStable(uniqueLabels, func(i, j int) bool {
+ return uniqueLabels[i].Label < uniqueLabels[j].Label
+ })
+ return uniqueLabels
+}
+
+func UniqueBazelLabelList(originalLabelList LabelList) LabelList {
+ var uniqueLabelList LabelList
+ uniqueLabelList.Includes = UniqueBazelLabels(originalLabelList.Includes)
+ uniqueLabelList.Excludes = UniqueBazelLabels(originalLabelList.Excludes)
+ return uniqueLabelList
+}
+
+// Subtract needle from haystack
+func SubtractStrings(haystack []string, needle []string) []string {
+ // This is really a set
+ remainder := make(map[string]bool)
+
+ for _, s := range haystack {
+ remainder[s] = true
+ }
+ for _, s := range needle {
+ delete(remainder, s)
+ }
+
+ var strings []string
+ for s, _ := range remainder {
+ strings = append(strings, s)
+ }
+
+ sort.SliceStable(strings, func(i, j int) bool {
+ return strings[i] < strings[j]
+ })
+
+ return strings
+}
+
+// Subtract needle from haystack
+func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
+ // This is really a set
+ remainder := make(map[Label]bool)
+
+ for _, label := range haystack {
+ remainder[label] = true
+ }
+ for _, label := range needle {
+ delete(remainder, label)
+ }
+
+ var labels []Label
+ for label, _ := range remainder {
+ labels = append(labels, label)
+ }
+
+ sort.SliceStable(labels, func(i, j int) bool {
+ return labels[i].Label < labels[j].Label
+ })
+
+ return labels
+}
+
+// Subtract needle from haystack
+func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
+ var result LabelList
+ result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
+ // NOTE: Excludes are intentionally not subtracted
+ result.Excludes = haystack.Excludes
+ return result
+}
+
+const (
+ // ArchType names in arch.go
+ ARCH_ARM = "arm"
+ ARCH_ARM64 = "arm64"
+ ARCH_X86 = "x86"
+ ARCH_X86_64 = "x86_64"
+
+ // OsType names in arch.go
+ OS_ANDROID = "android"
+ OS_DARWIN = "darwin"
+ OS_FUCHSIA = "fuchsia"
+ OS_LINUX = "linux_glibc"
+ OS_LINUX_BIONIC = "linux_bionic"
+ OS_WINDOWS = "windows"
+)
+
+var (
+ // These are the list of OSes and architectures with a Bazel config_setting
+ // and constraint value equivalent. These exist in arch.go, but the android
+ // package depends on the bazel package, so a cyclic dependency prevents
+ // using those variables here.
+
+ // A map of architectures to the Bazel label of the constraint_value
+ // for the @platforms//cpu:cpu constraint_setting
+ PlatformArchMap = map[string]string{
+ ARCH_ARM: "//build/bazel/platforms/arch:arm",
+ ARCH_ARM64: "//build/bazel/platforms/arch:arm64",
+ ARCH_X86: "//build/bazel/platforms/arch:x86",
+ ARCH_X86_64: "//build/bazel/platforms/arch:x86_64",
+ }
+
+ // A map of target operating systems to the Bazel label of the
+ // constraint_value for the @platforms//os:os constraint_setting
+ PlatformOsMap = map[string]string{
+ OS_ANDROID: "//build/bazel/platforms/os:android",
+ OS_DARWIN: "//build/bazel/platforms/os:darwin",
+ OS_FUCHSIA: "//build/bazel/platforms/os:fuchsia",
+ OS_LINUX: "//build/bazel/platforms/os:linux",
+ OS_LINUX_BIONIC: "//build/bazel/platforms/os:linux_bionic",
+ OS_WINDOWS: "//build/bazel/platforms/os:windows",
+ }
+)
+
+type Attribute interface {
+ HasConfigurableValues() bool
+}
+
+// Arch-specific label_list typed Bazel attribute values. This should correspond
+// to the types of architectures supported for compilation in arch.go.
+type labelListArchValues struct {
+ X86 LabelList
+ X86_64 LabelList
+ Arm LabelList
+ Arm64 LabelList
+ Common LabelList
+}
+
+type labelListOsValues struct {
+ Android LabelList
+ Darwin LabelList
+ Fuchsia LabelList
+ Linux LabelList
+ LinuxBionic LabelList
+ Windows LabelList
+}
+
+// LabelListAttribute is used to represent a list of Bazel labels as an
+// attribute.
+type LabelListAttribute struct {
+ // The non-arch specific attribute label list Value. Required.
+ Value LabelList
+
+ // The arch-specific attribute label list values. Optional. If used, these
+ // are generated in a select statement and appended to the non-arch specific
+ // label list Value.
+ ArchValues labelListArchValues
+
+ // The os-specific attribute label list values. Optional. If used, these
+ // are generated in a select statement and appended to the non-os specific
+ // label list Value.
+ OsValues labelListOsValues
+}
+
+// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
+func MakeLabelListAttribute(value LabelList) LabelListAttribute {
+ return LabelListAttribute{Value: UniqueBazelLabelList(value)}
+}
+
+// Append appends all values, including os and arch specific ones, from another
+// LabelListAttribute to this LabelListAttribute.
+func (attrs *LabelListAttribute) Append(other LabelListAttribute) {
+ for arch := range PlatformArchMap {
+ this := attrs.GetValueForArch(arch)
+ that := other.GetValueForArch(arch)
+ this.Append(that)
+ attrs.SetValueForArch(arch, this)
+ }
+
+ for os := range PlatformOsMap {
+ this := attrs.GetValueForOS(os)
+ that := other.GetValueForOS(os)
+ this.Append(that)
+ attrs.SetValueForOS(os, this)
+ }
+
+ attrs.Value.Append(other.Value)
+}
+
+// HasArchSpecificValues returns true if the attribute contains
+// architecture-specific label_list values.
+func (attrs LabelListAttribute) HasConfigurableValues() bool {
+ for arch := range PlatformArchMap {
+ if len(attrs.GetValueForArch(arch).Includes) > 0 {
+ return true
+ }
+ }
+
+ for os := range PlatformOsMap {
+ if len(attrs.GetValueForOS(os).Includes) > 0 {
+ return true
+ }
+ }
+ return false
+}
+
+func (attrs *LabelListAttribute) archValuePtrs() map[string]*LabelList {
+ return map[string]*LabelList{
+ ARCH_X86: &attrs.ArchValues.X86,
+ ARCH_X86_64: &attrs.ArchValues.X86_64,
+ ARCH_ARM: &attrs.ArchValues.Arm,
+ ARCH_ARM64: &attrs.ArchValues.Arm64,
+ }
+}
+
+// GetValueForArch returns the label_list attribute value for an architecture.
+func (attrs *LabelListAttribute) GetValueForArch(arch string) LabelList {
+ var v *LabelList
+ if v = attrs.archValuePtrs()[arch]; v == nil {
+ panic(fmt.Errorf("Unknown arch: %s", arch))
+ }
+ return *v
+}
+
+// SetValueForArch sets the label_list attribute value for an architecture.
+func (attrs *LabelListAttribute) SetValueForArch(arch string, value LabelList) {
+ var v *LabelList
+ if v = attrs.archValuePtrs()[arch]; v == nil {
+ panic(fmt.Errorf("Unknown arch: %s", arch))
+ }
+ *v = value
+}
+
+func (attrs *LabelListAttribute) osValuePtrs() map[string]*LabelList {
+ return map[string]*LabelList{
+ OS_ANDROID: &attrs.OsValues.Android,
+ OS_DARWIN: &attrs.OsValues.Darwin,
+ OS_FUCHSIA: &attrs.OsValues.Fuchsia,
+ OS_LINUX: &attrs.OsValues.Linux,
+ OS_LINUX_BIONIC: &attrs.OsValues.LinuxBionic,
+ OS_WINDOWS: &attrs.OsValues.Windows,
+ }
+}
+
+// GetValueForOS returns the label_list attribute value for an OS target.
+func (attrs *LabelListAttribute) GetValueForOS(os string) LabelList {
+ var v *LabelList
+ if v = attrs.osValuePtrs()[os]; v == nil {
+ panic(fmt.Errorf("Unknown os: %s", os))
+ }
+ return *v
+}
+
+// SetValueForArch sets the label_list attribute value for an OS target.
+func (attrs *LabelListAttribute) SetValueForOS(os string, value LabelList) {
+ var v *LabelList
+ if v = attrs.osValuePtrs()[os]; v == nil {
+ panic(fmt.Errorf("Unknown os: %s", os))
+ }
+ *v = value
+}
+
+// StringListAttribute corresponds to the string_list Bazel attribute type with
+// support for additional metadata, like configurations.
+type StringListAttribute struct {
+ // The base value of the string list attribute.
+ Value []string
+
+ // The arch-specific attribute string list values. Optional. If used, these
+ // are generated in a select statement and appended to the non-arch specific
+ // label list Value.
+ ArchValues stringListArchValues
+
+ // The os-specific attribute string list values. Optional. If used, these
+ // are generated in a select statement and appended to the non-os specific
+ // label list Value.
+ OsValues stringListOsValues
+}
+
+// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
+func MakeStringListAttribute(value []string) StringListAttribute {
+ // NOTE: These strings are not necessarily unique or sorted.
+ return StringListAttribute{Value: value}
+}
+
+// Arch-specific string_list typed Bazel attribute values. This should correspond
+// to the types of architectures supported for compilation in arch.go.
+type stringListArchValues struct {
+ X86 []string
+ X86_64 []string
+ Arm []string
+ Arm64 []string
+ Common []string
+}
+
+type stringListOsValues struct {
+ Android []string
+ Darwin []string
+ Fuchsia []string
+ Linux []string
+ LinuxBionic []string
+ Windows []string
+}
+
+// HasConfigurableValues returns true if the attribute contains
+// architecture-specific string_list values.
+func (attrs StringListAttribute) HasConfigurableValues() bool {
+ for arch := range PlatformArchMap {
+ if len(attrs.GetValueForArch(arch)) > 0 {
+ return true
+ }
+ }
+
+ for os := range PlatformOsMap {
+ if len(attrs.GetValueForOS(os)) > 0 {
+ return true
+ }
+ }
+ return false
+}
+
+func (attrs *StringListAttribute) archValuePtrs() map[string]*[]string {
+ return map[string]*[]string{
+ ARCH_X86: &attrs.ArchValues.X86,
+ ARCH_X86_64: &attrs.ArchValues.X86_64,
+ ARCH_ARM: &attrs.ArchValues.Arm,
+ ARCH_ARM64: &attrs.ArchValues.Arm64,
+ }
+}
+
+// GetValueForArch returns the string_list attribute value for an architecture.
+func (attrs *StringListAttribute) GetValueForArch(arch string) []string {
+ var v *[]string
+ if v = attrs.archValuePtrs()[arch]; v == nil {
+ panic(fmt.Errorf("Unknown arch: %s", arch))
+ }
+ return *v
+}
+
+// SetValueForArch sets the string_list attribute value for an architecture.
+func (attrs *StringListAttribute) SetValueForArch(arch string, value []string) {
+ var v *[]string
+ if v = attrs.archValuePtrs()[arch]; v == nil {
+ panic(fmt.Errorf("Unknown arch: %s", arch))
+ }
+ *v = value
+}
+
+func (attrs *StringListAttribute) osValuePtrs() map[string]*[]string {
+ return map[string]*[]string{
+ OS_ANDROID: &attrs.OsValues.Android,
+ OS_DARWIN: &attrs.OsValues.Darwin,
+ OS_FUCHSIA: &attrs.OsValues.Fuchsia,
+ OS_LINUX: &attrs.OsValues.Linux,
+ OS_LINUX_BIONIC: &attrs.OsValues.LinuxBionic,
+ OS_WINDOWS: &attrs.OsValues.Windows,
+ }
+}
+
+// GetValueForOS returns the string_list attribute value for an OS target.
+func (attrs *StringListAttribute) GetValueForOS(os string) []string {
+ var v *[]string
+ if v = attrs.osValuePtrs()[os]; v == nil {
+ panic(fmt.Errorf("Unknown os: %s", os))
+ }
+ return *v
+}
+
+// SetValueForArch sets the string_list attribute value for an OS target.
+func (attrs *StringListAttribute) SetValueForOS(os string, value []string) {
+ var v *[]string
+ if v = attrs.osValuePtrs()[os]; v == nil {
+ panic(fmt.Errorf("Unknown os: %s", os))
+ }
+ *v = value
+}
+
+// TryVariableSubstitution, replace string substitution formatting within each string in slice with
+// Starlark string.format compatible tag for productVariable.
+func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
+ ret := make([]string, 0, len(slice))
+ changesMade := false
+ for _, s := range slice {
+ newS, changed := TryVariableSubstitution(s, productVariable)
+ ret = append(ret, newS)
+ changesMade = changesMade || changed
+ }
+ return ret, changesMade
+}
+
+// TryVariableSubstitution, replace string substitution formatting within s with Starlark
+// string.format compatible tag for productVariable.
+func TryVariableSubstitution(s string, productVariable string) (string, bool) {
+ sub := productVariableSubstitutionPattern.ReplaceAllString(s, "{"+productVariable+"}")
+ return sub, s != sub
}
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
new file mode 100644
index 0000000..56840ef
--- /dev/null
+++ b/bazel/properties_test.go
@@ -0,0 +1,165 @@
+// Copyright 2021 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 bazel
+
+import (
+ "reflect"
+ "testing"
+)
+
+func TestUniqueBazelLabels(t *testing.T) {
+ testCases := []struct {
+ originalLabels []Label
+ expectedUniqueLabels []Label
+ }{
+ {
+ originalLabels: []Label{
+ {Label: "a"},
+ {Label: "b"},
+ {Label: "a"},
+ {Label: "c"},
+ },
+ expectedUniqueLabels: []Label{
+ {Label: "a"},
+ {Label: "b"},
+ {Label: "c"},
+ },
+ },
+ }
+ for _, tc := range testCases {
+ actualUniqueLabels := UniqueBazelLabels(tc.originalLabels)
+ if !reflect.DeepEqual(tc.expectedUniqueLabels, actualUniqueLabels) {
+ t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabels, actualUniqueLabels)
+ }
+ }
+}
+
+func TestSubtractStrings(t *testing.T) {
+ testCases := []struct {
+ haystack []string
+ needle []string
+ expectedResult []string
+ }{
+ {
+ haystack: []string{
+ "a",
+ "b",
+ "c",
+ },
+ needle: []string{
+ "a",
+ },
+ expectedResult: []string{
+ "b", "c",
+ },
+ },
+ }
+ for _, tc := range testCases {
+ actualResult := SubtractStrings(tc.haystack, tc.needle)
+ if !reflect.DeepEqual(tc.expectedResult, actualResult) {
+ t.Fatalf("Expected %v, got %v", tc.expectedResult, actualResult)
+ }
+ }
+}
+
+func TestSubtractBazelLabelList(t *testing.T) {
+ testCases := []struct {
+ haystack LabelList
+ needle LabelList
+ expectedResult LabelList
+ }{
+ {
+ haystack: LabelList{
+ Includes: []Label{
+ {Label: "a"},
+ {Label: "b"},
+ {Label: "c"},
+ },
+ Excludes: []Label{
+ {Label: "x"},
+ {Label: "y"},
+ {Label: "z"},
+ },
+ },
+ needle: LabelList{
+ Includes: []Label{
+ {Label: "a"},
+ },
+ Excludes: []Label{
+ {Label: "z"},
+ },
+ },
+ // NOTE: Excludes are intentionally not subtracted
+ expectedResult: LabelList{
+ Includes: []Label{
+ {Label: "b"},
+ {Label: "c"},
+ },
+ Excludes: []Label{
+ {Label: "x"},
+ {Label: "y"},
+ {Label: "z"},
+ },
+ },
+ },
+ }
+ for _, tc := range testCases {
+ actualResult := SubtractBazelLabelList(tc.haystack, tc.needle)
+ if !reflect.DeepEqual(tc.expectedResult, actualResult) {
+ t.Fatalf("Expected %v, got %v", tc.expectedResult, actualResult)
+ }
+ }
+}
+func TestUniqueBazelLabelList(t *testing.T) {
+ testCases := []struct {
+ originalLabelList LabelList
+ expectedUniqueLabelList LabelList
+ }{
+ {
+ originalLabelList: LabelList{
+ Includes: []Label{
+ {Label: "a"},
+ {Label: "b"},
+ {Label: "a"},
+ {Label: "c"},
+ },
+ Excludes: []Label{
+ {Label: "x"},
+ {Label: "x"},
+ {Label: "y"},
+ {Label: "z"},
+ },
+ },
+ expectedUniqueLabelList: LabelList{
+ Includes: []Label{
+ {Label: "a"},
+ {Label: "b"},
+ {Label: "c"},
+ },
+ Excludes: []Label{
+ {Label: "x"},
+ {Label: "y"},
+ {Label: "z"},
+ },
+ },
+ },
+ }
+ for _, tc := range testCases {
+ actualUniqueLabelList := UniqueBazelLabelList(tc.originalLabelList)
+ if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
+ t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
+ }
+ }
+}
diff --git a/bloaty/Android.bp b/bloaty/Android.bp
new file mode 100644
index 0000000..7e722a7
--- /dev/null
+++ b/bloaty/Android.bp
@@ -0,0 +1,45 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-bloaty",
+ pkgPath: "android/soong/bloaty",
+ deps: [
+ "blueprint",
+ "soong-android",
+ ],
+ srcs: [
+ "bloaty.go",
+ "testing.go",
+ ],
+ pluginFor: ["soong_build"],
+}
+
+python_test_host {
+ name: "bloaty_merger_test",
+ srcs: [
+ "bloaty_merger_test.py",
+ "bloaty_merger.py",
+ "file_sections.proto",
+ ],
+ proto: {
+ canonical_path_from_root: false,
+ },
+ libs: [
+ "pyfakefs",
+ "ninja_rsp",
+ ],
+}
+
+python_binary_host {
+ name: "bloaty_merger",
+ srcs: [
+ "bloaty_merger.py",
+ "file_sections.proto",
+ ],
+ proto: {
+ canonical_path_from_root: false,
+ },
+ libs: ["ninja_rsp"],
+}
diff --git a/bloaty/bloaty.go b/bloaty/bloaty.go
new file mode 100644
index 0000000..50f241f
--- /dev/null
+++ b/bloaty/bloaty.go
@@ -0,0 +1,114 @@
+// Copyright 2021 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 bloaty implements a singleton that measures binary (e.g. ELF
+// executable, shared library or Rust rlib) section sizes at build time.
+package bloaty
+
+import (
+ "android/soong/android"
+
+ "github.com/google/blueprint"
+)
+
+const bloatyDescriptorExt = ".bloaty.csv"
+const protoFilename = "binary_sizes.pb.gz"
+
+var (
+ fileSizeMeasurerKey blueprint.ProviderKey
+ pctx = android.NewPackageContext("android/soong/bloaty")
+
+ // bloaty is used to measure a binary section sizes.
+ bloaty = pctx.AndroidStaticRule("bloaty",
+ blueprint.RuleParams{
+ Command: "${bloaty} -n 0 --csv ${in} > ${out}",
+ CommandDeps: []string{"${bloaty}"},
+ })
+
+ // The bloaty merger script is used to combine the outputs from bloaty
+ // into a single protobuf.
+ bloatyMerger = pctx.AndroidStaticRule("bloatyMerger",
+ blueprint.RuleParams{
+ Command: "${bloatyMerger} ${out}.lst ${out}",
+ CommandDeps: []string{"${bloatyMerger}"},
+ Rspfile: "${out}.lst",
+ RspfileContent: "${in}",
+ })
+)
+
+func init() {
+ pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS)
+ pctx.SourcePathVariable("bloaty", "prebuilts/build-tools/${hostPrebuiltTag}/bin/bloaty")
+ pctx.HostBinToolVariable("bloatyMerger", "bloaty_merger")
+ android.RegisterSingletonType("file_metrics", fileSizesSingleton)
+ fileSizeMeasurerKey = blueprint.NewProvider(measuredFiles{})
+}
+
+// measuredFiles contains the paths of the files measured by a module.
+type measuredFiles struct {
+ paths []android.WritablePath
+}
+
+// MeasureSizeForPaths should be called by binary producers to measure the
+// sizes of artifacts. It must only be called once per module; it will panic
+// otherwise.
+func MeasureSizeForPaths(ctx android.ModuleContext, paths ...android.OptionalPath) {
+ mf := measuredFiles{}
+ for _, p := range paths {
+ if !p.Valid() {
+ continue
+ }
+ if p, ok := p.Path().(android.WritablePath); ok {
+ mf.paths = append(mf.paths, p)
+ }
+ }
+ ctx.SetProvider(fileSizeMeasurerKey, mf)
+}
+
+type sizesSingleton struct{}
+
+func fileSizesSingleton() android.Singleton {
+ return &sizesSingleton{}
+}
+
+func (singleton *sizesSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ var deps android.Paths
+ ctx.VisitAllModules(func(m android.Module) {
+ if !ctx.ModuleHasProvider(m, fileSizeMeasurerKey) {
+ return
+ }
+ filePaths := ctx.ModuleProvider(m, fileSizeMeasurerKey).(measuredFiles)
+ for _, path := range filePaths.paths {
+ filePath := path.(android.ModuleOutPath)
+ sizeFile := filePath.InSameDir(ctx, filePath.Base()+bloatyDescriptorExt)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: bloaty,
+ Description: "bloaty " + filePath.Rel(),
+ Input: filePath,
+ Output: sizeFile,
+ })
+ deps = append(deps, sizeFile)
+ }
+ })
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: bloatyMerger,
+ Inputs: android.SortedUniquePaths(deps),
+ Output: android.PathForOutput(ctx, protoFilename),
+ })
+}
+
+func (singleton *sizesSingleton) MakeVars(ctx android.MakeVarsContext) {
+ ctx.DistForGoalWithFilename("checkbuild", android.PathForOutput(ctx, protoFilename), protoFilename)
+}
diff --git a/bloaty/bloaty_merger.py b/bloaty/bloaty_merger.py
new file mode 100644
index 0000000..1034462
--- /dev/null
+++ b/bloaty/bloaty_merger.py
@@ -0,0 +1,81 @@
+# Copyright 2021 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.
+"""Bloaty CSV Merger
+
+Merges a list of .csv files from Bloaty into a protobuf. It takes the list as
+a first argument and the output as second. For instance:
+
+ $ bloaty_merger binary_sizes.lst binary_sizes.pb.gz
+
+"""
+
+import argparse
+import csv
+import gzip
+
+import ninja_rsp
+
+import file_sections_pb2
+
+BLOATY_EXTENSION = ".bloaty.csv"
+
+def parse_csv(path):
+ """Parses a Bloaty-generated CSV file into a protobuf.
+
+ Args:
+ path: The filepath to the CSV file, relative to $ANDROID_TOP.
+
+ Returns:
+ A file_sections_pb2.File if the file was found; None otherwise.
+ """
+ file_proto = None
+ with open(path, newline='') as csv_file:
+ file_proto = file_sections_pb2.File()
+ if path.endswith(BLOATY_EXTENSION):
+ file_proto.path = path[:-len(BLOATY_EXTENSION)]
+ section_reader = csv.DictReader(csv_file)
+ for row in section_reader:
+ section = file_proto.sections.add()
+ section.name = row["sections"]
+ section.vm_size = int(row["vmsize"])
+ section.file_size = int(row["filesize"])
+ return file_proto
+
+def create_file_size_metrics(input_list, output_proto):
+ """Creates a FileSizeMetrics proto from a list of CSV files.
+
+ Args:
+ input_list: The path to the file which contains the list of CSV files. Each
+ filepath is separated by a space.
+ output_proto: The path for the output protobuf. It will be compressed using
+ gzip.
+ """
+ metrics = file_sections_pb2.FileSizeMetrics()
+ reader = ninja_rsp.NinjaRspFileReader(input_list)
+ for csv_path in reader:
+ file_proto = parse_csv(csv_path)
+ if file_proto:
+ metrics.files.append(file_proto)
+ with gzip.open(output_proto, "wb") as output:
+ output.write(metrics.SerializeToString())
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("input_list_file", help="List of bloaty csv files.")
+ parser.add_argument("output_proto", help="Output proto.")
+ args = parser.parse_args()
+ create_file_size_metrics(args.input_list_file, args.output_proto)
+
+if __name__ == '__main__':
+ main()
diff --git a/bloaty/bloaty_merger_test.py b/bloaty/bloaty_merger_test.py
new file mode 100644
index 0000000..9de049a
--- /dev/null
+++ b/bloaty/bloaty_merger_test.py
@@ -0,0 +1,66 @@
+# Copyright 2021 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.
+import gzip
+import unittest
+
+from pyfakefs import fake_filesystem_unittest
+
+import bloaty_merger
+import file_sections_pb2
+
+
+class BloatyMergerTestCase(fake_filesystem_unittest.TestCase):
+ def setUp(self):
+ self.setUpPyfakefs()
+
+ def test_parse_csv(self):
+ csv_content = "sections,vmsize,filesize\nsection1,2,3\n"
+ self.fs.create_file("file1.bloaty.csv", contents=csv_content)
+ pb = bloaty_merger.parse_csv("file1.bloaty.csv")
+ self.assertEqual(pb.path, "file1")
+ self.assertEqual(len(pb.sections), 1)
+ s = pb.sections[0]
+ self.assertEqual(s.name, "section1")
+ self.assertEqual(s.vm_size, 2)
+ self.assertEqual(s.file_size, 3)
+
+ def test_missing_file(self):
+ with self.assertRaises(FileNotFoundError):
+ bloaty_merger.parse_csv("missing.bloaty.csv")
+
+ def test_malformed_csv(self):
+ csv_content = "header1,heaVder2,header3\n4,5,6\n"
+ self.fs.create_file("file1.bloaty.csv", contents=csv_content)
+ with self.assertRaises(KeyError):
+ bloaty_merger.parse_csv("file1.bloaty.csv")
+
+ def test_create_file_metrics(self):
+ file_list = "file1.bloaty.csv file2.bloaty.csv"
+ file1_content = "sections,vmsize,filesize\nsection1,2,3\nsection2,7,8"
+ file2_content = "sections,vmsize,filesize\nsection1,4,5\n"
+
+ self.fs.create_file("files.lst", contents=file_list)
+ self.fs.create_file("file1.bloaty.csv", contents=file1_content)
+ self.fs.create_file("file2.bloaty.csv", contents=file2_content)
+
+ bloaty_merger.create_file_size_metrics("files.lst", "output.pb.gz")
+
+ metrics = file_sections_pb2.FileSizeMetrics()
+ with gzip.open("output.pb.gz", "rb") as output:
+ metrics.ParseFromString(output.read())
+
+
+if __name__ == '__main__':
+ suite = unittest.TestLoader().loadTestsFromTestCase(BloatyMergerTestCase)
+ unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/bloaty/file_sections.proto b/bloaty/file_sections.proto
new file mode 100644
index 0000000..34a32db
--- /dev/null
+++ b/bloaty/file_sections.proto
@@ -0,0 +1,39 @@
+// Copyright 2021 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 file_sections;
+
+message SectionDescriptior {
+ // Name of the section (e.g. .rodata)
+ optional string name = 1;
+
+ // Size of that section as part of the file.
+ optional uint64 file_size = 2;
+
+ // Size of that section when loaded in memory.
+ optional uint64 vm_size = 3;
+}
+
+message File {
+ // Relative path from $OUT_DIR.
+ optional string path = 1;
+
+ // File sections.
+ repeated SectionDescriptior sections = 2;
+}
+
+message FileSizeMetrics {
+ repeated File files = 1;
+}
diff --git a/bloaty/testing.go b/bloaty/testing.go
new file mode 100644
index 0000000..5f5ec84
--- /dev/null
+++ b/bloaty/testing.go
@@ -0,0 +1,25 @@
+// Copyright 2021 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 bloaty
+
+import (
+ "android/soong/android"
+)
+
+// Preparer that will define the default bloaty singleton.
+var PrepareForTestWithBloatyDefaultModules = android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+ ctx.RegisterSingletonType("file_metrics", fileSizesSingleton)
+ }))
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
new file mode 100644
index 0000000..d2a8729
--- /dev/null
+++ b/bp2build/Android.bp
@@ -0,0 +1,41 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-bp2build",
+ pkgPath: "android/soong/bp2build",
+ srcs: [
+ "androidbp_to_build_templates.go",
+ "bp2build.go",
+ "build_conversion.go",
+ "bzl_conversion.go",
+ "configurability.go",
+ "constants.go",
+ "conversion.go",
+ "metrics.go",
+ ],
+ deps: [
+ "soong-android",
+ "soong-bazel",
+ "soong-cc",
+ "soong-genrule",
+ "soong-python",
+ "soong-sh",
+ ],
+ testSrcs: [
+ "build_conversion_test.go",
+ "bzl_conversion_test.go",
+ "cc_library_conversion_test.go",
+ "cc_library_headers_conversion_test.go",
+ "cc_library_static_conversion_test.go",
+ "cc_object_conversion_test.go",
+ "conversion_test.go",
+ "python_binary_conversion_test.go",
+ "sh_conversion_test.go",
+ "testing.go",
+ ],
+ pluginFor: [
+ "soong_build",
+ ],
+}
diff --git a/cmd/soong_build/queryview_templates.go b/bp2build/androidbp_to_build_templates.go
similarity index 71%
rename from cmd/soong_build/queryview_templates.go
rename to bp2build/androidbp_to_build_templates.go
index 359c0d8..5fed4fa 100644
--- a/cmd/soong_build/queryview_templates.go
+++ b/bp2build/androidbp_to_build_templates.go
@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package main
+package bp2build
const (
- // The default `load` preamble for every generated BUILD file.
+ // The default `load` preamble for every generated queryview BUILD file.
soongModuleLoad = `package(default_visibility = ["//visibility:public"])
load("//build/bazel/queryview_rules:soong_module.bzl", "soong_module")
@@ -25,16 +25,20 @@
// for expanding more attributes.
soongModuleTarget = `soong_module(
name = "%s",
- module_name = "%s",
- module_type = "%s",
- module_variant = "%s",
- module_deps = %s,
+ soong_module_name = "%s",
+ soong_module_type = "%s",
+ soong_module_variant = "%s",
+ soong_module_deps = %s,
+%s)`
+
+ bazelTarget = `%s(
+ name = "%s",
%s)`
// A simple provider to mark and differentiate Soong module rule shims from
// regular Bazel rules. Every Soong module rule shim returns a
// SoongModuleInfo provider, and can only depend on rules returning
- // SoongModuleInfo in the `module_deps` attribute.
+ // SoongModuleInfo in the `soong_module_deps` attribute.
providersBzl = `SoongModuleInfo = provider(
fields = {
"name": "Name of module",
@@ -53,19 +57,19 @@
def _generic_soong_module_impl(ctx):
return [
SoongModuleInfo(
- name = ctx.attr.module_name,
- type = ctx.attr.module_type,
- variant = ctx.attr.module_variant,
+ name = ctx.attr.soong_module_name,
+ type = ctx.attr.soong_module_type,
+ variant = ctx.attr.soong_module_variant,
),
]
generic_soong_module = rule(
implementation = _generic_soong_module_impl,
attrs = {
- "module_name": attr.string(mandatory = True),
- "module_type": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ "soong_module_name": attr.string(mandatory = True),
+ "soong_module_type": attr.string(mandatory = True),
+ "soong_module_variant": attr.string(),
+ "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
},
)
@@ -85,20 +89,20 @@
else:
return False
-# soong_module is a macro that supports arbitrary kwargs, and uses module_type to
+# soong_module is a macro that supports arbitrary kwargs, and uses soong_module_type to
# expand to the right underlying shim.
-def soong_module(name, module_type, **kwargs):
- soong_module_rule = soong_module_rule_map.get(module_type)
+def soong_module(name, soong_module_type, **kwargs):
+ soong_module_rule = soong_module_rule_map.get(soong_module_type)
if soong_module_rule == None:
# This module type does not have an existing rule to map to, so use the
# generic_soong_module rule instead.
generic_soong_module(
name = name,
- module_type = module_type,
- module_name = kwargs.pop("module_name", ""),
- module_variant = kwargs.pop("module_variant", ""),
- module_deps = kwargs.pop("module_deps", []),
+ soong_module_type = soong_module_type,
+ soong_module_name = kwargs.pop("soong_module_name", ""),
+ soong_module_variant = kwargs.pop("soong_module_variant", ""),
+ soong_module_deps = kwargs.pop("soong_module_deps", []),
)
else:
supported_kwargs = dict()
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
new file mode 100644
index 0000000..007d6d8
--- /dev/null
+++ b/bp2build/bp2build.go
@@ -0,0 +1,65 @@
+// Copyright 2020 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 bp2build
+
+import (
+ "android/soong/android"
+ "fmt"
+ "os"
+ "strings"
+)
+
+// Codegen is the backend of bp2build. The code generator is responsible for
+// writing .bzl files that are equivalent to Android.bp files that are capable
+// of being built with Bazel.
+func Codegen(ctx *CodegenContext) CodegenMetrics {
+ outputDir := android.PathForOutput(ctx, "bp2build")
+ android.RemoveAllOutputDir(outputDir)
+
+ buildToTargets, metrics := GenerateBazelTargets(ctx)
+
+ filesToWrite := CreateBazelFiles(nil, buildToTargets, ctx.mode)
+
+ generatedBuildFiles := []string{}
+ for _, f := range filesToWrite {
+ p := getOrCreateOutputDir(outputDir, ctx, f.Dir).Join(ctx, f.Basename)
+ if err := writeFile(ctx, p, f.Contents); err != nil {
+ fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err)
+ }
+ // if these generated files are modified, regenerate on next run.
+ generatedBuildFiles = append(generatedBuildFiles, p.String())
+ }
+
+ // The MANIFEST file contains the full list of files generated by bp2build, excluding itself.
+ // Its purpose is for downstream tools to understand the set of files converted by bp2build.
+ manifestFile := outputDir.Join(ctx, "MANIFEST")
+ writeFile(ctx, manifestFile, strings.Join(generatedBuildFiles, "\n"))
+ generatedBuildFiles = append(generatedBuildFiles, manifestFile.String())
+
+ return metrics
+}
+
+// Get the output directory and create it if it doesn't exist.
+func getOrCreateOutputDir(outputDir android.OutputPath, ctx android.PathContext, dir string) android.OutputPath {
+ dirPath := outputDir.Join(ctx, dir)
+ android.CreateOutputDirIfNonexistent(dirPath, os.ModePerm)
+ return dirPath
+}
+
+func writeFile(ctx android.PathContext, pathToFile android.OutputPath, content string) error {
+ // These files are made editable to allow users to modify and iterate on them
+ // in the source tree.
+ return android.WriteFileToOutputDir(pathToFile, []byte(content), 0644)
+}
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
new file mode 100644
index 0000000..b7a2810
--- /dev/null
+++ b/bp2build/build_conversion.go
@@ -0,0 +1,562 @@
+// Copyright 2020 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 bp2build
+
+import (
+ "android/soong/android"
+ "android/soong/bazel"
+ "fmt"
+ "reflect"
+ "strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+type BazelAttributes struct {
+ Attrs map[string]string
+}
+
+type BazelTarget struct {
+ name string
+ content string
+ ruleClass string
+ bzlLoadLocation string
+}
+
+// IsLoadedFromStarlark determines if the BazelTarget's rule class is loaded from a .bzl file,
+// as opposed to a native rule built into Bazel.
+func (t BazelTarget) IsLoadedFromStarlark() bool {
+ return t.bzlLoadLocation != ""
+}
+
+// BazelTargets is a typedef for a slice of BazelTarget objects.
+type BazelTargets []BazelTarget
+
+// String returns the string representation of BazelTargets, without load
+// statements (use LoadStatements for that), since the targets are usually not
+// adjacent to the load statements at the top of the BUILD file.
+func (targets BazelTargets) String() string {
+ var res string
+ for i, target := range targets {
+ res += target.content
+ if i != len(targets)-1 {
+ res += "\n\n"
+ }
+ }
+ return res
+}
+
+// LoadStatements return the string representation of the sorted and deduplicated
+// Starlark rule load statements needed by a group of BazelTargets.
+func (targets BazelTargets) LoadStatements() string {
+ bzlToLoadedSymbols := map[string][]string{}
+ for _, target := range targets {
+ if target.IsLoadedFromStarlark() {
+ bzlToLoadedSymbols[target.bzlLoadLocation] =
+ append(bzlToLoadedSymbols[target.bzlLoadLocation], target.ruleClass)
+ }
+ }
+
+ var loadStatements []string
+ for bzl, ruleClasses := range bzlToLoadedSymbols {
+ loadStatement := "load(\""
+ loadStatement += bzl
+ loadStatement += "\", "
+ ruleClasses = android.SortedUniqueStrings(ruleClasses)
+ for i, ruleClass := range ruleClasses {
+ loadStatement += "\"" + ruleClass + "\""
+ if i != len(ruleClasses)-1 {
+ loadStatement += ", "
+ }
+ }
+ loadStatement += ")"
+ loadStatements = append(loadStatements, loadStatement)
+ }
+ return strings.Join(android.SortedUniqueStrings(loadStatements), "\n")
+}
+
+type bpToBuildContext interface {
+ ModuleName(module blueprint.Module) string
+ ModuleDir(module blueprint.Module) string
+ ModuleSubDir(module blueprint.Module) string
+ ModuleType(module blueprint.Module) string
+
+ VisitAllModules(visit func(blueprint.Module))
+ VisitDirectDeps(module blueprint.Module, visit func(blueprint.Module))
+}
+
+type CodegenContext struct {
+ config android.Config
+ context android.Context
+ mode CodegenMode
+ additionalDeps []string
+}
+
+func (c *CodegenContext) Mode() CodegenMode {
+ return c.mode
+}
+
+// CodegenMode is an enum to differentiate code-generation modes.
+type CodegenMode int
+
+const (
+ // Bp2Build: generate BUILD files with targets buildable by Bazel directly.
+ //
+ // This mode is used for the Soong->Bazel build definition conversion.
+ Bp2Build CodegenMode = iota
+
+ // QueryView: generate BUILD files with targets representing fully mutated
+ // Soong modules, representing the fully configured Soong module graph with
+ // variants and dependency endges.
+ //
+ // This mode is used for discovering and introspecting the existing Soong
+ // module graph.
+ QueryView
+)
+
+func (mode CodegenMode) String() string {
+ switch mode {
+ case Bp2Build:
+ return "Bp2Build"
+ case QueryView:
+ return "QueryView"
+ default:
+ return fmt.Sprintf("%d", mode)
+ }
+}
+
+// AddNinjaFileDeps adds dependencies on the specified files to be added to the ninja manifest. The
+// primary builder will be rerun whenever the specified files are modified. Allows us to fulfill the
+// PathContext interface in order to add dependencies on hand-crafted BUILD files. Note: must also
+// call AdditionalNinjaDeps and add them manually to the ninja file.
+func (ctx *CodegenContext) AddNinjaFileDeps(deps ...string) {
+ ctx.additionalDeps = append(ctx.additionalDeps, deps...)
+}
+
+// AdditionalNinjaDeps returns additional ninja deps added by CodegenContext
+func (ctx *CodegenContext) AdditionalNinjaDeps() []string {
+ return ctx.additionalDeps
+}
+
+func (ctx *CodegenContext) Config() android.Config { return ctx.config }
+func (ctx *CodegenContext) Context() android.Context { return ctx.context }
+
+// NewCodegenContext creates a wrapper context that conforms to PathContext for
+// writing BUILD files in the output directory.
+func NewCodegenContext(config android.Config, context android.Context, mode CodegenMode) *CodegenContext {
+ return &CodegenContext{
+ context: context,
+ config: config,
+ mode: mode,
+ }
+}
+
+// props is an unsorted map. This function ensures that
+// the generated attributes are sorted to ensure determinism.
+func propsToAttributes(props map[string]string) string {
+ var attributes string
+ for _, propName := range android.SortedStringKeys(props) {
+ if shouldGenerateAttribute(propName) {
+ attributes += fmt.Sprintf(" %s = %s,\n", propName, props[propName])
+ }
+ }
+ return attributes
+}
+
+func GenerateBazelTargets(ctx *CodegenContext) (map[string]BazelTargets, CodegenMetrics) {
+ buildFileToTargets := make(map[string]BazelTargets)
+ buildFileToAppend := make(map[string]bool)
+
+ // Simple metrics tracking for bp2build
+ metrics := CodegenMetrics{
+ RuleClassCount: make(map[string]int),
+ }
+
+ bpCtx := ctx.Context()
+ bpCtx.VisitAllModules(func(m blueprint.Module) {
+ dir := bpCtx.ModuleDir(m)
+ var t BazelTarget
+
+ switch ctx.Mode() {
+ case Bp2Build:
+ if b, ok := m.(android.Bazelable); ok && b.HasHandcraftedLabel() {
+ metrics.handCraftedTargetCount += 1
+ metrics.TotalModuleCount += 1
+ pathToBuildFile := getBazelPackagePath(b)
+ // We are using the entire contents of handcrafted build file, so if multiple targets within
+ // a package have handcrafted targets, we only want to include the contents one time.
+ if _, exists := buildFileToAppend[pathToBuildFile]; exists {
+ return
+ }
+ var err error
+ t, err = getHandcraftedBuildContent(ctx, b, pathToBuildFile)
+ if err != nil {
+ panic(fmt.Errorf("Error converting %s: %s", bpCtx.ModuleName(m), err))
+ }
+ // TODO(b/181575318): currently we append the whole BUILD file, let's change that to do
+ // something more targeted based on the rule type and target
+ buildFileToAppend[pathToBuildFile] = true
+ } else if btm, ok := m.(android.BazelTargetModule); ok {
+ t = generateBazelTarget(bpCtx, m, btm)
+ metrics.RuleClassCount[t.ruleClass] += 1
+ } else {
+ metrics.TotalModuleCount += 1
+ return
+ }
+ case QueryView:
+ // Blocklist certain module types from being generated.
+ if canonicalizeModuleType(bpCtx.ModuleType(m)) == "package" {
+ // package module name contain slashes, and thus cannot
+ // be mapped cleanly to a bazel label.
+ return
+ }
+ t = generateSoongModuleTarget(bpCtx, m)
+ default:
+ panic(fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode()))
+ }
+
+ buildFileToTargets[dir] = append(buildFileToTargets[dir], t)
+ })
+
+ return buildFileToTargets, metrics
+}
+
+func getBazelPackagePath(b android.Bazelable) string {
+ label := b.HandcraftedLabel()
+ pathToBuildFile := strings.TrimPrefix(label, "//")
+ pathToBuildFile = strings.Split(pathToBuildFile, ":")[0]
+ return pathToBuildFile
+}
+
+func getHandcraftedBuildContent(ctx *CodegenContext, b android.Bazelable, pathToBuildFile string) (BazelTarget, error) {
+ p := android.ExistentPathForSource(ctx, pathToBuildFile, HandcraftedBuildFileName)
+ if !p.Valid() {
+ return BazelTarget{}, fmt.Errorf("Could not find file %q for handcrafted target.", pathToBuildFile)
+ }
+ c, err := b.GetBazelBuildFileContents(ctx.Config(), pathToBuildFile, HandcraftedBuildFileName)
+ if err != nil {
+ return BazelTarget{}, err
+ }
+ // TODO(b/181575318): once this is more targeted, we need to include name, rule class, etc
+ return BazelTarget{
+ content: c,
+ }, nil
+}
+
+func generateBazelTarget(ctx bpToBuildContext, m blueprint.Module, btm android.BazelTargetModule) BazelTarget {
+ ruleClass := btm.RuleClass()
+ bzlLoadLocation := btm.BzlLoadLocation()
+
+ // extract the bazel attributes from the module.
+ props := getBuildProperties(ctx, m)
+
+ delete(props.Attrs, "bp2build_available")
+
+ // Return the Bazel target with rule class and attributes, ready to be
+ // code-generated.
+ attributes := propsToAttributes(props.Attrs)
+ targetName := targetNameForBp2Build(ctx, m)
+ return BazelTarget{
+ name: targetName,
+ ruleClass: ruleClass,
+ bzlLoadLocation: bzlLoadLocation,
+ content: fmt.Sprintf(
+ bazelTarget,
+ ruleClass,
+ targetName,
+ attributes,
+ ),
+ }
+}
+
+// Convert a module and its deps and props into a Bazel macro/rule
+// representation in the BUILD file.
+func generateSoongModuleTarget(ctx bpToBuildContext, m blueprint.Module) BazelTarget {
+ props := getBuildProperties(ctx, m)
+
+ // TODO(b/163018919): DirectDeps can have duplicate (module, variant)
+ // items, if the modules are added using different DependencyTag. Figure
+ // out the implications of that.
+ depLabels := map[string]bool{}
+ if aModule, ok := m.(android.Module); ok {
+ ctx.VisitDirectDeps(aModule, func(depModule blueprint.Module) {
+ depLabels[qualifiedTargetLabel(ctx, depModule)] = true
+ })
+ }
+ attributes := propsToAttributes(props.Attrs)
+
+ depLabelList := "[\n"
+ for depLabel, _ := range depLabels {
+ depLabelList += fmt.Sprintf(" %q,\n", depLabel)
+ }
+ depLabelList += " ]"
+
+ targetName := targetNameWithVariant(ctx, m)
+ return BazelTarget{
+ name: targetName,
+ content: fmt.Sprintf(
+ soongModuleTarget,
+ targetName,
+ ctx.ModuleName(m),
+ canonicalizeModuleType(ctx.ModuleType(m)),
+ ctx.ModuleSubDir(m),
+ depLabelList,
+ attributes),
+ }
+}
+
+func getBuildProperties(ctx bpToBuildContext, m blueprint.Module) BazelAttributes {
+ var allProps map[string]string
+ // TODO: this omits properties for blueprint modules (blueprint_go_binary,
+ // bootstrap_go_binary, bootstrap_go_package), which will have to be handled separately.
+ if aModule, ok := m.(android.Module); ok {
+ allProps = ExtractModuleProperties(aModule)
+ }
+
+ return BazelAttributes{
+ Attrs: allProps,
+ }
+}
+
+// Generically extract module properties and types into a map, keyed by the module property name.
+func ExtractModuleProperties(aModule android.Module) map[string]string {
+ ret := map[string]string{}
+
+ // Iterate over this android.Module's property structs.
+ for _, properties := range aModule.GetProperties() {
+ propertiesValue := reflect.ValueOf(properties)
+ // Check that propertiesValue is a pointer to the Properties struct, like
+ // *cc.BaseLinkerProperties or *java.CompilerProperties.
+ //
+ // propertiesValue can also be type-asserted to the structs to
+ // manipulate internal props, if needed.
+ if isStructPtr(propertiesValue.Type()) {
+ structValue := propertiesValue.Elem()
+ for k, v := range extractStructProperties(structValue, 0) {
+ ret[k] = v
+ }
+ } else {
+ panic(fmt.Errorf(
+ "properties must be a pointer to a struct, got %T",
+ propertiesValue.Interface()))
+ }
+ }
+
+ return ret
+}
+
+func isStructPtr(t reflect.Type) bool {
+ return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
+}
+
+// prettyPrint a property value into the equivalent Starlark representation
+// recursively.
+func prettyPrint(propertyValue reflect.Value, indent int) (string, error) {
+ if isZero(propertyValue) {
+ // A property value being set or unset actually matters -- Soong does set default
+ // values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
+ // https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
+ //
+ // In Bazel-parlance, we would use "attr.<type>(default = <default
+ // value>)" to set the default value of unset attributes. In the cases
+ // where the bp2build converter didn't set the default value within the
+ // mutator when creating the BazelTargetModule, this would be a zero
+ // value. For those cases, we return an empty string so we don't
+ // unnecessarily generate empty values.
+ return "", nil
+ }
+
+ var ret string
+ switch propertyValue.Kind() {
+ case reflect.String:
+ ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
+ case reflect.Bool:
+ ret = strings.Title(fmt.Sprintf("%v", propertyValue.Interface()))
+ case reflect.Int, reflect.Uint, reflect.Int64:
+ ret = fmt.Sprintf("%v", propertyValue.Interface())
+ case reflect.Ptr:
+ return prettyPrint(propertyValue.Elem(), indent)
+ case reflect.Slice:
+ if propertyValue.Len() == 0 {
+ return "", nil
+ }
+
+ if propertyValue.Len() == 1 {
+ // Single-line list for list with only 1 element
+ ret += "["
+ indexedValue, err := prettyPrint(propertyValue.Index(0), indent)
+ if err != nil {
+ return "", err
+ }
+ ret += indexedValue
+ ret += "]"
+ } else {
+ // otherwise, use a multiline list.
+ ret += "[\n"
+ for i := 0; i < propertyValue.Len(); i++ {
+ indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1)
+ if err != nil {
+ return "", err
+ }
+
+ if indexedValue != "" {
+ ret += makeIndent(indent + 1)
+ ret += indexedValue
+ ret += ",\n"
+ }
+ }
+ ret += makeIndent(indent)
+ ret += "]"
+ }
+
+ case reflect.Struct:
+ // Special cases where the bp2build sends additional information to the codegenerator
+ // by wrapping the attributes in a custom struct type.
+ if attr, ok := propertyValue.Interface().(bazel.Attribute); ok {
+ return prettyPrintAttribute(attr, indent)
+ } else if label, ok := propertyValue.Interface().(bazel.Label); ok {
+ return fmt.Sprintf("%q", label.Label), nil
+ }
+
+ ret = "{\n"
+ // Sort and print the struct props by the key.
+ structProps := extractStructProperties(propertyValue, indent)
+ for _, k := range android.SortedStringKeys(structProps) {
+ ret += makeIndent(indent + 1)
+ ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
+ }
+ ret += makeIndent(indent)
+ ret += "}"
+ case reflect.Interface:
+ // TODO(b/164227191): implement pretty print for interfaces.
+ // Interfaces are used for for arch, multilib and target properties.
+ return "", nil
+ default:
+ return "", fmt.Errorf(
+ "unexpected kind for property struct field: %s", propertyValue.Kind())
+ }
+ return ret, nil
+}
+
+// Converts a reflected property struct value into a map of property names and property values,
+// which each property value correctly pretty-printed and indented at the right nest level,
+// since property structs can be nested. In Starlark, nested structs are represented as nested
+// dicts: https://docs.bazel.build/skylark/lib/dict.html
+func extractStructProperties(structValue reflect.Value, indent int) map[string]string {
+ if structValue.Kind() != reflect.Struct {
+ panic(fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind()))
+ }
+
+ ret := map[string]string{}
+ structType := structValue.Type()
+ for i := 0; i < structValue.NumField(); i++ {
+ field := structType.Field(i)
+ if shouldSkipStructField(field) {
+ continue
+ }
+
+ fieldValue := structValue.Field(i)
+ if isZero(fieldValue) {
+ // Ignore zero-valued fields
+ continue
+ }
+
+ propertyName := proptools.PropertyNameForField(field.Name)
+ prettyPrintedValue, err := prettyPrint(fieldValue, indent+1)
+ if err != nil {
+ panic(
+ fmt.Errorf(
+ "Error while parsing property: %q. %s",
+ propertyName,
+ err))
+ }
+ if prettyPrintedValue != "" {
+ ret[propertyName] = prettyPrintedValue
+ }
+ }
+
+ return ret
+}
+
+func isZero(value reflect.Value) bool {
+ switch value.Kind() {
+ case reflect.Func, reflect.Map, reflect.Slice:
+ return value.IsNil()
+ case reflect.Array:
+ valueIsZero := true
+ for i := 0; i < value.Len(); i++ {
+ valueIsZero = valueIsZero && isZero(value.Index(i))
+ }
+ return valueIsZero
+ case reflect.Struct:
+ valueIsZero := true
+ for i := 0; i < value.NumField(); i++ {
+ if value.Field(i).CanSet() {
+ valueIsZero = valueIsZero && isZero(value.Field(i))
+ }
+ }
+ return valueIsZero
+ case reflect.Ptr:
+ if !value.IsNil() {
+ return isZero(reflect.Indirect(value))
+ } else {
+ return true
+ }
+ default:
+ zeroValue := reflect.Zero(value.Type())
+ result := value.Interface() == zeroValue.Interface()
+ return result
+ }
+}
+
+func escapeString(s string) string {
+ s = strings.ReplaceAll(s, "\\", "\\\\")
+
+ // b/184026959: Reverse the application of some common control sequences.
+ // These must be generated literally in the BUILD file.
+ s = strings.ReplaceAll(s, "\t", "\\t")
+ s = strings.ReplaceAll(s, "\n", "\\n")
+ s = strings.ReplaceAll(s, "\r", "\\r")
+
+ return strings.ReplaceAll(s, "\"", "\\\"")
+}
+
+func makeIndent(indent int) string {
+ if indent < 0 {
+ panic(fmt.Errorf("indent column cannot be less than 0, but got %d", indent))
+ }
+ return strings.Repeat(" ", indent)
+}
+
+func targetNameForBp2Build(c bpToBuildContext, logicModule blueprint.Module) string {
+ return strings.Replace(c.ModuleName(logicModule), bazel.BazelTargetModuleNamePrefix, "", 1)
+}
+
+func targetNameWithVariant(c bpToBuildContext, logicModule blueprint.Module) string {
+ name := ""
+ if c.ModuleSubDir(logicModule) != "" {
+ // TODO(b/162720883): Figure out a way to drop the "--" variant suffixes.
+ name = c.ModuleName(logicModule) + "--" + c.ModuleSubDir(logicModule)
+ } else {
+ name = c.ModuleName(logicModule)
+ }
+
+ return strings.Replace(name, "//", "", 1)
+}
+
+func qualifiedTargetLabel(c bpToBuildContext, logicModule blueprint.Module) string {
+ return fmt.Sprintf("//%s:%s", c.ModuleDir(logicModule), targetNameWithVariant(c, logicModule))
+}
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
new file mode 100644
index 0000000..1ede442
--- /dev/null
+++ b/bp2build/build_conversion_test.go
@@ -0,0 +1,1439 @@
+// Copyright 2020 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 bp2build
+
+import (
+ "android/soong/android"
+ "android/soong/genrule"
+ "strings"
+ "testing"
+)
+
+func TestGenerateSoongModuleTargets(t *testing.T) {
+ testCases := []struct {
+ bp string
+ expectedBazelTarget string
+ }{
+ {
+ bp: `custom { name: "foo" }
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ soong_module_name = "foo",
+ soong_module_type = "custom",
+ soong_module_variant = "",
+ soong_module_deps = [
+ ],
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ ramdisk: true,
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ soong_module_name = "foo",
+ soong_module_type = "custom",
+ soong_module_variant = "",
+ soong_module_deps = [
+ ],
+ ramdisk = True,
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ owner: "a_string_with\"quotes\"_and_\\backslashes\\\\",
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ soong_module_name = "foo",
+ soong_module_type = "custom",
+ soong_module_variant = "",
+ soong_module_deps = [
+ ],
+ owner = "a_string_with\"quotes\"_and_\\backslashes\\\\",
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ required: ["bar"],
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ soong_module_name = "foo",
+ soong_module_type = "custom",
+ soong_module_variant = "",
+ soong_module_deps = [
+ ],
+ required = ["bar"],
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ target_required: ["qux", "bazqux"],
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ soong_module_name = "foo",
+ soong_module_type = "custom",
+ soong_module_variant = "",
+ soong_module_deps = [
+ ],
+ target_required = [
+ "qux",
+ "bazqux",
+ ],
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ dist: {
+ targets: ["goal_foo"],
+ tag: ".foo",
+ },
+ dists: [{
+ targets: ["goal_bar"],
+ tag: ".bar",
+ }],
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ soong_module_name = "foo",
+ soong_module_type = "custom",
+ soong_module_variant = "",
+ soong_module_deps = [
+ ],
+ dist = {
+ "tag": ".foo",
+ "targets": ["goal_foo"],
+ },
+ dists = [{
+ "tag": ".bar",
+ "targets": ["goal_bar"],
+ }],
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ required: ["bar"],
+ target_required: ["qux", "bazqux"],
+ ramdisk: true,
+ owner: "custom_owner",
+ dists: [
+ {
+ tag: ".tag",
+ targets: ["my_goal"],
+ },
+ ],
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ soong_module_name = "foo",
+ soong_module_type = "custom",
+ soong_module_variant = "",
+ soong_module_deps = [
+ ],
+ dists = [{
+ "tag": ".tag",
+ "targets": ["my_goal"],
+ }],
+ owner = "custom_owner",
+ ramdisk = True,
+ required = ["bar"],
+ target_required = [
+ "qux",
+ "bazqux",
+ ],
+)`,
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+ ctx := android.NewTestContext(config)
+
+ ctx.RegisterModuleType("custom", customModuleFactory)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+
+ codegenCtx := NewCodegenContext(config, *ctx.Context, QueryView)
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+ if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
+ t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount)
+ }
+
+ actualBazelTarget := bazelTargets[0]
+ if actualBazelTarget.content != testCase.expectedBazelTarget {
+ t.Errorf(
+ "Expected generated Bazel target to be '%s', got '%s'",
+ testCase.expectedBazelTarget,
+ actualBazelTarget.content,
+ )
+ }
+ }
+}
+
+func TestGenerateBazelTargetModules(t *testing.T) {
+ testCases := []struct {
+ bp string
+ expectedBazelTarget string
+ }{
+ {
+ bp: `custom {
+ name: "foo",
+ string_list_prop: ["a", "b"],
+ string_prop: "a",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTarget: `custom(
+ name = "foo",
+ string_list_prop = [
+ "a",
+ "b",
+ ],
+ string_prop = "a",
+)`,
+ },
+ {
+ bp: `custom {
+ name: "control_characters",
+ string_list_prop: ["\t", "\n"],
+ string_prop: "a\t\n\r",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTarget: `custom(
+ name = "control_characters",
+ string_list_prop = [
+ "\t",
+ "\n",
+ ],
+ string_prop = "a\t\n\r",
+)`,
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+ ctx := android.NewTestContext(config)
+
+ ctx.RegisterModuleType("custom", customModuleFactory)
+ ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutator)
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+ if Errored(t, "", errs) {
+ continue
+ }
+ _, errs = ctx.ResolveDependencies(config)
+ if Errored(t, "", errs) {
+ continue
+ }
+
+ codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+
+ if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
+ t.Errorf("Expected %d bazel target, got %d", expectedCount, actualCount)
+ } else {
+ actualBazelTarget := bazelTargets[0]
+ if actualBazelTarget.content != testCase.expectedBazelTarget {
+ t.Errorf(
+ "Expected generated Bazel target to be '%s', got '%s'",
+ testCase.expectedBazelTarget,
+ actualBazelTarget.content,
+ )
+ }
+ }
+ }
+}
+
+func TestLoadStatements(t *testing.T) {
+ testCases := []struct {
+ bazelTargets BazelTargets
+ expectedLoadStatements string
+ }{
+ {
+ bazelTargets: BazelTargets{
+ BazelTarget{
+ name: "foo",
+ ruleClass: "cc_library",
+ bzlLoadLocation: "//build/bazel/rules:cc.bzl",
+ },
+ },
+ expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_library")`,
+ },
+ {
+ bazelTargets: BazelTargets{
+ BazelTarget{
+ name: "foo",
+ ruleClass: "cc_library",
+ bzlLoadLocation: "//build/bazel/rules:cc.bzl",
+ },
+ BazelTarget{
+ name: "bar",
+ ruleClass: "cc_library",
+ bzlLoadLocation: "//build/bazel/rules:cc.bzl",
+ },
+ },
+ expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_library")`,
+ },
+ {
+ bazelTargets: BazelTargets{
+ BazelTarget{
+ name: "foo",
+ ruleClass: "cc_library",
+ bzlLoadLocation: "//build/bazel/rules:cc.bzl",
+ },
+ BazelTarget{
+ name: "bar",
+ ruleClass: "cc_binary",
+ bzlLoadLocation: "//build/bazel/rules:cc.bzl",
+ },
+ },
+ expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_binary", "cc_library")`,
+ },
+ {
+ bazelTargets: BazelTargets{
+ BazelTarget{
+ name: "foo",
+ ruleClass: "cc_library",
+ bzlLoadLocation: "//build/bazel/rules:cc.bzl",
+ },
+ BazelTarget{
+ name: "bar",
+ ruleClass: "cc_binary",
+ bzlLoadLocation: "//build/bazel/rules:cc.bzl",
+ },
+ BazelTarget{
+ name: "baz",
+ ruleClass: "java_binary",
+ bzlLoadLocation: "//build/bazel/rules:java.bzl",
+ },
+ },
+ expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_binary", "cc_library")
+load("//build/bazel/rules:java.bzl", "java_binary")`,
+ },
+ {
+ bazelTargets: BazelTargets{
+ BazelTarget{
+ name: "foo",
+ ruleClass: "cc_binary",
+ bzlLoadLocation: "//build/bazel/rules:cc.bzl",
+ },
+ BazelTarget{
+ name: "bar",
+ ruleClass: "java_binary",
+ bzlLoadLocation: "//build/bazel/rules:java.bzl",
+ },
+ BazelTarget{
+ name: "baz",
+ ruleClass: "genrule",
+ // Note: no bzlLoadLocation for native rules
+ },
+ },
+ expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_binary")
+load("//build/bazel/rules:java.bzl", "java_binary")`,
+ },
+ }
+
+ for _, testCase := range testCases {
+ actual := testCase.bazelTargets.LoadStatements()
+ expected := testCase.expectedLoadStatements
+ if actual != expected {
+ t.Fatalf("Expected load statements to be %s, got %s", expected, actual)
+ }
+ }
+
+}
+
+func TestGenerateBazelTargetModules_OneToMany_LoadedFromStarlark(t *testing.T) {
+ testCases := []struct {
+ bp string
+ expectedBazelTarget string
+ expectedBazelTargetCount int
+ expectedLoadStatements string
+ }{
+ {
+ bp: `custom {
+ name: "bar",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTarget: `my_library(
+ name = "bar",
+)
+
+my_proto_library(
+ name = "bar_my_proto_library_deps",
+)
+
+proto_library(
+ name = "bar_proto_library_deps",
+)`,
+ expectedBazelTargetCount: 3,
+ expectedLoadStatements: `load("//build/bazel/rules:proto.bzl", "my_proto_library", "proto_library")
+load("//build/bazel/rules:rules.bzl", "my_library")`,
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+ ctx := android.NewTestContext(config)
+ ctx.RegisterModuleType("custom", customModuleFactory)
+ ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutatorFromStarlark)
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.ResolveDependencies(config)
+ android.FailIfErrored(t, errs)
+
+ codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+ if actualCount := len(bazelTargets); actualCount != testCase.expectedBazelTargetCount {
+ t.Fatalf("Expected %d bazel target, got %d", testCase.expectedBazelTargetCount, actualCount)
+ }
+
+ actualBazelTargets := bazelTargets.String()
+ if actualBazelTargets != testCase.expectedBazelTarget {
+ t.Errorf(
+ "Expected generated Bazel target to be '%s', got '%s'",
+ testCase.expectedBazelTarget,
+ actualBazelTargets,
+ )
+ }
+
+ actualLoadStatements := bazelTargets.LoadStatements()
+ if actualLoadStatements != testCase.expectedLoadStatements {
+ t.Errorf(
+ "Expected generated load statements to be '%s', got '%s'",
+ testCase.expectedLoadStatements,
+ actualLoadStatements,
+ )
+ }
+ }
+}
+
+func TestModuleTypeBp2Build(t *testing.T) {
+ otherGenruleBp := map[string]string{
+ "other/Android.bp": `genrule {
+ name: "foo.tool",
+ out: ["foo_tool.out"],
+ srcs: ["foo_tool.in"],
+ cmd: "cp $(in) $(out)",
+}
+genrule {
+ name: "other.tool",
+ out: ["other_tool.out"],
+ srcs: ["other_tool.in"],
+ cmd: "cp $(in) $(out)",
+}`,
+ }
+
+ testCases := []struct {
+ description string
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+ preArchMutators []android.RegisterMutatorFunc
+ depsMutators []android.RegisterMutatorFunc
+ bp string
+ expectedBazelTargets []string
+ fs map[string]string
+ dir string
+ }{
+ {
+ description: "filegroup with does not specify srcs",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ bp: `filegroup {
+ name: "fg_foo",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ `filegroup(
+ name = "fg_foo",
+)`,
+ },
+ },
+ {
+ description: "filegroup with no srcs",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ bp: `filegroup {
+ name: "fg_foo",
+ srcs: [],
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ `filegroup(
+ name = "fg_foo",
+)`,
+ },
+ },
+ {
+ description: "filegroup with srcs",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ bp: `filegroup {
+ name: "fg_foo",
+ srcs: ["a", "b"],
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{`filegroup(
+ name = "fg_foo",
+ srcs = [
+ "a",
+ "b",
+ ],
+)`,
+ },
+ },
+ {
+ description: "filegroup with excludes srcs",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ bp: `filegroup {
+ name: "fg_foo",
+ srcs: ["a", "b"],
+ exclude_srcs: ["a"],
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{`filegroup(
+ name = "fg_foo",
+ srcs = ["b"],
+)`,
+ },
+ },
+ {
+ description: "filegroup with glob",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ bp: `filegroup {
+ name: "foo",
+ srcs: ["**/*.txt"],
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{`filegroup(
+ name = "foo",
+ srcs = [
+ "other/a.txt",
+ "other/b.txt",
+ "other/subdir/a.txt",
+ ],
+)`,
+ },
+ fs: map[string]string{
+ "other/a.txt": "",
+ "other/b.txt": "",
+ "other/subdir/a.txt": "",
+ "other/file": "",
+ },
+ },
+ {
+ description: "filegroup with glob in subdir",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ bp: `filegroup {
+ name: "foo",
+ srcs: ["a.txt"],
+ bazel_module: { bp2build_available: true },
+}`,
+ dir: "other",
+ expectedBazelTargets: []string{`filegroup(
+ name = "fg_foo",
+ srcs = [
+ "a.txt",
+ "b.txt",
+ "subdir/a.txt",
+ ],
+)`,
+ },
+ fs: map[string]string{
+ "other/Android.bp": `filegroup {
+ name: "fg_foo",
+ srcs: ["**/*.txt"],
+ bazel_module: { bp2build_available: true },
+}`,
+ "other/a.txt": "",
+ "other/b.txt": "",
+ "other/subdir/a.txt": "",
+ "other/file": "",
+ },
+ },
+ {
+ description: "depends_on_other_dir_module",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ bp: `filegroup {
+ name: "foobar",
+ srcs: [
+ ":foo",
+ "c",
+ ],
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{`filegroup(
+ name = "foobar",
+ srcs = [
+ "//other:foo",
+ "c",
+ ],
+)`,
+ },
+ fs: map[string]string{
+ "other/Android.bp": `filegroup {
+ name: "foo",
+ srcs: ["a", "b"],
+}`,
+ },
+ },
+ {
+ description: "genrule with command line variable replacements",
+ moduleTypeUnderTest: "genrule",
+ moduleTypeUnderTestFactory: genrule.GenRuleFactory,
+ moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+ depsMutators: []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
+ bp: `genrule {
+ name: "foo.tool",
+ out: ["foo_tool.out"],
+ srcs: ["foo_tool.in"],
+ cmd: "cp $(in) $(out)",
+ bazel_module: { bp2build_available: true },
+}
+
+genrule {
+ name: "foo",
+ out: ["foo.out"],
+ srcs: ["foo.in"],
+ tools: [":foo.tool"],
+ cmd: "$(location :foo.tool) --genDir=$(genDir) arg $(in) $(out)",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{
+ `genrule(
+ name = "foo",
+ cmd = "$(location :foo.tool) --genDir=$(GENDIR) arg $(SRCS) $(OUTS)",
+ outs = ["foo.out"],
+ srcs = ["foo.in"],
+ tools = [":foo.tool"],
+)`,
+ `genrule(
+ name = "foo.tool",
+ cmd = "cp $(SRCS) $(OUTS)",
+ outs = ["foo_tool.out"],
+ srcs = ["foo_tool.in"],
+)`,
+ },
+ },
+ {
+ description: "genrule using $(locations :label)",
+ moduleTypeUnderTest: "genrule",
+ moduleTypeUnderTestFactory: genrule.GenRuleFactory,
+ moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+ depsMutators: []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
+ bp: `genrule {
+ name: "foo.tools",
+ out: ["foo_tool.out", "foo_tool2.out"],
+ srcs: ["foo_tool.in"],
+ cmd: "cp $(in) $(out)",
+ bazel_module: { bp2build_available: true },
+}
+
+genrule {
+ name: "foo",
+ out: ["foo.out"],
+ srcs: ["foo.in"],
+ tools: [":foo.tools"],
+ cmd: "$(locations :foo.tools) -s $(out) $(in)",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{`genrule(
+ name = "foo",
+ cmd = "$(locations :foo.tools) -s $(OUTS) $(SRCS)",
+ outs = ["foo.out"],
+ srcs = ["foo.in"],
+ tools = [":foo.tools"],
+)`,
+ `genrule(
+ name = "foo.tools",
+ cmd = "cp $(SRCS) $(OUTS)",
+ outs = [
+ "foo_tool.out",
+ "foo_tool2.out",
+ ],
+ srcs = ["foo_tool.in"],
+)`,
+ },
+ },
+ {
+ description: "genrule using $(locations //absolute:label)",
+ moduleTypeUnderTest: "genrule",
+ moduleTypeUnderTestFactory: genrule.GenRuleFactory,
+ moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+ depsMutators: []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
+ bp: `genrule {
+ name: "foo",
+ out: ["foo.out"],
+ srcs: ["foo.in"],
+ tool_files: [":foo.tool"],
+ cmd: "$(locations :foo.tool) -s $(out) $(in)",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{`genrule(
+ name = "foo",
+ cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)",
+ outs = ["foo.out"],
+ srcs = ["foo.in"],
+ tools = ["//other:foo.tool"],
+)`,
+ },
+ fs: otherGenruleBp,
+ },
+ {
+ description: "genrule srcs using $(locations //absolute:label)",
+ moduleTypeUnderTest: "genrule",
+ moduleTypeUnderTestFactory: genrule.GenRuleFactory,
+ moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+ depsMutators: []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
+ bp: `genrule {
+ name: "foo",
+ out: ["foo.out"],
+ srcs: [":other.tool"],
+ tool_files: [":foo.tool"],
+ cmd: "$(locations :foo.tool) -s $(out) $(location :other.tool)",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{`genrule(
+ name = "foo",
+ cmd = "$(locations //other:foo.tool) -s $(OUTS) $(location //other:other.tool)",
+ outs = ["foo.out"],
+ srcs = ["//other:other.tool"],
+ tools = ["//other:foo.tool"],
+)`,
+ },
+ fs: otherGenruleBp,
+ },
+ {
+ description: "genrule using $(location) label should substitute first tool label automatically",
+ moduleTypeUnderTest: "genrule",
+ moduleTypeUnderTestFactory: genrule.GenRuleFactory,
+ moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+ depsMutators: []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
+ bp: `genrule {
+ name: "foo",
+ out: ["foo.out"],
+ srcs: ["foo.in"],
+ tool_files: [":foo.tool", ":other.tool"],
+ cmd: "$(location) -s $(out) $(in)",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{`genrule(
+ name = "foo",
+ cmd = "$(location //other:foo.tool) -s $(OUTS) $(SRCS)",
+ outs = ["foo.out"],
+ srcs = ["foo.in"],
+ tools = [
+ "//other:foo.tool",
+ "//other:other.tool",
+ ],
+)`,
+ },
+ fs: otherGenruleBp,
+ },
+ {
+ description: "genrule using $(locations) label should substitute first tool label automatically",
+ moduleTypeUnderTest: "genrule",
+ moduleTypeUnderTestFactory: genrule.GenRuleFactory,
+ moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+ depsMutators: []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
+ bp: `genrule {
+ name: "foo",
+ out: ["foo.out"],
+ srcs: ["foo.in"],
+ tools: [":foo.tool", ":other.tool"],
+ cmd: "$(locations) -s $(out) $(in)",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{`genrule(
+ name = "foo",
+ cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)",
+ outs = ["foo.out"],
+ srcs = ["foo.in"],
+ tools = [
+ "//other:foo.tool",
+ "//other:other.tool",
+ ],
+)`,
+ },
+ fs: otherGenruleBp,
+ },
+ {
+ description: "genrule without tools or tool_files can convert successfully",
+ moduleTypeUnderTest: "genrule",
+ moduleTypeUnderTestFactory: genrule.GenRuleFactory,
+ moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+ depsMutators: []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
+ bp: `genrule {
+ name: "foo",
+ out: ["foo.out"],
+ srcs: ["foo.in"],
+ cmd: "cp $(in) $(out)",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{`genrule(
+ name = "foo",
+ cmd = "cp $(SRCS) $(OUTS)",
+ outs = ["foo.out"],
+ srcs = ["foo.in"],
+)`,
+ },
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ fs := make(map[string][]byte)
+ toParse := []string{
+ "Android.bp",
+ }
+ for f, content := range testCase.fs {
+ if strings.HasSuffix(f, "Android.bp") {
+ toParse = append(toParse, f)
+ }
+ fs[f] = []byte(content)
+ }
+ config := android.TestConfig(buildDir, nil, testCase.bp, fs)
+ ctx := android.NewTestContext(config)
+ ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+ for _, m := range testCase.depsMutators {
+ ctx.DepsBp2BuildMutators(m)
+ }
+ ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, toParse)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+ _, errs = ctx.ResolveDependencies(config)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+
+ checkDir := dir
+ if testCase.dir != "" {
+ checkDir = testCase.dir
+ }
+
+ codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
+ if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+ t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+ } else {
+ for i, target := range bazelTargets {
+ if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+ t.Errorf(
+ "%s: Expected generated Bazel target to be '%s', got '%s'",
+ testCase.description,
+ w,
+ g,
+ )
+ }
+ }
+ }
+ }
+}
+
+func Errored(t *testing.T, desc string, errs []error) bool {
+ t.Helper()
+ if len(errs) > 0 {
+ for _, err := range errs {
+ t.Errorf("%s: %s", desc, err)
+ }
+ return true
+ }
+ return false
+}
+
+type bp2buildMutator = func(android.TopDownMutatorContext)
+
+func TestBp2BuildInlinesDefaults(t *testing.T) {
+ testCases := []struct {
+ moduleTypesUnderTest map[string]android.ModuleFactory
+ bp2buildMutatorsUnderTest map[string]bp2buildMutator
+ bp string
+ expectedBazelTarget string
+ description string
+ }{
+ {
+ moduleTypesUnderTest: map[string]android.ModuleFactory{
+ "genrule": genrule.GenRuleFactory,
+ "genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
+ },
+ bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
+ "genrule": genrule.GenruleBp2Build,
+ },
+ bp: `genrule_defaults {
+ name: "gen_defaults",
+ cmd: "do-something $(in) $(out)",
+}
+genrule {
+ name: "gen",
+ out: ["out"],
+ srcs: ["in1"],
+ defaults: ["gen_defaults"],
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTarget: `genrule(
+ name = "gen",
+ cmd = "do-something $(SRCS) $(OUTS)",
+ outs = ["out"],
+ srcs = ["in1"],
+)`,
+ description: "genrule applies properties from a genrule_defaults dependency if not specified",
+ },
+ {
+ moduleTypesUnderTest: map[string]android.ModuleFactory{
+ "genrule": genrule.GenRuleFactory,
+ "genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
+ },
+ bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
+ "genrule": genrule.GenruleBp2Build,
+ },
+ bp: `genrule_defaults {
+ name: "gen_defaults",
+ out: ["out-from-defaults"],
+ srcs: ["in-from-defaults"],
+ cmd: "cmd-from-defaults",
+}
+genrule {
+ name: "gen",
+ out: ["out"],
+ srcs: ["in1"],
+ defaults: ["gen_defaults"],
+ cmd: "do-something $(in) $(out)",
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTarget: `genrule(
+ name = "gen",
+ cmd = "do-something $(SRCS) $(OUTS)",
+ outs = [
+ "out-from-defaults",
+ "out",
+ ],
+ srcs = [
+ "in-from-defaults",
+ "in1",
+ ],
+)`,
+ description: "genrule does merges properties from a genrule_defaults dependency, latest-first",
+ },
+ {
+ moduleTypesUnderTest: map[string]android.ModuleFactory{
+ "genrule": genrule.GenRuleFactory,
+ "genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
+ },
+ bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
+ "genrule": genrule.GenruleBp2Build,
+ },
+ bp: `genrule_defaults {
+ name: "gen_defaults1",
+ cmd: "cp $(in) $(out)",
+}
+
+genrule_defaults {
+ name: "gen_defaults2",
+ srcs: ["in1"],
+}
+
+genrule {
+ name: "gen",
+ out: ["out"],
+ defaults: ["gen_defaults1", "gen_defaults2"],
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTarget: `genrule(
+ name = "gen",
+ cmd = "cp $(SRCS) $(OUTS)",
+ outs = ["out"],
+ srcs = ["in1"],
+)`,
+ description: "genrule applies properties from list of genrule_defaults",
+ },
+ {
+ moduleTypesUnderTest: map[string]android.ModuleFactory{
+ "genrule": genrule.GenRuleFactory,
+ "genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
+ },
+ bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
+ "genrule": genrule.GenruleBp2Build,
+ },
+ bp: `genrule_defaults {
+ name: "gen_defaults1",
+ defaults: ["gen_defaults2"],
+ cmd: "cmd1 $(in) $(out)", // overrides gen_defaults2's cmd property value.
+}
+
+genrule_defaults {
+ name: "gen_defaults2",
+ defaults: ["gen_defaults3"],
+ cmd: "cmd2 $(in) $(out)",
+ out: ["out-from-2"],
+ srcs: ["in1"],
+}
+
+genrule_defaults {
+ name: "gen_defaults3",
+ out: ["out-from-3"],
+ srcs: ["srcs-from-3"],
+}
+
+genrule {
+ name: "gen",
+ out: ["out"],
+ defaults: ["gen_defaults1"],
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTarget: `genrule(
+ name = "gen",
+ cmd = "cmd1 $(SRCS) $(OUTS)",
+ outs = [
+ "out-from-3",
+ "out-from-2",
+ "out",
+ ],
+ srcs = [
+ "in1",
+ "srcs-from-3",
+ ],
+)`,
+ description: "genrule applies properties from genrule_defaults transitively",
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+ ctx := android.NewTestContext(config)
+ for m, factory := range testCase.moduleTypesUnderTest {
+ ctx.RegisterModuleType(m, factory)
+ }
+ for mutator, f := range testCase.bp2buildMutatorsUnderTest {
+ ctx.RegisterBp2BuildMutator(mutator, f)
+ }
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.ResolveDependencies(config)
+ android.FailIfErrored(t, errs)
+
+ codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+ if actualCount := len(bazelTargets); actualCount != 1 {
+ t.Fatalf("%s: Expected 1 bazel target, got %d", testCase.description, actualCount)
+ }
+
+ actualBazelTarget := bazelTargets[0]
+ if actualBazelTarget.content != testCase.expectedBazelTarget {
+ t.Errorf(
+ "%s: Expected generated Bazel target to be '%s', got '%s'",
+ testCase.description,
+ testCase.expectedBazelTarget,
+ actualBazelTarget.content,
+ )
+ }
+ }
+}
+
+func TestAllowlistingBp2buildTargetsExplicitly(t *testing.T) {
+ testCases := []struct {
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ moduleTypeUnderTestBp2BuildMutator bp2buildMutator
+ bp string
+ expectedCount int
+ description string
+ }{
+ {
+ description: "explicitly unavailable",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ bp: `filegroup {
+ name: "foo",
+ srcs: ["a", "b"],
+ bazel_module: { bp2build_available: false },
+}`,
+ expectedCount: 0,
+ },
+ {
+ description: "implicitly unavailable",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ bp: `filegroup {
+ name: "foo",
+ srcs: ["a", "b"],
+}`,
+ expectedCount: 0,
+ },
+ {
+ description: "explicitly available",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ bp: `filegroup {
+ name: "foo",
+ srcs: ["a", "b"],
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedCount: 1,
+ },
+ {
+ description: "generates more than 1 target if needed",
+ moduleTypeUnderTest: "custom",
+ moduleTypeUnderTestFactory: customModuleFactory,
+ moduleTypeUnderTestBp2BuildMutator: customBp2BuildMutatorFromStarlark,
+ bp: `custom {
+ name: "foo",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedCount: 3,
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+ ctx := android.NewTestContext(config)
+ ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+ ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.ResolveDependencies(config)
+ android.FailIfErrored(t, errs)
+
+ codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+ if actualCount := len(bazelTargets); actualCount != testCase.expectedCount {
+ t.Fatalf("%s: Expected %d bazel target, got %d", testCase.description, testCase.expectedCount, actualCount)
+ }
+ }
+}
+
+func TestAllowlistingBp2buildTargetsWithConfig(t *testing.T) {
+ testCases := []struct {
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ moduleTypeUnderTestBp2BuildMutator bp2buildMutator
+ expectedCount map[string]int
+ description string
+ bp2buildConfig android.Bp2BuildConfig
+ checkDir string
+ fs map[string]string
+ }{
+ {
+ description: "test bp2build config package and subpackages config",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ expectedCount: map[string]int{
+ "migrated": 1,
+ "migrated/but_not_really": 0,
+ "migrated/but_not_really/but_really": 1,
+ "not_migrated": 0,
+ "also_not_migrated": 0,
+ },
+ bp2buildConfig: android.Bp2BuildConfig{
+ "migrated": android.Bp2BuildDefaultTrueRecursively,
+ "migrated/but_not_really": android.Bp2BuildDefaultFalse,
+ "not_migrated": android.Bp2BuildDefaultFalse,
+ },
+ fs: map[string]string{
+ "migrated/Android.bp": `filegroup { name: "a" }`,
+ "migrated/but_not_really/Android.bp": `filegroup { name: "b" }`,
+ "migrated/but_not_really/but_really/Android.bp": `filegroup { name: "c" }`,
+ "not_migrated/Android.bp": `filegroup { name: "d" }`,
+ "also_not_migrated/Android.bp": `filegroup { name: "e" }`,
+ },
+ },
+ {
+ description: "test bp2build config opt-in and opt-out",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ expectedCount: map[string]int{
+ "package-opt-in": 2,
+ "package-opt-in/subpackage": 0,
+ "package-opt-out": 1,
+ "package-opt-out/subpackage": 0,
+ },
+ bp2buildConfig: android.Bp2BuildConfig{
+ "package-opt-in": android.Bp2BuildDefaultFalse,
+ "package-opt-out": android.Bp2BuildDefaultTrueRecursively,
+ },
+ fs: map[string]string{
+ "package-opt-in/Android.bp": `
+filegroup { name: "opt-in-a" }
+filegroup { name: "opt-in-b", bazel_module: { bp2build_available: true } }
+filegroup { name: "opt-in-c", bazel_module: { bp2build_available: true } }
+`,
+
+ "package-opt-in/subpackage/Android.bp": `
+filegroup { name: "opt-in-d" } // parent package not configured to DefaultTrueRecursively
+`,
+
+ "package-opt-out/Android.bp": `
+filegroup { name: "opt-out-a" }
+filegroup { name: "opt-out-b", bazel_module: { bp2build_available: false } }
+filegroup { name: "opt-out-c", bazel_module: { bp2build_available: false } }
+`,
+
+ "package-opt-out/subpackage/Android.bp": `
+filegroup { name: "opt-out-g", bazel_module: { bp2build_available: false } }
+filegroup { name: "opt-out-h", bazel_module: { bp2build_available: false } }
+`,
+ },
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ fs := make(map[string][]byte)
+ toParse := []string{
+ "Android.bp",
+ }
+ for f, content := range testCase.fs {
+ if strings.HasSuffix(f, "Android.bp") {
+ toParse = append(toParse, f)
+ }
+ fs[f] = []byte(content)
+ }
+ config := android.TestConfig(buildDir, nil, "", fs)
+ ctx := android.NewTestContext(config)
+ ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+ ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+ ctx.RegisterBp2BuildConfig(testCase.bp2buildConfig)
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, toParse)
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.ResolveDependencies(config)
+ android.FailIfErrored(t, errs)
+
+ codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+
+ // For each directory, test that the expected number of generated targets is correct.
+ for dir, expectedCount := range testCase.expectedCount {
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+ if actualCount := len(bazelTargets); actualCount != expectedCount {
+ t.Fatalf(
+ "%s: Expected %d bazel target for %s package, got %d",
+ testCase.description,
+ expectedCount,
+ dir,
+ actualCount)
+ }
+
+ }
+ }
+}
+
+func TestCombineBuildFilesBp2buildTargets(t *testing.T) {
+ testCases := []struct {
+ description string
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+ preArchMutators []android.RegisterMutatorFunc
+ depsMutators []android.RegisterMutatorFunc
+ bp string
+ expectedBazelTargets []string
+ fs map[string]string
+ dir string
+ }{
+ {
+ description: "filegroup bazel_module.label",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ bp: `filegroup {
+ name: "fg_foo",
+ bazel_module: { label: "//other:fg_foo" },
+}`,
+ expectedBazelTargets: []string{
+ `// BUILD file`,
+ },
+ fs: map[string]string{
+ "other/BUILD.bazel": `// BUILD file`,
+ },
+ },
+ {
+ description: "multiple bazel_module.label same BUILD",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ bp: `filegroup {
+ name: "fg_foo",
+ bazel_module: { label: "//other:fg_foo" },
+}
+
+filegroup {
+ name: "foo",
+ bazel_module: { label: "//other:foo" },
+}`,
+ expectedBazelTargets: []string{
+ `// BUILD file`,
+ },
+ fs: map[string]string{
+ "other/BUILD.bazel": `// BUILD file`,
+ },
+ },
+ {
+ description: "filegroup bazel_module.label and bp2build",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ bp: `filegroup {
+ name: "fg_foo",
+ bazel_module: {
+ label: "//other:fg_foo",
+ bp2build_available: true,
+ },
+}`,
+ expectedBazelTargets: []string{
+ `filegroup(
+ name = "fg_foo",
+)`,
+ `// BUILD file`,
+ },
+ fs: map[string]string{
+ "other/BUILD.bazel": `// BUILD file`,
+ },
+ },
+ {
+ description: "filegroup bazel_module.label and filegroup bp2build",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ bp: `filegroup {
+ name: "fg_foo",
+ bazel_module: {
+ label: "//other:fg_foo",
+ },
+}
+
+filegroup {
+ name: "fg_bar",
+ bazel_module: {
+ bp2build_available: true,
+ },
+}`,
+ expectedBazelTargets: []string{
+ `filegroup(
+ name = "fg_bar",
+)`,
+ `// BUILD file`,
+ },
+ fs: map[string]string{
+ "other/BUILD.bazel": `// BUILD file`,
+ },
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ fs := make(map[string][]byte)
+ toParse := []string{
+ "Android.bp",
+ }
+ for f, content := range testCase.fs {
+ if strings.HasSuffix(f, "Android.bp") {
+ toParse = append(toParse, f)
+ }
+ fs[f] = []byte(content)
+ }
+ config := android.TestConfig(buildDir, nil, testCase.bp, fs)
+ ctx := android.NewTestContext(config)
+ ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+ for _, m := range testCase.depsMutators {
+ ctx.DepsBp2BuildMutators(m)
+ }
+ ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, toParse)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+ _, errs = ctx.ResolveDependencies(config)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+
+ checkDir := dir
+ if testCase.dir != "" {
+ checkDir = testCase.dir
+ }
+ bazelTargets := generateBazelTargetsForDir(NewCodegenContext(config, *ctx.Context, Bp2Build), checkDir)
+ if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+ t.Errorf("%s: Expected %d bazel target, got %d\n%s", testCase.description, expectedCount, actualCount, bazelTargets)
+ } else {
+ for i, target := range bazelTargets {
+ if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+ t.Errorf(
+ "%s: Expected generated Bazel target to be '%s', got '%s'",
+ testCase.description,
+ w,
+ g,
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/bp2build/bzl_conversion.go b/bp2build/bzl_conversion.go
new file mode 100644
index 0000000..f2f6b01
--- /dev/null
+++ b/bp2build/bzl_conversion.go
@@ -0,0 +1,230 @@
+// Copyright 2020 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 bp2build
+
+import (
+ "android/soong/android"
+ "fmt"
+ "reflect"
+ "runtime"
+ "sort"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+var (
+ // An allowlist of prop types that are surfaced from module props to rule
+ // attributes. (nested) dictionaries are notably absent here, because while
+ // Soong supports multi value typed and nested dictionaries, Bazel's rule
+ // attr() API supports only single-level string_dicts.
+ allowedPropTypes = map[string]bool{
+ "int": true, // e.g. 42
+ "bool": true, // e.g. True
+ "string_list": true, // e.g. ["a", "b"]
+ "string": true, // e.g. "a"
+ }
+)
+
+type rule struct {
+ name string
+ attrs string
+}
+
+type RuleShim struct {
+ // The rule class shims contained in a bzl file. e.g. ["cc_object", "cc_library", ..]
+ rules []string
+
+ // The generated string content of the bzl file.
+ content string
+}
+
+// Create <module>.bzl containing Bazel rule shims for every module type available in Soong and
+// user-specified Go plugins.
+//
+// This function reuses documentation generation APIs to ensure parity between modules-as-docs
+// and modules-as-code, including the names and types of morule properties.
+func CreateRuleShims(moduleTypeFactories map[string]android.ModuleFactory) map[string]RuleShim {
+ ruleShims := map[string]RuleShim{}
+ for pkg, rules := range generateRules(moduleTypeFactories) {
+ shim := RuleShim{
+ rules: make([]string, 0, len(rules)),
+ }
+ shim.content = "load(\"//build/bazel/queryview_rules:providers.bzl\", \"SoongModuleInfo\")\n"
+
+ bzlFileName := strings.ReplaceAll(pkg, "android/soong/", "")
+ bzlFileName = strings.ReplaceAll(bzlFileName, ".", "_")
+ bzlFileName = strings.ReplaceAll(bzlFileName, "/", "_")
+
+ for _, r := range rules {
+ shim.content += fmt.Sprintf(moduleRuleShim, r.name, r.attrs)
+ shim.rules = append(shim.rules, r.name)
+ }
+ sort.Strings(shim.rules)
+ ruleShims[bzlFileName] = shim
+ }
+ return ruleShims
+}
+
+// Generate the content of soong_module.bzl with the rule shim load statements
+// and mapping of module_type to rule shim map for every module type in Soong.
+func generateSoongModuleBzl(bzlLoads map[string]RuleShim) string {
+ var loadStmts string
+ var moduleRuleMap string
+ for _, bzlFileName := range android.SortedStringKeys(bzlLoads) {
+ loadStmt := "load(\"//build/bazel/queryview_rules:"
+ loadStmt += bzlFileName
+ loadStmt += ".bzl\""
+ ruleShim := bzlLoads[bzlFileName]
+ for _, rule := range ruleShim.rules {
+ loadStmt += fmt.Sprintf(", %q", rule)
+ moduleRuleMap += " \"" + rule + "\": " + rule + ",\n"
+ }
+ loadStmt += ")\n"
+ loadStmts += loadStmt
+ }
+
+ return fmt.Sprintf(soongModuleBzl, loadStmts, moduleRuleMap)
+}
+
+func generateRules(moduleTypeFactories map[string]android.ModuleFactory) map[string][]rule {
+ // TODO: add shims for bootstrap/blueprint go modules types
+
+ rules := make(map[string][]rule)
+ // TODO: allow registration of a bzl rule when registring a factory
+ for _, moduleType := range android.SortedStringKeys(moduleTypeFactories) {
+ factory := moduleTypeFactories[moduleType]
+ factoryName := runtime.FuncForPC(reflect.ValueOf(factory).Pointer()).Name()
+ pkg := strings.Split(factoryName, ".")[0]
+ attrs := `{
+ "soong_module_name": attr.string(mandatory = True),
+ "soong_module_variant": attr.string(),
+ "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
+`
+ attrs += getAttributes(factory)
+ attrs += " },"
+
+ r := rule{
+ name: canonicalizeModuleType(moduleType),
+ attrs: attrs,
+ }
+
+ rules[pkg] = append(rules[pkg], r)
+ }
+ return rules
+}
+
+type property struct {
+ name string
+ starlarkAttrType string
+ properties []property
+}
+
+const (
+ attributeIndent = " "
+)
+
+func (p *property) attributeString() string {
+ if !shouldGenerateAttribute(p.name) {
+ return ""
+ }
+
+ if _, ok := allowedPropTypes[p.starlarkAttrType]; !ok {
+ // a struct -- let's just comment out sub-props
+ s := fmt.Sprintf(attributeIndent+"# %s start\n", p.name)
+ for _, nestedP := range p.properties {
+ s += "# " + nestedP.attributeString()
+ }
+ s += fmt.Sprintf(attributeIndent+"# %s end\n", p.name)
+ return s
+ }
+ return fmt.Sprintf(attributeIndent+"%q: attr.%s(),\n", p.name, p.starlarkAttrType)
+}
+
+func extractPropertyDescriptionsFromStruct(structType reflect.Type) []property {
+ properties := make([]property, 0)
+ for i := 0; i < structType.NumField(); i++ {
+ field := structType.Field(i)
+ if shouldSkipStructField(field) {
+ continue
+ }
+
+ properties = append(properties, extractPropertyDescriptions(field.Name, field.Type)...)
+ }
+ return properties
+}
+
+func extractPropertyDescriptions(name string, t reflect.Type) []property {
+ name = proptools.PropertyNameForField(name)
+
+ // TODO: handle android:paths tags, they should be changed to label types
+
+ starlarkAttrType := fmt.Sprintf("%s", t.Name())
+ props := make([]property, 0)
+
+ switch t.Kind() {
+ case reflect.Bool, reflect.String:
+ // do nothing
+ case reflect.Uint, reflect.Int, reflect.Int64:
+ starlarkAttrType = "int"
+ case reflect.Slice:
+ if t.Elem().Kind() != reflect.String {
+ // TODO: handle lists of non-strings (currently only list of Dist)
+ return []property{}
+ }
+ starlarkAttrType = "string_list"
+ case reflect.Struct:
+ props = extractPropertyDescriptionsFromStruct(t)
+ case reflect.Ptr:
+ return extractPropertyDescriptions(name, t.Elem())
+ case reflect.Interface:
+ // Interfaces are used for for arch, multilib and target properties, which are handled at runtime.
+ // These will need to be handled in a bazel-specific version of the arch mutator.
+ return []property{}
+ }
+
+ prop := property{
+ name: name,
+ starlarkAttrType: starlarkAttrType,
+ properties: props,
+ }
+
+ return []property{prop}
+}
+
+func getPropertyDescriptions(props []interface{}) []property {
+ // there may be duplicate properties, e.g. from defaults libraries
+ propertiesByName := make(map[string]property)
+ for _, p := range props {
+ for _, prop := range extractPropertyDescriptionsFromStruct(reflect.ValueOf(p).Elem().Type()) {
+ propertiesByName[prop.name] = prop
+ }
+ }
+
+ properties := make([]property, 0, len(propertiesByName))
+ for _, key := range android.SortedStringKeys(propertiesByName) {
+ properties = append(properties, propertiesByName[key])
+ }
+
+ return properties
+}
+
+func getAttributes(factory android.ModuleFactory) string {
+ attrs := ""
+ for _, p := range getPropertyDescriptions(factory().GetProperties()) {
+ attrs += p.attributeString()
+ }
+ return attrs
+}
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
new file mode 100644
index 0000000..30c1a5b
--- /dev/null
+++ b/bp2build/bzl_conversion_test.go
@@ -0,0 +1,212 @@
+// Copyright 2020 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 bp2build
+
+import (
+ "android/soong/android"
+ "io/ioutil"
+ "os"
+ "strings"
+ "testing"
+)
+
+var buildDir string
+
+func setUp() {
+ var err error
+ buildDir, err = ioutil.TempDir("", "bazel_queryview_test")
+ if err != nil {
+ panic(err)
+ }
+}
+
+func tearDown() {
+ os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+ run := func() int {
+ setUp()
+ defer tearDown()
+
+ return m.Run()
+ }
+
+ os.Exit(run())
+}
+
+func TestGenerateModuleRuleShims(t *testing.T) {
+ moduleTypeFactories := map[string]android.ModuleFactory{
+ "custom": customModuleFactoryBase,
+ "custom_test": customTestModuleFactoryBase,
+ "custom_defaults": customDefaultsModuleFactoryBasic,
+ }
+ ruleShims := CreateRuleShims(moduleTypeFactories)
+
+ if len(ruleShims) != 1 {
+ t.Errorf("Expected to generate 1 rule shim, but got %d", len(ruleShims))
+ }
+
+ ruleShim := ruleShims["bp2build"]
+ expectedRules := []string{
+ "custom",
+ "custom_defaults",
+ "custom_test_",
+ }
+
+ if len(ruleShim.rules) != len(expectedRules) {
+ t.Errorf("Expected %d rules, but got %d", len(expectedRules), len(ruleShim.rules))
+ }
+
+ for i, rule := range ruleShim.rules {
+ if rule != expectedRules[i] {
+ t.Errorf("Expected rule shim to contain %s, but got %s", expectedRules[i], rule)
+ }
+ }
+ expectedBzl := `load("//build/bazel/queryview_rules:providers.bzl", "SoongModuleInfo")
+
+def _custom_impl(ctx):
+ return [SoongModuleInfo()]
+
+custom = rule(
+ implementation = _custom_impl,
+ attrs = {
+ "soong_module_name": attr.string(mandatory = True),
+ "soong_module_variant": attr.string(),
+ "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ # bazel_module start
+# "label": attr.string(),
+# "bp2build_available": attr.bool(),
+ # bazel_module end
+ "bool_prop": attr.bool(),
+ "bool_ptr_prop": attr.bool(),
+ "int64_ptr_prop": attr.int(),
+ # nested_props start
+# "nested_prop": attr.string(),
+ # nested_props end
+ # nested_props_ptr start
+# "nested_prop": attr.string(),
+ # nested_props_ptr end
+ "string_list_prop": attr.string_list(),
+ "string_prop": attr.string(),
+ "string_ptr_prop": attr.string(),
+ },
+)
+
+def _custom_defaults_impl(ctx):
+ return [SoongModuleInfo()]
+
+custom_defaults = rule(
+ implementation = _custom_defaults_impl,
+ attrs = {
+ "soong_module_name": attr.string(mandatory = True),
+ "soong_module_variant": attr.string(),
+ "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ "bool_prop": attr.bool(),
+ "bool_ptr_prop": attr.bool(),
+ "int64_ptr_prop": attr.int(),
+ # nested_props start
+# "nested_prop": attr.string(),
+ # nested_props end
+ # nested_props_ptr start
+# "nested_prop": attr.string(),
+ # nested_props_ptr end
+ "string_list_prop": attr.string_list(),
+ "string_prop": attr.string(),
+ "string_ptr_prop": attr.string(),
+ },
+)
+
+def _custom_test__impl(ctx):
+ return [SoongModuleInfo()]
+
+custom_test_ = rule(
+ implementation = _custom_test__impl,
+ attrs = {
+ "soong_module_name": attr.string(mandatory = True),
+ "soong_module_variant": attr.string(),
+ "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ "bool_prop": attr.bool(),
+ "bool_ptr_prop": attr.bool(),
+ "int64_ptr_prop": attr.int(),
+ # nested_props start
+# "nested_prop": attr.string(),
+ # nested_props end
+ # nested_props_ptr start
+# "nested_prop": attr.string(),
+ # nested_props_ptr end
+ "string_list_prop": attr.string_list(),
+ "string_prop": attr.string(),
+ "string_ptr_prop": attr.string(),
+ # test_prop start
+# "test_string_prop": attr.string(),
+ # test_prop end
+ },
+)
+`
+
+ if ruleShim.content != expectedBzl {
+ t.Errorf(
+ "Expected the generated rule shim bzl to be:\n%s\nbut got:\n%s",
+ expectedBzl,
+ ruleShim.content)
+ }
+}
+
+func TestGenerateSoongModuleBzl(t *testing.T) {
+ ruleShims := map[string]RuleShim{
+ "file1": RuleShim{
+ rules: []string{"a", "b"},
+ content: "irrelevant",
+ },
+ "file2": RuleShim{
+ rules: []string{"c", "d"},
+ content: "irrelevant",
+ },
+ }
+ files := CreateBazelFiles(ruleShims, make(map[string]BazelTargets), QueryView)
+
+ var actualSoongModuleBzl BazelFile
+ for _, f := range files {
+ if f.Basename == "soong_module.bzl" {
+ actualSoongModuleBzl = f
+ }
+ }
+
+ expectedLoad := `load("//build/bazel/queryview_rules:file1.bzl", "a", "b")
+load("//build/bazel/queryview_rules:file2.bzl", "c", "d")
+`
+ expectedRuleMap := `soong_module_rule_map = {
+ "a": a,
+ "b": b,
+ "c": c,
+ "d": d,
+}`
+ if !strings.Contains(actualSoongModuleBzl.Contents, expectedLoad) {
+ t.Errorf(
+ "Generated soong_module.bzl:\n\n%s\n\n"+
+ "Could not find the load statement in the generated soong_module.bzl:\n%s",
+ actualSoongModuleBzl.Contents,
+ expectedLoad)
+ }
+
+ if !strings.Contains(actualSoongModuleBzl.Contents, expectedRuleMap) {
+ t.Errorf(
+ "Generated soong_module.bzl:\n\n%s\n\n"+
+ "Could not find the module -> rule map in the generated soong_module.bzl:\n%s",
+ actualSoongModuleBzl.Contents,
+ expectedRuleMap)
+ }
+}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
new file mode 100644
index 0000000..783af2e
--- /dev/null
+++ b/bp2build/cc_library_conversion_test.go
@@ -0,0 +1,271 @@
+// Copyright 2021 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 bp2build
+
+import (
+ "android/soong/android"
+ "android/soong/cc"
+ "strings"
+ "testing"
+)
+
+const (
+ // See cc/testing.go for more context
+ soongCcLibraryPreamble = `
+cc_defaults {
+ name: "linux_bionic_supported",
+}
+
+toolchain_library {
+ name: "libclang_rt.builtins-x86_64-android",
+ defaults: ["linux_bionic_supported"],
+ vendor_available: true,
+ vendor_ramdisk_available: true,
+ product_available: true,
+ recovery_available: true,
+ native_bridge_supported: true,
+ src: "",
+}`
+)
+
+func TestCcLibraryBp2Build(t *testing.T) {
+ testCases := []struct {
+ description string
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+ bp string
+ expectedBazelTargets []string
+ filesystem map[string]string
+ dir string
+ }{
+ {
+ description: "cc_library - simple example",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ filesystem: map[string]string{
+ "android.cpp": "",
+ "darwin.cpp": "",
+ // Refer to cc.headerExts for the supported header extensions in Soong.
+ "header.h": "",
+ "header.hh": "",
+ "header.hpp": "",
+ "header.hxx": "",
+ "header.h++": "",
+ "header.inl": "",
+ "header.inc": "",
+ "header.ipp": "",
+ "header.h.generic": "",
+ "impl.cpp": "",
+ "linux.cpp": "",
+ "x86.cpp": "",
+ "x86_64.cpp": "",
+ "foo-dir/a.h": "",
+ },
+ bp: soongCcLibraryPreamble + `
+cc_library_headers { name: "some-headers" }
+cc_library {
+ name: "foo-lib",
+ srcs: ["impl.cpp"],
+ cflags: ["-Wall"],
+ header_libs: ["some-headers"],
+ export_include_dirs: ["foo-dir"],
+ ldflags: ["-Wl,--exclude-libs=bar.a"],
+ arch: {
+ x86: {
+ ldflags: ["-Wl,--exclude-libs=baz.a"],
+ srcs: ["x86.cpp"],
+ },
+ x86_64: {
+ ldflags: ["-Wl,--exclude-libs=qux.a"],
+ srcs: ["x86_64.cpp"],
+ },
+ },
+ target: {
+ android: {
+ srcs: ["android.cpp"],
+ },
+ linux_glibc: {
+ srcs: ["linux.cpp"],
+ },
+ darwin: {
+ srcs: ["darwin.cpp"],
+ },
+ },
+}
+`,
+ expectedBazelTargets: []string{`cc_library(
+ name = "foo-lib",
+ copts = ["-Wall"],
+ deps = [":some-headers"],
+ hdrs = [
+ "header.h",
+ "header.hh",
+ "header.hpp",
+ "header.hxx",
+ "header.h++",
+ "header.inl",
+ "header.inc",
+ "header.ipp",
+ "header.h.generic",
+ "foo-dir/a.h",
+ ],
+ includes = ["foo-dir"],
+ linkopts = ["-Wl,--exclude-libs=bar.a"] + select({
+ "//build/bazel/platforms/arch:x86": ["-Wl,--exclude-libs=baz.a"],
+ "//build/bazel/platforms/arch:x86_64": ["-Wl,--exclude-libs=qux.a"],
+ "//conditions:default": [],
+ }),
+ srcs = ["impl.cpp"] + select({
+ "//build/bazel/platforms/arch:x86": ["x86.cpp"],
+ "//build/bazel/platforms/arch:x86_64": ["x86_64.cpp"],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os:android": ["android.cpp"],
+ "//build/bazel/platforms/os:darwin": ["darwin.cpp"],
+ "//build/bazel/platforms/os:linux": ["linux.cpp"],
+ "//conditions:default": [],
+ }),
+)`},
+ },
+ {
+ description: "cc_library - trimmed example of //bionic/linker:ld-android",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ filesystem: map[string]string{
+ "ld-android.cpp": "",
+ "linked_list.h": "",
+ "linker.h": "",
+ "linker_block_allocator.h": "",
+ "linker_cfi.h": "",
+ },
+ bp: soongCcLibraryPreamble + `
+cc_library_headers { name: "libc_headers" }
+cc_library {
+ name: "fake-ld-android",
+ srcs: ["ld_android.cpp"],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Wunused",
+ "-Werror",
+ ],
+ header_libs: ["libc_headers"],
+ ldflags: [
+ "-Wl,--exclude-libs=libgcc.a",
+ "-Wl,--exclude-libs=libgcc_stripped.a",
+ "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
+ "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a",
+ "-Wl,--exclude-libs=libclang_rt.builtins-i686-android.a",
+ "-Wl,--exclude-libs=libclang_rt.builtins-x86_64-android.a",
+ ],
+ arch: {
+ x86: {
+ ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
+ },
+ x86_64: {
+ ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
+ },
+ },
+}
+`,
+ expectedBazelTargets: []string{`cc_library(
+ name = "fake-ld-android",
+ copts = [
+ "-Wall",
+ "-Wextra",
+ "-Wunused",
+ "-Werror",
+ ],
+ deps = [":libc_headers"],
+ hdrs = [
+ "linked_list.h",
+ "linker.h",
+ "linker_block_allocator.h",
+ "linker_cfi.h",
+ ],
+ linkopts = [
+ "-Wl,--exclude-libs=libgcc.a",
+ "-Wl,--exclude-libs=libgcc_stripped.a",
+ "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
+ "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a",
+ "-Wl,--exclude-libs=libclang_rt.builtins-i686-android.a",
+ "-Wl,--exclude-libs=libclang_rt.builtins-x86_64-android.a",
+ ] + select({
+ "//build/bazel/platforms/arch:x86": ["-Wl,--exclude-libs=libgcc_eh.a"],
+ "//build/bazel/platforms/arch:x86_64": ["-Wl,--exclude-libs=libgcc_eh.a"],
+ "//conditions:default": [],
+ }),
+ srcs = ["ld_android.cpp"],
+)`},
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ filesystem := make(map[string][]byte)
+ toParse := []string{
+ "Android.bp",
+ }
+ for f, content := range testCase.filesystem {
+ if strings.HasSuffix(f, "Android.bp") {
+ toParse = append(toParse, f)
+ }
+ filesystem[f] = []byte(content)
+ }
+ config := android.TestConfig(buildDir, nil, testCase.bp, filesystem)
+ ctx := android.NewTestContext(config)
+
+ cc.RegisterCCBuildComponents(ctx)
+ ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
+ ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
+ ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+ ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+ ctx.RegisterBp2BuildConfig(bp2buildConfig) // TODO(jingwen): make this the default for all tests
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, toParse)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+ _, errs = ctx.ResolveDependencies(config)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+
+ checkDir := dir
+ if testCase.dir != "" {
+ checkDir = testCase.dir
+ }
+ codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
+ if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+ t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+ } else {
+ for i, target := range bazelTargets {
+ if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+ t.Errorf(
+ "%s: Expected generated Bazel target to be '%s', got '%s'",
+ testCase.description,
+ w,
+ g,
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
new file mode 100644
index 0000000..c59241f
--- /dev/null
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -0,0 +1,320 @@
+// Copyright 2021 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 bp2build
+
+import (
+ "android/soong/android"
+ "android/soong/cc"
+ "strings"
+ "testing"
+)
+
+const (
+ // See cc/testing.go for more context
+ soongCcLibraryHeadersPreamble = `
+cc_defaults {
+ name: "linux_bionic_supported",
+}
+
+toolchain_library {
+ name: "libclang_rt.builtins-x86_64-android",
+ defaults: ["linux_bionic_supported"],
+ vendor_available: true,
+ vendor_ramdisk_available: true,
+ product_available: true,
+ recovery_available: true,
+ native_bridge_supported: true,
+ src: "",
+}`
+)
+
+func TestCcLibraryHeadersLoadStatement(t *testing.T) {
+ testCases := []struct {
+ bazelTargets BazelTargets
+ expectedLoadStatements string
+ }{
+ {
+ bazelTargets: BazelTargets{
+ BazelTarget{
+ name: "cc_library_headers_target",
+ ruleClass: "cc_library_headers",
+ // Note: no bzlLoadLocation for native rules
+ },
+ },
+ expectedLoadStatements: ``,
+ },
+ }
+
+ for _, testCase := range testCases {
+ actual := testCase.bazelTargets.LoadStatements()
+ expected := testCase.expectedLoadStatements
+ if actual != expected {
+ t.Fatalf("Expected load statements to be %s, got %s", expected, actual)
+ }
+ }
+
+}
+
+func TestCcLibraryHeadersBp2Build(t *testing.T) {
+ testCases := []struct {
+ description string
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+ preArchMutators []android.RegisterMutatorFunc
+ depsMutators []android.RegisterMutatorFunc
+ bp string
+ expectedBazelTargets []string
+ filesystem map[string]string
+ dir string
+ }{
+ {
+ description: "cc_library_headers test",
+ moduleTypeUnderTest: "cc_library_headers",
+ moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
+ filesystem: map[string]string{
+ "lib-1/lib1a.h": "",
+ "lib-1/lib1b.h": "",
+ "lib-2/lib2a.h": "",
+ "lib-2/lib2b.h": "",
+ "dir-1/dir1a.h": "",
+ "dir-1/dir1b.h": "",
+ "dir-2/dir2a.h": "",
+ "dir-2/dir2b.h": "",
+ "arch_arm64_exported_include_dir/a.h": "",
+ "arch_x86_exported_include_dir/b.h": "",
+ "arch_x86_64_exported_include_dir/c.h": "",
+ },
+ bp: soongCcLibraryHeadersPreamble + `
+cc_library_headers {
+ name: "lib-1",
+ export_include_dirs: ["lib-1"],
+}
+
+cc_library_headers {
+ name: "lib-2",
+ export_include_dirs: ["lib-2"],
+}
+
+cc_library_headers {
+ name: "foo_headers",
+ export_include_dirs: ["dir-1", "dir-2"],
+ header_libs: ["lib-1", "lib-2"],
+
+ arch: {
+ arm64: {
+ // We expect dir-1 headers to be dropped, because dir-1 is already in export_include_dirs
+ export_include_dirs: ["arch_arm64_exported_include_dir", "dir-1"],
+ },
+ x86: {
+ export_include_dirs: ["arch_x86_exported_include_dir"],
+ },
+ x86_64: {
+ export_include_dirs: ["arch_x86_64_exported_include_dir"],
+ },
+ },
+
+ // TODO: Also support export_header_lib_headers
+}`,
+ expectedBazelTargets: []string{`cc_library_headers(
+ name = "foo_headers",
+ deps = [
+ ":lib-1",
+ ":lib-2",
+ ],
+ hdrs = [
+ "dir-1/dir1a.h",
+ "dir-1/dir1b.h",
+ "dir-2/dir2a.h",
+ "dir-2/dir2b.h",
+ ] + select({
+ "//build/bazel/platforms/arch:arm64": ["arch_arm64_exported_include_dir/a.h"],
+ "//build/bazel/platforms/arch:x86": ["arch_x86_exported_include_dir/b.h"],
+ "//build/bazel/platforms/arch:x86_64": ["arch_x86_64_exported_include_dir/c.h"],
+ "//conditions:default": [],
+ }),
+ includes = [
+ "dir-1",
+ "dir-2",
+ ] + select({
+ "//build/bazel/platforms/arch:arm64": ["arch_arm64_exported_include_dir"],
+ "//build/bazel/platforms/arch:x86": ["arch_x86_exported_include_dir"],
+ "//build/bazel/platforms/arch:x86_64": ["arch_x86_64_exported_include_dir"],
+ "//conditions:default": [],
+ }),
+)`, `cc_library_headers(
+ name = "lib-1",
+ hdrs = [
+ "lib-1/lib1a.h",
+ "lib-1/lib1b.h",
+ ],
+ includes = ["lib-1"],
+)`, `cc_library_headers(
+ name = "lib-2",
+ hdrs = [
+ "lib-2/lib2a.h",
+ "lib-2/lib2b.h",
+ ],
+ includes = ["lib-2"],
+)`},
+ },
+ {
+ description: "cc_library_headers test with os-specific header_libs props",
+ moduleTypeUnderTest: "cc_library_headers",
+ moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
+ depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+ filesystem: map[string]string{},
+ bp: soongCcLibraryPreamble + `
+cc_library_headers { name: "android-lib" }
+cc_library_headers { name: "base-lib" }
+cc_library_headers { name: "darwin-lib" }
+cc_library_headers { name: "fuchsia-lib" }
+cc_library_headers { name: "linux-lib" }
+cc_library_headers { name: "linux_bionic-lib" }
+cc_library_headers { name: "windows-lib" }
+cc_library_headers {
+ name: "foo_headers",
+ header_libs: ["base-lib"],
+ target: {
+ android: { header_libs: ["android-lib"] },
+ darwin: { header_libs: ["darwin-lib"] },
+ fuchsia: { header_libs: ["fuchsia-lib"] },
+ linux_bionic: { header_libs: ["linux_bionic-lib"] },
+ linux_glibc: { header_libs: ["linux-lib"] },
+ windows: { header_libs: ["windows-lib"] },
+ },
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{`cc_library_headers(
+ name = "android-lib",
+)`, `cc_library_headers(
+ name = "base-lib",
+)`, `cc_library_headers(
+ name = "darwin-lib",
+)`, `cc_library_headers(
+ name = "foo_headers",
+ deps = [":base-lib"] + select({
+ "//build/bazel/platforms/os:android": [":android-lib"],
+ "//build/bazel/platforms/os:darwin": [":darwin-lib"],
+ "//build/bazel/platforms/os:fuchsia": [":fuchsia-lib"],
+ "//build/bazel/platforms/os:linux": [":linux-lib"],
+ "//build/bazel/platforms/os:linux_bionic": [":linux_bionic-lib"],
+ "//build/bazel/platforms/os:windows": [":windows-lib"],
+ "//conditions:default": [],
+ }),
+)`, `cc_library_headers(
+ name = "fuchsia-lib",
+)`, `cc_library_headers(
+ name = "linux-lib",
+)`, `cc_library_headers(
+ name = "linux_bionic-lib",
+)`, `cc_library_headers(
+ name = "windows-lib",
+)`},
+ },
+ {
+ description: "cc_library_headers test with os-specific header_libs and export_header_lib_headers props",
+ moduleTypeUnderTest: "cc_library_headers",
+ moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
+ depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+ filesystem: map[string]string{},
+ bp: soongCcLibraryPreamble + `
+cc_library_headers { name: "android-lib" }
+cc_library_headers { name: "exported-lib" }
+cc_library_headers {
+ name: "foo_headers",
+ target: {
+ android: { header_libs: ["android-lib"], export_header_lib_headers: ["exported-lib"] },
+ },
+}`,
+ expectedBazelTargets: []string{`cc_library_headers(
+ name = "android-lib",
+)`, `cc_library_headers(
+ name = "exported-lib",
+)`, `cc_library_headers(
+ name = "foo_headers",
+ deps = select({
+ "//build/bazel/platforms/os:android": [
+ ":android-lib",
+ ":exported-lib",
+ ],
+ "//conditions:default": [],
+ }),
+)`},
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ filesystem := make(map[string][]byte)
+ toParse := []string{
+ "Android.bp",
+ }
+ for f, content := range testCase.filesystem {
+ if strings.HasSuffix(f, "Android.bp") {
+ toParse = append(toParse, f)
+ }
+ filesystem[f] = []byte(content)
+ }
+ config := android.TestConfig(buildDir, nil, testCase.bp, filesystem)
+ ctx := android.NewTestContext(config)
+
+ // TODO(jingwen): make this default for all bp2build tests
+ ctx.RegisterBp2BuildConfig(bp2buildConfig)
+
+ cc.RegisterCCBuildComponents(ctx)
+ ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
+
+ ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+ for _, m := range testCase.depsMutators {
+ ctx.DepsBp2BuildMutators(m)
+ }
+ ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, toParse)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+ _, errs = ctx.ResolveDependencies(config)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+
+ checkDir := dir
+ if testCase.dir != "" {
+ checkDir = testCase.dir
+ }
+ codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
+ if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+ t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+ } else {
+ for i, target := range bazelTargets {
+ if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+ t.Errorf(
+ "%s: Expected generated Bazel target to be '%s', got '%s'",
+ testCase.description,
+ w,
+ g,
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
new file mode 100644
index 0000000..7e72a8b
--- /dev/null
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -0,0 +1,387 @@
+// Copyright 2021 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 bp2build
+
+import (
+ "android/soong/android"
+ "android/soong/cc"
+ "strings"
+ "testing"
+)
+
+const (
+ // See cc/testing.go for more context
+ soongCcLibraryStaticPreamble = `
+cc_defaults {
+ name: "linux_bionic_supported",
+}
+
+toolchain_library {
+ name: "libclang_rt.builtins-x86_64-android",
+ defaults: ["linux_bionic_supported"],
+ vendor_available: true,
+ vendor_ramdisk_available: true,
+ product_available: true,
+ recovery_available: true,
+ native_bridge_supported: true,
+ src: "",
+}`
+)
+
+func TestCcLibraryStaticLoadStatement(t *testing.T) {
+ testCases := []struct {
+ bazelTargets BazelTargets
+ expectedLoadStatements string
+ }{
+ {
+ bazelTargets: BazelTargets{
+ BazelTarget{
+ name: "cc_library_static_target",
+ ruleClass: "cc_library_static",
+ // NOTE: No bzlLoadLocation for native rules
+ },
+ },
+ expectedLoadStatements: ``,
+ },
+ }
+
+ for _, testCase := range testCases {
+ actual := testCase.bazelTargets.LoadStatements()
+ expected := testCase.expectedLoadStatements
+ if actual != expected {
+ t.Fatalf("Expected load statements to be %s, got %s", expected, actual)
+ }
+ }
+
+}
+
+func TestCcLibraryStaticBp2Build(t *testing.T) {
+ testCases := []struct {
+ description string
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+ preArchMutators []android.RegisterMutatorFunc
+ depsMutators []android.RegisterMutatorFunc
+ bp string
+ expectedBazelTargets []string
+ filesystem map[string]string
+ dir string
+ }{
+ {
+ description: "cc_library_static test",
+ moduleTypeUnderTest: "cc_library_static",
+ moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ filesystem: map[string]string{
+ // NOTE: include_dir headers *should not* appear in Bazel hdrs later (?)
+ "include_dir_1/include_dir_1_a.h": "",
+ "include_dir_1/include_dir_1_b.h": "",
+ "include_dir_2/include_dir_2_a.h": "",
+ "include_dir_2/include_dir_2_b.h": "",
+ // NOTE: local_include_dir headers *should not* appear in Bazel hdrs later (?)
+ "local_include_dir_1/local_include_dir_1_a.h": "",
+ "local_include_dir_1/local_include_dir_1_b.h": "",
+ "local_include_dir_2/local_include_dir_2_a.h": "",
+ "local_include_dir_2/local_include_dir_2_b.h": "",
+ // NOTE: export_include_dir headers *should* appear in Bazel hdrs later
+ "export_include_dir_1/export_include_dir_1_a.h": "",
+ "export_include_dir_1/export_include_dir_1_b.h": "",
+ "export_include_dir_2/export_include_dir_2_a.h": "",
+ "export_include_dir_2/export_include_dir_2_b.h": "",
+ // NOTE: Soong implicitly includes headers in the current directory
+ "implicit_include_1.h": "",
+ "implicit_include_2.h": "",
+ },
+ bp: soongCcLibraryStaticPreamble + `
+cc_library_headers {
+ name: "header_lib_1",
+ export_include_dirs: ["header_lib_1"],
+}
+
+cc_library_headers {
+ name: "header_lib_2",
+ export_include_dirs: ["header_lib_2"],
+}
+
+cc_library_static {
+ name: "static_lib_1",
+ srcs: ["static_lib_1.cc"],
+ bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+ name: "static_lib_2",
+ srcs: ["static_lib_2.cc"],
+ bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+ name: "whole_static_lib_1",
+ srcs: ["whole_static_lib_1.cc"],
+ bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+ name: "whole_static_lib_2",
+ srcs: ["whole_static_lib_2.cc"],
+ bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+ name: "foo_static",
+ srcs: [
+ "foo_static1.cc",
+ "foo_static2.cc",
+ ],
+ cflags: [
+ "-Dflag1",
+ "-Dflag2"
+ ],
+ static_libs: [
+ "static_lib_1",
+ "static_lib_2"
+ ],
+ whole_static_libs: [
+ "whole_static_lib_1",
+ "whole_static_lib_2"
+ ],
+ include_dirs: [
+ "include_dir_1",
+ "include_dir_2",
+ ],
+ local_include_dirs: [
+ "local_include_dir_1",
+ "local_include_dir_2",
+ ],
+ export_include_dirs: [
+ "export_include_dir_1",
+ "export_include_dir_2"
+ ],
+ header_libs: [
+ "header_lib_1",
+ "header_lib_2"
+ ],
+
+ // TODO: Also support export_header_lib_headers
+
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{`cc_library_static(
+ name = "foo_static",
+ copts = [
+ "-Dflag1",
+ "-Dflag2",
+ ],
+ deps = [
+ ":header_lib_1",
+ ":header_lib_2",
+ ":static_lib_1",
+ ":static_lib_2",
+ ":whole_static_lib_1",
+ ":whole_static_lib_2",
+ ],
+ hdrs = [
+ "implicit_include_1.h",
+ "implicit_include_2.h",
+ "export_include_dir_1/export_include_dir_1_a.h",
+ "export_include_dir_1/export_include_dir_1_b.h",
+ "export_include_dir_2/export_include_dir_2_a.h",
+ "export_include_dir_2/export_include_dir_2_b.h",
+ ],
+ includes = [
+ "export_include_dir_1",
+ "export_include_dir_2",
+ "include_dir_1",
+ "include_dir_2",
+ "local_include_dir_1",
+ "local_include_dir_2",
+ ".",
+ ],
+ linkstatic = True,
+ srcs = [
+ "foo_static1.cc",
+ "foo_static2.cc",
+ "include_dir_1/include_dir_1_a.h",
+ "include_dir_1/include_dir_1_b.h",
+ "include_dir_2/include_dir_2_a.h",
+ "include_dir_2/include_dir_2_b.h",
+ "local_include_dir_1/local_include_dir_1_a.h",
+ "local_include_dir_1/local_include_dir_1_b.h",
+ "local_include_dir_2/local_include_dir_2_a.h",
+ "local_include_dir_2/local_include_dir_2_b.h",
+ "implicit_include_1.h",
+ "implicit_include_2.h",
+ ],
+)`, `cc_library_static(
+ name = "static_lib_1",
+ hdrs = [
+ "implicit_include_1.h",
+ "implicit_include_2.h",
+ ],
+ includes = ["."],
+ linkstatic = True,
+ srcs = [
+ "static_lib_1.cc",
+ "implicit_include_1.h",
+ "implicit_include_2.h",
+ ],
+)`, `cc_library_static(
+ name = "static_lib_2",
+ hdrs = [
+ "implicit_include_1.h",
+ "implicit_include_2.h",
+ ],
+ includes = ["."],
+ linkstatic = True,
+ srcs = [
+ "static_lib_2.cc",
+ "implicit_include_1.h",
+ "implicit_include_2.h",
+ ],
+)`, `cc_library_static(
+ name = "whole_static_lib_1",
+ hdrs = [
+ "implicit_include_1.h",
+ "implicit_include_2.h",
+ ],
+ includes = ["."],
+ linkstatic = True,
+ srcs = [
+ "whole_static_lib_1.cc",
+ "implicit_include_1.h",
+ "implicit_include_2.h",
+ ],
+)`, `cc_library_static(
+ name = "whole_static_lib_2",
+ hdrs = [
+ "implicit_include_1.h",
+ "implicit_include_2.h",
+ ],
+ includes = ["."],
+ linkstatic = True,
+ srcs = [
+ "whole_static_lib_2.cc",
+ "implicit_include_1.h",
+ "implicit_include_2.h",
+ ],
+)`},
+ },
+ {
+ description: "cc_library_static subpackage test",
+ moduleTypeUnderTest: "cc_library_static",
+ moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ filesystem: map[string]string{
+ // subpackage with subdirectory
+ "subpackage/Android.bp": "",
+ "subpackage/subpackage_header.h": "",
+ "subpackage/subdirectory/subdirectory_header.h": "",
+ // subsubpackage with subdirectory
+ "subpackage/subsubpackage/Android.bp": "",
+ "subpackage/subsubpackage/subsubpackage_header.h": "",
+ "subpackage/subsubpackage/subdirectory/subdirectory_header.h": "",
+ // subsubsubpackage with subdirectory
+ "subpackage/subsubpackage/subsubsubpackage/Android.bp": "",
+ "subpackage/subsubpackage/subsubsubpackage/subsubsubpackage_header.h": "",
+ "subpackage/subsubpackage/subsubsubpackage/subdirectory/subdirectory_header.h": "",
+ },
+ bp: soongCcLibraryStaticPreamble + `
+cc_library_static {
+ name: "foo_static",
+ srcs: [
+ ],
+ include_dirs: [
+ "subpackage",
+ ],
+
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{`cc_library_static(
+ name = "foo_static",
+ includes = [
+ "subpackage",
+ ".",
+ ],
+ linkstatic = True,
+ srcs = [
+ "//subpackage:subpackage_header.h",
+ "//subpackage:subdirectory/subdirectory_header.h",
+ "//subpackage/subsubpackage:subsubpackage_header.h",
+ "//subpackage/subsubpackage:subdirectory/subdirectory_header.h",
+ "//subpackage/subsubpackage/subsubsubpackage:subsubsubpackage_header.h",
+ "//subpackage/subsubpackage/subsubsubpackage:subdirectory/subdirectory_header.h",
+ ],
+)`},
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ filesystem := make(map[string][]byte)
+ toParse := []string{
+ "Android.bp",
+ }
+ for f, content := range testCase.filesystem {
+ if strings.HasSuffix(f, "Android.bp") {
+ toParse = append(toParse, f)
+ }
+ filesystem[f] = []byte(content)
+ }
+ config := android.TestConfig(buildDir, nil, testCase.bp, filesystem)
+ ctx := android.NewTestContext(config)
+
+ cc.RegisterCCBuildComponents(ctx)
+ ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
+ ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
+
+ ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+ for _, m := range testCase.depsMutators {
+ ctx.DepsBp2BuildMutators(m)
+ }
+ ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, toParse)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+ _, errs = ctx.ResolveDependencies(config)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+
+ checkDir := dir
+ if testCase.dir != "" {
+ checkDir = testCase.dir
+ }
+ codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
+ if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+ t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+ } else {
+ for i, target := range bazelTargets {
+ if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+ t.Errorf(
+ "%s: Expected generated Bazel target to be '%s', got '%s'",
+ testCase.description,
+ w,
+ g,
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
new file mode 100644
index 0000000..a9d24ac
--- /dev/null
+++ b/bp2build/cc_object_conversion_test.go
@@ -0,0 +1,441 @@
+// Copyright 2021 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 bp2build
+
+import (
+ "android/soong/android"
+ "android/soong/cc"
+ "fmt"
+ "strings"
+ "testing"
+)
+
+func TestCcObjectBp2Build(t *testing.T) {
+ testCases := []struct {
+ description string
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+ blueprint string
+ expectedBazelTargets []string
+ filesystem map[string]string
+ }{
+ {
+ description: "simple cc_object generates cc_object with include header dep",
+ moduleTypeUnderTest: "cc_object",
+ moduleTypeUnderTestFactory: cc.ObjectFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ filesystem: map[string]string{
+ "a/b/foo.h": "",
+ "a/b/bar.h": "",
+ "a/b/exclude.c": "",
+ "a/b/c.c": "",
+ },
+ blueprint: `cc_object {
+ name: "foo",
+ local_include_dirs: ["include"],
+ cflags: [
+ "-Wno-gcc-compat",
+ "-Wall",
+ "-Werror",
+ ],
+ srcs: [
+ "a/b/*.c"
+ ],
+ exclude_srcs: ["a/b/exclude.c"],
+
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTargets: []string{`cc_object(
+ name = "foo",
+ copts = [
+ "-fno-addrsig",
+ "-Wno-gcc-compat",
+ "-Wall",
+ "-Werror",
+ ],
+ hdrs = [
+ "a/b/bar.h",
+ "a/b/foo.h",
+ ],
+ local_include_dirs = [
+ "include",
+ ".",
+ ],
+ srcs = ["a/b/c.c"],
+)`,
+ },
+ },
+ {
+ description: "simple cc_object with defaults",
+ moduleTypeUnderTest: "cc_object",
+ moduleTypeUnderTestFactory: cc.ObjectFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ blueprint: `cc_object {
+ name: "foo",
+ local_include_dirs: ["include"],
+ srcs: [
+ "a/b/*.h",
+ "a/b/c.c"
+ ],
+
+ defaults: ["foo_defaults"],
+ bazel_module: { bp2build_available: true },
+}
+
+cc_defaults {
+ name: "foo_defaults",
+ defaults: ["foo_bar_defaults"],
+}
+
+cc_defaults {
+ name: "foo_bar_defaults",
+ cflags: [
+ "-Wno-gcc-compat",
+ "-Wall",
+ "-Werror",
+ ],
+}
+`,
+ expectedBazelTargets: []string{`cc_object(
+ name = "foo",
+ copts = [
+ "-Wno-gcc-compat",
+ "-Wall",
+ "-Werror",
+ "-fno-addrsig",
+ ],
+ local_include_dirs = [
+ "include",
+ ".",
+ ],
+ srcs = ["a/b/c.c"],
+)`,
+ },
+ },
+ {
+ description: "cc_object with cc_object deps in objs props",
+ moduleTypeUnderTest: "cc_object",
+ moduleTypeUnderTestFactory: cc.ObjectFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ filesystem: map[string]string{
+ "a/b/c.c": "",
+ "x/y/z.c": "",
+ },
+ blueprint: `cc_object {
+ name: "foo",
+ srcs: ["a/b/c.c"],
+ objs: ["bar"],
+
+ bazel_module: { bp2build_available: true },
+}
+
+cc_object {
+ name: "bar",
+ srcs: ["x/y/z.c"],
+
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTargets: []string{`cc_object(
+ name = "bar",
+ copts = ["-fno-addrsig"],
+ local_include_dirs = ["."],
+ srcs = ["x/y/z.c"],
+)`, `cc_object(
+ name = "foo",
+ copts = ["-fno-addrsig"],
+ deps = [":bar"],
+ local_include_dirs = ["."],
+ srcs = ["a/b/c.c"],
+)`,
+ },
+ },
+ {
+ description: "cc_object with include_build_dir: false",
+ moduleTypeUnderTest: "cc_object",
+ moduleTypeUnderTestFactory: cc.ObjectFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ filesystem: map[string]string{
+ "a/b/c.c": "",
+ "x/y/z.c": "",
+ },
+ blueprint: `cc_object {
+ name: "foo",
+ srcs: ["a/b/c.c"],
+ include_build_directory: false,
+
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTargets: []string{`cc_object(
+ name = "foo",
+ copts = ["-fno-addrsig"],
+ srcs = ["a/b/c.c"],
+)`,
+ },
+ },
+ {
+ description: "cc_object with product variable",
+ moduleTypeUnderTest: "cc_object",
+ moduleTypeUnderTestFactory: cc.ObjectFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ blueprint: `cc_object {
+ name: "foo",
+ include_build_directory: false,
+ product_variables: {
+ platform_sdk_version: {
+ asflags: ["-DPLATFORM_SDK_VERSION=%d"],
+ },
+ },
+
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTargets: []string{`cc_object(
+ name = "foo",
+ asflags = ["-DPLATFORM_SDK_VERSION={Platform_sdk_version}"],
+ copts = ["-fno-addrsig"],
+)`,
+ },
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ filesystem := make(map[string][]byte)
+ toParse := []string{
+ "Android.bp",
+ }
+ for f, content := range testCase.filesystem {
+ if strings.HasSuffix(f, "Android.bp") {
+ toParse = append(toParse, f)
+ }
+ filesystem[f] = []byte(content)
+ }
+ config := android.TestConfig(buildDir, nil, testCase.blueprint, filesystem)
+ ctx := android.NewTestContext(config)
+ // Always register cc_defaults module factory
+ ctx.RegisterModuleType("cc_defaults", func() android.Module { return cc.DefaultsFactory() })
+
+ ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+ ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, toParse)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+ _, errs = ctx.ResolveDependencies(config)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+
+ codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+ if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+ fmt.Println(bazelTargets)
+ t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+ } else {
+ for i, target := range bazelTargets {
+ if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+ t.Errorf(
+ "%s: Expected generated Bazel target to be '%s', got '%s'",
+ testCase.description,
+ w,
+ g,
+ )
+ }
+ }
+ }
+ }
+}
+
+func TestCcObjectConfigurableAttributesBp2Build(t *testing.T) {
+ testCases := []struct {
+ description string
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+ blueprint string
+ expectedBazelTargets []string
+ filesystem map[string]string
+ }{
+ {
+ description: "cc_object setting cflags for one arch",
+ moduleTypeUnderTest: "cc_object",
+ moduleTypeUnderTestFactory: cc.ObjectFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ blueprint: `cc_object {
+ name: "foo",
+ srcs: ["a.cpp"],
+ arch: {
+ x86: {
+ cflags: ["-fPIC"], // string list
+ },
+ arm: {
+ srcs: ["arch/arm/file.S"], // label list
+ },
+ },
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTargets: []string{
+ `cc_object(
+ name = "foo",
+ copts = ["-fno-addrsig"] + select({
+ "//build/bazel/platforms/arch:x86": ["-fPIC"],
+ "//conditions:default": [],
+ }),
+ local_include_dirs = ["."],
+ srcs = ["a.cpp"] + select({
+ "//build/bazel/platforms/arch:arm": ["arch/arm/file.S"],
+ "//conditions:default": [],
+ }),
+)`,
+ },
+ },
+ {
+ description: "cc_object setting cflags for 4 architectures",
+ moduleTypeUnderTest: "cc_object",
+ moduleTypeUnderTestFactory: cc.ObjectFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ blueprint: `cc_object {
+ name: "foo",
+ srcs: ["base.cpp"],
+ arch: {
+ x86: {
+ srcs: ["x86.cpp"],
+ cflags: ["-fPIC"],
+ },
+ x86_64: {
+ srcs: ["x86_64.cpp"],
+ cflags: ["-fPIC"],
+ },
+ arm: {
+ srcs: ["arm.cpp"],
+ cflags: ["-Wall"],
+ },
+ arm64: {
+ srcs: ["arm64.cpp"],
+ cflags: ["-Wall"],
+ },
+ },
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTargets: []string{
+ `cc_object(
+ name = "foo",
+ copts = ["-fno-addrsig"] + select({
+ "//build/bazel/platforms/arch:arm": ["-Wall"],
+ "//build/bazel/platforms/arch:arm64": ["-Wall"],
+ "//build/bazel/platforms/arch:x86": ["-fPIC"],
+ "//build/bazel/platforms/arch:x86_64": ["-fPIC"],
+ "//conditions:default": [],
+ }),
+ local_include_dirs = ["."],
+ srcs = ["base.cpp"] + select({
+ "//build/bazel/platforms/arch:arm": ["arm.cpp"],
+ "//build/bazel/platforms/arch:arm64": ["arm64.cpp"],
+ "//build/bazel/platforms/arch:x86": ["x86.cpp"],
+ "//build/bazel/platforms/arch:x86_64": ["x86_64.cpp"],
+ "//conditions:default": [],
+ }),
+)`,
+ },
+ },
+ {
+ description: "cc_object setting cflags for multiple OSes",
+ moduleTypeUnderTest: "cc_object",
+ moduleTypeUnderTestFactory: cc.ObjectFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ blueprint: `cc_object {
+ name: "foo",
+ srcs: ["base.cpp"],
+ target: {
+ android: {
+ cflags: ["-fPIC"],
+ },
+ windows: {
+ cflags: ["-fPIC"],
+ },
+ darwin: {
+ cflags: ["-Wall"],
+ },
+ },
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTargets: []string{
+ `cc_object(
+ name = "foo",
+ copts = ["-fno-addrsig"] + select({
+ "//build/bazel/platforms/os:android": ["-fPIC"],
+ "//build/bazel/platforms/os:darwin": ["-Wall"],
+ "//build/bazel/platforms/os:windows": ["-fPIC"],
+ "//conditions:default": [],
+ }),
+ local_include_dirs = ["."],
+ srcs = ["base.cpp"],
+)`,
+ },
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ filesystem := make(map[string][]byte)
+ toParse := []string{
+ "Android.bp",
+ }
+ config := android.TestConfig(buildDir, nil, testCase.blueprint, filesystem)
+ ctx := android.NewTestContext(config)
+ // Always register cc_defaults module factory
+ ctx.RegisterModuleType("cc_defaults", func() android.Module { return cc.DefaultsFactory() })
+
+ ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+ ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, toParse)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+ _, errs = ctx.ResolveDependencies(config)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+
+ codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+ if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+ fmt.Println(bazelTargets)
+ t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+ } else {
+ for i, target := range bazelTargets {
+ if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+ t.Errorf(
+ "%s: Expected generated Bazel target to be '%s', got '%s'",
+ testCase.description,
+ w,
+ g,
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
new file mode 100644
index 0000000..97729df
--- /dev/null
+++ b/bp2build/configurability.go
@@ -0,0 +1,142 @@
+package bp2build
+
+import (
+ "android/soong/android"
+ "android/soong/bazel"
+ "fmt"
+ "reflect"
+)
+
+// Configurability support for bp2build.
+
+type selects map[string]reflect.Value
+
+func getStringListValues(list bazel.StringListAttribute) (reflect.Value, selects, selects) {
+ value := reflect.ValueOf(list.Value)
+ if !list.HasConfigurableValues() {
+ return value, nil, nil
+ }
+
+ archSelects := map[string]reflect.Value{}
+ for arch, selectKey := range bazel.PlatformArchMap {
+ archSelects[selectKey] = reflect.ValueOf(list.GetValueForArch(arch))
+ }
+
+ osSelects := map[string]reflect.Value{}
+ for os, selectKey := range bazel.PlatformOsMap {
+ osSelects[selectKey] = reflect.ValueOf(list.GetValueForOS(os))
+ }
+
+ return value, archSelects, osSelects
+}
+
+func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, selects, selects) {
+ value := reflect.ValueOf(list.Value.Includes)
+ if !list.HasConfigurableValues() {
+ return value, nil, nil
+ }
+
+ archSelects := map[string]reflect.Value{}
+ for arch, selectKey := range bazel.PlatformArchMap {
+ archSelects[selectKey] = reflect.ValueOf(list.GetValueForArch(arch).Includes)
+ }
+
+ osSelects := map[string]reflect.Value{}
+ for os, selectKey := range bazel.PlatformOsMap {
+ osSelects[selectKey] = reflect.ValueOf(list.GetValueForOS(os).Includes)
+ }
+
+ return value, archSelects, osSelects
+}
+
+// prettyPrintAttribute converts an Attribute to its Bazel syntax. May contain
+// select statements.
+func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) {
+ var value reflect.Value
+ var archSelects, osSelects selects
+
+ switch list := v.(type) {
+ case bazel.StringListAttribute:
+ value, archSelects, osSelects = getStringListValues(list)
+ case bazel.LabelListAttribute:
+ value, archSelects, osSelects = getLabelListValues(list)
+ default:
+ return "", fmt.Errorf("Not a supported Bazel attribute type: %s", v)
+ }
+
+ ret, err := prettyPrint(value, indent)
+ if err != nil {
+ return ret, err
+ }
+
+ // Convenience function to append selects components to an attribute value.
+ appendSelects := func(selectsData selects, defaultValue, s string) (string, error) {
+ selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent)
+ if err != nil {
+ return "", err
+ }
+ if s != "" && selectMap != "" {
+ s += " + "
+ }
+ s += selectMap
+
+ return s, nil
+ }
+
+ ret, err = appendSelects(archSelects, "[]", ret)
+ if err != nil {
+ return "", err
+ }
+
+ ret, err = appendSelects(osSelects, "[]", ret)
+ return ret, err
+}
+
+// prettyPrintSelectMap converts a map of select keys to reflected Values as a generic way
+// to construct a select map for any kind of attribute type.
+func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue string, indent int) (string, error) {
+ if selectMap == nil {
+ return "", nil
+ }
+
+ var selects string
+ for _, selectKey := range android.SortedStringKeys(selectMap) {
+ value := selectMap[selectKey]
+ if isZero(value) {
+ // Ignore zero values to not generate empty lists.
+ continue
+ }
+ s, err := prettyPrintSelectEntry(value, selectKey, indent)
+ if err != nil {
+ return "", err
+ }
+ selects += s + ",\n"
+ }
+
+ if len(selects) == 0 {
+ // No conditions (or all values are empty lists), so no need for a map.
+ return "", nil
+ }
+
+ // Create the map.
+ ret := "select({\n"
+ ret += selects
+ // default condition comes last.
+ ret += fmt.Sprintf("%s\"%s\": %s,\n", makeIndent(indent+1), "//conditions:default", defaultValue)
+ ret += makeIndent(indent)
+ ret += "})"
+
+ return ret, nil
+}
+
+// prettyPrintSelectEntry converts a reflect.Value into an entry in a select map
+// with a provided key.
+func prettyPrintSelectEntry(value reflect.Value, key string, indent int) (string, error) {
+ s := makeIndent(indent + 1)
+ v, err := prettyPrint(value, indent+1)
+ if err != nil {
+ return "", err
+ }
+ s += fmt.Sprintf("\"%s\": %s", key, v)
+ return s, nil
+}
diff --git a/bp2build/constants.go b/bp2build/constants.go
new file mode 100644
index 0000000..70f320e
--- /dev/null
+++ b/bp2build/constants.go
@@ -0,0 +1,25 @@
+// Copyright 2020 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 bp2build
+
+var (
+ // When both a BUILD and BUILD.bazel file are exist in the same package, the BUILD.bazel file will
+ // be preferred for use within a Bazel build.
+
+ // The file name used for automatically generated files.
+ GeneratedBuildFileName = "BUILD"
+ // The file name used for hand-crafted build targets.
+ HandcraftedBuildFileName = "BUILD.bazel"
+)
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
new file mode 100644
index 0000000..6b47cd1
--- /dev/null
+++ b/bp2build/conversion.go
@@ -0,0 +1,142 @@
+package bp2build
+
+import (
+ "android/soong/android"
+ "reflect"
+ "sort"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+type BazelFile struct {
+ Dir string
+ Basename string
+ Contents string
+}
+
+func CreateBazelFiles(
+ ruleShims map[string]RuleShim,
+ buildToTargets map[string]BazelTargets,
+ mode CodegenMode) []BazelFile {
+
+ var files []BazelFile
+
+ if mode == QueryView {
+ // Write top level WORKSPACE.
+ files = append(files, newFile("", "WORKSPACE", ""))
+
+ // Used to denote that the top level directory is a package.
+ files = append(files, newFile("", GeneratedBuildFileName, ""))
+
+ files = append(files, newFile(bazelRulesSubDir, GeneratedBuildFileName, ""))
+
+ // These files are only used for queryview.
+ files = append(files, newFile(bazelRulesSubDir, "providers.bzl", providersBzl))
+
+ for bzlFileName, ruleShim := range ruleShims {
+ files = append(files, newFile(bazelRulesSubDir, bzlFileName+".bzl", ruleShim.content))
+ }
+ files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
+ }
+
+ files = append(files, createBuildFiles(buildToTargets, mode)...)
+
+ return files
+}
+
+func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
+ files := make([]BazelFile, 0, len(buildToTargets))
+ for _, dir := range android.SortedStringKeys(buildToTargets) {
+ targets := buildToTargets[dir]
+ sort.Slice(targets, func(i, j int) bool {
+ // this will cover all bp2build generated targets
+ if targets[i].name < targets[j].name {
+ return true
+ }
+ // give a strict ordering to content from hand-crafted targets
+ return targets[i].content < targets[j].content
+ })
+ content := soongModuleLoad
+ if mode == Bp2Build {
+ content = `# This file was automatically generated by bp2build for the Bazel migration project.
+# Feel free to edit or test it, but do *not* check it into your version control system.`
+ content += "\n\n"
+ content += "package(default_visibility = [\"//visibility:public\"])"
+ content += "\n\n"
+ content += targets.LoadStatements()
+ }
+ if content != "" {
+ // If there are load statements, add a couple of newlines.
+ content += "\n\n"
+ }
+ content += targets.String()
+ files = append(files, newFile(dir, GeneratedBuildFileName, content))
+ }
+ return files
+}
+
+func newFile(dir, basename, content string) BazelFile {
+ return BazelFile{
+ Dir: dir,
+ Basename: basename,
+ Contents: content,
+ }
+}
+
+const (
+ bazelRulesSubDir = "build/bazel/queryview_rules"
+
+ // additional files:
+ // * workspace file
+ // * base BUILD file
+ // * rules BUILD file
+ // * rules providers.bzl file
+ // * rules soong_module.bzl file
+ numAdditionalFiles = 5
+)
+
+var (
+ // Certain module property names are blocklisted/ignored here, for the reasons commented.
+ ignoredPropNames = map[string]bool{
+ "name": true, // redundant, since this is explicitly generated for every target
+ "from": true, // reserved keyword
+ "in": true, // reserved keyword
+ "size": true, // reserved for tests
+ "arch": true, // interface prop type is not supported yet.
+ "multilib": true, // interface prop type is not supported yet.
+ "target": true, // interface prop type is not supported yet.
+ "visibility": true, // Bazel has native visibility semantics. Handle later.
+ "features": true, // There is already a built-in attribute 'features' which cannot be overridden.
+ }
+)
+
+func shouldGenerateAttribute(prop string) bool {
+ return !ignoredPropNames[prop]
+}
+
+func shouldSkipStructField(field reflect.StructField) bool {
+ if field.PkgPath != "" {
+ // Skip unexported fields. Some properties are
+ // internal to Soong only, and these fields do not have PkgPath.
+ return true
+ }
+ // fields with tag `blueprint:"mutated"` are exported to enable modification in mutators, etc
+ // but cannot be set in a .bp file
+ if proptools.HasTag(field, "blueprint", "mutated") {
+ return true
+ }
+ return false
+}
+
+// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
+// testonly = True, forcing other rules that depend on _test rules to also be
+// marked as testonly = True. This semantic constraint is not present in Soong.
+// To work around, rename "*_test" rules to "*_test_".
+func canonicalizeModuleType(moduleName string) string {
+ if strings.HasSuffix(moduleName, "_test") {
+ return moduleName + "_"
+ }
+
+ return moduleName
+}
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
new file mode 100644
index 0000000..9fd6817
--- /dev/null
+++ b/bp2build/conversion_test.go
@@ -0,0 +1,87 @@
+// Copyright 2020 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 bp2build
+
+import (
+ "sort"
+ "testing"
+)
+
+type filepath struct {
+ dir string
+ basename string
+}
+
+func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) {
+ files := CreateBazelFiles(map[string]RuleShim{}, map[string]BazelTargets{}, QueryView)
+ expectedFilePaths := []filepath{
+ {
+ dir: "",
+ basename: "BUILD",
+ },
+ {
+ dir: "",
+ basename: "WORKSPACE",
+ },
+ {
+ dir: bazelRulesSubDir,
+ basename: "BUILD",
+ },
+ {
+ dir: bazelRulesSubDir,
+ basename: "providers.bzl",
+ },
+ {
+ dir: bazelRulesSubDir,
+ basename: "soong_module.bzl",
+ },
+ }
+
+ // Compare number of files
+ if a, e := len(files), len(expectedFilePaths); a != e {
+ t.Errorf("Expected %d files, got %d", e, a)
+ }
+
+ // Sort the files to be deterministic
+ sort.Slice(files, func(i, j int) bool {
+ if dir1, dir2 := files[i].Dir, files[j].Dir; dir1 == dir2 {
+ return files[i].Basename < files[j].Basename
+ } else {
+ return dir1 < dir2
+ }
+ })
+
+ // Compare the file contents
+ for i := range files {
+ actualFile, expectedFile := files[i], expectedFilePaths[i]
+
+ if actualFile.Dir != expectedFile.dir || actualFile.Basename != expectedFile.basename {
+ t.Errorf("Did not find expected file %s/%s", actualFile.Dir, actualFile.Basename)
+ } else if actualFile.Basename == "BUILD" || actualFile.Basename == "WORKSPACE" {
+ if actualFile.Contents != "" {
+ t.Errorf("Expected %s to have no content.", actualFile)
+ }
+ } else if actualFile.Contents == "" {
+ t.Errorf("Contents of %s unexpected empty.", actualFile)
+ }
+ }
+}
+
+func TestCreateBazelFiles_Bp2Build_CreatesNoFilesWithNoTargets(t *testing.T) {
+ files := CreateBazelFiles(map[string]RuleShim{}, map[string]BazelTargets{}, Bp2Build)
+ if len(files) != 0 {
+ t.Errorf("Expected no files, got %d", len(files))
+ }
+}
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
new file mode 100644
index 0000000..65b06c6
--- /dev/null
+++ b/bp2build/metrics.go
@@ -0,0 +1,34 @@
+package bp2build
+
+import (
+ "android/soong/android"
+ "fmt"
+)
+
+// Simple metrics struct to collect information about a Blueprint to BUILD
+// conversion process.
+type CodegenMetrics struct {
+ // Total number of Soong/Blueprint modules
+ TotalModuleCount int
+
+ // Counts of generated Bazel targets per Bazel rule class
+ RuleClassCount map[string]int
+
+ // Total number of handcrafted targets
+ handCraftedTargetCount int
+}
+
+// Print the codegen metrics to stdout.
+func (metrics CodegenMetrics) Print() {
+ generatedTargetCount := 0
+ for _, ruleClass := range android.SortedStringKeys(metrics.RuleClassCount) {
+ count := metrics.RuleClassCount[ruleClass]
+ fmt.Printf("[bp2build] %s: %d targets\n", ruleClass, count)
+ generatedTargetCount += count
+ }
+ fmt.Printf(
+ "[bp2build] Generated %d total BUILD targets and included %d handcrafted BUILD targets from %d Android.bp modules.\n",
+ generatedTargetCount,
+ metrics.handCraftedTargetCount,
+ metrics.TotalModuleCount)
+}
diff --git a/bp2build/python_binary_conversion_test.go b/bp2build/python_binary_conversion_test.go
new file mode 100644
index 0000000..2054e06
--- /dev/null
+++ b/bp2build/python_binary_conversion_test.go
@@ -0,0 +1,157 @@
+package bp2build
+
+import (
+ "android/soong/android"
+ "android/soong/python"
+ "fmt"
+ "strings"
+ "testing"
+)
+
+func TestPythonBinaryHost(t *testing.T) {
+ testCases := []struct {
+ description string
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+ blueprint string
+ expectedBazelTargets []string
+ filesystem map[string]string
+ }{
+ {
+ description: "simple python_binary_host converts to a native py_binary",
+ moduleTypeUnderTest: "python_binary_host",
+ moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+ moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+ filesystem: map[string]string{
+ "a.py": "",
+ "b/c.py": "",
+ "b/d.py": "",
+ "b/e.py": "",
+ "files/data.txt": "",
+ },
+ blueprint: `python_binary_host {
+ name: "foo",
+ main: "a.py",
+ srcs: ["**/*.py"],
+ exclude_srcs: ["b/e.py"],
+ data: ["files/data.txt",],
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTargets: []string{`py_binary(
+ name = "foo",
+ data = ["files/data.txt"],
+ main = "a.py",
+ srcs = [
+ "a.py",
+ "b/c.py",
+ "b/d.py",
+ ],
+)`,
+ },
+ },
+ {
+ description: "py2 python_binary_host",
+ moduleTypeUnderTest: "python_binary_host",
+ moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+ moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+ blueprint: `python_binary_host {
+ name: "foo",
+ srcs: ["a.py"],
+ version: {
+ py2: {
+ enabled: true,
+ },
+ py3: {
+ enabled: false,
+ },
+ },
+
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTargets: []string{`py_binary(
+ name = "foo",
+ python_version = "PY2",
+ srcs = ["a.py"],
+)`,
+ },
+ },
+ {
+ description: "py3 python_binary_host",
+ moduleTypeUnderTest: "python_binary_host",
+ moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
+ moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+ blueprint: `python_binary_host {
+ name: "foo",
+ srcs: ["a.py"],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ },
+ },
+
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTargets: []string{
+ // python_version is PY3 by default.
+ `py_binary(
+ name = "foo",
+ srcs = ["a.py"],
+)`,
+ },
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ filesystem := make(map[string][]byte)
+ toParse := []string{
+ "Android.bp",
+ }
+ for f, content := range testCase.filesystem {
+ if strings.HasSuffix(f, "Android.bp") {
+ toParse = append(toParse, f)
+ }
+ filesystem[f] = []byte(content)
+ }
+ config := android.TestConfig(buildDir, nil, testCase.blueprint, filesystem)
+ ctx := android.NewTestContext(config)
+
+ ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+ ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, toParse)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+ _, errs = ctx.ResolveDependencies(config)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+
+ codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+ if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+ fmt.Println(bazelTargets)
+ t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+ } else {
+ for i, target := range bazelTargets {
+ if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+ t.Errorf(
+ "%s: Expected generated Bazel target to be '%s', got '%s'",
+ testCase.description,
+ w,
+ g,
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/bp2build/sh_conversion_test.go b/bp2build/sh_conversion_test.go
new file mode 100644
index 0000000..37f542e
--- /dev/null
+++ b/bp2build/sh_conversion_test.go
@@ -0,0 +1,133 @@
+// Copyright 2021 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 bp2build
+
+import (
+ "android/soong/android"
+ "android/soong/sh"
+ "strings"
+ "testing"
+)
+
+func TestShBinaryLoadStatement(t *testing.T) {
+ testCases := []struct {
+ bazelTargets BazelTargets
+ expectedLoadStatements string
+ }{
+ {
+ bazelTargets: BazelTargets{
+ BazelTarget{
+ name: "sh_binary_target",
+ ruleClass: "sh_binary",
+ // Note: no bzlLoadLocation for native rules
+ // TODO(ruperts): Could open source the existing, experimental Starlark sh_ rules?
+ },
+ },
+ expectedLoadStatements: ``,
+ },
+ }
+
+ for _, testCase := range testCases {
+ actual := testCase.bazelTargets.LoadStatements()
+ expected := testCase.expectedLoadStatements
+ if actual != expected {
+ t.Fatalf("Expected load statements to be %s, got %s", expected, actual)
+ }
+ }
+
+}
+
+func TestShBinaryBp2Build(t *testing.T) {
+ testCases := []struct {
+ description string
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+ preArchMutators []android.RegisterMutatorFunc
+ depsMutators []android.RegisterMutatorFunc
+ bp string
+ expectedBazelTargets []string
+ filesystem map[string]string
+ dir string
+ }{
+ {
+ description: "sh_binary test",
+ moduleTypeUnderTest: "sh_binary",
+ moduleTypeUnderTestFactory: sh.ShBinaryFactory,
+ moduleTypeUnderTestBp2BuildMutator: sh.ShBinaryBp2Build,
+ bp: `sh_binary {
+ name: "foo",
+ src: "foo.sh",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{`sh_binary(
+ name = "foo",
+ srcs = ["foo.sh"],
+)`},
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ filesystem := make(map[string][]byte)
+ toParse := []string{
+ "Android.bp",
+ }
+ for f, content := range testCase.filesystem {
+ if strings.HasSuffix(f, "Android.bp") {
+ toParse = append(toParse, f)
+ }
+ filesystem[f] = []byte(content)
+ }
+ config := android.TestConfig(buildDir, nil, testCase.bp, filesystem)
+ ctx := android.NewTestContext(config)
+ ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+ for _, m := range testCase.depsMutators {
+ ctx.DepsBp2BuildMutators(m)
+ }
+ ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, toParse)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+ _, errs = ctx.ResolveDependencies(config)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+
+ checkDir := dir
+ if testCase.dir != "" {
+ checkDir = testCase.dir
+ }
+ codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
+ if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+ t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+ } else {
+ for i, target := range bazelTargets {
+ if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+ t.Errorf(
+ "%s: Expected generated Bazel target to be '%s', got '%s'",
+ testCase.description,
+ w,
+ g,
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/bp2build/testing.go b/bp2build/testing.go
new file mode 100644
index 0000000..ef3a78f
--- /dev/null
+++ b/bp2build/testing.go
@@ -0,0 +1,188 @@
+package bp2build
+
+import (
+ "android/soong/android"
+ "android/soong/bazel"
+)
+
+var (
+ // A default configuration for tests to not have to specify bp2build_available on top level targets.
+ bp2buildConfig = android.Bp2BuildConfig{
+ android.BP2BUILD_TOPLEVEL: android.Bp2BuildDefaultTrueRecursively,
+ }
+)
+
+type nestedProps struct {
+ Nested_prop string
+}
+
+type customProps struct {
+ Bool_prop bool
+ Bool_ptr_prop *bool
+ // Ensure that properties tagged `blueprint:mutated` are omitted
+ Int_prop int `blueprint:"mutated"`
+ Int64_ptr_prop *int64
+ String_prop string
+ String_ptr_prop *string
+ String_list_prop []string
+
+ Nested_props nestedProps
+ Nested_props_ptr *nestedProps
+}
+
+type customModule struct {
+ android.ModuleBase
+ android.BazelModuleBase
+
+ props customProps
+}
+
+// OutputFiles is needed because some instances of this module use dist with a
+// tag property which requires the module implements OutputFileProducer.
+func (m *customModule) OutputFiles(tag string) (android.Paths, error) {
+ return android.PathsForTesting("path" + tag), nil
+}
+
+func (m *customModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // nothing for now.
+}
+
+func customModuleFactoryBase() android.Module {
+ module := &customModule{}
+ module.AddProperties(&module.props)
+ android.InitBazelModule(module)
+ return module
+}
+
+func customModuleFactory() android.Module {
+ m := customModuleFactoryBase()
+ android.InitAndroidModule(m)
+ return m
+}
+
+type testProps struct {
+ Test_prop struct {
+ Test_string_prop string
+ }
+}
+
+type customTestModule struct {
+ android.ModuleBase
+
+ props customProps
+ test_props testProps
+}
+
+func (m *customTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // nothing for now.
+}
+
+func customTestModuleFactoryBase() android.Module {
+ m := &customTestModule{}
+ m.AddProperties(&m.props)
+ m.AddProperties(&m.test_props)
+ return m
+}
+
+func customTestModuleFactory() android.Module {
+ m := customTestModuleFactoryBase()
+ android.InitAndroidModule(m)
+ return m
+}
+
+type customDefaultsModule struct {
+ android.ModuleBase
+ android.DefaultsModuleBase
+}
+
+func customDefaultsModuleFactoryBase() android.DefaultsModule {
+ module := &customDefaultsModule{}
+ module.AddProperties(&customProps{})
+ return module
+}
+
+func customDefaultsModuleFactoryBasic() android.Module {
+ return customDefaultsModuleFactoryBase()
+}
+
+func customDefaultsModuleFactory() android.Module {
+ m := customDefaultsModuleFactoryBase()
+ android.InitDefaultsModule(m)
+ return m
+}
+
+type customBazelModuleAttributes struct {
+ String_prop string
+ String_list_prop []string
+}
+
+type customBazelModule struct {
+ android.BazelTargetModuleBase
+ customBazelModuleAttributes
+}
+
+func customBazelModuleFactory() android.Module {
+ module := &customBazelModule{}
+ module.AddProperties(&module.customBazelModuleAttributes)
+ android.InitBazelTargetModule(module)
+ return module
+}
+
+func (m *customBazelModule) Name() string { return m.BaseModuleName() }
+func (m *customBazelModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+
+func customBp2BuildMutator(ctx android.TopDownMutatorContext) {
+ if m, ok := ctx.Module().(*customModule); ok {
+ if !m.ConvertWithBp2build(ctx) {
+ return
+ }
+
+ attrs := &customBazelModuleAttributes{
+ String_prop: m.props.String_prop,
+ String_list_prop: m.props.String_list_prop,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "custom",
+ }
+
+ ctx.CreateBazelTargetModule(customBazelModuleFactory, m.Name(), props, attrs)
+ }
+}
+
+// A bp2build mutator that uses load statements and creates a 1:M mapping from
+// module to target.
+func customBp2BuildMutatorFromStarlark(ctx android.TopDownMutatorContext) {
+ if m, ok := ctx.Module().(*customModule); ok {
+ if !m.ConvertWithBp2build(ctx) {
+ return
+ }
+
+ baseName := m.Name()
+ attrs := &customBazelModuleAttributes{}
+
+ myLibraryProps := bazel.BazelTargetModuleProperties{
+ Rule_class: "my_library",
+ Bzl_load_location: "//build/bazel/rules:rules.bzl",
+ }
+ ctx.CreateBazelTargetModule(customBazelModuleFactory, baseName, myLibraryProps, attrs)
+
+ protoLibraryProps := bazel.BazelTargetModuleProperties{
+ Rule_class: "proto_library",
+ Bzl_load_location: "//build/bazel/rules:proto.bzl",
+ }
+ ctx.CreateBazelTargetModule(customBazelModuleFactory, baseName+"_proto_library_deps", protoLibraryProps, attrs)
+
+ myProtoLibraryProps := bazel.BazelTargetModuleProperties{
+ Rule_class: "my_proto_library",
+ Bzl_load_location: "//build/bazel/rules:proto.bzl",
+ }
+ ctx.CreateBazelTargetModule(customBazelModuleFactory, baseName+"_my_proto_library_deps", myProtoLibraryProps, attrs)
+ }
+}
+
+// Helper method for tests to easily access the targets in a dir.
+func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) BazelTargets {
+ buildFileToTargets, _ := GenerateBazelTargets(codegenCtx)
+ return buildFileToTargets[dir]
+}
diff --git a/bpf/Android.bp b/bpf/Android.bp
index 882cd8a..3ffa29f 100644
--- a/bpf/Android.bp
+++ b/bpf/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-bpf",
pkgPath: "android/soong/bpf",
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 297e13a..fa1a84d 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -26,7 +26,7 @@
)
func init() {
- android.RegisterModuleType("bpf", BpfFactory)
+ registerBpfBuildComponents(android.InitRegistrationContext)
pctx.Import("android/soong/cc/config")
}
@@ -43,6 +43,12 @@
"ccCmd", "cFlags")
)
+func registerBpfBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("bpf", BpfFactory)
+}
+
+var PrepareForTestWithBpf = android.FixtureRegisterWithContext(registerBpfBuildComponents)
+
// BpfModule interface is used by the apex package to gather information from a bpf module.
type BpfModule interface {
android.Module
@@ -120,6 +126,7 @@
names = append(names, objName)
fmt.Fprintln(w, "include $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_MODULE := ", objName)
+ data.Entries.WriteLicenseVariables(w)
fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", obj.String())
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", obj.Base())
fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC")
@@ -129,6 +136,7 @@
}
fmt.Fprintln(w, "include $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_MODULE := ", name)
+ data.Entries.WriteLicenseVariables(w)
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(names, " "))
fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
},
diff --git a/bpf/bpf_test.go b/bpf/bpf_test.go
index be9e36e..51fbc15 100644
--- a/bpf/bpf_test.go
+++ b/bpf/bpf_test.go
@@ -15,7 +15,6 @@
package bpf
import (
- "io/ioutil"
"os"
"testing"
@@ -23,47 +22,20 @@
"android/soong/cc"
)
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "genrule_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ os.Exit(m.Run())
}
-func testConfig(buildDir string, env map[string]string, bp string) android.Config {
- mockFS := map[string][]byte{
- "bpf.c": nil,
- "BpfTest.cpp": nil,
- }
-
- return cc.TestConfig(buildDir, android.Android, env, bp, mockFS)
-}
-
-func testContext(config android.Config) *android.TestContext {
- ctx := cc.CreateTestContext(config)
- ctx.RegisterModuleType("bpf", BpfFactory)
- ctx.Register()
-
- return ctx
-}
+var prepareForBpfTest = android.GroupFixturePreparers(
+ cc.PrepareForTestWithCcDefaultModules,
+ android.FixtureMergeMockFs(
+ map[string][]byte{
+ "bpf.c": nil,
+ "BpfTest.cpp": nil,
+ },
+ ),
+ PrepareForTestWithBpf,
+)
func TestBpfDataDependency(t *testing.T) {
bp := `
@@ -80,16 +52,7 @@
}
`
- config := testConfig(buildDir, nil, bp)
- ctx := testContext(config)
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- if errs == nil {
- _, errs = ctx.PrepareBuildActions(config)
- }
- if errs != nil {
- t.Fatal(errs)
- }
+ prepareForBpfTest.RunTestWithBp(t, bp)
// We only verify the above BP configuration is processed successfully since the data property
// value is not available for testing from this package.
diff --git a/bpfix/Android.bp b/bpfix/Android.bp
index b244e3a..345dbd0 100644
--- a/bpfix/Android.bp
+++ b/bpfix/Android.bp
@@ -16,6 +16,10 @@
// androidmk Android.mk to Blueprints translator
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "bpfix",
srcs: [
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index 94b8252..fae6101 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -670,6 +670,26 @@
return nil
}
+func RewriteRuntimeResourceOverlay(f *Fixer) error {
+ for _, def := range f.tree.Defs {
+ mod, ok := def.(*parser.Module)
+ if !(ok && mod.Type == "runtime_resource_overlay") {
+ continue
+ }
+ // runtime_resource_overlays are always product specific in Make.
+ if _, ok := mod.GetProperty("product_specific"); !ok {
+ prop := &parser.Property{
+ Name: "product_specific",
+ Value: &parser.Bool{
+ Value: true,
+ },
+ }
+ mod.Properties = append(mod.Properties, prop)
+ }
+ }
+ return nil
+}
+
// Removes library dependencies which are empty (and restricted from usage in Soong)
func removeEmptyLibDependencies(f *Fixer) error {
emptyLibraries := []string{
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index ef9814f..61dfe1a 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -1056,3 +1056,71 @@
})
}
}
+
+func TestRewriteRuntimeResourceOverlay(t *testing.T) {
+ tests := []struct {
+ name string
+ in string
+ out string
+ }{
+ {
+ name: "product_specific runtime_resource_overlay",
+ in: `
+ runtime_resource_overlay {
+ name: "foo",
+ resource_dirs: ["res"],
+ product_specific: true,
+ }
+ `,
+ out: `
+ runtime_resource_overlay {
+ name: "foo",
+ resource_dirs: ["res"],
+ product_specific: true,
+ }
+ `,
+ },
+ {
+ // It's probably wrong for runtime_resource_overlay not to be product specific, but let's not
+ // debate it here.
+ name: "non-product_specific runtime_resource_overlay",
+ in: `
+ runtime_resource_overlay {
+ name: "foo",
+ resource_dirs: ["res"],
+ product_specific: false,
+ }
+ `,
+ out: `
+ runtime_resource_overlay {
+ name: "foo",
+ resource_dirs: ["res"],
+ product_specific: false,
+ }
+ `,
+ },
+ {
+ name: "runtime_resource_overlay without product_specific value",
+ in: `
+ runtime_resource_overlay {
+ name: "foo",
+ resource_dirs: ["res"],
+ }
+ `,
+ out: `
+ runtime_resource_overlay {
+ name: "foo",
+ resource_dirs: ["res"],
+ product_specific: true,
+ }
+ `,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ runPass(t, test.in, test.out, func(fixer *Fixer) error {
+ return RewriteRuntimeResourceOverlay(fixer)
+ })
+ })
+ }
+}
diff --git a/build_kzip.bash b/build_kzip.bash
index 0018ea9..a4659d4 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -7,14 +7,17 @@
# BUILD_NUMBER build number, used to generate unique ID (will use UUID if not set)
# DIST_DIR where the resulting all.kzip will be placed
# KYTHE_KZIP_ENCODING proto or json (proto is default)
+# KYTHE_JAVA_SOURCE_BATCH_SIZE maximum number of the Java source files in a compilation unit
# OUT_DIR output directory (out if not specified})
# TARGET_BUILD_VARIANT variant, e.g., `userdebug`
# TARGET_PRODUCT target device name, e.g., 'aosp_blueline'
# XREF_CORPUS source code repository URI, e.g., 'android.googlesource.com/platform/superproject'
: ${BUILD_NUMBER:=$(uuidgen)}
+: ${KYTHE_JAVA_SOURCE_BATCH_SIZE:=500}
: ${KYTHE_KZIP_ENCODING:=proto}
-export KYTHE_KZIP_ENCODING
+: ${XREF_CORPUS:?should be set}
+export KYTHE_JAVA_SOURCE_BATCH_SIZE KYTHE_KZIP_ENCODING
# The extraction might fail for some source files, so run with -k and then check that
# sufficiently many files were generated.
@@ -27,11 +30,15 @@
declare -r abspath_out=$(realpath "${out}")
declare -r go_extractor=$(realpath prebuilts/build-tools/linux-x86/bin/go_extractor)
declare -r go_root=$(realpath prebuilts/go/linux-x86)
-declare -r vnames_path=$(realpath build/soong/vnames.go.json)
declare -r source_root=$PWD
+
+# TODO(asmundak): Until b/182183061 is fixed, default corpus has to be specified
+# in the rules file. Generate this file on the fly with corpus value set from the
+# environment variable.
for dir in blueprint soong; do
(cd "build/$dir";
- KYTHE_ROOT_DIRECTORY="${source_root}" "$go_extractor" --goroot="$go_root" --rules="${vnames_path}" \
+ KYTHE_ROOT_DIRECTORY="${source_root}" "$go_extractor" --goroot="$go_root" \
+ --rules=<(printf '[{"pattern": "(.*)","vname": {"path": "@1@", "corpus":"%s"}}]' "${XREF_CORPUS}") \
--canonicalize_package_corpus --output "${abspath_out}/soong/build_${dir}.go.kzip" ./...
)
done
diff --git a/cc/Android.bp b/cc/Android.bp
index 33f3db2..cc4d9bc 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-cc",
pkgPath: "android/soong/cc",
@@ -6,6 +10,7 @@
"blueprint-pathtools",
"soong",
"soong-android",
+ "soong-bazel",
"soong-cc-config",
"soong-etc",
"soong-genrule",
@@ -15,6 +20,7 @@
"androidmk.go",
"api_level.go",
"builder.go",
+ "bp2build.go",
"cc.go",
"ccdeps.go",
"check.go",
@@ -87,6 +93,7 @@
"prebuilt_test.go",
"proto_test.go",
"test_data_test.go",
+ "vendor_snapshot_test.go",
],
pluginFor: ["soong_build"],
}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 4ada55d..536efa4 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -26,9 +26,9 @@
var (
nativeBridgeSuffix = ".native_bridge"
productSuffix = ".product"
- vendorSuffix = ".vendor"
+ VendorSuffix = ".vendor"
ramdiskSuffix = ".ramdisk"
- vendorRamdiskSuffix = ".vendor_ramdisk"
+ VendorRamdiskSuffix = ".vendor_ramdisk"
recoverySuffix = ".recovery"
sdkSuffix = ".sdk"
)
@@ -83,7 +83,7 @@
Include: "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
if len(c.Properties.Logtags) > 0 {
entries.AddStrings("LOCAL_LOGTAGS_FILES", c.Properties.Logtags...)
}
@@ -162,16 +162,17 @@
func androidMkWriteExtraTestConfigs(extraTestConfigs android.Paths, entries *android.AndroidMkEntries) {
if len(extraTestConfigs) > 0 {
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
- entries.AddStrings("LOCAL_EXTRA_FULL_TEST_CONFIGS", extraTestConfigs.Strings()...)
- })
+ entries.ExtraEntries = append(entries.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.AddStrings("LOCAL_EXTRA_FULL_TEST_CONFIGS", extraTestConfigs.Strings()...)
+ })
}
}
-func androidMkWriteTestData(data []android.DataPath, ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func AndroidMkWriteTestData(data []android.DataPath, entries *android.AndroidMkEntries) {
testFiles := android.AndroidMkDataPaths(data)
if len(testFiles) > 0 {
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.AddStrings("LOCAL_TEST_DATA", testFiles...)
})
}
@@ -224,7 +225,7 @@
entries.Class = "STATIC_LIBRARIES"
} else if library.shared() {
entries.Class = "SHARED_LIBRARIES"
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_SOONG_TOC", library.toc().String())
if !library.buildStubs() {
entries.SetString("LOCAL_SOONG_UNSTRIPPED_BINARY", library.unstrippedOutputFile.String())
@@ -244,7 +245,7 @@
entries.DistFiles = android.MakeDefaultDistFiles(library.distFile)
}
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
library.androidMkWriteExportedFlags(entries)
library.androidMkEntriesWriteAdditionalDependenciesForSourceAbiDiff(entries)
@@ -272,7 +273,7 @@
if library.buildStubs() && library.stubsVersion() != "" {
entries.SubName = "." + library.stubsVersion()
}
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
// library.makeUninstallable() depends on this to bypass HideFromMake() for
// static libraries.
entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
@@ -315,7 +316,7 @@
entries.Class = "EXECUTABLES"
entries.DistFiles = binary.distFiles
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_SOONG_UNSTRIPPED_BINARY", binary.unstrippedOutputFile.String())
if len(binary.symlinks) > 0 {
entries.AddStrings("LOCAL_MODULE_SYMLINKS", binary.symlinks...)
@@ -337,7 +338,7 @@
func (benchmark *benchmarkDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
ctx.subAndroidMk(entries, benchmark.binaryDecorator)
entries.Class = "NATIVE_TESTS"
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
if len(benchmark.Properties.Test_suites) > 0 {
entries.AddCompatibilityTestSuites(benchmark.Properties.Test_suites...)
}
@@ -353,7 +354,7 @@
for _, srcPath := range benchmark.data {
dataPaths = append(dataPaths, android.DataPath{SrcPath: srcPath})
}
- androidMkWriteTestData(dataPaths, ctx, entries)
+ AndroidMkWriteTestData(dataPaths, entries)
}
func (test *testBinary) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
@@ -362,7 +363,7 @@
if Bool(test.Properties.Test_per_src) {
entries.SubName = "_" + String(test.binaryDecorator.Properties.Stem)
}
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
if len(test.Properties.Test_suites) > 0 {
entries.AddCompatibilityTestSuites(test.Properties.Test_suites...)
}
@@ -378,7 +379,7 @@
}
})
- androidMkWriteTestData(test.data, ctx, entries)
+ AndroidMkWriteTestData(test.data, entries)
androidMkWriteExtraTestConfigs(test.extraTestConfigs, entries)
}
@@ -406,7 +407,7 @@
filepath.Dir(fuzz.config.String())+":config.json")
}
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetBool("LOCAL_IS_FUZZ_TARGET", true)
if len(fuzzFiles) > 0 {
entries.AddStrings("LOCAL_TEST_DATA", fuzzFiles...)
@@ -423,7 +424,7 @@
func (library *toolchainLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
entries.Class = "STATIC_LIBRARIES"
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
_, suffix, _ := android.SplitFileExt(entries.OutputFile.Path().Base())
entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
})
@@ -439,7 +440,7 @@
entries.OutputFile = android.OptionalPathForPath(installer.path)
}
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
path, file := filepath.Split(installer.path.ToMakePath().String())
stem, suffix, _ := android.SplitFileExt(file)
entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
@@ -457,7 +458,7 @@
return
}
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
path, file := filepath.Split(c.installPath.String())
stem, suffix, _ := android.SplitFileExt(file)
entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
@@ -481,7 +482,7 @@
entries.SubName = c.androidMkSuffix
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
c.libraryDecorator.androidMkWriteExportedFlags(entries)
// Specifying stem is to pass check_elf_files when vendor modules link against vndk prebuilt.
@@ -519,11 +520,9 @@
entries.SubName += ".cfi"
}
- if c.androidMkVendorSuffix {
- entries.SubName += vendorSuffix
- }
+ entries.SubName += c.baseProperties.Androidmk_suffix
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
c.libraryDecorator.androidMkWriteExportedFlags(entries)
if c.shared() || c.static() {
@@ -548,26 +547,16 @@
func (c *snapshotBinaryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
entries.Class = "EXECUTABLES"
+ entries.SubName = c.baseProperties.Androidmk_suffix
- if c.androidMkVendorSuffix {
- entries.SubName = vendorSuffix
- } else {
- entries.SubName = ""
- }
-
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.AddStrings("LOCAL_MODULE_SYMLINKS", c.Properties.Symlinks...)
})
}
func (c *snapshotObjectLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
entries.Class = "STATIC_LIBRARIES"
-
- if c.androidMkVendorSuffix {
- entries.SubName = vendorSuffix
- } else {
- entries.SubName = ""
- }
+ entries.SubName = c.baseProperties.Androidmk_suffix
entries.ExtraFooters = append(entries.ExtraFooters,
func(w io.Writer, name, prefix, moduleDir string) {
@@ -587,7 +576,7 @@
entries.Class = "SHARED_LIBRARIES"
entries.SubName = vendorPublicLibrarySuffix
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
c.libraryDecorator.androidMkWriteExportedFlags(entries)
_, _, ext := android.SplitFileExt(entries.OutputFile.Path().Base())
@@ -598,7 +587,7 @@
}
func (p *prebuiltLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
if p.properties.Check_elf_files != nil {
entries.SetBool("LOCAL_CHECK_ELF_FILES", *p.properties.Check_elf_files)
} else {
@@ -628,7 +617,7 @@
func androidMkWriteAllowUndefinedSymbols(linker *baseLinker, entries *android.AndroidMkEntries) {
allow := linker.Properties.Allow_undefined_symbols
if allow != nil {
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetBool("LOCAL_ALLOW_UNDEFINED_SYMBOLS", *allow)
})
}
diff --git a/cc/api_level.go b/cc/api_level.go
index c93d6ed..fd145a9 100644
--- a/cc/api_level.go
+++ b/cc/api_level.go
@@ -53,14 +53,6 @@
return value, nil
}
-func nativeApiLevelFromUserWithDefault(ctx android.BaseModuleContext,
- raw string, defaultValue string) (android.ApiLevel, error) {
- if raw == "" {
- raw = defaultValue
- }
- return nativeApiLevelFromUser(ctx, raw)
-}
-
func nativeApiLevelOrPanic(ctx android.BaseModuleContext,
raw string) android.ApiLevel {
value, err := nativeApiLevelFromUser(ctx, raw)
diff --git a/cc/binary.go b/cc/binary.go
index 71c865b..999b82c 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -182,7 +182,7 @@
}
}
- if !binary.static() && inList("libc", deps.StaticLibs) {
+ if !binary.static() && inList("libc", deps.StaticLibs) && !ctx.BazelConversionMode() {
ctx.ModuleErrorf("statically linking libc to dynamic executable, please remove libc\n" +
"from static libs or set static_executable: true")
}
diff --git a/cc/bp2build.go b/cc/bp2build.go
new file mode 100644
index 0000000..0bca30a
--- /dev/null
+++ b/cc/bp2build.go
@@ -0,0 +1,226 @@
+// Copyright 2021 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/bazel"
+ "strings"
+)
+
+// bp2build functions and helpers for converting cc_* modules to Bazel.
+
+func init() {
+ android.DepsBp2BuildMutators(RegisterDepsBp2Build)
+}
+
+func RegisterDepsBp2Build(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("cc_bp2build_deps", depsBp2BuildMutator)
+}
+
+// A naive deps mutator to add deps on all modules across all combinations of
+// target props for cc modules. This is needed to make module -> bazel label
+// resolution work in the bp2build mutator later. This is probably
+// the wrong way to do it, but it works.
+//
+// TODO(jingwen): can we create a custom os mutator in depsBp2BuildMutator to do this?
+func depsBp2BuildMutator(ctx android.BottomUpMutatorContext) {
+ module, ok := ctx.Module().(*Module)
+ if !ok {
+ // Not a cc module
+ return
+ }
+
+ if !module.ConvertWithBp2build(ctx) {
+ return
+ }
+
+ var allDeps []string
+
+ for _, p := range module.GetTargetProperties(&BaseLinkerProperties{}) {
+ // arch specific linker props
+ if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
+ allDeps = append(allDeps, baseLinkerProps.Header_libs...)
+ allDeps = append(allDeps, baseLinkerProps.Export_header_lib_headers...)
+ }
+ }
+
+ ctx.AddDependency(module, nil, android.SortedUniqueStrings(allDeps)...)
+}
+
+// Convenience struct to hold all attributes parsed from compiler properties.
+type compilerAttributes struct {
+ copts bazel.StringListAttribute
+ srcs bazel.LabelListAttribute
+ hdrs bazel.LabelListAttribute
+}
+
+// bp2BuildParseCompilerProps returns copts, srcs and hdrs and other attributes.
+func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Module) compilerAttributes {
+ var hdrs, srcs bazel.LabelListAttribute
+ var copts bazel.StringListAttribute
+
+ hdrsAndSrcs := func(baseCompilerProps *BaseCompilerProperties) (bazel.LabelList, bazel.LabelList) {
+ srcsList := android.BazelLabelForModuleSrcExcludes(
+ ctx, baseCompilerProps.Srcs, baseCompilerProps.Exclude_srcs)
+ hdrsList := android.BazelLabelForModuleSrc(ctx, srcsList.LooseHdrsGlobs(headerExts))
+ return hdrsList, srcsList
+ }
+
+ for _, props := range module.compiler.compilerProps() {
+ if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+ hdrs.Value, srcs.Value = hdrsAndSrcs(baseCompilerProps)
+ copts.Value = baseCompilerProps.Cflags
+ break
+ }
+ }
+
+ for arch, props := range module.GetArchProperties(&BaseCompilerProperties{}) {
+ if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+ hdrsList, srcsList := hdrsAndSrcs(baseCompilerProps)
+ hdrs.SetValueForArch(arch.Name, bazel.SubtractBazelLabelList(hdrsList, hdrs.Value))
+ srcs.SetValueForArch(arch.Name, srcsList)
+ copts.SetValueForArch(arch.Name, baseCompilerProps.Cflags)
+ }
+ }
+
+ for os, props := range module.GetTargetProperties(&BaseCompilerProperties{}) {
+ if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+ hdrsList, srcsList := hdrsAndSrcs(baseCompilerProps)
+ hdrs.SetValueForOS(os.Name, bazel.SubtractBazelLabelList(hdrsList, hdrs.Value))
+ srcs.SetValueForOS(os.Name, srcsList)
+ copts.SetValueForOS(os.Name, baseCompilerProps.Cflags)
+ }
+ }
+
+ return compilerAttributes{
+ hdrs: hdrs,
+ srcs: srcs,
+ copts: copts,
+ }
+}
+
+// Convenience struct to hold all attributes parsed from linker properties.
+type linkerAttributes struct {
+ deps bazel.LabelListAttribute
+ linkopts bazel.StringListAttribute
+}
+
+// bp2BuildParseLinkerProps creates a label list attribute containing the header library deps of a module, including
+// configurable attribute values.
+func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) linkerAttributes {
+
+ var deps bazel.LabelListAttribute
+ var linkopts bazel.StringListAttribute
+
+ for _, linkerProps := range module.linker.linkerProps() {
+ if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
+ libs := baseLinkerProps.Header_libs
+ libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
+ deps = bazel.MakeLabelListAttribute(
+ android.BazelLabelForModuleDeps(ctx, android.SortedUniqueStrings(libs)))
+ linkopts.Value = baseLinkerProps.Ldflags
+ break
+ }
+ }
+
+ for arch, p := range module.GetArchProperties(&BaseLinkerProperties{}) {
+ if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
+ libs := baseLinkerProps.Header_libs
+ libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
+ libs = android.SortedUniqueStrings(libs)
+ deps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, libs))
+ linkopts.SetValueForArch(arch.Name, baseLinkerProps.Ldflags)
+ }
+ }
+
+ for os, p := range module.GetTargetProperties(&BaseLinkerProperties{}) {
+ if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
+ libs := baseLinkerProps.Header_libs
+ libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
+ libs = android.SortedUniqueStrings(libs)
+ deps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, libs))
+ linkopts.SetValueForOS(os.Name, baseLinkerProps.Ldflags)
+ }
+ }
+
+ return linkerAttributes{
+ deps: deps,
+ linkopts: linkopts,
+ }
+}
+
+func bp2BuildListHeadersInDir(ctx android.TopDownMutatorContext, includeDir string) bazel.LabelList {
+ globs := bazel.GlobsInDir(includeDir, includeDir != ".", headerExts)
+ return android.BazelLabelForModuleSrc(ctx, globs)
+}
+
+// Bazel wants include paths to be relative to the module
+func bp2BuildMakePathsRelativeToModule(ctx android.TopDownMutatorContext, paths []string) []string {
+ var relativePaths []string
+ for _, path := range paths {
+ relativePath := strings.TrimPrefix(path, ctx.ModuleDir()+"/")
+ relativePaths = append(relativePaths, relativePath)
+ }
+ return relativePaths
+}
+
+// bp2BuildParseExportedIncludes creates a label list attribute contains the
+// exported included directories of a module.
+func bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) (bazel.StringListAttribute, bazel.LabelListAttribute) {
+ libraryDecorator := module.linker.(*libraryDecorator)
+
+ includeDirs := libraryDecorator.flagExporter.Properties.Export_system_include_dirs
+ includeDirs = append(includeDirs, libraryDecorator.flagExporter.Properties.Export_include_dirs...)
+ includeDirs = bp2BuildMakePathsRelativeToModule(ctx, includeDirs)
+ includeDirsAttribute := bazel.MakeStringListAttribute(includeDirs)
+
+ var headersAttribute bazel.LabelListAttribute
+ var headers bazel.LabelList
+ for _, includeDir := range includeDirs {
+ headers.Append(bp2BuildListHeadersInDir(ctx, includeDir))
+ }
+ headers = bazel.UniqueBazelLabelList(headers)
+ headersAttribute.Value = headers
+
+ for arch, props := range module.GetArchProperties(&FlagExporterProperties{}) {
+ if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
+ archIncludeDirs := flagExporterProperties.Export_system_include_dirs
+ archIncludeDirs = append(archIncludeDirs, flagExporterProperties.Export_include_dirs...)
+ archIncludeDirs = bp2BuildMakePathsRelativeToModule(ctx, archIncludeDirs)
+
+ // To avoid duplicate includes when base includes + arch includes are combined
+ archIncludeDirs = bazel.SubtractStrings(archIncludeDirs, includeDirs)
+
+ if len(archIncludeDirs) > 0 {
+ includeDirsAttribute.SetValueForArch(arch.Name, archIncludeDirs)
+ }
+
+ var archHeaders bazel.LabelList
+ for _, archIncludeDir := range archIncludeDirs {
+ archHeaders.Append(bp2BuildListHeadersInDir(ctx, archIncludeDir))
+ }
+ archHeaders = bazel.UniqueBazelLabelList(archHeaders)
+
+ // To avoid duplicate headers when base headers + arch headers are combined
+ archHeaders = bazel.SubtractBazelLabelList(archHeaders, headers)
+
+ if len(archHeaders.Includes) > 0 || len(archHeaders.Excludes) > 0 {
+ headersAttribute.SetValueForArch(arch.Name, archHeaders)
+ }
+ }
+ }
+
+ return includeDirsAttribute, headersAttribute
+}
diff --git a/cc/builder.go b/cc/builder.go
index 9cd78d5..da8501c 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -59,7 +59,7 @@
// Rules to invoke ld to link binaries. Uses a .rsp file to list dependencies, as there may
// be many.
- ld, ldRE = remoteexec.StaticRules(pctx, "ld",
+ ld, ldRE = pctx.RemoteStaticRules("ld",
blueprint.RuleParams{
Command: "$reTemplate$ldCmd ${crtBegin} @${out}.rsp " +
"${libFlags} ${crtEnd} -o ${out} ${ldFlags} ${extraLibFlags}",
@@ -73,14 +73,14 @@
Labels: map[string]string{"type": "link", "tool": "clang"},
ExecStrategy: "${config.RECXXLinksExecStrategy}",
Inputs: []string{"${out}.rsp", "$implicitInputs"},
- RSPFile: "${out}.rsp",
+ RSPFiles: []string{"${out}.rsp"},
OutputFiles: []string{"${out}", "$implicitOutputs"},
ToolchainInputs: []string{"$ldCmd"},
Platform: map[string]string{remoteexec.PoolKey: "${config.RECXXLinksPool}"},
}, []string{"ldCmd", "crtBegin", "libFlags", "crtEnd", "ldFlags", "extraLibFlags"}, []string{"implicitInputs", "implicitOutputs"})
// Rules for .o files to combine to other .o files, using ld partial linking.
- partialLd, partialLdRE = remoteexec.StaticRules(pctx, "partialLd",
+ partialLd, partialLdRE = pctx.RemoteStaticRules("partialLd",
blueprint.RuleParams{
// Without -no-pie, clang 7.0 adds -pie to link Android files,
// but -r and -pie cannot be used together.
@@ -132,7 +132,7 @@
blueprint.RuleParams{
Depfile: "${out}.d",
Deps: blueprint.DepsGCC,
- Command: "CROSS_COMPILE=$crossCompile XZ=$xzCmd CLANG_BIN=${config.ClangBin} $stripPath ${args} -i ${in} -o ${out} -d ${out}.d",
+ Command: "XZ=$xzCmd CLANG_BIN=${config.ClangBin} $stripPath ${args} -i ${in} -o ${out} -d ${out}.d",
CommandDeps: []string{"$stripPath", "$xzCmd"},
Pool: darwinStripPool,
},
@@ -182,14 +182,14 @@
blueprint.RuleParams{
Depfile: "${out}.d",
Deps: blueprint.DepsGCC,
- Command: "CROSS_COMPILE=$crossCompile $tocPath $format -i ${in} -o ${out} -d ${out}.d",
+ Command: "CLANG_BIN=$clangBin $tocPath $format -i ${in} -o ${out} -d ${out}.d",
CommandDeps: []string{"$tocPath"},
Restat: true,
},
- "crossCompile", "format")
+ "clangBin", "format")
// Rule for invoking clang-tidy (a clang-based linter).
- clangTidy, clangTidyRE = remoteexec.StaticRules(pctx, "clangTidy",
+ clangTidy, clangTidyRE = pctx.RemoteStaticRules("clangTidy",
blueprint.RuleParams{
Command: "rm -f $out && $reTemplate${config.ClangBin}/clang-tidy $tidyFlags $in -- $cFlags && touch $out",
CommandDeps: []string{"${config.ClangBin}/clang-tidy"},
@@ -228,7 +228,7 @@
_ = pctx.SourcePathVariable("sAbiDumper", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-dumper")
// -w has been added since header-abi-dumper does not need to produce any sort of diagnostic information.
- sAbiDump, sAbiDumpRE = remoteexec.StaticRules(pctx, "sAbiDump",
+ sAbiDump, sAbiDumpRE = pctx.RemoteStaticRules("sAbiDump",
blueprint.RuleParams{
Command: "rm -f $out && $reTemplate$sAbiDumper -o ${out} $in $exportDirs -- $cFlags -w -isystem prebuilts/clang-tools/${config.HostPrebuiltTag}/clang-headers",
CommandDeps: []string{"$sAbiDumper"},
@@ -246,7 +246,7 @@
// Rule to combine .dump sAbi dump files from multiple source files into a single .ldump
// sAbi dump file.
- sAbiLink, sAbiLinkRE = remoteexec.StaticRules(pctx, "sAbiLink",
+ sAbiLink, sAbiLinkRE = pctx.RemoteStaticRules("sAbiLink",
blueprint.RuleParams{
Command: "$reTemplate$sAbiLinker -o ${out} $symbolFilter -arch $arch $exportedHeaderFlags @${out}.rsp ",
CommandDeps: []string{"$sAbiLinker"},
@@ -256,7 +256,7 @@
Labels: map[string]string{"type": "tool", "name": "abi-linker"},
ExecStrategy: "${config.REAbiLinkerExecStrategy}",
Inputs: []string{"$sAbiLinkerLibs", "${out}.rsp", "$implicitInputs"},
- RSPFile: "${out}.rsp",
+ RSPFiles: []string{"${out}.rsp"},
OutputFiles: []string{"$out"},
ToolchainInputs: []string{"$sAbiLinker"},
Platform: map[string]string{remoteexec.PoolKey: "${config.RECXXPool}"},
@@ -331,7 +331,6 @@
pctx.StaticVariable("relPwd", PwdPrefix())
pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
- pctx.Import("android/soong/remoteexec")
}
// builderFlags contains various types of command line flags (and settings) for use in building
@@ -919,16 +918,12 @@
outputFile android.WritablePath, flags builderFlags) {
var format string
- var crossCompile string
if ctx.Darwin() {
format = "--macho"
- crossCompile = "${config.MacToolPath}"
} else if ctx.Windows() {
format = "--pe"
- crossCompile = gccCmd(flags.toolchain, "")
} else {
format = "--elf"
- crossCompile = gccCmd(flags.toolchain, "")
}
ctx.Build(pctx, android.BuildParams{
@@ -937,8 +932,8 @@
Output: outputFile,
Input: inputFile,
Args: map[string]string{
- "crossCompile": crossCompile,
- "format": format,
+ "clangBin": "${config.ClangBin}",
+ "format": format,
},
})
}
diff --git a/cc/cc.go b/cc/cc.go
index b815268..7f69d56 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -50,19 +50,17 @@
ctx.BottomUp("version", versionMutator).Parallel()
ctx.BottomUp("begin", BeginMutator).Parallel()
ctx.BottomUp("sysprop_cc", SyspropMutator).Parallel()
- ctx.BottomUp("vendor_snapshot", VendorSnapshotMutator).Parallel()
- ctx.BottomUp("vendor_snapshot_source", VendorSnapshotSourceMutator).Parallel()
})
ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.TopDown("asan_deps", sanitizerDepsMutator(asan))
- ctx.BottomUp("asan", sanitizerMutator(asan)).Parallel()
+ ctx.TopDown("asan_deps", sanitizerDepsMutator(Asan))
+ ctx.BottomUp("asan", sanitizerMutator(Asan)).Parallel()
- ctx.TopDown("hwasan_deps", sanitizerDepsMutator(hwasan))
- ctx.BottomUp("hwasan", sanitizerMutator(hwasan)).Parallel()
+ ctx.TopDown("hwasan_deps", sanitizerDepsMutator(Hwasan))
+ ctx.BottomUp("hwasan", sanitizerMutator(Hwasan)).Parallel()
- ctx.TopDown("fuzzer_deps", sanitizerDepsMutator(fuzzer))
- ctx.BottomUp("fuzzer", sanitizerMutator(fuzzer)).Parallel()
+ ctx.TopDown("fuzzer_deps", sanitizerDepsMutator(Fuzzer))
+ ctx.BottomUp("fuzzer", sanitizerMutator(Fuzzer)).Parallel()
// cfi mutator shouldn't run before sanitizers that return true for
// incompatibleWithCfi()
@@ -254,11 +252,27 @@
// Deprecated. true is the default, false is invalid.
Clang *bool `android:"arch_variant"`
- // Minimum sdk version supported when compiling against the ndk. Setting this property causes
- // two variants to be built, one for the platform and one for apps.
+ // The API level that this module is built against. The APIs of this API level will be
+ // visible at build time, but use of any APIs newer than min_sdk_version will render the
+ // module unloadable on older devices. In the future it will be possible to weakly-link new
+ // APIs, making the behavior match Java: such modules will load on older devices, but
+ // calling new APIs on devices that do not support them will result in a crash.
+ //
+ // This property has the same behavior as sdk_version does for Java modules. For those
+ // familiar with Android Gradle, the property behaves similarly to how compileSdkVersion
+ // does for Java code.
+ //
+ // In addition, setting this property causes two variants to be built, one for the platform
+ // and one for apps.
Sdk_version *string
- // Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
+ // Minimum OS API level supported by this C or C++ module. This property becomes the value
+ // of the __ANDROID_API__ macro. When the C or C++ module is included in an APEX or an APK,
+ // this property is also used to ensure that the min_sdk_version of the containing module is
+ // not older (i.e. less) than this module's min_sdk_version. When not set, this property
+ // defaults to the value of sdk_version. When this is set to "apex_inherit", this tracks
+ // min_sdk_version of the containing APEX. When the module
+ // is not built for an APEX, "apex_inherit" defaults to sdk_version.
Min_sdk_version *string
// If true, always create an sdk variant and don't create a platform variant.
@@ -334,16 +348,22 @@
// Normally Soong uses the directory structure to decide which modules
// should be included (framework) or excluded (non-framework) from the
- // different snapshots (vendor, recovery, etc.), but these properties
- // allow a partner to exclude a module normally thought of as a
- // framework module from a snapshot.
- Exclude_from_vendor_snapshot *bool
+ // different snapshots (vendor, recovery, etc.), but this property
+ // allows a partner to exclude a module normally thought of as a
+ // framework module from the vendor snapshot.
+ Exclude_from_vendor_snapshot *bool
+
+ // Normally Soong uses the directory structure to decide which modules
+ // should be included (framework) or excluded (non-framework) from the
+ // different snapshots (vendor, recovery, etc.), but this property
+ // allows a partner to exclude a module normally thought of as a
+ // framework module from the recovery snapshot.
Exclude_from_recovery_snapshot *bool
// List of APEXes that this module has private access to for testing purpose. The module
// can depend on libraries that are not exported by the APEXes and use private symbols
// from the exported libraries.
- Test_for []string
+ Test_for []string `android:"arch_variant"`
}
type VendorProperties struct {
@@ -361,14 +381,22 @@
// If set to false, this module becomes inaccessible from /vendor modules.
//
// The modules with vndk: {enabled: true} must define 'vendor_available'
- // to either 'true' or 'false'. In this case, 'vendor_available: false' has
- // a different meaning than that of non-VNDK modules.
- // 'vendor_available: false' for a VNDK module means 'VNDK-private' that
- // can only be depended on by VNDK libraries, not by non-VNDK vendor modules.
+ // to 'true'.
//
// Nothing happens if BOARD_VNDK_VERSION isn't set in the BoardConfig.mk
Vendor_available *bool
+ // This is the same as the "vendor_available" except that the install path
+ // of the vendor variant is /odm or /vendor/odm.
+ // By replacing "vendor_available: true" with "odm_available: true", the
+ // module will install its vendor variant to the /odm partition or /vendor/odm.
+ // As the modules with "odm_available: true" still create the vendor variants,
+ // they can link to the other vendor modules as the vendor_available modules do.
+ // Also, the vendor modules can link to odm_available modules.
+ //
+ // It may not be used for VNDK modules.
+ Odm_available *bool
+
// whether this module should be allowed to be directly depended by other
// modules with `product_specific: true` or `product_available: true`.
// If set to true, an additional product variant will be built separately
@@ -386,13 +414,6 @@
// vndk: {enabled: true} don't have to define 'product_available'. The VNDK
// library without 'product_available' may not be depended on by any other
// modules that has product variants including the product available VNDKs.
- // However, for the modules with vndk: {enabled: true},
- // 'product_available: false' creates the product variant that is available
- // only for the other product available VNDK modules but not by non-VNDK
- // product modules.
- // In the case of the modules with vndk: {enabled: true}, if
- // 'product_available' is defined, it must have the same value with the
- // 'vendor_available'.
//
// Nothing happens if BOARD_VNDK_VERSION isn't set in the BoardConfig.mk
// and PRODUCT_PRODUCT_VNDK_VERSION isn't set.
@@ -410,9 +431,22 @@
// IsLLNDK is set to true for the vendor variant of a cc_library module that has LLNDK stubs.
IsLLNDK bool `blueprint:"mutated"`
- // IsLLNDKPrivate is set to true for the vendor variant of a cc_library module that has LLNDK
- // stubs and also sets llndk.vendor_available: false.
- IsLLNDKPrivate bool `blueprint:"mutated"`
+ // IsVNDKUsingCoreVariant is true for VNDK modules if the global VndkUseCoreVariant option is
+ // set and the module is not listed in VndkMustUseVendorVariantList.
+ IsVNDKUsingCoreVariant bool `blueprint:"mutated"`
+
+ // IsVNDKCore is set if a VNDK module does not set the vndk.support_system_process property.
+ IsVNDKCore bool `blueprint:"mutated"`
+
+ // IsVNDKSP is set if a VNDK module sets the vndk.support_system_process property.
+ IsVNDKSP bool `blueprint:"mutated"`
+
+ // IsVNDKPrivate is set if a VNDK module sets the vndk.private property or an LLNDK
+ // module sets the llndk.private property.
+ IsVNDKPrivate bool `blueprint:"mutated"`
+
+ // IsVNDKProduct is set if a VNDK module sets the product_available property.
+ IsVNDKProduct bool `blueprint:"mutated"`
}
// ModuleContextIntf is an interface (on a module context helper) consisting of functions related
@@ -422,6 +456,7 @@
type ModuleContextIntf interface {
static() bool
staticBinary() bool
+ testBinary() bool
header() bool
binary() bool
object() bool
@@ -429,6 +464,8 @@
canUseSdk() bool
useSdk() bool
sdkVersion() string
+ minSdkVersion() string
+ isSdkVariant() bool
useVndk() bool
isNdk(config android.Config) bool
IsLlndk() bool
@@ -457,6 +494,7 @@
nativeCoverage() bool
directlyInAnyApex() bool
isPreventInstall() bool
+ isCfiAssemblySupportEnabled() bool
}
type ModuleContext interface {
@@ -539,6 +577,17 @@
makeUninstallable(mod *Module)
}
+// bazelHandler is the interface for a helper object related to deferring to Bazel for
+// processing a module (during Bazel mixed builds). Individual module types should define
+// their own bazel handler if they support deferring to Bazel.
+type bazelHandler interface {
+ // Issue query to Bazel to retrieve information about Bazel's view of the current module.
+ // If Bazel returns this information, set module properties on the current module to reflect
+ // the returned information.
+ // Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
+ generateBazelBuildActions(ctx android.ModuleContext, label string) bool
+}
+
type xref interface {
XrefCcFiles() android.Paths
}
@@ -724,6 +773,7 @@
android.DefaultableModuleBase
android.ApexModuleBase
android.SdkBase
+ android.BazelModuleBase
Properties BaseProperties
VendorProperties VendorProperties
@@ -740,9 +790,10 @@
// type-specific logic. These members may reference different objects or the same object.
// Functions of these decorators will be invoked to initialize and register type-specific
// build statements.
- compiler compiler
- linker linker
- installer installer
+ compiler compiler
+ linker linker
+ installer installer
+ bazelHandler bazelHandler
features []feature
stl *stl
@@ -777,6 +828,14 @@
hideApexVariantFromMake bool
}
+func (c *Module) SetPreventInstall() {
+ c.Properties.PreventInstall = true
+}
+
+func (c *Module) SetHideFromMake() {
+ c.Properties.HideFromMake = true
+}
+
func (c *Module) Toc() android.OptionalPath {
if c.linker != nil {
if library, ok := c.linker.(libraryInterface); ok {
@@ -1004,11 +1063,8 @@
c.AddProperties(feature.props()...)
}
- c.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, os android.OsType) bool {
- // Windows builds always prefer 32-bit
- return os == android.Windows
- })
android.InitAndroidArchModule(c, c.hod, c.multilib)
+ android.InitBazelModule(c)
android.InitApexModule(c)
android.InitSdkAwareModule(c)
android.InitDefaultableModule(c)
@@ -1018,7 +1074,7 @@
// Returns true for dependency roots (binaries)
// TODO(ccross): also handle dlopenable libraries
-func (c *Module) isDependencyRoot() bool {
+func (c *Module) IsDependencyRoot() bool {
if root, ok := c.linker.(interface {
isDependencyRoot() bool
}); ok {
@@ -1038,7 +1094,8 @@
}
func (c *Module) canUseSdk() bool {
- return c.Os() == android.Android && !c.UseVndk() && !c.InRamdisk() && !c.InRecovery() && !c.InVendorRamdisk()
+ return c.Os() == android.Android && c.Target().NativeBridge == android.NativeBridgeDisabled &&
+ !c.UseVndk() && !c.InRamdisk() && !c.InRecovery() && !c.InVendorRamdisk()
}
func (c *Module) UseSdk() bool {
@@ -1056,14 +1113,31 @@
return inList(c.BaseModuleName(), *getNDKKnownLibs(config))
}
-// isLLndk returns true for both LLNDK (public) and LLNDK-private libs.
func (c *Module) IsLlndk() bool {
return c.VendorProperties.IsLLNDK
}
-// IsLlndkPublic returns true only for LLNDK (public) libs.
func (c *Module) IsLlndkPublic() bool {
- return c.VendorProperties.IsLLNDK && !c.VendorProperties.IsLLNDKPrivate
+ return c.VendorProperties.IsLLNDK && !c.VendorProperties.IsVNDKPrivate
+}
+
+func (c *Module) IsLlndkHeaders() bool {
+ if _, ok := c.linker.(*llndkHeadersDecorator); ok {
+ return true
+ }
+ return false
+}
+
+func (c *Module) IsLlndkLibrary() bool {
+ if _, ok := c.linker.(*llndkStubDecorator); ok {
+ return true
+ }
+ return false
+}
+
+func (m *Module) HasLlndkStubs() bool {
+ lib := moduleLibraryInterface(m)
+ return lib != nil && lib.hasLLNDKStubs()
}
// isImplementationForLLNDKPublic returns true for any variant of a cc_library that has LLNDK stubs
@@ -1071,11 +1145,11 @@
func (c *Module) isImplementationForLLNDKPublic() bool {
library, _ := c.library.(*libraryDecorator)
return library != nil && library.hasLLNDKStubs() &&
- (Bool(library.Properties.Llndk.Vendor_available) ||
+ (!Bool(library.Properties.Llndk.Private) ||
// TODO(b/170784825): until the LLNDK properties are moved into the cc_library,
// the non-Vendor variants of the cc_library don't know if the corresponding
- // llndk_library set vendor_available: false. Since libft2 is the only
- // private LLNDK library, hardcode it during the transition.
+ // llndk_library set private: true. Since libft2 is the only private LLNDK
+ // library, hardcode it during the transition.
c.BaseModuleName() != "libft2")
}
@@ -1083,20 +1157,12 @@
func (c *Module) IsVndkPrivate() bool {
// Check if VNDK-core-private or VNDK-SP-private
if c.IsVndk() {
- if Bool(c.vndkdep.Properties.Vndk.Private) {
- return true
- }
- // TODO(b/175768895) remove this when we clean up "vendor_available: false" use cases.
- if c.VendorProperties.Vendor_available != nil && !Bool(c.VendorProperties.Vendor_available) {
- return true
- }
- return false
+ return Bool(c.vndkdep.Properties.Vndk.Private)
}
// Check if LLNDK-private
if library, ok := c.library.(*libraryDecorator); ok && c.IsLlndk() {
- // TODO(b/175768895) replace this with 'private' property.
- return !Bool(library.Properties.Llndk.Vendor_available)
+ return Bool(library.Properties.Llndk.Private)
}
return false
@@ -1137,6 +1203,10 @@
return false
}
+func (c *Module) SubName() string {
+ return c.Properties.SubName
+}
+
func (c *Module) MustUseVendorVariant() bool {
return c.isVndkSp() || c.Properties.MustUseVendorVariant
}
@@ -1197,8 +1267,8 @@
return c.linker != nil && c.linker.nativeCoverage()
}
-func (c *Module) isSnapshotPrebuilt() bool {
- if p, ok := c.linker.(interface{ isSnapshotPrebuilt() bool }); ok {
+func (c *Module) IsSnapshotPrebuilt() bool {
+ if p, ok := c.linker.(snapshotInterface); ok {
return p.isSnapshotPrebuilt()
}
return false
@@ -1231,6 +1301,11 @@
return c.kytheFiles
}
+func (c *Module) isCfiAssemblySupportEnabled() bool {
+ return c.sanitize != nil &&
+ Bool(c.sanitize.Properties.Sanitize.Config.Cfi_assembly_support)
+}
+
type baseModuleContext struct {
android.BaseModuleContext
moduleContextImpl
@@ -1263,8 +1338,12 @@
return ctx.mod.staticBinary()
}
+func (ctx *moduleContextImpl) testBinary() bool {
+ return ctx.mod.testBinary()
+}
+
func (ctx *moduleContextImpl) header() bool {
- return ctx.mod.header()
+ return ctx.mod.Header()
}
func (ctx *moduleContextImpl) binary() bool {
@@ -1297,6 +1376,42 @@
return ""
}
+func (ctx *moduleContextImpl) minSdkVersion() string {
+ ver := ctx.mod.MinSdkVersion()
+ if ver == "apex_inherit" && !ctx.isForPlatform() {
+ ver = ctx.apexSdkVersion().String()
+ }
+ if ver == "apex_inherit" || ver == "" {
+ ver = ctx.sdkVersion()
+ }
+ // For crt objects, the meaning of min_sdk_version is very different from other types of
+ // module. For them, min_sdk_version defines the oldest version that the build system will
+ // create versioned variants for. For example, if min_sdk_version is 16, then sdk variant of
+ // the crt object has local variants of 16, 17, ..., up to the latest version. sdk_version
+ // and min_sdk_version properties of the variants are set to the corresponding version
+ // numbers. However, the platform (non-sdk) variant of the crt object is left untouched.
+ // min_sdk_version: 16 doesn't actually mean that the platform variant has to support such
+ // an old version. Since the variant is for the platform, it's preferred to target the
+ // latest version.
+ if ctx.mod.SplitPerApiLevel() && !ctx.isSdkVariant() {
+ ver = strconv.Itoa(android.FutureApiLevelInt)
+ }
+
+ // Also make sure that minSdkVersion is not greater than sdkVersion, if they are both numbers
+ sdkVersionInt, err := strconv.Atoi(ctx.sdkVersion())
+ minSdkVersionInt, err2 := strconv.Atoi(ver)
+ if err == nil && err2 == nil {
+ if sdkVersionInt < minSdkVersionInt {
+ return strconv.Itoa(sdkVersionInt)
+ }
+ }
+ return ver
+}
+
+func (ctx *moduleContextImpl) isSdkVariant() bool {
+ return ctx.mod.IsSdkVariant()
+}
+
func (ctx *moduleContextImpl) useVndk() bool {
return ctx.mod.UseVndk()
}
@@ -1392,6 +1507,10 @@
return ctx.mod.Properties.PreventInstall
}
+func (ctx *moduleContextImpl) isCfiAssemblySupportEnabled() bool {
+ return ctx.mod.isCfiAssemblySupportEnabled()
+}
+
func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
return &Module{
hod: hod,
@@ -1421,6 +1540,10 @@
return nil
}
+func (c *Module) IsPrebuilt() bool {
+ return c.Prebuilt() != nil
+}
+
func (c *Module) Name() string {
name := c.ModuleBase.Name()
if p, ok := c.linker.(interface {
@@ -1460,11 +1583,16 @@
var vndkVersion string
var nameSuffix string
if c.InProduct() {
+ if c.ProductSpecific() {
+ // If the module is product specific with 'product_specific: true',
+ // do not add a name suffix because it is a base module.
+ return ""
+ }
vndkVersion = ctx.DeviceConfig().ProductVndkVersion()
nameSuffix = productSuffix
} else {
vndkVersion = ctx.DeviceConfig().VndkVersion()
- nameSuffix = vendorSuffix
+ nameSuffix = VendorSuffix
}
if vndkVersion == "current" {
vndkVersion = ctx.DeviceConfig().PlatformVndkVersion()
@@ -1477,24 +1605,7 @@
return nameSuffix
}
-func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
- // Handle the case of a test module split by `test_per_src` mutator.
- //
- // The `test_per_src` mutator adds an extra variation named "", depending on all the other
- // `test_per_src` variations of the test module. Set `outputFile` to an empty path for this
- // module and return early, as this module does not produce an output file per se.
- if c.IsTestPerSrcAllTestsVariation() {
- c.outputFile = android.OptionalPath{}
- return
- }
-
- apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
- if !apexInfo.IsForPlatform() {
- c.hideApexVariantFromMake = true
- }
-
- c.makeLinkType = GetMakeLinkType(actx, c)
-
+func (c *Module) setSubnameProperty(actx android.ModuleContext) {
c.Properties.SubName = ""
if c.Target().NativeBridge == android.NativeBridgeEnabled {
@@ -1511,11 +1622,11 @@
} else if _, ok := c.linker.(*vndkPrebuiltLibraryDecorator); ok {
// .vendor suffix is added for backward compatibility with VNDK snapshot whose names with
// such suffixes are already hard-coded in prebuilts/vndk/.../Android.bp.
- c.Properties.SubName += vendorSuffix
+ c.Properties.SubName += VendorSuffix
} else if c.InRamdisk() && !c.OnlyInRamdisk() {
c.Properties.SubName += ramdiskSuffix
} else if c.InVendorRamdisk() && !c.OnlyInVendorRamdisk() {
- c.Properties.SubName += vendorRamdiskSuffix
+ c.Properties.SubName += VendorRamdiskSuffix
} else if c.InRecovery() && !c.OnlyInRecovery() {
c.Properties.SubName += recoverySuffix
} else if c.IsSdkVariant() && (c.Properties.SdkAndPlatformVariantVisibleToMake || c.SplitPerApiLevel()) {
@@ -1524,6 +1635,43 @@
c.Properties.SubName += "." + c.SdkVersion()
}
}
+}
+
+// Returns true if Bazel was successfully used for the analysis of this module.
+func (c *Module) maybeGenerateBazelActions(actx android.ModuleContext) bool {
+ bazelModuleLabel := c.GetBazelLabel(actx, c)
+ bazelActionsUsed := false
+ if c.bazelHandler != nil && actx.Config().BazelContext.BazelEnabled() && len(bazelModuleLabel) > 0 {
+ bazelActionsUsed = c.bazelHandler.generateBazelBuildActions(actx, bazelModuleLabel)
+ }
+ return bazelActionsUsed
+}
+
+func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
+ // TODO(cparsons): Any logic in this method occurring prior to querying Bazel should be
+ // requested from Bazel instead.
+
+ // Handle the case of a test module split by `test_per_src` mutator.
+ //
+ // The `test_per_src` mutator adds an extra variation named "", depending on all the other
+ // `test_per_src` variations of the test module. Set `outputFile` to an empty path for this
+ // module and return early, as this module does not produce an output file per se.
+ if c.IsTestPerSrcAllTestsVariation() {
+ c.outputFile = android.OptionalPath{}
+ return
+ }
+
+ c.setSubnameProperty(actx)
+ apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ if !apexInfo.IsForPlatform() {
+ c.hideApexVariantFromMake = true
+ }
+
+ c.makeLinkType = GetMakeLinkType(actx, c)
+
+ if c.maybeGenerateBazelActions(actx) {
+ return
+ }
ctx := &moduleContext{
ModuleContext: actx,
@@ -1634,8 +1782,9 @@
// Note: this is still non-installable
}
- // glob exported headers for snapshot, if BOARD_VNDK_VERSION is current.
- if i, ok := c.linker.(snapshotLibraryInterface); ok && ctx.DeviceConfig().VndkVersion() == "current" {
+ // glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or
+ // RECOVERY_SNAPSHOT_VERSION is current.
+ if i, ok := c.linker.(snapshotLibraryInterface); ok {
if shouldCollectHeadersForSnapshot(ctx, c, apexInfo) {
i.collectHeadersForSnapshot(ctx)
}
@@ -1665,7 +1814,7 @@
func (c *Module) toolchain(ctx android.BaseModuleContext) config.Toolchain {
if c.cachedToolchain == nil {
- c.cachedToolchain = config.FindToolchain(ctx.Os(), ctx.Arch())
+ c.cachedToolchain = config.FindToolchainWithContext(ctx)
}
return c.cachedToolchain
}
@@ -1718,12 +1867,6 @@
if c.compiler != nil {
deps = c.compiler.compilerDeps(ctx, deps)
}
- // Add the PGO dependency (the clang_rt.profile runtime library), which
- // sometimes depends on symbols from libgcc, before libgcc gets added
- // in linkerDeps().
- if c.pgo != nil {
- deps = c.pgo.deps(ctx, deps)
- }
if c.linker != nil {
deps = c.linker.linkerDeps(ctx, deps)
}
@@ -1757,6 +1900,12 @@
deps.HeaderLibs = android.LastUniqueStrings(deps.HeaderLibs)
deps.RuntimeLibs = android.LastUniqueStrings(deps.RuntimeLibs)
+ // In Bazel conversion mode, we dependency and build validations will occur in Bazel, so there is
+ // no need to do so in Soong.
+ if ctx.BazelConversionMode() {
+ return deps
+ }
+
for _, lib := range deps.ReexportSharedLibHeaders {
if !inList(lib, deps.SharedLibs) {
ctx.PropertyErrorf("export_shared_lib_headers", "Shared library not in shared_libs: '%s'", lib)
@@ -1764,8 +1913,8 @@
}
for _, lib := range deps.ReexportStaticLibHeaders {
- if !inList(lib, deps.StaticLibs) {
- ctx.PropertyErrorf("export_static_lib_headers", "Static library not in static_libs: '%s'", lib)
+ if !inList(lib, deps.StaticLibs) && !inList(lib, deps.WholeStaticLibs) {
+ ctx.PropertyErrorf("export_static_lib_headers", "Static library not in static_libs or whole_static_libs: '%s'", lib)
}
}
@@ -1812,9 +1961,14 @@
return nil
}
if m.UseSdk() {
+ // Choose the CRT that best satisfies the min_sdk_version requirement of this module
+ minSdkVersion := m.MinSdkVersion()
+ if minSdkVersion == "" || minSdkVersion == "apex_inherit" {
+ minSdkVersion = m.SdkVersion()
+ }
return []blueprint.Variation{
{Mutator: "sdk", Variation: "sdk"},
- {Mutator: "version", Variation: m.SdkVersion()},
+ {Mutator: "version", Variation: minSdkVersion},
}
}
return []blueprint.Variation{
@@ -1857,6 +2011,40 @@
c.Properties.AndroidMkSystemSharedLibs = deps.SystemSharedLibs
+ var snapshotInfo *SnapshotInfo
+ getSnapshot := func() SnapshotInfo {
+ // Only modules with BOARD_VNDK_VERSION uses snapshot. Others use the zero value of
+ // SnapshotInfo, which provides no mappings.
+ if snapshotInfo == nil {
+ // Only retrieve the snapshot on demand in order to avoid circular dependencies
+ // between the modules in the snapshot and the snapshot itself.
+ var snapshotModule []blueprint.Module
+ if c.InVendor() && c.VndkVersion() == actx.DeviceConfig().VndkVersion() {
+ snapshotModule = ctx.AddVariationDependencies(nil, nil, "vendor_snapshot")
+ } else if recoverySnapshotVersion := actx.DeviceConfig().RecoverySnapshotVersion(); recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" && c.InRecovery() {
+ snapshotModule = ctx.AddVariationDependencies(nil, nil, "recovery_snapshot")
+ }
+ if len(snapshotModule) > 0 {
+ snapshot := ctx.OtherModuleProvider(snapshotModule[0], SnapshotInfoProvider).(SnapshotInfo)
+ snapshotInfo = &snapshot
+ // republish the snapshot for use in later mutators on this module
+ ctx.SetProvider(SnapshotInfoProvider, snapshot)
+ } else {
+ snapshotInfo = &SnapshotInfo{}
+ }
+ }
+
+ return *snapshotInfo
+ }
+
+ rewriteSnapshotLib := func(lib string, snapshotMap map[string]string) string {
+ if snapshot, ok := snapshotMap[lib]; ok {
+ return snapshot
+ }
+
+ return lib
+ }
+
variantNdkLibs := []string{}
variantLateNdkLibs := []string{}
if ctx.Os() == android.Android {
@@ -1876,20 +2064,6 @@
// nonvariantLibs
vendorPublicLibraries := vendorPublicLibraries(actx.Config())
- vendorSnapshotSharedLibs := vendorSnapshotSharedLibs(actx.Config())
-
- rewriteVendorLibs := func(lib string) string {
- // only modules with BOARD_VNDK_VERSION uses snapshot.
- if c.VndkVersion() != actx.DeviceConfig().VndkVersion() {
- return lib
- }
-
- if snapshot, ok := vendorSnapshotSharedLibs.get(lib, actx.Arch().ArchType); ok {
- return snapshot
- }
-
- return lib
- }
rewriteLibs := func(list []string) (nonvariantLibs []string, variantLibs []string) {
variantLibs = []string{}
@@ -1897,10 +2071,12 @@
for _, entry := range list {
// strip #version suffix out
name, _ := StubsLibNameAndVersion(entry)
- if ctx.useSdk() && inList(name, *getNDKKnownLibs(ctx.Config())) {
+ if c.InRecovery() {
+ nonvariantLibs = append(nonvariantLibs, rewriteSnapshotLib(entry, getSnapshot().SharedLibs))
+ } else if ctx.useSdk() && inList(name, *getNDKKnownLibs(ctx.Config())) {
variantLibs = append(variantLibs, name+ndkLibrarySuffix)
} else if ctx.useVndk() {
- nonvariantLibs = append(nonvariantLibs, rewriteVendorLibs(entry))
+ nonvariantLibs = append(nonvariantLibs, rewriteSnapshotLib(entry, getSnapshot().SharedLibs))
} else if (ctx.Platform() || ctx.ProductSpecific()) && inList(name, *vendorPublicLibraries) {
vendorPublicLib := name + vendorPublicLibrarySuffix
if actx.OtherModuleExists(vendorPublicLib) {
@@ -1922,34 +2098,19 @@
deps.SharedLibs, variantNdkLibs = rewriteLibs(deps.SharedLibs)
deps.LateSharedLibs, variantLateNdkLibs = rewriteLibs(deps.LateSharedLibs)
deps.ReexportSharedLibHeaders, _ = rewriteLibs(deps.ReexportSharedLibHeaders)
- if ctx.useVndk() {
- for idx, lib := range deps.RuntimeLibs {
- deps.RuntimeLibs[idx] = rewriteVendorLibs(lib)
- }
+
+ for idx, lib := range deps.RuntimeLibs {
+ deps.RuntimeLibs[idx] = rewriteSnapshotLib(lib, getSnapshot().SharedLibs)
}
}
- rewriteSnapshotLibs := func(lib string, snapshotMap *snapshotMap) string {
- // only modules with BOARD_VNDK_VERSION uses snapshot.
- if c.VndkVersion() != actx.DeviceConfig().VndkVersion() {
- return lib
- }
-
- if snapshot, ok := snapshotMap.get(lib, actx.Arch().ArchType); ok {
- return snapshot
- }
-
- return lib
- }
-
- vendorSnapshotHeaderLibs := vendorSnapshotHeaderLibs(actx.Config())
for _, lib := range deps.HeaderLibs {
depTag := libraryDependencyTag{Kind: headerLibraryDependency}
if inList(lib, deps.ReexportHeaderLibHeaders) {
depTag.reexportFlags = true
}
- lib = rewriteSnapshotLibs(lib, vendorSnapshotHeaderLibs)
+ lib = rewriteSnapshotLib(lib, getSnapshot().HeaderLibs)
if c.IsStubs() {
actx.AddFarVariationDependencies(append(ctx.Target().Variations(), c.ImageVariation()),
@@ -1965,7 +2126,6 @@
// map from sysprop_library to implementation library; it will be used in whole_static_libs,
// static_libs, and shared_libs.
syspropImplLibraries := syspropImplLibraries(actx.Config())
- vendorSnapshotStaticLibs := vendorSnapshotStaticLibs(actx.Config())
for _, lib := range deps.WholeStaticLibs {
depTag := libraryDependencyTag{Kind: staticLibraryDependency, wholeStatic: true, reexportFlags: true}
@@ -1973,7 +2133,7 @@
lib = impl
}
- lib = rewriteSnapshotLibs(lib, vendorSnapshotStaticLibs)
+ lib = rewriteSnapshotLib(lib, getSnapshot().StaticLibs)
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
@@ -1993,7 +2153,7 @@
lib = impl
}
- lib = rewriteSnapshotLibs(lib, vendorSnapshotStaticLibs)
+ lib = rewriteSnapshotLib(lib, getSnapshot().StaticLibs)
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
@@ -2007,14 +2167,14 @@
depTag := libraryDependencyTag{Kind: staticLibraryDependency, staticUnwinder: true}
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
- }, depTag, rewriteSnapshotLibs(staticUnwinder(actx), vendorSnapshotStaticLibs))
+ }, depTag, rewriteSnapshotLib(staticUnwinder(actx), getSnapshot().StaticLibs))
}
for _, lib := range deps.LateStaticLibs {
depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency}
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
- }, depTag, rewriteSnapshotLibs(lib, vendorSnapshotStaticLibs))
+ }, depTag, rewriteSnapshotLib(lib, getSnapshot().StaticLibs))
}
// shared lib names without the #version suffix
@@ -2074,17 +2234,15 @@
actx.AddDependency(c, depTag, gen)
}
- vendorSnapshotObjects := vendorSnapshotObjects(actx.Config())
-
crtVariations := GetCrtVariations(ctx, c)
actx.AddVariationDependencies(crtVariations, objDepTag, deps.ObjFiles...)
if deps.CrtBegin != "" {
actx.AddVariationDependencies(crtVariations, CrtBeginDepTag,
- rewriteSnapshotLibs(deps.CrtBegin, vendorSnapshotObjects))
+ rewriteSnapshotLib(deps.CrtBegin, getSnapshot().Objects))
}
if deps.CrtEnd != "" {
actx.AddVariationDependencies(crtVariations, CrtEndDepTag,
- rewriteSnapshotLibs(deps.CrtEnd, vendorSnapshotObjects))
+ rewriteSnapshotLib(deps.CrtEnd, getSnapshot().Objects))
}
if deps.LinkerFlagsFile != "" {
actx.AddDependency(c, linkerFlagsDepTag, deps.LinkerFlagsFile)
@@ -2152,12 +2310,7 @@
if ccFrom.vndkdep != nil {
ccFrom.vndkdep.vndkCheckLinkType(ctx, ccTo, tag)
}
- } else if linkableMod, ok := to.(LinkableInterface); ok {
- // Static libraries from other languages can be linked
- if !linkableMod.Static() {
- ctx.ModuleErrorf("Attempting to link VNDK cc.Module with unsupported module type")
- }
- } else {
+ } else if _, ok := to.(LinkableInterface); !ok {
ctx.ModuleErrorf("Attempting to link VNDK cc.Module with unsupported module type")
}
return
@@ -2283,10 +2436,18 @@
return false
}
+ // These dependencies are not excercised at runtime. Tracking these will give us
+ // false negative, so skip.
depTag := ctx.OtherModuleDependencyTag(child)
if IsHeaderDepTag(depTag) {
return false
}
+ if depTag == staticVariantTag {
+ return false
+ }
+ if depTag == stubImplDepTag {
+ return false
+ }
// Even if target lib has no vendor variant, keep checking dependency
// graph in case it depends on vendor_available or product_available
@@ -2295,58 +2456,30 @@
return true
}
- if to.isVndkSp() || to.IsLlndk() || Bool(to.VendorProperties.Double_loadable) {
+ // The happy path. Keep tracking dependencies until we hit a non double-loadable
+ // one.
+ if Bool(to.VendorProperties.Double_loadable) {
+ return true
+ }
+
+ if to.isVndkSp() || to.IsLlndk() {
return false
}
- var stringPath []string
- for _, m := range ctx.GetWalkPath() {
- stringPath = append(stringPath, m.Name())
- }
ctx.ModuleErrorf("links a library %q which is not LL-NDK, "+
"VNDK-SP, or explicitly marked as 'double_loadable:true'. "+
- "(dependency: %s)", ctx.OtherModuleName(to), strings.Join(stringPath, " -> "))
+ "Dependency list: %s", ctx.OtherModuleName(to), ctx.GetPathString(false))
return false
}
if module, ok := ctx.Module().(*Module); ok {
if lib, ok := module.linker.(*libraryDecorator); ok && lib.shared() {
- if lib.hasLLNDKStubs() || Bool(module.VendorProperties.Double_loadable) {
+ if lib.hasLLNDKStubs() {
ctx.WalkDeps(check)
}
}
}
}
-// Returns the highest version which is <= maxSdkVersion.
-// For example, with maxSdkVersion is 10 and versionList is [9,11]
-// it returns 9 as string. The list of stubs must be in order from
-// oldest to newest.
-func (c *Module) chooseSdkVersion(ctx android.PathContext, stubsInfo []SharedStubLibrary,
- maxSdkVersion android.ApiLevel) (SharedStubLibrary, error) {
-
- for i := range stubsInfo {
- stubInfo := stubsInfo[len(stubsInfo)-i-1]
- var ver android.ApiLevel
- if stubInfo.Version == "" {
- ver = android.FutureApiLevel
- } else {
- var err error
- ver, err = android.ApiLevelFromUser(ctx, stubInfo.Version)
- if err != nil {
- return SharedStubLibrary{}, err
- }
- }
- if ver.LessThanOrEqualTo(maxSdkVersion) {
- return stubInfo, nil
- }
- }
- var versionList []string
- for _, stubInfo := range stubsInfo {
- versionList = append(versionList, stubInfo.Version)
- }
- return SharedStubLibrary{}, fmt.Errorf("not found a version(<=%s) in versionList: %v", maxSdkVersion.String(), versionList)
-}
-
// Convert dependencies to paths. Returns a PathDeps containing paths
func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
var depPaths PathDeps
@@ -2366,7 +2499,7 @@
c.apexSdkVersion = android.FutureApiLevel
apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
if !apexInfo.IsForPlatform() {
- c.apexSdkVersion = apexInfo.MinSdkVersion(ctx)
+ c.apexSdkVersion = apexInfo.MinSdkVersion
}
if android.InList("hwaddress", ctx.Config().SanitizeDevice()) {
@@ -2514,14 +2647,31 @@
// However, for host, ramdisk, vendor_ramdisk, recovery or bootstrap modules,
// always link to non-stub variant
useStubs = dep.(android.ApexModule).NotInPlatform() && !c.bootstrap()
- // Another exception: if this module is bundled with an APEX, then
- // it is linked with the non-stub variant of a module in the APEX
- // as if this is part of the APEX.
- testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
- for _, apexContents := range testFor.ApexContents {
- if apexContents.DirectlyInApex(depName) {
+ if useStubs {
+ // Another exception: if this module is a test for an APEX, then
+ // it is linked with the non-stub variant of a module in the APEX
+ // as if this is part of the APEX.
+ testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
+ for _, apexContents := range testFor.ApexContents {
+ if apexContents.DirectlyInApex(depName) {
+ useStubs = false
+ break
+ }
+ }
+ }
+ if useStubs {
+ // Yet another exception: If this module and the dependency are
+ // available to the same APEXes then skip stubs between their
+ // platform variants. This complements the test_for case above,
+ // which avoids the stubs on a direct APEX library dependency, by
+ // avoiding stubs for indirect test dependencies as well.
+ //
+ // TODO(b/183882457): This doesn't work if the two libraries have
+ // only partially overlapping apex_available. For that test_for
+ // modules would need to be split into APEX variants and resolved
+ // separately for each APEX they have access to.
+ if android.AvailableToSameApexes(c, dep.(android.ApexModule)) {
useStubs = false
- break
}
}
} else {
@@ -2530,16 +2680,12 @@
useStubs = !android.DirectlyInAllApexes(apexInfo, depName)
}
- // when to use (unspecified) stubs, check min_sdk_version and choose the right one
+ // when to use (unspecified) stubs, use the latest one.
if useStubs {
- sharedLibraryStubsInfo, err :=
- c.chooseSdkVersion(ctx, sharedLibraryStubsInfo.SharedStubLibraries, c.apexSdkVersion)
- if err != nil {
- ctx.OtherModuleErrorf(dep, err.Error())
- return
- }
- sharedLibraryInfo = sharedLibraryStubsInfo.SharedLibraryInfo
- depExporterInfo = sharedLibraryStubsInfo.FlagExporterInfo
+ stubs := sharedLibraryStubsInfo.SharedStubLibraries
+ toUse := stubs[len(stubs)-1]
+ sharedLibraryInfo = toUse.SharedLibraryInfo
+ depExporterInfo = toUse.FlagExporterInfo
}
}
@@ -2677,7 +2823,7 @@
c.sabi.Properties.ReexportedIncludes, depExporterInfo.IncludeDirs.Strings()...)
}
- makeLibName := c.makeLibName(ctx, ccDep, depName) + libDepTag.makeSuffix
+ makeLibName := MakeLibName(ctx, c, ccDep, depName) + libDepTag.makeSuffix
switch {
case libDepTag.header():
c.Properties.AndroidMkHeaderLibs = append(
@@ -2716,7 +2862,7 @@
switch depTag {
case runtimeDepTag:
c.Properties.AndroidMkRuntimeLibs = append(
- c.Properties.AndroidMkRuntimeLibs, c.makeLibName(ctx, ccDep, depName)+libDepTag.makeSuffix)
+ c.Properties.AndroidMkRuntimeLibs, MakeLibName(ctx, c, ccDep, depName)+libDepTag.makeSuffix)
// Record baseLibName for snapshots.
c.Properties.SnapshotRuntimeLibs = append(c.Properties.SnapshotRuntimeLibs, baseLibName(depName))
case objDepTag:
@@ -2794,30 +2940,23 @@
return libName
}
-func (c *Module) makeLibName(ctx android.ModuleContext, ccDep LinkableInterface, depName string) string {
- vendorSuffixModules := vendorSuffixModules(ctx.Config())
+func MakeLibName(ctx android.ModuleContext, c LinkableInterface, ccDep LinkableInterface, depName string) string {
+
vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
libName := baseLibName(depName)
ccDepModule, _ := ccDep.(*Module)
isLLndk := ccDepModule != nil && ccDepModule.IsLlndk()
isVendorPublicLib := inList(libName, *vendorPublicLibraries)
- bothVendorAndCoreVariantsExist := ccDep.HasVendorVariant() || isLLndk
+ nonSystemVariantsExist := ccDep.HasNonSystemVariants() || isLLndk
- if c, ok := ccDep.(*Module); ok {
+ if ccDepModule != nil {
+ // TODO(ivanlozano) Support snapshots for Rust-produced C library variants.
// Use base module name for snapshots when exporting to Makefile.
- if c.isSnapshotPrebuilt() {
- baseName := c.BaseModuleName()
+ if snapshotPrebuilt, ok := ccDepModule.linker.(snapshotInterface); ok {
+ baseName := ccDepModule.BaseModuleName()
- if c.IsVndk() {
- return baseName + ".vendor"
- }
-
- if vendorSuffixModules[baseName] {
- return baseName + ".vendor"
- } else {
- return baseName
- }
+ return baseName + snapshotPrebuilt.snapshotAndroidMkSuffix()
}
}
@@ -2826,16 +2965,16 @@
// The vendor module is a no-vendor-variant VNDK library. Depend on the
// core module instead.
return libName
- } else if c.UseVndk() && bothVendorAndCoreVariantsExist {
- // The vendor module in Make will have been renamed to not conflict with the core
- // module, so update the dependency name here accordingly.
- return libName + c.getNameSuffixWithVndkVersion(ctx)
+ } else if ccDep.UseVndk() && nonSystemVariantsExist {
+ // The vendor and product modules in Make will have been renamed to not conflict with the
+ // core module, so update the dependency name here accordingly.
+ return libName + ccDep.SubName()
} else if (ctx.Platform() || ctx.ProductSpecific()) && isVendorPublicLib {
return libName + vendorPublicLibrarySuffix
} else if ccDep.InRamdisk() && !ccDep.OnlyInRamdisk() {
return libName + ramdiskSuffix
} else if ccDep.InVendorRamdisk() && !ccDep.OnlyInVendorRamdisk() {
- return libName + vendorRamdiskSuffix
+ return libName + VendorRamdiskSuffix
} else if ccDep.InRecovery() && !ccDep.OnlyInRecovery() {
return libName + recoverySuffix
} else if ccDep.Target().NativeBridge == android.NativeBridgeEnabled {
@@ -2923,7 +3062,17 @@
return false
}
-func (c *Module) header() bool {
+func (c *Module) testBinary() bool {
+ if test, ok := c.linker.(interface {
+ testBinary() bool
+ }); ok {
+ return test.testBinary()
+ }
+ return false
+}
+
+// Header returns true if the module is a header-only variant. (See cc/library.go header()).
+func (c *Module) Header() bool {
if h, ok := c.linker.(interface {
header() bool
}); ok {
@@ -3096,6 +3245,12 @@
// We don't track beyond LLNDK or from an implementation library to its stubs.
return false
}
+ if depTag == staticVariantTag {
+ // This dependency is for optimization (reuse *.o from the static lib). It doesn't
+ // actually mean that the static lib (and its dependencies) are copied into the
+ // APEX.
+ return false
+ }
return true
}
@@ -3147,6 +3302,10 @@
type Defaults struct {
android.ModuleBase
android.DefaultsModuleBase
+ // Included to support setting bazel_module.label for multiple Soong modules to the same Bazel
+ // target. This is primarily useful for modules that were architecture specific and instead are
+ // handled in Bazel as a select().
+ android.BazelModuleBase
android.ApexModuleBase
}
@@ -3193,6 +3352,8 @@
&RustBindgenClangProperties{},
)
+ // Bazel module must be initialized _before_ Defaults to be included in cc_defaults module.
+ android.InitBazelModule(module)
android.InitDefaultsModule(module)
return module
diff --git a/cc/cc_test.go b/cc/cc_test.go
index fb85336..07dcc95 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -16,118 +16,142 @@
import (
"fmt"
- "io/ioutil"
"os"
"path/filepath"
"reflect"
+ "regexp"
"strings"
"testing"
"android/soong/android"
)
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_cc_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ os.Exit(m.Run())
}
+var prepareForCcTest = android.GroupFixturePreparers(
+ PrepareForTestWithCcIncludeVndk,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.DeviceVndkVersion = StringPtr("current")
+ variables.ProductVndkVersion = StringPtr("current")
+ variables.Platform_vndk_version = StringPtr("29")
+ }),
+)
+
+// testCcWithConfig runs tests using the prepareForCcTest
+//
+// See testCc for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
func testCcWithConfig(t *testing.T, config android.Config) *android.TestContext {
t.Helper()
- ctx := CreateTestContext(config)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
-
- return ctx
+ result := prepareForCcTest.RunTestWithConfig(t, config)
+ return result.TestContext
}
+// testCc runs tests using the prepareForCcTest
+//
+// Do not add any new usages of this, instead use the prepareForCcTest directly as it makes it much
+// easier to customize the test behavior.
+//
+// If it is necessary to customize the behavior of an existing test that uses this then please first
+// convert the test to using prepareForCcTest first and then in a following change add the
+// appropriate fixture preparers. Keeping the conversion change separate makes it easy to verify
+// that it did not change the test behavior unexpectedly.
+//
+// deprecated
func testCc(t *testing.T, bp string) *android.TestContext {
t.Helper()
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
- config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-
- return testCcWithConfig(t, config)
+ result := prepareForCcTest.RunTestWithBp(t, bp)
+ return result.TestContext
}
+// testCcNoVndk runs tests using the prepareForCcTest
+//
+// See testCc for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
func testCcNoVndk(t *testing.T, bp string) *android.TestContext {
t.Helper()
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
return testCcWithConfig(t, config)
}
+// testCcNoProductVndk runs tests using the prepareForCcTest
+//
+// See testCc for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
+func testCcNoProductVndk(t *testing.T, bp string) *android.TestContext {
+ t.Helper()
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
+
+ return testCcWithConfig(t, config)
+}
+
+// testCcErrorWithConfig runs tests using the prepareForCcTest
+//
+// See testCc for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
func testCcErrorWithConfig(t *testing.T, pattern string, config android.Config) {
t.Helper()
- ctx := CreateTestContext(config)
- ctx.Register()
-
- _, 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)
+ prepareForCcTest.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
+ RunTestWithConfig(t, config)
}
+// testCcError runs tests using the prepareForCcTest
+//
+// See testCc for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
func testCcError(t *testing.T, pattern string, bp string) {
t.Helper()
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
testCcErrorWithConfig(t, pattern, config)
return
}
+// testCcErrorProductVndk runs tests using the prepareForCcTest
+//
+// See testCc for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
func testCcErrorProductVndk(t *testing.T, pattern string, bp string) {
t.Helper()
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.ProductVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
testCcErrorWithConfig(t, pattern, config)
return
}
const (
coreVariant = "android_arm64_armv8-a_shared"
- vendorVariant = "android_vendor.VER_arm64_armv8-a_shared"
- productVariant = "android_product.VER_arm64_armv8-a_shared"
+ vendorVariant = "android_vendor.29_arm64_armv8-a_shared"
+ productVariant = "android_product.29_arm64_armv8-a_shared"
recoveryVariant = "android_recovery_arm64_armv8-a_shared"
)
+// Test that the PrepareForTestWithCcDefaultModules provides all the files that it uses by
+// running it in a fixture that requires all source files to exist.
+func TestPrepareForTestWithCcDefaultModules(t *testing.T) {
+ android.GroupFixturePreparers(
+ PrepareForTestWithCcDefaultModules,
+ android.PrepareForTestDisallowNonExistentPaths,
+ ).RunTest(t)
+}
+
func TestFuchsiaDeps(t *testing.T) {
t.Helper()
@@ -142,13 +166,15 @@
},
}`
- config := TestConfig(buildDir, android.Fuchsia, nil, bp, nil)
- ctx := testCcWithConfig(t, config)
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ PrepareForTestOnFuchsia,
+ ).RunTestWithBp(t, bp)
rt := false
fb := false
- ld := ctx.ModuleForTests("libTest", "fuchsia_arm64_shared").Rule("ld")
+ ld := result.ModuleForTests("libTest", "fuchsia_arm64_shared").Rule("ld")
implicits := ld.Implicits
for _, lib := range implicits {
if strings.Contains(lib.Rel(), "libcompiler_rt") {
@@ -179,16 +205,16 @@
},
}`
- config := TestConfig(buildDir, android.Fuchsia, nil, bp, nil)
- ctx := testCcWithConfig(t, config)
- ld := ctx.ModuleForTests("libTest", "fuchsia_arm64_shared").Rule("ld")
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ PrepareForTestOnFuchsia,
+ ).RunTestWithBp(t, bp)
+ ld := result.ModuleForTests("libTest", "fuchsia_arm64_shared").Rule("ld")
var objs []string
for _, o := range ld.Inputs {
objs = append(objs, o.Base())
}
- if len(objs) != 2 || objs[0] != "foo.o" || objs[1] != "bar.o" {
- t.Errorf("inputs of libTest must be []string{\"foo.o\", \"bar.o\"}, but was %#v.", objs)
- }
+ android.AssertArrayString(t, "libTest inputs", []string{"foo.o", "bar.o"}, objs)
}
func TestVendorSrc(t *testing.T) {
@@ -218,15 +244,126 @@
}
}
+func checkInstallPartition(t *testing.T, ctx *android.TestContext, name, variant, expected string) {
+ mod := ctx.ModuleForTests(name, variant).Module().(*Module)
+ partitionDefined := false
+ checkPartition := func(specific bool, partition string) {
+ if specific {
+ if expected != partition && !partitionDefined {
+ // The variant is installed to the 'partition'
+ t.Errorf("%s variant of %q must not be installed to %s partition", variant, name, partition)
+ }
+ partitionDefined = true
+ } else {
+ // The variant is not installed to the 'partition'
+ if expected == partition {
+ t.Errorf("%s variant of %q must be installed to %s partition", variant, name, partition)
+ }
+ }
+ }
+ socSpecific := func(m *Module) bool {
+ return m.SocSpecific() || m.socSpecificModuleContext()
+ }
+ deviceSpecific := func(m *Module) bool {
+ return m.DeviceSpecific() || m.deviceSpecificModuleContext()
+ }
+ productSpecific := func(m *Module) bool {
+ return m.ProductSpecific() || m.productSpecificModuleContext()
+ }
+ systemExtSpecific := func(m *Module) bool {
+ return m.SystemExtSpecific()
+ }
+ checkPartition(socSpecific(mod), "vendor")
+ checkPartition(deviceSpecific(mod), "odm")
+ checkPartition(productSpecific(mod), "product")
+ checkPartition(systemExtSpecific(mod), "system_ext")
+ if !partitionDefined && expected != "system" {
+ t.Errorf("%s variant of %q is expected to be installed to %s partition,"+
+ " but installed to system partition", variant, name, expected)
+ }
+}
+
+func TestInstallPartition(t *testing.T) {
+ t.Helper()
+ ctx := prepareForCcTest.RunTestWithBp(t, `
+ cc_library {
+ name: "libsystem",
+ }
+ cc_library {
+ name: "libsystem_ext",
+ system_ext_specific: true,
+ }
+ cc_library {
+ name: "libproduct",
+ product_specific: true,
+ }
+ cc_library {
+ name: "libvendor",
+ vendor: true,
+ }
+ cc_library {
+ name: "libodm",
+ device_specific: true,
+ }
+ cc_library {
+ name: "liball_available",
+ vendor_available: true,
+ product_available: true,
+ }
+ cc_library {
+ name: "libsystem_ext_all_available",
+ system_ext_specific: true,
+ vendor_available: true,
+ product_available: true,
+ }
+ cc_library {
+ name: "liball_available_odm",
+ odm_available: true,
+ product_available: true,
+ }
+ cc_library {
+ name: "libproduct_vendoravailable",
+ product_specific: true,
+ vendor_available: true,
+ }
+ cc_library {
+ name: "libproduct_odmavailable",
+ product_specific: true,
+ odm_available: true,
+ }
+ `).TestContext
+
+ checkInstallPartition(t, ctx, "libsystem", coreVariant, "system")
+ checkInstallPartition(t, ctx, "libsystem_ext", coreVariant, "system_ext")
+ checkInstallPartition(t, ctx, "libproduct", productVariant, "product")
+ checkInstallPartition(t, ctx, "libvendor", vendorVariant, "vendor")
+ checkInstallPartition(t, ctx, "libodm", vendorVariant, "odm")
+
+ checkInstallPartition(t, ctx, "liball_available", coreVariant, "system")
+ checkInstallPartition(t, ctx, "liball_available", productVariant, "product")
+ checkInstallPartition(t, ctx, "liball_available", vendorVariant, "vendor")
+
+ checkInstallPartition(t, ctx, "libsystem_ext_all_available", coreVariant, "system_ext")
+ checkInstallPartition(t, ctx, "libsystem_ext_all_available", productVariant, "product")
+ checkInstallPartition(t, ctx, "libsystem_ext_all_available", vendorVariant, "vendor")
+
+ checkInstallPartition(t, ctx, "liball_available_odm", coreVariant, "system")
+ checkInstallPartition(t, ctx, "liball_available_odm", productVariant, "product")
+ checkInstallPartition(t, ctx, "liball_available_odm", vendorVariant, "odm")
+
+ checkInstallPartition(t, ctx, "libproduct_vendoravailable", productVariant, "product")
+ checkInstallPartition(t, ctx, "libproduct_vendoravailable", vendorVariant, "vendor")
+
+ checkInstallPartition(t, ctx, "libproduct_odmavailable", productVariant, "product")
+ checkInstallPartition(t, ctx, "libproduct_odmavailable", vendorVariant, "odm")
+}
+
func checkVndkModule(t *testing.T, ctx *android.TestContext, name, subDir string,
isVndkSp bool, extends string, variant string) {
t.Helper()
mod := ctx.ModuleForTests(name, variant).Module().(*Module)
- if !mod.HasVendorVariant() {
- t.Errorf("%q must have variant %q", name, variant)
- }
// Check library properties.
lib, ok := mod.compiler.(*libraryDecorator)
@@ -259,15 +396,11 @@
}
}
-func checkSnapshotIncludeExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string, include bool) {
+func checkSnapshotIncludeExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string, include bool, fake bool) {
t.Helper()
- mod, ok := ctx.ModuleForTests(moduleName, variant).Module().(android.OutputFileProducer)
- if !ok {
- t.Errorf("%q must have output\n", moduleName)
- return
- }
- outputFiles, err := mod.OutputFiles("")
- if err != nil || len(outputFiles) != 1 {
+ mod := ctx.ModuleForTests(moduleName, variant)
+ outputFiles := mod.OutputFiles(t, "")
+ if len(outputFiles) != 1 {
t.Errorf("%q must have single output\n", moduleName)
return
}
@@ -275,8 +408,14 @@
if include {
out := singleton.Output(snapshotPath)
- if out.Input.String() != outputFiles[0].String() {
- t.Errorf("The input of snapshot %q must be %q, but %q", moduleName, out.Input.String(), outputFiles[0])
+ if fake {
+ if out.Rule == nil {
+ t.Errorf("Missing rule for module %q output file %q", moduleName, outputFiles[0])
+ }
+ } else {
+ if out.Input.String() != outputFiles[0].String() {
+ t.Errorf("The input of snapshot %q must be %q, but %q", moduleName, out.Input.String(), outputFiles[0])
+ }
}
} else {
out := singleton.MaybeOutput(snapshotPath)
@@ -287,11 +426,18 @@
}
func checkSnapshot(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
- checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true)
+ t.Helper()
+ checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, false)
}
func checkSnapshotExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
- checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, false)
+ t.Helper()
+ checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, false, false)
+}
+
+func checkSnapshotRule(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+ t.Helper()
+ checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, true)
}
func checkWriteFileOutput(t *testing.T, params android.TestingBuildParams, expected []string) {
@@ -309,16 +455,8 @@
func checkVndkLibrariesOutput(t *testing.T, ctx *android.TestContext, module string, expected []string) {
t.Helper()
- vndkLibraries := ctx.ModuleForTests(module, "")
-
- var output string
- if module != "vndkcorevariant.libraries.txt" {
- output = insertVndkVersion(module, "VER")
- } else {
- output = module
- }
-
- checkWriteFileOutput(t, vndkLibraries.Output(output), expected)
+ got := ctx.ModuleForTests(module, "").Module().(*vndkLibrariesTxt).fileNames
+ assertArrayString(t, got, expected)
}
func TestVndk(t *testing.T) {
@@ -408,27 +546,31 @@
},
}
- vndk_libraries_txt {
+ llndk_libraries_txt {
name: "llndk.libraries.txt",
}
- vndk_libraries_txt {
+ vndkcore_libraries_txt {
name: "vndkcore.libraries.txt",
}
- vndk_libraries_txt {
+ vndksp_libraries_txt {
name: "vndksp.libraries.txt",
}
- vndk_libraries_txt {
+ vndkprivate_libraries_txt {
name: "vndkprivate.libraries.txt",
}
- vndk_libraries_txt {
+ vndkproduct_libraries_txt {
+ name: "vndkproduct.libraries.txt",
+ }
+ vndkcorevariant_libraries_txt {
name: "vndkcorevariant.libraries.txt",
+ insert_vndk_version: false,
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.ProductVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
ctx := testCcWithConfig(t, config)
@@ -445,9 +587,8 @@
checkVndkModule(t, ctx, "libvndk_sp_product_private", "", true, "", productVariant)
// Check VNDK snapshot output.
-
snapshotDir := "vndk-snapshot"
- snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
vndkLibPath := filepath.Join(snapshotVariantPath, fmt.Sprintf("arch-%s-%s",
"arm64", "armv8-a"))
@@ -459,8 +600,8 @@
vndkCoreLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "vndk-core")
vndkSpLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "vndk-sp")
- variant := "android_vendor.VER_arm64_armv8-a_shared"
- variant2nd := "android_vendor.VER_arm_armv7-a-neon_shared"
+ variant := "android_vendor.29_arm64_armv8-a_shared"
+ variant2nd := "android_vendor.29_arm_armv7-a-neon_shared"
snapshotSingleton := ctx.SingletonForTests("vndk-snapshot")
@@ -476,6 +617,7 @@
checkSnapshot(t, ctx, snapshotSingleton, "vndkcore.libraries.txt", "vndkcore.libraries.txt", snapshotConfigsPath, "")
checkSnapshot(t, ctx, snapshotSingleton, "vndksp.libraries.txt", "vndksp.libraries.txt", snapshotConfigsPath, "")
checkSnapshot(t, ctx, snapshotSingleton, "vndkprivate.libraries.txt", "vndkprivate.libraries.txt", snapshotConfigsPath, "")
+ checkSnapshot(t, ctx, snapshotSingleton, "vndkproduct.libraries.txt", "vndkproduct.libraries.txt", snapshotConfigsPath, "")
checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
"LLNDK: libc.so",
@@ -493,11 +635,15 @@
"VNDK-private: libvndk-private.so",
"VNDK-private: libvndk_sp_private-x.so",
"VNDK-private: libvndk_sp_product_private-x.so",
+ "VNDK-product: libc++.so",
+ "VNDK-product: libvndk_product.so",
+ "VNDK-product: libvndk_sp_product_private-x.so",
})
checkVndkLibrariesOutput(t, ctx, "llndk.libraries.txt", []string{"libc.so", "libdl.so", "libft2.so", "libm.so"})
checkVndkLibrariesOutput(t, ctx, "vndkcore.libraries.txt", []string{"libvndk-private.so", "libvndk.so", "libvndk_product.so"})
checkVndkLibrariesOutput(t, ctx, "vndksp.libraries.txt", []string{"libc++.so", "libvndk_sp-x.so", "libvndk_sp_private-x.so", "libvndk_sp_product_private-x.so"})
checkVndkLibrariesOutput(t, ctx, "vndkprivate.libraries.txt", []string{"libft2.so", "libvndk-private.so", "libvndk_sp_private-x.so", "libvndk_sp_product_private-x.so"})
+ checkVndkLibrariesOutput(t, ctx, "vndkproduct.libraries.txt", []string{"libc++.so", "libvndk_product.so", "libvndk_sp_product_private-x.so"})
checkVndkLibrariesOutput(t, ctx, "vndkcorevariant.libraries.txt", nil)
}
@@ -529,7 +675,7 @@
}
}
- vndk_libraries_txt {
+ vndkcore_libraries_txt {
name: "vndkcore.libraries.txt",
}
`)
@@ -539,17 +685,18 @@
func TestVndkLibrariesTxtAndroidMk(t *testing.T) {
bp := `
- vndk_libraries_txt {
+ llndk_libraries_txt {
name: "llndk.libraries.txt",
+ insert_vndk_version: true,
}`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
ctx := testCcWithConfig(t, config)
module := ctx.ModuleForTests("llndk.libraries.txt", "")
- entries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
- assertArrayString(t, entries.EntryMap["LOCAL_MODULE_STEM"], []string{"llndk.libraries.VER.txt"})
+ entries := android.AndroidMkEntriesForTest(t, ctx, module.Module())[0]
+ assertArrayString(t, entries.EntryMap["LOCAL_MODULE_STEM"], []string{"llndk.libraries.29.txt"})
}
func TestVndkUsingCoreVariant(t *testing.T) {
@@ -586,14 +733,15 @@
nocrt: true,
}
- vndk_libraries_txt {
+ vndkcorevariant_libraries_txt {
name: "vndkcorevariant.libraries.txt",
+ insert_vndk_version: false,
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
setVndkMustUseVendorVariantListForTest(config, []string{"libvndk"})
@@ -618,9 +766,9 @@
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
ctx := testCcWithConfig(t, config)
@@ -669,9 +817,9 @@
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
ctx := testCcWithConfig(t, config)
@@ -693,7 +841,7 @@
if !strings.HasSuffix(outputPath, "/main_test") {
t.Errorf("expected test output file to be 'main_test', but was '%s'", outputPath)
}
- entries := android.AndroidMkEntriesForTest(t, config, "", module)[0]
+ entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][0], ":test_lib.so:foo/bar/baz") {
t.Errorf("expected LOCAL_TEST_DATA to end with `:test_lib.so:foo/bar/baz`,"+
" but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][0])
@@ -711,22 +859,54 @@
},
nocrt: true,
}
+ cc_library {
+ name: "libvndk-private",
+ vendor_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ private: true,
+ },
+ nocrt: true,
+ }
+
+ cc_library {
+ name: "libllndk",
+ llndk_stubs: "libllndk.llndk",
+ }
+
+ llndk_library {
+ name: "libllndk.llndk",
+ symbol_file: "",
+ export_llndk_headers: ["libllndk_headers"],
+ }
+
+ llndk_headers {
+ name: "libllndk_headers",
+ export_include_dirs: ["include"],
+ }
`)
checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
"LLNDK: libc.so",
"LLNDK: libdl.so",
"LLNDK: libft2.so",
+ "LLNDK: libllndk.so",
"LLNDK: libm.so",
"VNDK-SP: libc++.so",
+ "VNDK-core: libvndk-private.so",
"VNDK-core: libvndk.so",
"VNDK-private: libft2.so",
+ "VNDK-private: libvndk-private.so",
+ "VNDK-product: libc++.so",
+ "VNDK-product: libvndk-private.so",
+ "VNDK-product: libvndk.so",
})
}
func TestVndkModuleError(t *testing.T) {
// Check the error message for vendor_available and product_available properties.
- testCcErrorProductVndk(t, "vndk: vendor_available must be set to either true or false when `vndk: {enabled: true}`", `
+ testCcErrorProductVndk(t, "vndk: vendor_available must be set to true when `vndk: {enabled: true}`", `
cc_library {
name: "libvndk",
vndk: {
@@ -736,7 +916,7 @@
}
`)
- testCcErrorProductVndk(t, "vndk: vendor_available must be set to either true or false when `vndk: {enabled: true}`", `
+ testCcErrorProductVndk(t, "vndk: vendor_available must be set to true when `vndk: {enabled: true}`", `
cc_library {
name: "libvndk",
product_available: true,
@@ -1069,747 +1249,6 @@
`)
}
-func TestVendorSnapshotCapture(t *testing.T) {
- bp := `
- cc_library {
- name: "libvndk",
- vendor_available: true,
- product_available: true,
- vndk: {
- enabled: true,
- },
- nocrt: true,
- }
-
- cc_library {
- name: "libvendor",
- vendor: true,
- nocrt: true,
- }
-
- cc_library {
- name: "libvendor_available",
- vendor_available: true,
- nocrt: true,
- }
-
- cc_library_headers {
- name: "libvendor_headers",
- vendor_available: true,
- nocrt: true,
- }
-
- cc_binary {
- name: "vendor_bin",
- vendor: true,
- nocrt: true,
- }
-
- cc_binary {
- name: "vendor_available_bin",
- vendor_available: true,
- nocrt: true,
- }
-
- toolchain_library {
- name: "libb",
- vendor_available: true,
- src: "libb.a",
- }
-
- cc_object {
- name: "obj",
- vendor_available: true,
- }
-
- cc_library {
- name: "libllndk",
- llndk_stubs: "libllndk.llndk",
- }
-
- llndk_library {
- name: "libllndk.llndk",
- symbol_file: "",
- }
-`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
- config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
- ctx := testCcWithConfig(t, config)
-
- // Check Vendor snapshot output.
-
- snapshotDir := "vendor-snapshot"
- snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
- snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
-
- var jsonFiles []string
-
- for _, arch := range [][]string{
- []string{"arm64", "armv8-a"},
- []string{"arm", "armv7-a-neon"},
- } {
- archType := arch[0]
- archVariant := arch[1]
- archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
- // For shared libraries, only non-VNDK vendor_available modules are captured
- sharedVariant := fmt.Sprintf("android_vendor.VER_%s_%s_shared", archType, archVariant)
- sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
- checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
- checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
- jsonFiles = append(jsonFiles,
- filepath.Join(sharedDir, "libvendor.so.json"),
- filepath.Join(sharedDir, "libvendor_available.so.json"))
-
- // LLNDK modules are not captured
- checkSnapshotExclude(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", sharedDir, sharedVariant)
-
- // For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
- // Also cfi variants are captured, except for prebuilts like toolchain_library
- staticVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static", archType, archVariant)
- staticCfiVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static_cfi", archType, archVariant)
- staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
- checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
- checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.a", staticDir, staticVariant)
- checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.cfi.a", staticDir, staticCfiVariant)
- checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.a", staticDir, staticVariant)
- checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.cfi.a", staticDir, staticCfiVariant)
- checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.a", staticDir, staticVariant)
- checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.cfi.a", staticDir, staticCfiVariant)
- jsonFiles = append(jsonFiles,
- filepath.Join(staticDir, "libb.a.json"),
- filepath.Join(staticDir, "libvndk.a.json"),
- filepath.Join(staticDir, "libvndk.cfi.a.json"),
- filepath.Join(staticDir, "libvendor.a.json"),
- filepath.Join(staticDir, "libvendor.cfi.a.json"),
- filepath.Join(staticDir, "libvendor_available.a.json"),
- filepath.Join(staticDir, "libvendor_available.cfi.a.json"))
-
- // For binary executables, all vendor:true and vendor_available modules are captured.
- if archType == "arm64" {
- binaryVariant := fmt.Sprintf("android_vendor.VER_%s_%s", archType, archVariant)
- binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
- checkSnapshot(t, ctx, snapshotSingleton, "vendor_bin", "vendor_bin", binaryDir, binaryVariant)
- checkSnapshot(t, ctx, snapshotSingleton, "vendor_available_bin", "vendor_available_bin", binaryDir, binaryVariant)
- jsonFiles = append(jsonFiles,
- filepath.Join(binaryDir, "vendor_bin.json"),
- filepath.Join(binaryDir, "vendor_available_bin.json"))
- }
-
- // For header libraries, all vendor:true and vendor_available modules are captured.
- headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
- jsonFiles = append(jsonFiles, filepath.Join(headerDir, "libvendor_headers.json"))
-
- // For object modules, all vendor:true and vendor_available modules are captured.
- objectVariant := fmt.Sprintf("android_vendor.VER_%s_%s", archType, archVariant)
- objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
- checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
- jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
- }
-
- for _, jsonFile := range jsonFiles {
- // verify all json files exist
- if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
- t.Errorf("%q expected but not found", jsonFile)
- }
- }
-}
-
-func TestVendorSnapshotUse(t *testing.T) {
- frameworkBp := `
- cc_library {
- name: "libvndk",
- vendor_available: true,
- product_available: true,
- vndk: {
- enabled: true,
- },
- nocrt: true,
- compile_multilib: "64",
- }
-
- cc_library {
- name: "libvendor",
- vendor: true,
- nocrt: true,
- no_libcrt: true,
- stl: "none",
- system_shared_libs: [],
- compile_multilib: "64",
- }
-
- cc_binary {
- name: "bin",
- vendor: true,
- nocrt: true,
- no_libcrt: true,
- stl: "none",
- system_shared_libs: [],
- compile_multilib: "64",
- }
-`
-
- vndkBp := `
- vndk_prebuilt_shared {
- name: "libvndk",
- version: "BOARD",
- target_arch: "arm64",
- vendor_available: true,
- product_available: true,
- vndk: {
- enabled: true,
- },
- arch: {
- arm64: {
- srcs: ["libvndk.so"],
- export_include_dirs: ["include/libvndk"],
- },
- },
- }
-`
-
- vendorProprietaryBp := `
- cc_library {
- name: "libvendor_without_snapshot",
- vendor: true,
- nocrt: true,
- no_libcrt: true,
- stl: "none",
- system_shared_libs: [],
- compile_multilib: "64",
- }
-
- cc_library_shared {
- name: "libclient",
- vendor: true,
- nocrt: true,
- no_libcrt: true,
- stl: "none",
- system_shared_libs: [],
- shared_libs: ["libvndk"],
- static_libs: ["libvendor", "libvendor_without_snapshot"],
- compile_multilib: "64",
- srcs: ["client.cpp"],
- }
-
- cc_binary {
- name: "bin_without_snapshot",
- vendor: true,
- nocrt: true,
- no_libcrt: true,
- stl: "none",
- system_shared_libs: [],
- static_libs: ["libvndk"],
- compile_multilib: "64",
- srcs: ["bin.cpp"],
- }
-
- vendor_snapshot_static {
- name: "libvndk",
- version: "BOARD",
- target_arch: "arm64",
- vendor: true,
- arch: {
- arm64: {
- src: "libvndk.a",
- export_include_dirs: ["include/libvndk"],
- },
- },
- }
-
- vendor_snapshot_shared {
- name: "libvendor",
- version: "BOARD",
- target_arch: "arm64",
- vendor: true,
- arch: {
- arm64: {
- src: "libvendor.so",
- export_include_dirs: ["include/libvendor"],
- },
- },
- }
-
- vendor_snapshot_static {
- name: "libvendor",
- version: "BOARD",
- target_arch: "arm64",
- vendor: true,
- arch: {
- arm64: {
- src: "libvendor.a",
- export_include_dirs: ["include/libvendor"],
- },
- },
- }
-
- vendor_snapshot_binary {
- name: "bin",
- version: "BOARD",
- target_arch: "arm64",
- vendor: true,
- arch: {
- arm64: {
- src: "bin",
- },
- },
- }
-`
- depsBp := GatherRequiredDepsForTest(android.Android)
-
- mockFS := map[string][]byte{
- "deps/Android.bp": []byte(depsBp),
- "framework/Android.bp": []byte(frameworkBp),
- "vendor/Android.bp": []byte(vendorProprietaryBp),
- "vendor/bin": nil,
- "vendor/bin.cpp": nil,
- "vendor/client.cpp": nil,
- "vendor/include/libvndk/a.h": nil,
- "vendor/include/libvendor/b.h": nil,
- "vendor/libvndk.a": nil,
- "vendor/libvendor.a": nil,
- "vendor/libvendor.so": nil,
- "vndk/Android.bp": []byte(vndkBp),
- "vndk/include/libvndk/a.h": nil,
- "vndk/libvndk.so": nil,
- }
-
- config := TestConfig(buildDir, android.Android, nil, "", mockFS)
- config.TestProductVariables.DeviceVndkVersion = StringPtr("BOARD")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
- ctx := CreateTestContext(config)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "vendor/Android.bp", "vndk/Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
-
- sharedVariant := "android_vendor.BOARD_arm64_armv8-a_shared"
- staticVariant := "android_vendor.BOARD_arm64_armv8-a_static"
- binaryVariant := "android_vendor.BOARD_arm64_armv8-a"
-
- // libclient uses libvndk.vndk.BOARD.arm64, libvendor.vendor_static.BOARD.arm64, libvendor_without_snapshot
- libclientCcFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("cc").Args["cFlags"]
- for _, includeFlags := range []string{
- "-Ivndk/include/libvndk", // libvndk
- "-Ivendor/include/libvendor", // libvendor
- } {
- if !strings.Contains(libclientCcFlags, includeFlags) {
- t.Errorf("flags for libclient must contain %#v, but was %#v.",
- includeFlags, libclientCcFlags)
- }
- }
-
- libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("ld").Args["libFlags"]
- for _, input := range [][]string{
- []string{sharedVariant, "libvndk.vndk.BOARD.arm64"},
- []string{staticVariant, "libvendor.vendor_static.BOARD.arm64"},
- []string{staticVariant, "libvendor_without_snapshot"},
- } {
- outputPaths := getOutputPaths(ctx, input[0] /* variant */, []string{input[1]} /* module name */)
- if !strings.Contains(libclientLdFlags, outputPaths[0].String()) {
- t.Errorf("libflags for libclient must contain %#v, but was %#v", outputPaths[0], libclientLdFlags)
- }
- }
-
- // bin_without_snapshot uses libvndk.vendor_static.BOARD.arm64
- binWithoutSnapshotCcFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("cc").Args["cFlags"]
- if !strings.Contains(binWithoutSnapshotCcFlags, "-Ivendor/include/libvndk") {
- t.Errorf("flags for bin_without_snapshot must contain %#v, but was %#v.",
- "-Ivendor/include/libvndk", binWithoutSnapshotCcFlags)
- }
-
- binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("ld").Args["libFlags"]
- libVndkStaticOutputPaths := getOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.BOARD.arm64"})
- if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) {
- t.Errorf("libflags for bin_without_snapshot must contain %#v, but was %#v",
- libVndkStaticOutputPaths[0], binWithoutSnapshotLdFlags)
- }
-
- // libvendor.so is installed by libvendor.vendor_shared.BOARD.arm64
- ctx.ModuleForTests("libvendor.vendor_shared.BOARD.arm64", sharedVariant).Output("libvendor.so")
-
- // libvendor_without_snapshot.so is installed by libvendor_without_snapshot
- ctx.ModuleForTests("libvendor_without_snapshot", sharedVariant).Output("libvendor_without_snapshot.so")
-
- // bin is installed by bin.vendor_binary.BOARD.arm64
- ctx.ModuleForTests("bin.vendor_binary.BOARD.arm64", binaryVariant).Output("bin")
-
- // bin_without_snapshot is installed by bin_without_snapshot
- ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Output("bin_without_snapshot")
-
- // libvendor and bin don't have vendor.BOARD variant
- libvendorVariants := ctx.ModuleVariantsForTests("libvendor")
- if inList(sharedVariant, libvendorVariants) {
- t.Errorf("libvendor must not have variant %#v, but it does", sharedVariant)
- }
-
- binVariants := ctx.ModuleVariantsForTests("bin")
- if inList(binaryVariant, binVariants) {
- t.Errorf("bin must not have variant %#v, but it does", sharedVariant)
- }
-}
-
-func TestVendorSnapshotSanitizer(t *testing.T) {
- bp := `
- vendor_snapshot_static {
- name: "libsnapshot",
- vendor: true,
- target_arch: "arm64",
- version: "BOARD",
- arch: {
- arm64: {
- src: "libsnapshot.a",
- cfi: {
- src: "libsnapshot.cfi.a",
- }
- },
- },
- }
-`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
- config.TestProductVariables.DeviceVndkVersion = StringPtr("BOARD")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
- ctx := testCcWithConfig(t, config)
-
- // Check non-cfi and cfi variant.
- staticVariant := "android_vendor.BOARD_arm64_armv8-a_static"
- staticCfiVariant := "android_vendor.BOARD_arm64_armv8-a_static_cfi"
-
- staticModule := ctx.ModuleForTests("libsnapshot.vendor_static.BOARD.arm64", staticVariant).Module().(*Module)
- assertString(t, staticModule.outputFile.Path().Base(), "libsnapshot.a")
-
- staticCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.BOARD.arm64", staticCfiVariant).Module().(*Module)
- assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a")
-}
-
-func assertExcludeFromVendorSnapshotIs(t *testing.T, c *Module, expected bool) {
- t.Helper()
- if c.ExcludeFromVendorSnapshot() != expected {
- t.Errorf("expected %q ExcludeFromVendorSnapshot to be %t", c.String(), expected)
- }
-}
-
-func TestVendorSnapshotExclude(t *testing.T) {
-
- // This test verifies that the exclude_from_vendor_snapshot property
- // makes its way from the Android.bp source file into the module data
- // structure. It also verifies that modules are correctly included or
- // excluded in the vendor snapshot based on their path (framework or
- // vendor) and the exclude_from_vendor_snapshot property.
-
- frameworkBp := `
- cc_library_shared {
- name: "libinclude",
- srcs: ["src/include.cpp"],
- vendor_available: true,
- }
- cc_library_shared {
- name: "libexclude",
- srcs: ["src/exclude.cpp"],
- vendor: true,
- exclude_from_vendor_snapshot: true,
- }
- `
-
- vendorProprietaryBp := `
- cc_library_shared {
- name: "libvendor",
- srcs: ["vendor.cpp"],
- vendor: true,
- }
- `
-
- depsBp := GatherRequiredDepsForTest(android.Android)
-
- mockFS := map[string][]byte{
- "deps/Android.bp": []byte(depsBp),
- "framework/Android.bp": []byte(frameworkBp),
- "framework/include.cpp": nil,
- "framework/exclude.cpp": nil,
- "device/Android.bp": []byte(vendorProprietaryBp),
- "device/vendor.cpp": nil,
- }
-
- config := TestConfig(buildDir, android.Android, nil, "", mockFS)
- config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
- ctx := CreateTestContext(config)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "device/Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
-
- // Test an include and exclude framework module.
- assertExcludeFromVendorSnapshotIs(t, ctx.ModuleForTests("libinclude", coreVariant).Module().(*Module), false)
- assertExcludeFromVendorSnapshotIs(t, ctx.ModuleForTests("libinclude", vendorVariant).Module().(*Module), false)
- assertExcludeFromVendorSnapshotIs(t, ctx.ModuleForTests("libexclude", vendorVariant).Module().(*Module), true)
-
- // A vendor module is excluded, but by its path, not the
- // exclude_from_vendor_snapshot property.
- assertExcludeFromVendorSnapshotIs(t, ctx.ModuleForTests("libvendor", vendorVariant).Module().(*Module), false)
-
- // Verify the content of the vendor snapshot.
-
- snapshotDir := "vendor-snapshot"
- snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
- snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
-
- var includeJsonFiles []string
- var excludeJsonFiles []string
-
- for _, arch := range [][]string{
- []string{"arm64", "armv8-a"},
- []string{"arm", "armv7-a-neon"},
- } {
- archType := arch[0]
- archVariant := arch[1]
- archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
- sharedVariant := fmt.Sprintf("android_vendor.VER_%s_%s_shared", archType, archVariant)
- sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-
- // Included modules
- checkSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
- includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
-
- // Excluded modules
- checkSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
- excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
- checkSnapshotExclude(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
- excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libvendor.so.json"))
- }
-
- // Verify that each json file for an included module has a rule.
- for _, jsonFile := range includeJsonFiles {
- if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
- t.Errorf("include json file %q not found", jsonFile)
- }
- }
-
- // Verify that each json file for an excluded module has no rule.
- for _, jsonFile := range excludeJsonFiles {
- if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
- t.Errorf("exclude json file %q found", jsonFile)
- }
- }
-}
-
-func TestVendorSnapshotExcludeInVendorProprietaryPathErrors(t *testing.T) {
-
- // This test verifies that using the exclude_from_vendor_snapshot
- // property on a module in a vendor proprietary path generates an
- // error. These modules are already excluded, so we prohibit using the
- // property in this way, which could add to confusion.
-
- vendorProprietaryBp := `
- cc_library_shared {
- name: "libvendor",
- srcs: ["vendor.cpp"],
- vendor: true,
- exclude_from_vendor_snapshot: true,
- }
- `
-
- depsBp := GatherRequiredDepsForTest(android.Android)
-
- mockFS := map[string][]byte{
- "deps/Android.bp": []byte(depsBp),
- "device/Android.bp": []byte(vendorProprietaryBp),
- "device/vendor.cpp": nil,
- }
-
- config := TestConfig(buildDir, android.Android, nil, "", mockFS)
- config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
- ctx := CreateTestContext(config)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "device/Android.bp"})
- android.FailIfErrored(t, errs)
-
- _, errs = ctx.PrepareBuildActions(config)
- android.CheckErrorsAgainstExpectations(t, errs, []string{
- `module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
- `module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
- `module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
- `module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
- })
-}
-
-func TestVendorSnapshotExcludeWithVendorAvailable(t *testing.T) {
-
- // This test verifies that using the exclude_from_vendor_snapshot
- // property on a module that is vendor available generates an error. A
- // vendor available module must be captured in the vendor snapshot and
- // must not built from source when building the vendor image against
- // the vendor snapshot.
-
- frameworkBp := `
- cc_library_shared {
- name: "libinclude",
- srcs: ["src/include.cpp"],
- vendor_available: true,
- exclude_from_vendor_snapshot: true,
- }
- `
-
- depsBp := GatherRequiredDepsForTest(android.Android)
-
- mockFS := map[string][]byte{
- "deps/Android.bp": []byte(depsBp),
- "framework/Android.bp": []byte(frameworkBp),
- "framework/include.cpp": nil,
- }
-
- config := TestConfig(buildDir, android.Android, nil, "", mockFS)
- config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
- ctx := CreateTestContext(config)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp"})
- android.FailIfErrored(t, errs)
-
- _, errs = ctx.PrepareBuildActions(config)
- android.CheckErrorsAgainstExpectations(t, errs, []string{
- `module "libinclude\{.+,image:,arch:arm64_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
- `module "libinclude\{.+,image:,arch:arm_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
- `module "libinclude\{.+,image:vendor.+,arch:arm64_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
- `module "libinclude\{.+,image:vendor.+,arch:arm_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
- })
-}
-
-func TestRecoverySnapshotCapture(t *testing.T) {
- bp := `
- cc_library {
- name: "libvndk",
- vendor_available: true,
- recovery_available: true,
- product_available: true,
- vndk: {
- enabled: true,
- },
- nocrt: true,
- }
-
- cc_library {
- name: "librecovery",
- recovery: true,
- nocrt: true,
- }
-
- cc_library {
- name: "librecovery_available",
- recovery_available: true,
- nocrt: true,
- }
-
- cc_library_headers {
- name: "librecovery_headers",
- recovery_available: true,
- nocrt: true,
- }
-
- cc_binary {
- name: "recovery_bin",
- recovery: true,
- nocrt: true,
- }
-
- cc_binary {
- name: "recovery_available_bin",
- recovery_available: true,
- nocrt: true,
- }
-
- toolchain_library {
- name: "libb",
- recovery_available: true,
- src: "libb.a",
- }
-
- cc_object {
- name: "obj",
- recovery_available: true,
- }
-`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
- config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
- ctx := testCcWithConfig(t, config)
-
- // Check Recovery snapshot output.
-
- snapshotDir := "recovery-snapshot"
- snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
- snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
-
- var jsonFiles []string
-
- for _, arch := range [][]string{
- []string{"arm64", "armv8-a"},
- } {
- archType := arch[0]
- archVariant := arch[1]
- archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
- // For shared libraries, only recovery_available modules are captured.
- sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
- sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
- checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", sharedDir, sharedVariant)
- checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
- checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
- jsonFiles = append(jsonFiles,
- filepath.Join(sharedDir, "libvndk.so.json"),
- filepath.Join(sharedDir, "librecovery.so.json"),
- filepath.Join(sharedDir, "librecovery_available.so.json"))
-
- // For static libraries, all recovery:true and recovery_available modules are captured.
- staticVariant := fmt.Sprintf("android_recovery_%s_%s_static", archType, archVariant)
- staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
- checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
- checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.a", staticDir, staticVariant)
- checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.a", staticDir, staticVariant)
- jsonFiles = append(jsonFiles,
- filepath.Join(staticDir, "libb.a.json"),
- filepath.Join(staticDir, "librecovery.a.json"),
- filepath.Join(staticDir, "librecovery_available.a.json"))
-
- // For binary executables, all recovery:true and recovery_available modules are captured.
- if archType == "arm64" {
- binaryVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
- binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
- checkSnapshot(t, ctx, snapshotSingleton, "recovery_bin", "recovery_bin", binaryDir, binaryVariant)
- checkSnapshot(t, ctx, snapshotSingleton, "recovery_available_bin", "recovery_available_bin", binaryDir, binaryVariant)
- jsonFiles = append(jsonFiles,
- filepath.Join(binaryDir, "recovery_bin.json"),
- filepath.Join(binaryDir, "recovery_available_bin.json"))
- }
-
- // For header libraries, all vendor:true and vendor_available modules are captured.
- headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
- jsonFiles = append(jsonFiles, filepath.Join(headerDir, "librecovery_headers.json"))
-
- // For object modules, all vendor:true and vendor_available modules are captured.
- objectVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
- objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
- checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
- jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
- }
-
- for _, jsonFile := range jsonFiles {
- // verify all json files exist
- if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
- t.Errorf("%q expected but not found", jsonFile)
- }
- }
-}
-
func TestDoubleLoadableDepError(t *testing.T) {
// Check whether an error is emitted when a LLNDK depends on a non-double_loadable VNDK lib.
testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
@@ -1854,64 +1293,6 @@
}
`)
- // Check whether an error is emitted when a double_loadable lib depends on a non-double_loadable vendor_available lib.
- testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
- cc_library {
- name: "libdoubleloadable",
- vendor_available: true,
- double_loadable: true,
- shared_libs: ["libnondoubleloadable"],
- }
-
- cc_library {
- name: "libnondoubleloadable",
- vendor_available: true,
- }
- `)
-
- // Check whether an error is emitted when a double_loadable lib depends on a non-double_loadable VNDK lib.
- testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
- cc_library {
- name: "libdoubleloadable",
- vendor_available: true,
- double_loadable: true,
- shared_libs: ["libnondoubleloadable"],
- }
-
- cc_library {
- name: "libnondoubleloadable",
- vendor_available: true,
- product_available: true,
- vndk: {
- enabled: true,
- },
- }
- `)
-
- // Check whether an error is emitted when a double_loadable VNDK depends on a non-double_loadable VNDK private lib.
- testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
- cc_library {
- name: "libdoubleloadable",
- vendor_available: true,
- product_available: true,
- vndk: {
- enabled: true,
- },
- double_loadable: true,
- shared_libs: ["libnondoubleloadable"],
- }
-
- cc_library {
- name: "libnondoubleloadable",
- vendor_available: true,
- product_available: true,
- vndk: {
- enabled: true,
- private: true,
- },
- }
- `)
-
// Check whether an error is emitted when a LLNDK depends on a non-double_loadable indirectly.
testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
cc_library {
@@ -1936,6 +1317,29 @@
vendor_available: true,
}
`)
+
+ // The error is not from 'client' but from 'libllndk'
+ testCcError(t, "module \"libllndk\".* links a library \"libnondoubleloadable\".*double_loadable", `
+ cc_library {
+ name: "client",
+ vendor_available: true,
+ double_loadable: true,
+ shared_libs: ["libllndk"],
+ }
+ cc_library {
+ name: "libllndk",
+ shared_libs: ["libnondoubleloadable"],
+ llndk_stubs: "libllndk.llndk",
+ }
+ llndk_library {
+ name: "libllndk.llndk",
+ symbol_file: "",
+ }
+ cc_library {
+ name: "libnondoubleloadable",
+ vendor_available: true,
+ }
+ `)
}
func TestCheckVndkMembershipBeforeDoubleLoadable(t *testing.T) {
@@ -2038,10 +1442,10 @@
nocrt: true,
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.ProductVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
ctx := testCcWithConfig(t, config)
@@ -2088,7 +1492,7 @@
func TestVndkExtWithoutProductVndkVersion(t *testing.T) {
// This test checks the VNDK-Ext properties when PRODUCT_PRODUCT_VNDK_VERSION is not set.
- ctx := testCc(t, `
+ ctx := testCcNoProductVndk(t, `
cc_library {
name: "libvndk",
vendor_available: true,
@@ -2483,10 +1887,10 @@
nocrt: true,
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.ProductVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
testCcWithConfig(t, config)
}
@@ -2766,6 +2170,7 @@
vendor_available: true,
product_available: true,
nocrt: true,
+ srcs: ["foo.c"],
target: {
vendor: {
suffix: "-vendor",
@@ -2809,12 +2214,7 @@
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
- config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.ProductVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-
- ctx := testCcWithConfig(t, config)
+ ctx := prepareForCcTest.RunTestWithBp(t, bp).TestContext
checkVndkModule(t, ctx, "libvndk", "", false, "", productVariant)
checkVndkModule(t, ctx, "libvndk_sp", "", true, "", productVariant)
@@ -2824,10 +2224,37 @@
mod_product := ctx.ModuleForTests("libboth_available", productVariant).Module().(*Module)
assertString(t, mod_product.outputFile.Path().Base(), "libboth_available-product.so")
+
+ ensureStringContains := func(t *testing.T, str string, substr string) {
+ t.Helper()
+ if !strings.Contains(str, substr) {
+ t.Errorf("%q is not found in %v", substr, str)
+ }
+ }
+ ensureStringNotContains := func(t *testing.T, str string, substr string) {
+ t.Helper()
+ if strings.Contains(str, substr) {
+ t.Errorf("%q is found in %v", substr, str)
+ }
+ }
+
+ // _static variant is used since _shared reuses *.o from the static variant
+ vendor_static := ctx.ModuleForTests("libboth_available", strings.Replace(vendorVariant, "_shared", "_static", 1))
+ product_static := ctx.ModuleForTests("libboth_available", strings.Replace(productVariant, "_shared", "_static", 1))
+
+ vendor_cflags := vendor_static.Rule("cc").Args["cFlags"]
+ ensureStringContains(t, vendor_cflags, "-D__ANDROID_VNDK__")
+ ensureStringContains(t, vendor_cflags, "-D__ANDROID_VENDOR__")
+ ensureStringNotContains(t, vendor_cflags, "-D__ANDROID_PRODUCT__")
+
+ product_cflags := product_static.Rule("cc").Args["cFlags"]
+ ensureStringContains(t, product_cflags, "-D__ANDROID_VNDK__")
+ ensureStringContains(t, product_cflags, "-D__ANDROID_PRODUCT__")
+ ensureStringNotContains(t, product_cflags, "-D__ANDROID_VENDOR__")
}
func TestEnforceProductVndkVersionErrors(t *testing.T) {
- testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.VER", `
+ testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.29", `
cc_library {
name: "libprod",
product_specific: true,
@@ -2842,7 +2269,7 @@
nocrt: true,
}
`)
- testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.VER", `
+ testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.29", `
cc_library {
name: "libprod",
product_specific: true,
@@ -2856,7 +2283,7 @@
nocrt: true,
}
`)
- testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.VER", `
+ testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.29", `
cc_library {
name: "libprod",
product_specific: true,
@@ -2891,7 +2318,7 @@
nocrt: true,
}
`)
- testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.VER", `
+ testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.29", `
cc_library {
name: "libprod",
product_specific: true,
@@ -2993,24 +2420,42 @@
}
llndk_library {
name: "libllndkprivate.llndk",
- vendor_available: false,
+ private: true,
symbol_file: "",
- }`
+ }
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ llndk_libraries_txt {
+ name: "llndk.libraries.txt",
+ }
+ vndkcore_libraries_txt {
+ name: "vndkcore.libraries.txt",
+ }
+ vndksp_libraries_txt {
+ name: "vndksp.libraries.txt",
+ }
+ vndkprivate_libraries_txt {
+ name: "vndkprivate.libraries.txt",
+ }
+ vndkcorevariant_libraries_txt {
+ name: "vndkcorevariant.libraries.txt",
+ insert_vndk_version: false,
+ }
+ `
+
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
// native:vndk
ctx := testCcWithConfig(t, config)
- assertMapKeys(t, vndkCoreLibraries(config),
- []string{"libvndk", "libvndkprivate"})
- assertMapKeys(t, vndkSpLibraries(config),
- []string{"libc++", "libvndksp"})
- assertMapKeys(t, llndkLibraries(config),
- []string{"libc", "libdl", "libft2", "libllndk", "libllndkprivate", "libm"})
- assertMapKeys(t, vndkPrivateLibraries(config),
- []string{"libft2", "libllndkprivate", "libvndkprivate"})
+ checkVndkLibrariesOutput(t, ctx, "vndkcore.libraries.txt",
+ []string{"libvndk.so", "libvndkprivate.so"})
+ checkVndkLibrariesOutput(t, ctx, "vndksp.libraries.txt",
+ []string{"libc++.so", "libvndksp.so"})
+ checkVndkLibrariesOutput(t, ctx, "llndk.libraries.txt",
+ []string{"libc.so", "libdl.so", "libft2.so", "libllndk.so", "libllndkprivate.so", "libm.so"})
+ checkVndkLibrariesOutput(t, ctx, "vndkprivate.libraries.txt",
+ []string{"libft2.so", "libllndkprivate.so", "libvndkprivate.so"})
vendorVariant27 := "android_vendor.27_arm64_armv8-a_shared"
@@ -3203,7 +2648,7 @@
func getOutputPaths(ctx *android.TestContext, variant string, moduleNames []string) (paths android.Paths) {
for _, moduleName := range moduleNames {
module := ctx.ModuleForTests(moduleName, variant).Module().(*Module)
- output := module.outputFile.Path()
+ output := module.outputFile.Path().RelativeToTop()
paths = append(paths, output)
}
return paths
@@ -3234,7 +2679,8 @@
variant := "android_arm64_armv8-a_static"
moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
- actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).TransitiveStaticLibrariesForOrdering.ToList()
+ actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
+ TransitiveStaticLibrariesForOrdering.ToList().RelativeToTop()
expected := getOutputPaths(ctx, variant, []string{"a", "c", "b", "d"})
if !reflect.DeepEqual(actual, expected) {
@@ -3268,7 +2714,8 @@
variant := "android_arm64_armv8-a_static"
moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
- actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).TransitiveStaticLibrariesForOrdering.ToList()
+ actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
+ TransitiveStaticLibrariesForOrdering.ToList().RelativeToTop()
expected := getOutputPaths(ctx, variant, []string{"a", "c", "b"})
if !reflect.DeepEqual(actual, expected) {
@@ -3331,25 +2778,27 @@
`)
actual := ctx.ModuleVariantsForTests("libllndk")
for i := 0; i < len(actual); i++ {
- if !strings.HasPrefix(actual[i], "android_vendor.VER_") {
+ if !strings.HasPrefix(actual[i], "android_vendor.29_") {
actual = append(actual[:i], actual[i+1:]...)
i--
}
}
expected := []string{
- "android_vendor.VER_arm64_armv8-a_shared_1",
- "android_vendor.VER_arm64_armv8-a_shared_2",
- "android_vendor.VER_arm64_armv8-a_shared",
- "android_vendor.VER_arm_armv7-a-neon_shared_1",
- "android_vendor.VER_arm_armv7-a-neon_shared_2",
- "android_vendor.VER_arm_armv7-a-neon_shared",
+ "android_vendor.29_arm64_armv8-a_shared_1",
+ "android_vendor.29_arm64_armv8-a_shared_2",
+ "android_vendor.29_arm64_armv8-a_shared_current",
+ "android_vendor.29_arm64_armv8-a_shared",
+ "android_vendor.29_arm_armv7-a-neon_shared_1",
+ "android_vendor.29_arm_armv7-a-neon_shared_2",
+ "android_vendor.29_arm_armv7-a-neon_shared_current",
+ "android_vendor.29_arm_armv7-a-neon_shared",
}
checkEquals(t, "variants for llndk stubs", expected, actual)
- params := ctx.ModuleForTests("libllndk", "android_vendor.VER_arm_armv7-a-neon_shared").Description("generate stub")
+ params := ctx.ModuleForTests("libllndk", "android_vendor.29_arm_armv7-a-neon_shared").Description("generate stub")
checkEquals(t, "use VNDK version for default stubs", "current", params.Args["apiLevel"])
- params = ctx.ModuleForTests("libllndk", "android_vendor.VER_arm_armv7-a-neon_shared_1").Description("generate stub")
+ params = ctx.ModuleForTests("libllndk", "android_vendor.29_arm_armv7-a-neon_shared_1").Description("generate stub")
checkEquals(t, "override apiLevel for versioned stubs", "1", params.Args["apiLevel"])
}
@@ -3379,7 +2828,7 @@
`)
// _static variant is used since _shared reuses *.o from the static variant
- cc := ctx.ModuleForTests("libvendor", "android_vendor.VER_arm_armv7-a-neon_static").Rule("cc")
+ cc := ctx.ModuleForTests("libvendor", "android_vendor.29_arm_armv7-a-neon_static").Rule("cc")
cflags := cc.Args["cFlags"]
if !strings.Contains(cflags, "-Imy_include") {
t.Errorf("cflags for libvendor must contain -Imy_include, but was %#v.", cflags)
@@ -3400,8 +2849,17 @@
const runtimeLibAndroidBp = `
cc_library {
+ name: "liball_available",
+ vendor_available: true,
+ product_available: true,
+ no_libcrt : true,
+ nocrt : true,
+ system_shared_libs : [],
+ }
+ cc_library {
name: "libvendor_available1",
vendor_available: true,
+ runtime_libs: ["liball_available"],
no_libcrt : true,
nocrt : true,
system_shared_libs : [],
@@ -3409,18 +2867,10 @@
cc_library {
name: "libvendor_available2",
vendor_available: true,
- runtime_libs: ["libvendor_available1"],
- no_libcrt : true,
- nocrt : true,
- system_shared_libs : [],
- }
- cc_library {
- name: "libvendor_available3",
- vendor_available: true,
- runtime_libs: ["libvendor_available1"],
+ runtime_libs: ["liball_available"],
target: {
vendor: {
- exclude_runtime_libs: ["libvendor_available1"],
+ exclude_runtime_libs: ["liball_available"],
}
},
no_libcrt : true,
@@ -3428,8 +2878,16 @@
system_shared_libs : [],
}
cc_library {
+ name: "libproduct_vendor",
+ product_specific: true,
+ vendor_available: true,
+ no_libcrt : true,
+ nocrt : true,
+ system_shared_libs : [],
+ }
+ cc_library {
name: "libcore",
- runtime_libs: ["libvendor_available1"],
+ runtime_libs: ["liball_available"],
no_libcrt : true,
nocrt : true,
system_shared_libs : [],
@@ -3444,7 +2902,30 @@
cc_library {
name: "libvendor2",
vendor: true,
- runtime_libs: ["libvendor_available1", "libvendor1"],
+ runtime_libs: ["liball_available", "libvendor1", "libproduct_vendor"],
+ no_libcrt : true,
+ nocrt : true,
+ system_shared_libs : [],
+ }
+ cc_library {
+ name: "libproduct_available1",
+ product_available: true,
+ runtime_libs: ["liball_available"],
+ no_libcrt : true,
+ nocrt : true,
+ system_shared_libs : [],
+ }
+ cc_library {
+ name: "libproduct1",
+ product_specific: true,
+ no_libcrt : true,
+ nocrt : true,
+ system_shared_libs : [],
+ }
+ cc_library {
+ name: "libproduct2",
+ product_specific: true,
+ runtime_libs: ["liball_available", "libproduct1", "libproduct_vendor"],
no_libcrt : true,
nocrt : true,
system_shared_libs : [],
@@ -3457,32 +2938,45 @@
// runtime_libs for core variants use the module names without suffixes.
variant := "android_arm64_armv8-a_shared"
- module := ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"libvendor_available1"}, module)
+ module := ctx.ModuleForTests("libvendor_available1", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"liball_available"}, module)
+
+ module = ctx.ModuleForTests("libproduct_available1", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"liball_available"}, module)
module = ctx.ModuleForTests("libcore", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"libvendor_available1"}, module)
+ checkRuntimeLibs(t, []string{"liball_available"}, module)
// runtime_libs for vendor variants have '.vendor' suffixes if the modules have both core
// and vendor variants.
- variant = "android_vendor.VER_arm64_armv8-a_shared"
+ variant = "android_vendor.29_arm64_armv8-a_shared"
- module = ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"libvendor_available1.vendor"}, module)
+ module = ctx.ModuleForTests("libvendor_available1", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"liball_available.vendor"}, module)
module = ctx.ModuleForTests("libvendor2", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"libvendor_available1.vendor", "libvendor1"}, module)
+ checkRuntimeLibs(t, []string{"liball_available.vendor", "libvendor1", "libproduct_vendor.vendor"}, module)
+
+ // runtime_libs for product variants have '.product' suffixes if the modules have both core
+ // and product variants.
+ variant = "android_product.29_arm64_armv8-a_shared"
+
+ module = ctx.ModuleForTests("libproduct_available1", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"liball_available.product"}, module)
+
+ module = ctx.ModuleForTests("libproduct2", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"liball_available.product", "libproduct1", "libproduct_vendor"}, module)
}
func TestExcludeRuntimeLibs(t *testing.T) {
ctx := testCc(t, runtimeLibAndroidBp)
variant := "android_arm64_armv8-a_shared"
- module := ctx.ModuleForTests("libvendor_available3", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"libvendor_available1"}, module)
+ module := ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"liball_available"}, module)
- variant = "android_vendor.VER_arm64_armv8-a_shared"
- module = ctx.ModuleForTests("libvendor_available3", variant).Module().(*Module)
+ variant = "android_vendor.29_arm64_armv8-a_shared"
+ module = ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
checkRuntimeLibs(t, nil, module)
}
@@ -3493,11 +2987,14 @@
variant := "android_arm64_armv8-a_shared"
- module := ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"libvendor_available1"}, module)
+ module := ctx.ModuleForTests("libvendor_available1", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"liball_available"}, module)
module = ctx.ModuleForTests("libvendor2", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"libvendor_available1", "libvendor1"}, module)
+ checkRuntimeLibs(t, []string{"liball_available", "libvendor1", "libproduct_vendor"}, module)
+
+ module = ctx.ModuleForTests("libproduct2", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"liball_available", "libproduct1", "libproduct_vendor"}, module)
}
func checkStaticLibs(t *testing.T, expected []string, module *Module) {
@@ -3529,13 +3026,13 @@
// Check the shared version of lib2.
variant := "android_arm64_armv8-a_shared"
module := ctx.ModuleForTests("lib2", variant).Module().(*Module)
- checkStaticLibs(t, []string{"lib1", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic"}, module)
+ checkStaticLibs(t, []string{"lib1", "libc++demangle", "libclang_rt.builtins-aarch64-android"}, module)
// Check the static version of lib2.
variant = "android_arm64_armv8-a_static"
module = ctx.ModuleForTests("lib2", variant).Module().(*Module)
// libc++_static is linked additionally.
- checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins-aarch64-android", "libatomic"}, module)
+ checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins-aarch64-android"}, module)
}
var compilerFlagsTestCases = []struct {
@@ -3661,7 +3158,7 @@
`)
coreVariant := "android_arm64_armv8-a_shared"
- vendorVariant := "android_vendor.VER_arm64_armv8-a_shared"
+ vendorVariant := "android_vendor.29_arm64_armv8-a_shared"
// test if header search paths are correctly added
// _static variant is used since _shared reuses *.o from the static variant
@@ -3739,9 +3236,9 @@
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
ctx := testCcWithConfig(t, config)
@@ -3763,7 +3260,7 @@
if !strings.HasSuffix(outputPath, "/main_test") {
t.Errorf("expected test output file to be 'main_test', but was '%s'", outputPath)
}
- entries := android.AndroidMkEntriesForTest(t, config, "", module)[0]
+ entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][0], ":test_lib.so:foo/bar/baz") {
t.Errorf("expected LOCAL_TEST_DATA to end with `:test_lib.so:foo/bar/baz`,"+
" but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][0])
@@ -3793,10 +3290,12 @@
"android_arm64_armv8-a_shared_1",
"android_arm64_armv8-a_shared_2",
"android_arm64_armv8-a_shared_3",
+ "android_arm64_armv8-a_shared_current",
"android_arm_armv7-a-neon_shared",
"android_arm_armv7-a-neon_shared_1",
"android_arm_armv7-a-neon_shared_2",
"android_arm_armv7-a-neon_shared_3",
+ "android_arm_armv7-a-neon_shared_current",
}
variantsMismatch := false
if len(variants) != len(expectedVariants) {
@@ -3983,6 +3482,9 @@
shared: {
srcs: ["baz.c"],
},
+ bazel_module: {
+ bp2build_available: true,
+ },
}
cc_library_static {
@@ -4055,24 +3557,17 @@
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
- config.TestProductVariables.Debuggable = BoolPtr(true)
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.PrepareForTestWithVariables,
- ctx := CreateTestContext(config)
- ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.BottomUp("variable", android.VariableMutator).Parallel()
- })
- ctx.Register()
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Debuggable = BoolPtr(true)
+ }),
+ ).RunTestWithBp(t, bp)
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
-
- libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Module().(*Module)
- if !android.InList("-DBAR", libfoo.flags.Local.CppFlags) {
- t.Errorf("expected -DBAR in cppflags, got %q", libfoo.flags.Local.CppFlags)
- }
+ libfoo := result.Module("libfoo", "android_arm64_armv8-a_static").(*Module)
+ android.AssertStringListContains(t, "cppflags", libfoo.flags.Local.CppFlags, "-DBAR")
}
func TestEmptyWholeStaticLibsAllowMissingDependencies(t *testing.T) {
@@ -4090,32 +3585,18 @@
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
- config.TestProductVariables.Allow_missing_dependencies = BoolPtr(true)
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.PrepareForTestWithAllowMissingDependencies,
+ ).RunTestWithBp(t, bp)
- ctx := CreateTestContext(config)
- ctx.SetAllowMissingDependencies(true)
- ctx.Register()
+ libbar := result.ModuleForTests("libbar", "android_arm64_armv8-a_static").Output("libbar.a")
+ android.AssertDeepEquals(t, "libbar rule", android.ErrorRule, libbar.Rule)
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
+ android.AssertStringDoesContain(t, "libbar error", libbar.Args["error"], "missing dependencies: libmissing")
- libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_static").Output("libbar.a")
- if g, w := libbar.Rule, android.ErrorRule; g != w {
- t.Fatalf("Expected libbar rule to be %q, got %q", w, g)
- }
-
- if g, w := libbar.Args["error"], "missing dependencies: libmissing"; !strings.Contains(g, w) {
- t.Errorf("Expected libbar error to contain %q, was %q", w, g)
- }
-
- libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Output("libfoo.a")
- if g, w := libfoo.Inputs.Strings(), libbar.Output.String(); !android.InList(w, g) {
- t.Errorf("Expected libfoo.a to depend on %q, got %q", w, g)
- }
-
+ libfoo := result.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Output("libfoo.a")
+ android.AssertStringListContains(t, "libfoo.a dependencies", libfoo.Inputs.Strings(), libbar.Output.String())
}
func TestInstallSharedLibs(t *testing.T) {
@@ -4157,7 +3638,7 @@
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
ctx := testCcWithConfig(t, config)
hostBin := ctx.ModuleForTests("bin", config.BuildOSTarget.String()).Description("install")
@@ -4244,3 +3725,656 @@
t.Errorf("expected %q in cflags, got %q", "-Iinclude/libbar", cFlags)
}
}
+
+func TestAidlFlagsPassedToTheAidlCompiler(t *testing.T) {
+ ctx := testCc(t, `
+ cc_library {
+ name: "libfoo",
+ srcs: ["a/Foo.aidl"],
+ aidl: { flags: ["-Werror"], },
+ }
+ `)
+
+ libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static")
+ manifest := android.RuleBuilderSboxProtoForTests(t, libfoo.Output("aidl.sbox.textproto"))
+ aidlCommand := manifest.Commands[0].GetCommand()
+ expectedAidlFlag := "-Werror"
+ if !strings.Contains(aidlCommand, expectedAidlFlag) {
+ t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+ }
+}
+
+func TestMinSdkVersionInClangTriple(t *testing.T) {
+ ctx := testCc(t, `
+ cc_library_shared {
+ name: "libfoo",
+ srcs: ["foo.c"],
+ min_sdk_version: "29",
+ }`)
+
+ cFlags := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+ android.AssertStringDoesContain(t, "min sdk version", cFlags, "-target aarch64-linux-android29")
+}
+
+func TestMinSdkVersionsOfCrtObjects(t *testing.T) {
+ ctx := testCc(t, `
+ cc_object {
+ name: "crt_foo",
+ srcs: ["foo.c"],
+ crt: true,
+ stl: "none",
+ min_sdk_version: "28",
+
+ }`)
+
+ arch := "android_arm64_armv8-a"
+ for _, v := range []string{"", "28", "29", "30", "current"} {
+ var variant string
+ if v == "" {
+ variant = arch
+ } else {
+ variant = arch + "_sdk_" + v
+ }
+ cflags := ctx.ModuleForTests("crt_foo", variant).Rule("cc").Args["cFlags"]
+ vNum := v
+ if v == "current" || v == "" {
+ vNum = "10000"
+ }
+ expected := "-target aarch64-linux-android" + vNum + " "
+ android.AssertStringDoesContain(t, "cflag", cflags, expected)
+ }
+}
+
+func TestUseCrtObjectOfCorrectVersion(t *testing.T) {
+ ctx := testCc(t, `
+ cc_binary {
+ name: "bin",
+ srcs: ["foo.c"],
+ stl: "none",
+ min_sdk_version: "29",
+ sdk_version: "current",
+ }
+ `)
+
+ // Sdk variant uses the crt object of the matching min_sdk_version
+ variant := "android_arm64_armv8-a_sdk"
+ crt := ctx.ModuleForTests("bin", variant).Rule("ld").Args["crtBegin"]
+ android.AssertStringDoesContain(t, "crt dep of sdk variant", crt,
+ variant+"_29/crtbegin_dynamic.o")
+
+ // platform variant uses the crt object built for platform
+ variant = "android_arm64_armv8-a"
+ crt = ctx.ModuleForTests("bin", variant).Rule("ld").Args["crtBegin"]
+ android.AssertStringDoesContain(t, "crt dep of platform variant", crt,
+ variant+"/crtbegin_dynamic.o")
+}
+
+type MemtagNoteType int
+
+const (
+ None MemtagNoteType = iota + 1
+ Sync
+ Async
+)
+
+func (t MemtagNoteType) str() string {
+ switch t {
+ case None:
+ return "none"
+ case Sync:
+ return "sync"
+ case Async:
+ return "async"
+ default:
+ panic("invalid note type")
+ }
+}
+
+func checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNoteType) {
+ note_async := "note_memtag_heap_async"
+ note_sync := "note_memtag_heap_sync"
+
+ found := None
+ implicits := m.Rule("ld").Implicits
+ for _, lib := range implicits {
+ if strings.Contains(lib.Rel(), note_async) {
+ found = Async
+ break
+ } else if strings.Contains(lib.Rel(), note_sync) {
+ found = Sync
+ break
+ }
+ }
+
+ if found != expected {
+ t.Errorf("Wrong Memtag note in target %q: found %q, expected %q", m.Module().(*Module).Name(), found.str(), expected.str())
+ }
+}
+
+var prepareForTestWithMemtagHeap = android.GroupFixturePreparers(
+ android.FixtureModifyMockFS(func(fs android.MockFS) {
+ templateBp := `
+ cc_test {
+ name: "%[1]s_test",
+ gtest: false,
+ }
+
+ cc_test {
+ name: "%[1]s_test_false",
+ gtest: false,
+ sanitize: { memtag_heap: false },
+ }
+
+ cc_test {
+ name: "%[1]s_test_true",
+ gtest: false,
+ sanitize: { memtag_heap: true },
+ }
+
+ cc_test {
+ name: "%[1]s_test_true_nodiag",
+ gtest: false,
+ sanitize: { memtag_heap: true, diag: { memtag_heap: false } },
+ }
+
+ cc_test {
+ name: "%[1]s_test_true_diag",
+ gtest: false,
+ sanitize: { memtag_heap: true, diag: { memtag_heap: true } },
+ }
+
+ cc_binary {
+ name: "%[1]s_binary",
+ }
+
+ cc_binary {
+ name: "%[1]s_binary_false",
+ sanitize: { memtag_heap: false },
+ }
+
+ cc_binary {
+ name: "%[1]s_binary_true",
+ sanitize: { memtag_heap: true },
+ }
+
+ cc_binary {
+ name: "%[1]s_binary_true_nodiag",
+ sanitize: { memtag_heap: true, diag: { memtag_heap: false } },
+ }
+
+ cc_binary {
+ name: "%[1]s_binary_true_diag",
+ sanitize: { memtag_heap: true, diag: { memtag_heap: true } },
+ }
+ `
+ subdirDefaultBp := fmt.Sprintf(templateBp, "default")
+ subdirExcludeBp := fmt.Sprintf(templateBp, "exclude")
+ subdirSyncBp := fmt.Sprintf(templateBp, "sync")
+ subdirAsyncBp := fmt.Sprintf(templateBp, "async")
+
+ fs.Merge(android.MockFS{
+ "subdir_default/Android.bp": []byte(subdirDefaultBp),
+ "subdir_exclude/Android.bp": []byte(subdirExcludeBp),
+ "subdir_sync/Android.bp": []byte(subdirSyncBp),
+ "subdir_async/Android.bp": []byte(subdirAsyncBp),
+ })
+ }),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
+ // "subdir_exclude" is covered by both include and exclude paths. Exclude wins.
+ variables.MemtagHeapSyncIncludePaths = []string{"subdir_sync", "subdir_exclude"}
+ variables.MemtagHeapAsyncIncludePaths = []string{"subdir_async", "subdir_exclude"}
+ }),
+)
+
+func TestSanitizeMemtagHeap(t *testing.T) {
+ variant := "android_arm64_armv8-a"
+
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ prepareForTestWithMemtagHeap,
+ ).RunTest(t)
+ ctx := result.TestContext
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
+}
+
+func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
+ variant := "android_arm64_armv8-a"
+
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ prepareForTestWithMemtagHeap,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.SanitizeDevice = []string{"memtag_heap"}
+ }),
+ ).RunTest(t)
+ ctx := result.TestContext
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
+}
+
+func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
+ variant := "android_arm64_armv8-a"
+
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ prepareForTestWithMemtagHeap,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.SanitizeDevice = []string{"memtag_heap"}
+ variables.SanitizeDeviceDiag = []string{"memtag_heap"}
+ }),
+ ).RunTest(t)
+ ctx := result.TestContext
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
+}
+
+func TestIncludeDirsExporting(t *testing.T) {
+
+ // Trim spaces from the beginning, end and immediately after any newline characters. Leaves
+ // embedded newline characters alone.
+ trimIndentingSpaces := func(s string) string {
+ return strings.TrimSpace(regexp.MustCompile("(^|\n)\\s+").ReplaceAllString(s, "$1"))
+ }
+
+ checkPaths := func(t *testing.T, message string, expected string, paths android.Paths) {
+ t.Helper()
+ expected = trimIndentingSpaces(expected)
+ actual := trimIndentingSpaces(strings.Join(android.FirstUniqueStrings(android.NormalizePathsForTesting(paths)), "\n"))
+ if expected != actual {
+ t.Errorf("%s: expected:\n%s\n actual:\n%s\n", message, expected, actual)
+ }
+ }
+
+ type exportedChecker func(t *testing.T, name string, exported FlagExporterInfo)
+
+ checkIncludeDirs := func(t *testing.T, ctx *android.TestContext, module android.Module, checkers ...exportedChecker) {
+ t.Helper()
+ exported := ctx.ModuleProvider(module, FlagExporterInfoProvider).(FlagExporterInfo)
+ name := module.Name()
+
+ for _, checker := range checkers {
+ checker(t, name, exported)
+ }
+ }
+
+ expectedIncludeDirs := func(expectedPaths string) exportedChecker {
+ return func(t *testing.T, name string, exported FlagExporterInfo) {
+ t.Helper()
+ checkPaths(t, fmt.Sprintf("%s: include dirs", name), expectedPaths, exported.IncludeDirs)
+ }
+ }
+
+ expectedSystemIncludeDirs := func(expectedPaths string) exportedChecker {
+ return func(t *testing.T, name string, exported FlagExporterInfo) {
+ t.Helper()
+ checkPaths(t, fmt.Sprintf("%s: system include dirs", name), expectedPaths, exported.SystemIncludeDirs)
+ }
+ }
+
+ expectedGeneratedHeaders := func(expectedPaths string) exportedChecker {
+ return func(t *testing.T, name string, exported FlagExporterInfo) {
+ t.Helper()
+ checkPaths(t, fmt.Sprintf("%s: generated headers", name), expectedPaths, exported.GeneratedHeaders)
+ }
+ }
+
+ expectedOrderOnlyDeps := func(expectedPaths string) exportedChecker {
+ return func(t *testing.T, name string, exported FlagExporterInfo) {
+ t.Helper()
+ checkPaths(t, fmt.Sprintf("%s: order only deps", name), expectedPaths, exported.Deps)
+ }
+ }
+
+ genRuleModules := `
+ genrule {
+ name: "genrule_foo",
+ cmd: "generate-foo",
+ out: [
+ "generated_headers/foo/generated_header.h",
+ ],
+ export_include_dirs: [
+ "generated_headers",
+ ],
+ }
+
+ genrule {
+ name: "genrule_bar",
+ cmd: "generate-bar",
+ out: [
+ "generated_headers/bar/generated_header.h",
+ ],
+ export_include_dirs: [
+ "generated_headers",
+ ],
+ }
+ `
+
+ t.Run("ensure exported include dirs are not automatically re-exported from shared_libs", func(t *testing.T) {
+ ctx := testCc(t, genRuleModules+`
+ cc_library {
+ name: "libfoo",
+ srcs: ["foo.c"],
+ export_include_dirs: ["foo/standard"],
+ export_system_include_dirs: ["foo/system"],
+ generated_headers: ["genrule_foo"],
+ export_generated_headers: ["genrule_foo"],
+ }
+
+ cc_library {
+ name: "libbar",
+ srcs: ["bar.c"],
+ shared_libs: ["libfoo"],
+ export_include_dirs: ["bar/standard"],
+ export_system_include_dirs: ["bar/system"],
+ generated_headers: ["genrule_bar"],
+ export_generated_headers: ["genrule_bar"],
+ }
+ `)
+ foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+ checkIncludeDirs(t, ctx, foo,
+ expectedIncludeDirs(`
+ foo/standard
+ .intermediates/genrule_foo/gen/generated_headers
+ `),
+ expectedSystemIncludeDirs(`foo/system`),
+ expectedGeneratedHeaders(`.intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h`),
+ expectedOrderOnlyDeps(`.intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h`),
+ )
+
+ bar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
+ checkIncludeDirs(t, ctx, bar,
+ expectedIncludeDirs(`
+ bar/standard
+ .intermediates/genrule_bar/gen/generated_headers
+ `),
+ expectedSystemIncludeDirs(`bar/system`),
+ expectedGeneratedHeaders(`.intermediates/genrule_bar/gen/generated_headers/bar/generated_header.h`),
+ expectedOrderOnlyDeps(`.intermediates/genrule_bar/gen/generated_headers/bar/generated_header.h`),
+ )
+ })
+
+ t.Run("ensure exported include dirs are automatically re-exported from whole_static_libs", func(t *testing.T) {
+ ctx := testCc(t, genRuleModules+`
+ cc_library {
+ name: "libfoo",
+ srcs: ["foo.c"],
+ export_include_dirs: ["foo/standard"],
+ export_system_include_dirs: ["foo/system"],
+ generated_headers: ["genrule_foo"],
+ export_generated_headers: ["genrule_foo"],
+ }
+
+ cc_library {
+ name: "libbar",
+ srcs: ["bar.c"],
+ whole_static_libs: ["libfoo"],
+ export_include_dirs: ["bar/standard"],
+ export_system_include_dirs: ["bar/system"],
+ generated_headers: ["genrule_bar"],
+ export_generated_headers: ["genrule_bar"],
+ }
+ `)
+ foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+ checkIncludeDirs(t, ctx, foo,
+ expectedIncludeDirs(`
+ foo/standard
+ .intermediates/genrule_foo/gen/generated_headers
+ `),
+ expectedSystemIncludeDirs(`foo/system`),
+ expectedGeneratedHeaders(`.intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h`),
+ expectedOrderOnlyDeps(`.intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h`),
+ )
+
+ bar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
+ checkIncludeDirs(t, ctx, bar,
+ expectedIncludeDirs(`
+ bar/standard
+ foo/standard
+ .intermediates/genrule_foo/gen/generated_headers
+ .intermediates/genrule_bar/gen/generated_headers
+ `),
+ expectedSystemIncludeDirs(`
+ bar/system
+ foo/system
+ `),
+ expectedGeneratedHeaders(`
+ .intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h
+ .intermediates/genrule_bar/gen/generated_headers/bar/generated_header.h
+ `),
+ expectedOrderOnlyDeps(`
+ .intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h
+ .intermediates/genrule_bar/gen/generated_headers/bar/generated_header.h
+ `),
+ )
+ })
+
+ t.Run("ensure only aidl headers are exported", func(t *testing.T) {
+ ctx := testCc(t, genRuleModules+`
+ cc_library_shared {
+ name: "libfoo",
+ srcs: [
+ "foo.c",
+ "b.aidl",
+ "a.proto",
+ ],
+ aidl: {
+ export_aidl_headers: true,
+ }
+ }
+ `)
+ foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+ checkIncludeDirs(t, ctx, foo,
+ expectedIncludeDirs(`
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl
+ `),
+ expectedSystemIncludeDirs(``),
+ expectedGeneratedHeaders(`
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/b.h
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bnb.h
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bpb.h
+ `),
+ expectedOrderOnlyDeps(`
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/b.h
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bnb.h
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bpb.h
+ `),
+ )
+ })
+
+ t.Run("ensure only proto headers are exported", func(t *testing.T) {
+ ctx := testCc(t, genRuleModules+`
+ cc_library_shared {
+ name: "libfoo",
+ srcs: [
+ "foo.c",
+ "b.aidl",
+ "a.proto",
+ ],
+ proto: {
+ export_proto_headers: true,
+ }
+ }
+ `)
+ foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+ checkIncludeDirs(t, ctx, foo,
+ expectedIncludeDirs(`
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/proto
+ `),
+ expectedSystemIncludeDirs(``),
+ expectedGeneratedHeaders(`
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/proto/a.pb.h
+ `),
+ expectedOrderOnlyDeps(`
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/proto/a.pb.h
+ `),
+ )
+ })
+
+ t.Run("ensure only sysprop headers are exported", func(t *testing.T) {
+ ctx := testCc(t, genRuleModules+`
+ cc_library_shared {
+ name: "libfoo",
+ srcs: [
+ "foo.c",
+ "a.sysprop",
+ "b.aidl",
+ "a.proto",
+ ],
+ }
+ `)
+ foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+ checkIncludeDirs(t, ctx, foo,
+ expectedIncludeDirs(`
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include
+ `),
+ expectedSystemIncludeDirs(``),
+ expectedGeneratedHeaders(`
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include/a.sysprop.h
+ `),
+ expectedOrderOnlyDeps(`
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include/a.sysprop.h
+ .intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/public/include/a.sysprop.h
+ `),
+ )
+ })
+}
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index d441c57..04536fc 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -319,6 +319,9 @@
if strings.HasPrefix(parameter, "-fsanitize-blacklist") {
return relativeFilePathFlag
}
+ if strings.HasPrefix(parameter, "-fprofile-sample-use") {
+ return relativeFilePathFlag
+ }
return flag
}
diff --git a/cc/compiler.go b/cc/compiler.go
index 2c05899..78a5a5d 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -123,6 +123,9 @@
// whether to generate traces (for systrace) for this interface
Generate_traces *bool
+
+ // list of flags that will be passed to the AIDL compiler
+ Flags []string
}
Renderscript struct {
@@ -227,6 +230,8 @@
// other modules and filegroups. May include source files that have not yet been translated to
// C/C++ (.aidl, .proto, etc.)
srcsBeforeGen android.Paths
+
+ generatedSourceInfo
}
var _ compiler = (*baseCompiler)(nil)
@@ -251,6 +256,10 @@
return []interface{}{&compiler.Properties, &compiler.Proto}
}
+func (compiler *baseCompiler) includeBuildDirectory() bool {
+ return proptools.BoolDefault(compiler.Properties.Include_build_directory, true)
+}
+
func (compiler *baseCompiler) compilerInit(ctx BaseModuleContext) {}
func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
@@ -327,8 +336,7 @@
flags.Local.YasmFlags = append(flags.Local.YasmFlags, f)
}
- if compiler.Properties.Include_build_directory == nil ||
- *compiler.Properties.Include_build_directory {
+ if compiler.includeBuildDirectory() {
flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-I"+modulePath)
flags.Local.YasmFlags = append(flags.Local.YasmFlags, "-I"+modulePath)
}
@@ -352,6 +360,11 @@
if ctx.useVndk() {
flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VNDK__")
+ if ctx.inVendor() {
+ flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VENDOR__")
+ } else if ctx.inProduct() {
+ flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_PRODUCT__")
+ }
}
if ctx.inRecovery() {
@@ -401,7 +414,7 @@
target := "-target " + tc.ClangTriple()
if ctx.Os().Class == android.Device {
- version := ctx.sdkVersion()
+ version := ctx.minSdkVersion()
if version == "" || version == "current" {
target += strconv.Itoa(android.FutureApiLevelInt)
} else {
@@ -432,7 +445,7 @@
fmt.Sprintf("${config.%sClangGlobalCflags}", hod))
if isThirdParty(modulePath) {
- flags.Global.CommonFlags = append([]string{"${config.ClangExternalCflags}"}, flags.Global.CommonFlags...)
+ flags.Global.CommonFlags = append(flags.Global.CommonFlags, "${config.ClangExternalCflags}")
}
if tc.Bionic() {
@@ -521,6 +534,7 @@
}
if compiler.hasSrcExt(".aidl") {
+ flags.aidlFlags = append(flags.aidlFlags, compiler.Properties.Aidl.Flags...)
if len(compiler.Properties.Aidl.Local_include_dirs) > 0 {
localAidlIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Aidl.Local_include_dirs)
flags.aidlFlags = append(flags.aidlFlags, includeDirsToFlags(localAidlIncludeDirs))
@@ -625,10 +639,11 @@
srcs := append(android.Paths(nil), compiler.srcsBeforeGen...)
- srcs, genDeps := genSources(ctx, srcs, buildFlags)
+ srcs, genDeps, info := genSources(ctx, srcs, buildFlags)
pathDeps = append(pathDeps, genDeps...)
compiler.pathDeps = pathDeps
+ compiler.generatedSourceInfo = info
compiler.cFlagsDeps = flags.CFlagsDeps
// Save src, buildFlags and context
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index ce4bdfb..5ef247d 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-cc-config",
pkgPath: "android/soong/cc/config",
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index e6024aa..864fba1 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -31,6 +31,10 @@
"armv8-a": []string{
"-march=armv8-a",
},
+ "armv8-a-branchprot": []string{
+ "-march=armv8-a",
+ "-mbranch-protection=standard",
+ },
"armv8-2a": []string{
"-march=armv8.2-a",
},
@@ -42,7 +46,6 @@
arm64Ldflags = []string{
"-Wl,--hash-style=gnu",
"-Wl,-z,separate-code",
- "-Wl,--icf=safe",
}
arm64Lldflags = append(ClangFilterUnknownLldflags(arm64Ldflags),
@@ -102,6 +105,7 @@
pctx.StaticVariable("Arm64ClangCppflags", strings.Join(ClangFilterUnknownCflags(arm64Cppflags), " "))
pctx.StaticVariable("Arm64ClangArmv8ACflags", strings.Join(arm64ArchVariantCflags["armv8-a"], " "))
+ pctx.StaticVariable("Arm64ClangArmv8ABranchProtCflags", strings.Join(arm64ArchVariantCflags["armv8-a-branchprot"], " "))
pctx.StaticVariable("Arm64ClangArmv82ACflags", strings.Join(arm64ArchVariantCflags["armv8-2a"], " "))
pctx.StaticVariable("Arm64ClangArmv82ADotprodCflags", strings.Join(arm64ArchVariantCflags["armv8-2a-dotprod"], " "))
@@ -123,9 +127,10 @@
var (
arm64ClangArchVariantCflagsVar = map[string]string{
- "armv8-a": "${config.Arm64ClangArmv8ACflags}",
- "armv8-2a": "${config.Arm64ClangArmv82ACflags}",
- "armv8-2a-dotprod": "${config.Arm64ClangArmv82ADotprodCflags}",
+ "armv8-a": "${config.Arm64ClangArmv8ACflags}",
+ "armv8-a-branchprot": "${config.Arm64ClangArmv8ABranchProtCflags}",
+ "armv8-2a": "${config.Arm64ClangArmv82ACflags}",
+ "armv8-2a-dotprod": "${config.Arm64ClangArmv82ADotprodCflags}",
}
arm64ClangCpuVariantCflagsVar = map[string]string{
@@ -202,6 +207,7 @@
func arm64ToolchainFactory(arch android.Arch) Toolchain {
switch arch.ArchVariant {
case "armv8-a":
+ case "armv8-a-branchprot":
case "armv8-2a":
case "armv8-2a-dotprod":
// Nothing extra for armv8-a/armv8-2a
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index f01c638..a402f8f 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -34,7 +34,6 @@
armCppflags = []string{}
armLdflags = []string{
- "-Wl,--icf=safe",
"-Wl,--hash-style=gnu",
"-Wl,-m,armelf",
}
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 519a9e2..5e46d5a 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -94,6 +94,7 @@
// `modernize-*`.
var ClangTidyDisableChecks = []string{
"misc-no-recursion",
+ "readability-function-cognitive-complexity", // http://b/175055536
}
func init() {
@@ -140,6 +141,16 @@
// Warnings from clang-10
// Nested and array designated initialization is nice to have.
"-Wno-c99-designator",
+
+ // Warnings from clang-12
+ "-Wno-gnu-folding-constant",
+
+ // Calls to the APIs that are newer than the min sdk version of the caller should be
+ // guarded with __builtin_available.
+ "-Wunguarded-availability",
+ // This macro allows the bionic versioning.h to indirectly determine whether the
+ // option -Wunguarded-availability is on or not.
+ "-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__",
}, " "))
pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
@@ -190,6 +201,8 @@
"-Wno-pessimizing-move", // http://b/154270751
// New warnings to be fixed after clang-r399163
"-Wno-non-c-typedef-for-linkage", // http://b/161304145
+ // New warnings to be fixed after clang-r407598
+ "-Wno-string-concatenation", // http://b/175068488
}, " "))
// Extra cflags for external third-party projects to disable warnings that
diff --git a/cc/config/global.go b/cc/config/global.go
index e5cb7ee..ed18300 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -53,6 +53,7 @@
"-Werror=date-time",
"-Werror=pragma-pack",
"-Werror=pragma-pack-suspicious-include",
+ "-Werror=string-plus-int",
"-Werror=unreachable-code-loop-increment",
}
@@ -90,9 +91,13 @@
"-Wl,--warn-shared-textrel",
"-Wl,--fatal-warnings",
"-Wl,--no-undefined-version",
+ // TODO: Eventually we should link against a libunwind.a with hidden symbols, and then these
+ // --exclude-libs arguments can be removed.
"-Wl,--exclude-libs,libgcc.a",
"-Wl,--exclude-libs,libgcc_stripped.a",
"-Wl,--exclude-libs,libunwind_llvm.a",
+ "-Wl,--exclude-libs,libunwind.a",
+ "-Wl,--icf=safe",
}
deviceGlobalLldflags = append(ClangFilterUnknownLldflags(deviceGlobalLdflags),
@@ -113,8 +118,13 @@
}
noOverrideGlobalCflags = []string{
+ "-Werror=bool-operation",
+ "-Werror=implicit-int-float-conversion",
+ "-Werror=int-in-bool-context",
"-Werror=int-to-pointer-cast",
"-Werror=pointer-to-int-cast",
+ "-Werror=string-compare",
+ "-Werror=xor-used-as-pow",
// http://b/161386391 for -Wno-void-pointer-to-enum-cast
"-Wno-void-pointer-to-enum-cast",
// http://b/161386391 for -Wno-void-pointer-to-int-cast
@@ -135,8 +145,8 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r399163b"
- ClangDefaultShortVersion = "11.0.5"
+ ClangDefaultVersion = "clang-r416183"
+ ClangDefaultShortVersion = "12.0.4"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
@@ -215,10 +225,6 @@
"frameworks/native/opengl/include",
"frameworks/av/include",
})
- // This is used by non-NDK modules to get jni.h. export_include_dirs doesn't help
- // with this, since there is no associated library.
- pctx.PrefixedExistentPathsForSourcesVariable("CommonNativehelperInclude", "-I",
- []string{"libnativehelper/include_jni"})
pctx.SourcePathVariable("ClangDefaultBase", ClangDefaultBase)
pctx.VariableFunc("ClangBase", func(ctx android.PackageVarContext) string {
@@ -265,13 +271,13 @@
return ""
})
- pctx.VariableFunc("RECXXPool", remoteexec.EnvOverrideFunc("RBE_CXX_POOL", remoteexec.DefaultPool))
- pctx.VariableFunc("RECXXLinksPool", remoteexec.EnvOverrideFunc("RBE_CXX_LINKS_POOL", remoteexec.DefaultPool))
- pctx.VariableFunc("REClangTidyPool", remoteexec.EnvOverrideFunc("RBE_CLANG_TIDY_POOL", remoteexec.DefaultPool))
- pctx.VariableFunc("RECXXLinksExecStrategy", remoteexec.EnvOverrideFunc("RBE_CXX_LINKS_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
- pctx.VariableFunc("REClangTidyExecStrategy", remoteexec.EnvOverrideFunc("RBE_CLANG_TIDY_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
- pctx.VariableFunc("REAbiDumperExecStrategy", remoteexec.EnvOverrideFunc("RBE_ABI_DUMPER_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
- pctx.VariableFunc("REAbiLinkerExecStrategy", remoteexec.EnvOverrideFunc("RBE_ABI_LINKER_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
+ pctx.StaticVariableWithEnvOverride("RECXXPool", "RBE_CXX_POOL", remoteexec.DefaultPool)
+ pctx.StaticVariableWithEnvOverride("RECXXLinksPool", "RBE_CXX_LINKS_POOL", remoteexec.DefaultPool)
+ pctx.StaticVariableWithEnvOverride("REClangTidyPool", "RBE_CLANG_TIDY_POOL", remoteexec.DefaultPool)
+ pctx.StaticVariableWithEnvOverride("RECXXLinksExecStrategy", "RBE_CXX_LINKS_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
+ pctx.StaticVariableWithEnvOverride("REClangTidyExecStrategy", "RBE_CLANG_TIDY_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
+ pctx.StaticVariableWithEnvOverride("REAbiDumperExecStrategy", "RBE_ABI_DUMPER_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
+ pctx.StaticVariableWithEnvOverride("REAbiLinkerExecStrategy", "RBE_ABI_LINKER_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
}
var HostPrebuiltTag = pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index 4ac9e58..c4563e2 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -20,25 +20,51 @@
)
func init() {
- // Most Android source files are not clang-tidy clean yet.
- // Global tidy checks include only google*, performance*,
- // and misc-macro-parentheses, but not google-readability*
- // or google-runtime-references.
+ // Many clang-tidy checks like altera-*, llvm-*, modernize-*
+ // are not designed for Android source code or creating too
+ // many (false-positive) warnings. The global default tidy checks
+ // should include only tested groups and exclude known noisy checks.
+ // See https://clang.llvm.org/extra/clang-tidy/checks/list.html
pctx.VariableFunc("TidyDefaultGlobalChecks", func(ctx android.PackageVarContext) string {
if override := ctx.Config().Getenv("DEFAULT_GLOBAL_TIDY_CHECKS"); override != "" {
return override
}
- return strings.Join([]string{
+ checks := strings.Join([]string{
"-*",
- "bugprone*",
+ "android-*",
+ "bugprone-*",
+ "cert-*",
"clang-diagnostic-unused-command-line-argument",
- "google*",
- "misc-macro-parentheses",
- "performance*",
+ "google-*",
+ "misc-*",
+ "performance-*",
+ "portability-*",
"-bugprone-narrowing-conversions",
"-google-readability*",
"-google-runtime-references",
+ "-misc-no-recursion",
+ "-misc-non-private-member-variables-in-classes",
+ "-misc-unused-parameters",
+ // the following groups are excluded by -*
+ // -altera-*
+ // -cppcoreguidelines-*
+ // -darwin-*
+ // -fuchsia-*
+ // -hicpp-*
+ // -llvm-*
+ // -llvmlibc-*
+ // -modernize-*
+ // -mpi-*
+ // -objc-*
+ // -readability-*
+ // -zircon-*
}, ",")
+ // clang-analyzer-* checks are too slow to be in the default for WITH_TIDY=1.
+ // nightly builds add CLANG_ANALYZER_CHECKS=1 to run those checks.
+ if ctx.Config().IsEnvTrue("CLANG_ANALYZER_CHECKS") {
+ checks += ",clang-analyzer-*"
+ }
+ return checks
})
// There are too many clang-tidy warnings in external and vendor projects.
@@ -60,27 +86,11 @@
}, ",")
})
- // Give warnings to header files only in selected directories.
- // Do not give warnings to external or vendor header files, which contain too
- // many warnings.
+ // To reduce duplicate warnings from the same header files,
+ // header-filter will contain only the module directory and
+ // those specified by DEFAULT_TIDY_HEADER_DIRS.
pctx.VariableFunc("TidyDefaultHeaderDirs", func(ctx android.PackageVarContext) string {
- if override := ctx.Config().Getenv("DEFAULT_TIDY_HEADER_DIRS"); override != "" {
- return override
- }
- return strings.Join([]string{
- "art/",
- "bionic/",
- "bootable/",
- "build/",
- "cts/",
- "dalvik/",
- "developers/",
- "development/",
- "frameworks/",
- "libcore/",
- "libnativehelper/",
- "system/",
- }, "|")
+ return ctx.Config().Getenv("DEFAULT_TIDY_HEADER_DIRS")
})
// Use WTIH_TIDY_FLAGS to pass extra global default clang-tidy flags.
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index db9092d..fce28c1 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -32,12 +32,42 @@
toolchainFactories[os][arch] = factory
}
+type toolchainContext interface {
+ Os() android.OsType
+ Arch() android.Arch
+}
+
+type conversionContext interface {
+ BazelConversionMode() bool
+}
+
+func FindToolchainWithContext(ctx toolchainContext) Toolchain {
+ t, err := findToolchain(ctx.Os(), ctx.Arch())
+ if err != nil {
+ if c, ok := ctx.(conversionContext); ok && c.BazelConversionMode() {
+ // TODO(b/179123288): determine conversion for toolchain
+ return &toolchainX86_64{}
+ } else {
+ panic(err)
+ }
+ }
+ return t
+}
+
func FindToolchain(os android.OsType, arch android.Arch) Toolchain {
+ t, err := findToolchain(os, arch)
+ if err != nil {
+ panic(err)
+ }
+ return t
+}
+
+func findToolchain(os android.OsType, arch android.Arch) (Toolchain, error) {
factory := toolchainFactories[os][arch.ArchType]
if factory == nil {
- panic(fmt.Errorf("Toolchain not found for %s arch %q", os.String(), arch.String()))
+ return nil, fmt.Errorf("Toolchain not found for %s arch %q", os.String(), arch.String())
}
- return factory(arch)
+ return factory(arch), nil
}
type Toolchain interface {
@@ -215,10 +245,6 @@
return LibclangRuntimeLibrary(t, "tsan")
}
-func ProfileRuntimeLibrary(t Toolchain) string {
- return LibclangRuntimeLibrary(t, "profile")
-}
-
func ScudoRuntimeLibrary(t Toolchain) string {
return LibclangRuntimeLibrary(t, "scudo")
}
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index 4bcad4b..425e349 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -17,16 +17,50 @@
// List of VNDK libraries that have different core variant and vendor variant.
// For these libraries, the vendor variants must be installed even if the device
// has VndkUseCoreVariant set.
+// TODO(b/150578172): clean up unstable and non-versioned aidl module
var VndkMustUseVendorVariantList = []string{
+ "android.hardware.authsecret-unstable-ndk_platform",
+ "android.hardware.authsecret-ndk_platform",
+ "android.hardware.authsecret-V1-ndk_platform",
"android.hardware.automotive.occupant_awareness-ndk_platform",
+ "android.hardware.automotive.occupant_awareness-V1-ndk_platform",
+ "android.hardware.health.storage-V1-ndk_platform",
+ "android.hardware.health.storage-ndk_platform",
+ "android.hardware.health.storage-unstable-ndk_platform",
+ "android.hardware.light-V1-ndk_platform",
"android.hardware.light-ndk_platform",
+ "android.hardware.identity-V2-ndk_platform",
"android.hardware.identity-ndk_platform",
"android.hardware.nfc@1.2",
+ "android.hardware.memtrack-V1-ndk_platform",
+ "android.hardware.memtrack-ndk_platform",
"android.hardware.memtrack-unstable-ndk_platform",
+ "android.hardware.oemlock-V1-ndk_platform",
+ "android.hardware.oemlock-ndk_platform",
+ "android.hardware.oemlock-unstable-ndk_platform",
+ "android.hardware.power-V1-ndk_platform",
"android.hardware.power-ndk_platform",
+ "android.hardware.rebootescrow-V1-ndk_platform",
+ "android.hardware.power.stats-V1-ndk_platform",
+ "android.hardware.power.stats-ndk_platform",
+ "android.hardware.power.stats-unstable-ndk_platform",
"android.hardware.rebootescrow-ndk_platform",
+ "android.hardware.security.keymint-V1-ndk_platform",
+ "android.hardware.security.keymint-ndk_platform",
"android.hardware.security.keymint-unstable-ndk_platform",
+ "android.hardware.security.secureclock-V1-ndk_platform",
+ "android.hardware.security.secureclock-unstable-ndk_platform",
+ "android.hardware.security.secureclock-ndk_platform",
+ "android.hardware.security.sharedsecret-V1-ndk_platform",
+ "android.hardware.security.sharedsecret-ndk_platform",
+ "android.hardware.security.sharedsecret-unstable-ndk_platform",
+ "android.hardware.vibrator-V1-ndk_platform",
"android.hardware.vibrator-ndk_platform",
+ "android.hardware.weaver-V1-ndk_platform",
+ "android.hardware.weaver-ndk_platform",
+ "android.hardware.weaver-unstable-ndk_platform",
+ "android.system.keystore2-V1-ndk_platform",
+ "android.system.keystore2-ndk_platform",
"android.system.keystore2-unstable-ndk_platform",
"libbinder",
"libcrypto",
diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go
index d7ff580..b0344af 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/x86_darwin_host.go
@@ -67,6 +67,7 @@
"10.14",
"10.15",
"11.0",
+ "11.1",
}
darwinAvailableLibraries = append(
@@ -135,7 +136,7 @@
func getMacTools(ctx android.PackageVarContext) *macPlatformTools {
macTools.once.Do(func() {
- xcrunTool := ctx.Config().NonHermeticHostSystemTool("xcrun")
+ xcrunTool := "/usr/bin/xcrun"
xcrun := func(args ...string) string {
if macTools.err != nil {
diff --git a/cc/coverage.go b/cc/coverage.go
index acf98dd..5b5ccf2 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -58,6 +58,8 @@
func getClangProfileLibraryName(ctx ModuleContextIntf) string {
if ctx.useSdk() {
return "libprofile-clang-extras_ndk"
+ } else if ctx.isCfiAssemblySupportEnabled() {
+ return "libprofile-clang-extras_cfi_support"
} else {
return "libprofile-clang-extras"
}
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 6b17c48..5219ebc 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -20,6 +20,8 @@
"sort"
"strings"
+ "github.com/google/blueprint/proptools"
+
"android/soong/android"
"android/soong/cc/config"
)
@@ -41,6 +43,12 @@
// Specify who should be acknowledged for CVEs in the Android Security
// Bulletin.
Acknowledgement []string `json:"acknowledgement,omitempty"`
+ // Additional options to be passed to libfuzzer when run in Haiku.
+ Libfuzzer_options []string `json:"libfuzzer_options,omitempty"`
+ // Additional options to be passed to HWASAN when running on-device in Haiku.
+ Hwasan_options []string `json:"hwasan_options,omitempty"`
+ // Additional options to be passed to HWASAN when running on host in Haiku.
+ Asan_options []string `json:"asan_options,omitempty"`
}
func (f *FuzzConfig) String() string {
@@ -166,7 +174,7 @@
// This function takes a module and determines if it is a unique shared library
// that should be installed in the fuzz target output directories. This function
// returns true, unless:
-// - The module is not a shared library, or
+// - The module is not an installable shared library, or
// - The module is a header, stub, or vendor-linked library, or
// - The module is a prebuilt and its source is available, or
// - The module is a versioned member of an SDK snapshot.
@@ -203,6 +211,11 @@
if _, isLLndkStubLibrary := ccLibrary.linker.(*stubDecorator); isLLndkStubLibrary {
return false
}
+ // Discard installable:false libraries because they are expected to be absent
+ // in runtime.
+ if !proptools.BoolDefault(ccLibrary.Properties.Installable, true) {
+ return false
+ }
}
// If the same library is present both as source and a prebuilt we must pick
@@ -315,7 +328,7 @@
module, binary := NewBinary(hod)
binary.baseInstaller = NewFuzzInstaller()
- module.sanitize.SetSanitizer(fuzzer, true)
+ module.sanitize.SetSanitizer(Fuzzer, true)
fuzz := &fuzzBinary{
binaryDecorator: binary,
@@ -427,7 +440,8 @@
command := builder.Command().BuiltTool("soong_zip").
Flag("-j").
FlagWithOutput("-o ", corpusZip)
- command.FlagWithRspFileInputList("-r ", fuzzModule.corpus)
+ rspFile := corpusZip.ReplaceExtension(ctx, "rsp")
+ command.FlagWithRspFileInputList("-r ", rspFile, fuzzModule.corpus)
files = append(files, fileToZip{corpusZip, ""})
}
diff --git a/cc/gen.go b/cc/gen.go
index 75b9e49..b152e02 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -111,9 +111,7 @@
return ret
}
-func genAidl(ctx android.ModuleContext, rule *android.RuleBuilder, aidlFile android.Path,
- outFile, depFile android.ModuleGenPath, aidlFlags string) android.Paths {
-
+func genAidl(ctx android.ModuleContext, rule *android.RuleBuilder, aidlFile android.Path, aidlFlags string) (cppFile android.OutputPath, headerFiles android.Paths) {
aidlPackage := strings.TrimSuffix(aidlFile.Rel(), aidlFile.Base())
baseName := strings.TrimSuffix(aidlFile.Base(), aidlFile.Ext())
shortName := baseName
@@ -126,6 +124,8 @@
}
outDir := android.PathForModuleGen(ctx, "aidl")
+ cppFile = outDir.Join(ctx, aidlPackage, baseName+".cpp")
+ depFile := outDir.Join(ctx, aidlPackage, baseName+".cpp.d")
headerI := outDir.Join(ctx, aidlPackage, baseName+".h")
headerBn := outDir.Join(ctx, aidlPackage, "Bn"+shortName+".h")
headerBp := outDir.Join(ctx, aidlPackage, "Bp"+shortName+".h")
@@ -142,14 +142,14 @@
Flag(aidlFlags).
Input(aidlFile).
OutputDir().
- Output(outFile).
+ Output(cppFile).
ImplicitOutputs(android.WritablePaths{
headerI,
headerBn,
headerBp,
})
- return android.Paths{
+ return cppFile, android.Paths{
headerI,
headerBn,
headerBp,
@@ -220,8 +220,35 @@
return rcFile, headerFile
}
+// Used to communicate information from the genSources method back to the library code that uses
+// it.
+type generatedSourceInfo struct {
+ // The headers created from .proto files
+ protoHeaders android.Paths
+
+ // The files that can be used as order only dependencies in order to ensure that the proto header
+ // files are up to date.
+ protoOrderOnlyDeps android.Paths
+
+ // The headers created from .aidl files
+ aidlHeaders android.Paths
+
+ // The files that can be used as order only dependencies in order to ensure that the aidl header
+ // files are up to date.
+ aidlOrderOnlyDeps android.Paths
+
+ // The headers created from .sysprop files
+ syspropHeaders android.Paths
+
+ // The files that can be used as order only dependencies in order to ensure that the sysprop
+ // header files are up to date.
+ syspropOrderOnlyDeps android.Paths
+}
+
func genSources(ctx android.ModuleContext, srcFiles android.Paths,
- buildFlags builderFlags) (android.Paths, android.Paths) {
+ buildFlags builderFlags) (android.Paths, android.Paths, generatedSourceInfo) {
+
+ var info generatedSourceInfo
var deps android.Paths
var rsFiles android.Paths
@@ -258,16 +285,22 @@
case ".proto":
ccFile, headerFile := genProto(ctx, srcFile, buildFlags)
srcFiles[i] = ccFile
- deps = append(deps, headerFile)
+ info.protoHeaders = append(info.protoHeaders, headerFile)
+ // Use the generated header as an order only dep to ensure that it is up to date when needed.
+ info.protoOrderOnlyDeps = append(info.protoOrderOnlyDeps, headerFile)
case ".aidl":
if aidlRule == nil {
aidlRule = android.NewRuleBuilder(pctx, ctx).Sbox(android.PathForModuleGen(ctx, "aidl"),
android.PathForModuleGen(ctx, "aidl.sbox.textproto"))
}
- cppFile := android.GenPathWithExt(ctx, "aidl", srcFile, "cpp")
- depFile := android.GenPathWithExt(ctx, "aidl", srcFile, "cpp.d")
+ cppFile, aidlHeaders := genAidl(ctx, aidlRule, srcFile, buildFlags.aidlFlags)
srcFiles[i] = cppFile
- deps = append(deps, genAidl(ctx, aidlRule, srcFile, cppFile, depFile, buildFlags.aidlFlags)...)
+
+ info.aidlHeaders = append(info.aidlHeaders, aidlHeaders...)
+ // Use the generated headers as order only deps to ensure that they are up to date when
+ // needed.
+ // TODO: Reduce the size of the ninja file by using one order only dep for the whole rule
+ info.aidlOrderOnlyDeps = append(info.aidlOrderOnlyDeps, aidlHeaders...)
case ".rscript", ".fs":
cppFile := rsGeneratedCppFile(ctx, srcFile)
rsFiles = append(rsFiles, srcFiles[i])
@@ -279,7 +312,10 @@
case ".sysprop":
cppFile, headerFiles := genSysprop(ctx, srcFile)
srcFiles[i] = cppFile
- deps = append(deps, headerFiles...)
+ info.syspropHeaders = append(info.syspropHeaders, headerFiles...)
+ // Use the generated headers as order only deps to ensure that they are up to date when
+ // needed.
+ info.syspropOrderOnlyDeps = append(info.syspropOrderOnlyDeps, headerFiles...)
}
}
@@ -291,9 +327,13 @@
yaccRule_.Build("yacc", "gen yacc")
}
+ deps = append(deps, info.protoOrderOnlyDeps...)
+ deps = append(deps, info.aidlOrderOnlyDeps...)
+ deps = append(deps, info.syspropOrderOnlyDeps...)
+
if len(rsFiles) > 0 {
deps = append(deps, rsGenerateCpp(ctx, rsFiles, buildFlags.rsFlags)...)
}
- return srcFiles, deps
+ return srcFiles, deps, info
}
diff --git a/cc/gen_test.go b/cc/gen_test.go
index 41ef95c..40a5716 100644
--- a/cc/gen_test.go
+++ b/cc/gen_test.go
@@ -36,8 +36,10 @@
aidl := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("aidl")
libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
- if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.Local.CommonFlags) {
- t.Errorf("missing aidl includes in global flags")
+ expected := "-I" + filepath.Dir(aidl.Output.String())
+ actual := android.StringsRelativeToTop(ctx.Config(), libfoo.flags.Local.CommonFlags)
+ if !inList(expected, actual) {
+ t.Errorf("missing aidl includes in global flags, expected %q, actual %q", expected, actual)
}
})
@@ -61,7 +63,7 @@
aidlManifest := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Output("aidl.sbox.textproto")
libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
- if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.Local.CommonFlags) {
+ if !inList("-I"+filepath.Dir(aidl.Output.String()), android.StringsRelativeToTop(ctx.Config(), libfoo.flags.Local.CommonFlags)) {
t.Errorf("missing aidl includes in global flags")
}
diff --git a/cc/genrule.go b/cc/genrule.go
index 9648869..82d7205 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -25,6 +25,7 @@
type GenruleExtraProperties struct {
Vendor_available *bool
+ Odm_available *bool
Product_available *bool
Ramdisk_available *bool
Vendor_ramdisk_available *bool
@@ -63,7 +64,7 @@
return false
}
- return Bool(g.Vendor_available) || Bool(g.Product_available) || !(ctx.SocSpecific() || ctx.DeviceSpecific())
+ return !(ctx.SocSpecific() || ctx.DeviceSpecific())
}
func (g *GenruleExtraProperties) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
@@ -74,8 +75,20 @@
return Bool(g.Vendor_ramdisk_available)
}
+func (g *GenruleExtraProperties) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
func (g *GenruleExtraProperties) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
- return Bool(g.Recovery_available)
+ // If the build is using a snapshot, the recovery variant under AOSP directories
+ // is not needed.
+ recoverySnapshotVersion := ctx.DeviceConfig().RecoverySnapshotVersion()
+ if recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" &&
+ !isRecoveryProprietaryModule(ctx) {
+ return false
+ } else {
+ return Bool(g.Recovery_available)
+ }
}
func (g *GenruleExtraProperties) ExtraImageVariations(ctx android.BaseModuleContext) []string {
@@ -84,7 +97,7 @@
}
var variants []string
- if Bool(g.Vendor_available) || ctx.SocSpecific() || ctx.DeviceSpecific() {
+ if Bool(g.Vendor_available) || Bool(g.Odm_available) || ctx.SocSpecific() || ctx.DeviceSpecific() {
vndkVersion := ctx.DeviceConfig().VndkVersion()
// If vndkVersion is current, we can always use PlatformVndkVersion.
// If not, we assume modules under proprietary paths are compatible for
diff --git a/cc/genrule_test.go b/cc/genrule_test.go
index fa0c6f2..45b343b 100644
--- a/cc/genrule_test.go
+++ b/cc/genrule_test.go
@@ -52,7 +52,7 @@
},
}
`
- config := android.TestArchConfig(buildDir, nil, bp, fs)
+ config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
ctx := testGenruleContext(config)
diff --git a/cc/image.go b/cc/image.go
index 13d77cc..bf662c6 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -26,36 +26,18 @@
var _ android.ImageInterface = (*Module)(nil)
-type imageVariantType string
+type ImageVariantType string
const (
- coreImageVariant imageVariantType = "core"
- vendorImageVariant imageVariantType = "vendor"
- productImageVariant imageVariantType = "product"
- ramdiskImageVariant imageVariantType = "ramdisk"
- vendorRamdiskImageVariant imageVariantType = "vendor_ramdisk"
- recoveryImageVariant imageVariantType = "recovery"
- hostImageVariant imageVariantType = "host"
+ coreImageVariant ImageVariantType = "core"
+ vendorImageVariant ImageVariantType = "vendor"
+ productImageVariant ImageVariantType = "product"
+ ramdiskImageVariant ImageVariantType = "ramdisk"
+ vendorRamdiskImageVariant ImageVariantType = "vendor_ramdisk"
+ recoveryImageVariant ImageVariantType = "recovery"
+ hostImageVariant ImageVariantType = "host"
)
-func (c *Module) getImageVariantType() imageVariantType {
- if c.Host() {
- return hostImageVariant
- } else if c.inVendor() {
- return vendorImageVariant
- } else if c.InProduct() {
- return productImageVariant
- } else if c.InRamdisk() {
- return ramdiskImageVariant
- } else if c.InVendorRamdisk() {
- return vendorRamdiskImageVariant
- } else if c.InRecovery() {
- return recoveryImageVariant
- } else {
- return coreImageVariant
- }
-}
-
const (
// VendorVariationPrefix is the variant prefix used for /vendor code that compiles
// against the VNDK.
@@ -67,14 +49,15 @@
)
func (ctx *moduleContext) ProductSpecific() bool {
- //TODO(b/150902910): Replace HasNonSystemVariants() with HasProductVariant()
- return ctx.ModuleContext.ProductSpecific() ||
- (ctx.mod.HasNonSystemVariants() && ctx.mod.InProduct())
+ return ctx.ModuleContext.ProductSpecific() || ctx.mod.productSpecificModuleContext()
}
func (ctx *moduleContext) SocSpecific() bool {
- return ctx.ModuleContext.SocSpecific() ||
- (ctx.mod.HasVendorVariant() && ctx.mod.inVendor())
+ return ctx.ModuleContext.SocSpecific() || ctx.mod.socSpecificModuleContext()
+}
+
+func (ctx *moduleContext) DeviceSpecific() bool {
+ return ctx.ModuleContext.DeviceSpecific() || ctx.mod.deviceSpecificModuleContext()
}
func (ctx *moduleContextImpl) inProduct() bool {
@@ -82,7 +65,7 @@
}
func (ctx *moduleContextImpl) inVendor() bool {
- return ctx.mod.inVendor()
+ return ctx.mod.InVendor()
}
func (ctx *moduleContextImpl) inRamdisk() bool {
@@ -97,20 +80,38 @@
return ctx.mod.InRecovery()
}
+func (c *Module) productSpecificModuleContext() bool {
+ // Additionally check if this module is inProduct() that means it is a "product" variant of a
+ // module. As well as product specific modules, product variants must be installed to /product.
+ return c.InProduct()
+}
+
+func (c *Module) socSpecificModuleContext() bool {
+ // Additionally check if this module is inVendor() that means it is a "vendor" variant of a
+ // module. As well as SoC specific modules, vendor variants must be installed to /vendor
+ // unless they have "odm_available: true".
+ return c.HasVendorVariant() && c.InVendor() && !c.VendorVariantToOdm()
+}
+
+func (c *Module) deviceSpecificModuleContext() bool {
+ // Some vendor variants want to be installed to /odm by setting "odm_available: true".
+ return c.InVendor() && c.VendorVariantToOdm()
+}
+
// Returns true when this module is configured to have core and vendor variants.
func (c *Module) HasVendorVariant() bool {
- // In case of a VNDK, 'vendor_available: false' still creates a vendor variant.
- return c.IsVndk() || Bool(c.VendorProperties.Vendor_available)
+ return Bool(c.VendorProperties.Vendor_available) || Bool(c.VendorProperties.Odm_available)
+}
+
+// Returns true when this module creates a vendor variant and wants to install the vendor variant
+// to the odm partition.
+func (c *Module) VendorVariantToOdm() bool {
+ return Bool(c.VendorProperties.Odm_available)
}
// Returns true when this module is configured to have core and product variants.
func (c *Module) HasProductVariant() bool {
- if c.VendorProperties.Product_available == nil {
- // Without 'product_available', product variant will not be created even for VNDKs.
- return false
- }
- // However, 'product_available: false' in a VNDK still creates a product variant.
- return c.IsVndk() || Bool(c.VendorProperties.Product_available)
+ return Bool(c.VendorProperties.Product_available)
}
// Returns true when this module is configured to have core and either product or vendor variants.
@@ -124,7 +125,7 @@
}
// Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
-func (c *Module) inVendor() bool {
+func (c *Module) InVendor() bool {
return c.Properties.ImageVariationPrefix == VendorVariationPrefix
}
@@ -187,7 +188,7 @@
// This function is used only for the VNDK modules that is available to both vendor
// and product partitions.
func (c *Module) compareVendorAndProductProps() bool {
- if !c.IsVndk() && c.VendorProperties.Product_available != nil {
+ if !c.IsVndk() && !Bool(c.VendorProperties.Product_available) {
panic(fmt.Errorf("This is only for product available VNDK libs. %q is not a VNDK library or not product available", c.Name()))
}
for _, properties := range c.GetProperties() {
@@ -198,39 +199,83 @@
return true
}
+// ImageMutatableModule provides a common image mutation interface for LinkableInterface modules.
+type ImageMutatableModule interface {
+ android.Module
+ LinkableInterface
+
+ // AndroidModuleBase returns the android.ModuleBase for this module
+ AndroidModuleBase() *android.ModuleBase
+
+ // VendorAvailable returns true if this module is available on the vendor image.
+ VendorAvailable() bool
+
+ // OdmAvailable returns true if this module is available on the odm image.
+ OdmAvailable() bool
+
+ // ProductAvailable returns true if this module is available on the product image.
+ ProductAvailable() bool
+
+ // RamdiskAvailable returns true if this module is available on the ramdisk image.
+ RamdiskAvailable() bool
+
+ // RecoveryAvailable returns true if this module is available on the recovery image.
+ RecoveryAvailable() bool
+
+ // VendorRamdiskAvailable returns true if this module is available on the vendor ramdisk image.
+ VendorRamdiskAvailable() bool
+
+ // IsSnapshotPrebuilt returns true if this module is a snapshot prebuilt.
+ IsSnapshotPrebuilt() bool
+
+ // SnapshotVersion returns the snapshot version for this module.
+ SnapshotVersion(mctx android.BaseModuleContext) string
+
+ // SdkVersion returns the SDK version for this module.
+ SdkVersion() string
+
+ // ExtraVariants returns the list of extra variants this module requires.
+ ExtraVariants() []string
+
+ // AppendExtraVariant returns an extra variant to the list of extra variants this module requires.
+ AppendExtraVariant(extraVariant string)
+
+ // SetRamdiskVariantNeeded sets whether the Ramdisk Variant is needed.
+ SetRamdiskVariantNeeded(b bool)
+
+ // SetVendorRamdiskVariantNeeded sets whether the Vendor Ramdisk Variant is needed.
+ SetVendorRamdiskVariantNeeded(b bool)
+
+ // SetRecoveryVariantNeeded sets whether the Recovery Variant is needed.
+ SetRecoveryVariantNeeded(b bool)
+
+ // SetCoreVariantNeeded sets whether the Core Variant is needed.
+ SetCoreVariantNeeded(b bool)
+}
+
+var _ ImageMutatableModule = (*Module)(nil)
+
func (m *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
- // Validation check
+ m.CheckVndkProperties(mctx)
+ MutateImage(mctx, m)
+}
+
+// CheckVndkProperties checks whether the VNDK-related properties are set correctly.
+// If properties are not set correctly, results in a module context property error.
+func (m *Module) CheckVndkProperties(mctx android.BaseModuleContext) {
vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
productSpecific := mctx.ProductSpecific()
- if m.VendorProperties.Vendor_available != nil {
- if vendorSpecific {
- mctx.PropertyErrorf("vendor_available",
- "doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
- }
- }
-
- if m.VendorProperties.Product_available != nil {
- if productSpecific {
- mctx.PropertyErrorf("product_available",
- "doesn't make sense at the same time as `product_specific: true`")
- }
- if vendorSpecific {
- mctx.PropertyErrorf("product_available",
- "cannot provide product variant from a vendor module. Please use `product_specific: true` with `vendor_available: true`")
- }
- }
-
if vndkdep := m.vndkdep; vndkdep != nil {
if vndkdep.isVndk() {
if vendorSpecific || productSpecific {
if !vndkdep.isVndkExt() {
mctx.PropertyErrorf("vndk",
"must set `extends: \"...\"` to vndk extension")
- } else if m.VendorProperties.Vendor_available != nil {
+ } else if Bool(m.VendorProperties.Vendor_available) {
mctx.PropertyErrorf("vendor_available",
"must not set at the same time as `vndk: {extends: \"...\"}`")
- } else if m.VendorProperties.Product_available != nil {
+ } else if Bool(m.VendorProperties.Product_available) {
mctx.PropertyErrorf("product_available",
"must not set at the same time as `vndk: {extends: \"...\"}`")
}
@@ -240,11 +285,11 @@
"must set `vendor: true` or `product_specific: true` to set `extends: %q`",
m.getVndkExtendsModuleName())
}
- if m.VendorProperties.Vendor_available == nil {
+ if !Bool(m.VendorProperties.Vendor_available) {
mctx.PropertyErrorf("vndk",
- "vendor_available must be set to either true or false when `vndk: {enabled: true}`")
+ "vendor_available must be set to true when `vndk: {enabled: true}`")
}
- if m.VendorProperties.Product_available != nil {
+ if Bool(m.VendorProperties.Product_available) {
// If a VNDK module creates both product and vendor variants, they
// must have the same properties since they share a single VNDK
// library on runtime.
@@ -265,6 +310,111 @@
}
}
}
+}
+
+func (m *Module) VendorAvailable() bool {
+ return Bool(m.VendorProperties.Vendor_available)
+}
+
+func (m *Module) OdmAvailable() bool {
+ return Bool(m.VendorProperties.Odm_available)
+}
+
+func (m *Module) ProductAvailable() bool {
+ return Bool(m.VendorProperties.Product_available)
+}
+
+func (m *Module) RamdiskAvailable() bool {
+ return Bool(m.Properties.Ramdisk_available)
+}
+
+func (m *Module) VendorRamdiskAvailable() bool {
+ return Bool(m.Properties.Vendor_ramdisk_available)
+}
+
+func (m *Module) AndroidModuleBase() *android.ModuleBase {
+ return &m.ModuleBase
+}
+
+func (m *Module) RecoveryAvailable() bool {
+ return Bool(m.Properties.Recovery_available)
+}
+
+func (m *Module) ExtraVariants() []string {
+ return m.Properties.ExtraVariants
+}
+
+func (m *Module) AppendExtraVariant(extraVariant string) {
+ m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, extraVariant)
+}
+
+func (m *Module) SetRamdiskVariantNeeded(b bool) {
+ m.Properties.RamdiskVariantNeeded = b
+}
+
+func (m *Module) SetVendorRamdiskVariantNeeded(b bool) {
+ m.Properties.VendorRamdiskVariantNeeded = b
+}
+
+func (m *Module) SetRecoveryVariantNeeded(b bool) {
+ m.Properties.RecoveryVariantNeeded = b
+}
+
+func (m *Module) SetCoreVariantNeeded(b bool) {
+ m.Properties.CoreVariantNeeded = b
+}
+
+func (m *Module) SnapshotVersion(mctx android.BaseModuleContext) string {
+ if snapshot, ok := m.linker.(snapshotInterface); ok {
+ return snapshot.version()
+ } else {
+ mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
+ // Should we be panicking here instead?
+ return ""
+ }
+}
+
+func (m *Module) KernelHeadersDecorator() bool {
+ if _, ok := m.linker.(*kernelHeadersDecorator); ok {
+ return true
+ }
+ return false
+}
+
+// MutateImage handles common image mutations for ImageMutatableModule interfaces.
+func MutateImage(mctx android.BaseModuleContext, m ImageMutatableModule) {
+ // Validation check
+ vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
+ productSpecific := mctx.ProductSpecific()
+
+ if m.VendorAvailable() {
+ if vendorSpecific {
+ mctx.PropertyErrorf("vendor_available",
+ "doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific: true`")
+ }
+ if m.OdmAvailable() {
+ mctx.PropertyErrorf("vendor_available",
+ "doesn't make sense at the same time as `odm_available: true`")
+ }
+ }
+
+ if m.OdmAvailable() {
+ if vendorSpecific {
+ mctx.PropertyErrorf("odm_available",
+ "doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific: true`")
+ }
+ }
+
+ if m.ProductAvailable() {
+ if productSpecific {
+ mctx.PropertyErrorf("product_available",
+ "doesn't make sense at the same time as `product_specific: true`")
+ }
+ if vendorSpecific {
+ mctx.PropertyErrorf("product_available",
+ "cannot provide product variant from a vendor module. Please use `product_specific: true` with `vendor_available: true`")
+ }
+ }
var coreVariantNeeded bool = false
var ramdiskVariantNeeded bool = false
@@ -277,6 +427,9 @@
platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
boardVndkVersion := mctx.DeviceConfig().VndkVersion()
productVndkVersion := mctx.DeviceConfig().ProductVndkVersion()
+ recoverySnapshotVersion := mctx.DeviceConfig().RecoverySnapshotVersion()
+ usingRecoverySnapshot := recoverySnapshotVersion != "current" &&
+ recoverySnapshotVersion != ""
if boardVndkVersion == "current" {
boardVndkVersion = platformVndkVersion
}
@@ -284,40 +437,36 @@
productVndkVersion = platformVndkVersion
}
- if boardVndkVersion == "" {
+ if m.IsLlndkLibrary() || m.IsLlndkHeaders() || m.HasLlndkStubs() {
+ // This is an LLNDK library. The implementation of the library will be on /system,
+ // and vendor and product variants will be created with LLNDK stubs.
+ // The LLNDK libraries need vendor variants even if there is no VNDK.
+ // The obsolete llndk_library and llndk_headers modules also need the vendor variants
+ // so the cc_library LLNDK stubs can depend on them.
+ if m.HasLlndkStubs() {
+ coreVariantNeeded = true
+ }
+ if platformVndkVersion != "" {
+ vendorVariants = append(vendorVariants, platformVndkVersion)
+ productVariants = append(productVariants, platformVndkVersion)
+ }
+ if boardVndkVersion != "" {
+ vendorVariants = append(vendorVariants, boardVndkVersion)
+ }
+ if productVndkVersion != "" {
+ productVariants = append(productVariants, productVndkVersion)
+ }
+ } else if boardVndkVersion == "" {
// If the device isn't compiling against the VNDK, we always
// use the core mode.
coreVariantNeeded = true
- } else if _, ok := m.linker.(*llndkStubDecorator); ok {
- // LL-NDK stubs only exist in the vendor and product variants,
- // since the real libraries will be used in the core variant.
- vendorVariants = append(vendorVariants,
- platformVndkVersion,
- boardVndkVersion,
- )
- productVariants = append(productVariants,
- platformVndkVersion,
- productVndkVersion,
- )
- } else if _, ok := m.linker.(*llndkHeadersDecorator); ok {
- // ... and LL-NDK headers as well
- vendorVariants = append(vendorVariants,
- platformVndkVersion,
- boardVndkVersion,
- )
- productVariants = append(productVariants,
- platformVndkVersion,
- productVndkVersion,
- )
- } else if m.isSnapshotPrebuilt() {
+ } else if m.IsSnapshotPrebuilt() {
// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
// PRODUCT_EXTRA_VNDK_VERSIONS.
- if snapshot, ok := m.linker.(interface {
- version() string
- }); ok {
- vendorVariants = append(vendorVariants, snapshot.version())
+ if m.InstallInRecovery() {
+ recoveryVariantNeeded = true
} else {
- mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
+ vendorVariants = append(vendorVariants, m.SnapshotVersion(mctx))
}
} else if m.HasNonSystemVariants() && !m.IsVndkExt() {
// This will be available to /system unless it is product_specific
@@ -343,7 +492,7 @@
productVariants = append(productVariants, productVndkVersion)
}
}
- } else if vendorSpecific && String(m.Properties.Sdk_version) == "" {
+ } else if vendorSpecific && m.SdkVersion() == "" {
// This will be available in /vendor (or /odm) only
// kernel_headers is a special module type whose exported headers
@@ -352,7 +501,7 @@
// For other modules, we assume that modules under proprietary
// paths are compatible for BOARD_VNDK_VERSION. The other modules
// are regarded as AOSP, which is PLATFORM_VNDK_VERSION.
- if _, ok := m.linker.(*kernelHeadersDecorator); ok {
+ if m.KernelHeadersDecorator() {
vendorVariants = append(vendorVariants,
platformVndkVersion,
boardVndkVersion,
@@ -362,18 +511,6 @@
} else {
vendorVariants = append(vendorVariants, platformVndkVersion)
}
- } else if lib := moduleLibraryInterface(m); lib != nil && lib.hasLLNDKStubs() {
- // This is an LLNDK library. The implementation of the library will be on /system,
- // and vendor and product variants will be created with LLNDK stubs.
- coreVariantNeeded = true
- vendorVariants = append(vendorVariants,
- platformVndkVersion,
- boardVndkVersion,
- )
- productVariants = append(productVariants,
- platformVndkVersion,
- productVndkVersion,
- )
} else {
// This is either in /system (or similar: /data), or is a
// modules built with the NDK. Modules built with the NDK
@@ -382,7 +519,7 @@
}
if boardVndkVersion != "" && productVndkVersion != "" {
- if coreVariantNeeded && productSpecific && String(m.Properties.Sdk_version) == "" {
+ if coreVariantNeeded && productSpecific && m.SdkVersion() == "" {
// The module has "product_specific: true" that does not create core variant.
coreVariantNeeded = false
productVariants = append(productVariants, productVndkVersion)
@@ -394,45 +531,62 @@
productVariants = []string{}
}
- if Bool(m.Properties.Ramdisk_available) {
+ if m.RamdiskAvailable() {
ramdiskVariantNeeded = true
}
- if m.ModuleBase.InstallInRamdisk() {
+ if m.AndroidModuleBase().InstallInRamdisk() {
ramdiskVariantNeeded = true
coreVariantNeeded = false
}
- if Bool(m.Properties.Vendor_ramdisk_available) {
+ if m.VendorRamdiskAvailable() {
vendorRamdiskVariantNeeded = true
}
- if m.ModuleBase.InstallInVendorRamdisk() {
+ if m.AndroidModuleBase().InstallInVendorRamdisk() {
vendorRamdiskVariantNeeded = true
coreVariantNeeded = false
}
- if Bool(m.Properties.Recovery_available) {
+ if m.RecoveryAvailable() {
recoveryVariantNeeded = true
}
- if m.ModuleBase.InstallInRecovery() {
+ if m.AndroidModuleBase().InstallInRecovery() {
recoveryVariantNeeded = true
coreVariantNeeded = false
}
+ // If using a snapshot, the recovery variant under AOSP directories is not needed,
+ // except for kernel headers, which needs all variants.
+ if m.KernelHeadersDecorator() &&
+ !m.IsSnapshotPrebuilt() &&
+ usingRecoverySnapshot &&
+ !isRecoveryProprietaryModule(mctx) {
+ recoveryVariantNeeded = false
+ }
+
for _, variant := range android.FirstUniqueStrings(vendorVariants) {
- m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, VendorVariationPrefix+variant)
+ m.AppendExtraVariant(VendorVariationPrefix + variant)
}
for _, variant := range android.FirstUniqueStrings(productVariants) {
- m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, ProductVariationPrefix+variant)
+ m.AppendExtraVariant(ProductVariationPrefix + variant)
}
- m.Properties.RamdiskVariantNeeded = ramdiskVariantNeeded
- m.Properties.VendorRamdiskVariantNeeded = vendorRamdiskVariantNeeded
- m.Properties.RecoveryVariantNeeded = recoveryVariantNeeded
- m.Properties.CoreVariantNeeded = coreVariantNeeded
+ m.SetRamdiskVariantNeeded(ramdiskVariantNeeded)
+ m.SetVendorRamdiskVariantNeeded(vendorRamdiskVariantNeeded)
+ m.SetRecoveryVariantNeeded(recoveryVariantNeeded)
+ m.SetCoreVariantNeeded(coreVariantNeeded)
+
+ // Disable the module if no variants are needed.
+ if !ramdiskVariantNeeded &&
+ !recoveryVariantNeeded &&
+ !coreVariantNeeded &&
+ len(m.ExtraVariants()) == 0 {
+ m.Disable()
+ }
}
func (c *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
@@ -447,6 +601,10 @@
return c.Properties.VendorRamdiskVariantNeeded
}
+func (c *Module) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
func (c *Module) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
return c.Properties.RecoveryVariantNeeded
}
diff --git a/cc/libbuildversion/Android.bp b/cc/libbuildversion/Android.bp
index b63338d..4debb1c 100644
--- a/cc/libbuildversion/Android.bp
+++ b/cc/libbuildversion/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_library_static {
name: "libbuildversion",
host_supported: true,
diff --git a/cc/libbuildversion/tests/Android.bp b/cc/libbuildversion/tests/Android.bp
index b3b2061..0e97fed 100644
--- a/cc/libbuildversion/tests/Android.bp
+++ b/cc/libbuildversion/tests/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_defaults {
name: "build_version_test_defaults",
use_version_lib: true,
diff --git a/cc/library.go b/cc/library.go
index 7dc925b..42115cc 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -19,6 +19,7 @@
"io"
"path/filepath"
"regexp"
+ "strconv"
"strings"
"sync"
@@ -26,6 +27,7 @@
"github.com/google/blueprint/pathtools"
"android/soong/android"
+ "android/soong/bazel"
"android/soong/cc/config"
)
@@ -64,7 +66,8 @@
// symbols that are exported for stubs variant of this library.
Symbol_file *string `android:"path"`
- // List versions to generate stubs libs for.
+ // List versions to generate stubs libs for. The version name "current" is always
+ // implicitly added.
Versions []string
}
@@ -170,6 +173,8 @@
// This variant is a stubs lib
BuildStubs bool `blueprint:"mutated"`
+ // This variant is the latest version
+ IsLatestVersion bool `blueprint:"mutated"`
// Version of the stubs lib
StubsVersion string `blueprint:"mutated"`
// List of all stubs versions associated with an implementation lib
@@ -200,6 +205,9 @@
func init() {
RegisterLibraryBuildComponents(android.InitRegistrationContext)
+
+ android.RegisterBp2BuildMutator("cc_library_static", CcLibraryStaticBp2Build)
+ android.RegisterBp2BuildMutator("cc_library", CcLibraryBp2Build)
}
func RegisterLibraryBuildComponents(ctx android.RegistrationContext) {
@@ -210,6 +218,67 @@
ctx.RegisterModuleType("cc_library_host_shared", LibraryHostSharedFactory)
}
+// For bp2build conversion.
+type bazelCcLibraryAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Hdrs bazel.LabelListAttribute
+ Copts bazel.StringListAttribute
+ Linkopts bazel.StringListAttribute
+ Deps bazel.LabelListAttribute
+ User_link_flags bazel.StringListAttribute
+ Includes bazel.StringListAttribute
+}
+
+type bazelCcLibrary struct {
+ android.BazelTargetModuleBase
+ bazelCcLibraryAttributes
+}
+
+func (m *bazelCcLibrary) Name() string {
+ return m.BaseModuleName()
+}
+
+func (m *bazelCcLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+
+func BazelCcLibraryFactory() android.Module {
+ module := &bazelCcLibrary{}
+ module.AddProperties(&module.bazelCcLibraryAttributes)
+ android.InitBazelTargetModule(module)
+ return module
+}
+
+func CcLibraryBp2Build(ctx android.TopDownMutatorContext) {
+ m, ok := ctx.Module().(*Module)
+ if !ok || !m.ConvertWithBp2build(ctx) {
+ return
+ }
+
+ if ctx.ModuleType() != "cc_library" {
+ return
+ }
+
+ compilerAttrs := bp2BuildParseCompilerProps(ctx, m)
+ linkerAttrs := bp2BuildParseLinkerProps(ctx, m)
+ exportedIncludes, exportedIncludesHeaders := bp2BuildParseExportedIncludes(ctx, m)
+ compilerAttrs.hdrs.Append(exportedIncludesHeaders)
+
+ attrs := &bazelCcLibraryAttributes{
+ Srcs: compilerAttrs.srcs,
+ Hdrs: compilerAttrs.hdrs,
+ Copts: compilerAttrs.copts,
+ Linkopts: linkerAttrs.linkopts,
+ Deps: linkerAttrs.deps,
+ Includes: exportedIncludes,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_library",
+ Bzl_load_location: "//build/bazel/rules:full_cc_library.bzl",
+ }
+
+ ctx.CreateBazelTargetModule(BazelCcLibraryFactory, m.Name(), props, attrs)
+}
+
// cc_library creates both static and/or shared libraries for a device and/or
// host. By default, a cc_library has a single variant that targets the device.
// Specifying `host_supported: true` also creates a library that targets the
@@ -230,6 +299,7 @@
module, library := NewLibrary(android.HostAndDeviceSupported)
library.BuildOnlyStatic()
module.sdkMemberTypes = []android.SdkMemberType{staticLibrarySdkMemberType}
+ module.bazelHandler = &staticLibraryBazelHandler{module: module}
return module.Init()
}
@@ -406,6 +476,67 @@
collectedSnapshotHeaders android.Paths
}
+type staticLibraryBazelHandler struct {
+ bazelHandler
+
+ module *Module
+}
+
+func (handler *staticLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+ bazelCtx := ctx.Config().BazelContext
+ ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+ if err != nil {
+ ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
+ return false
+ }
+ if !ok {
+ return ok
+ }
+ outputPaths := ccInfo.OutputFiles
+ objPaths := ccInfo.CcObjectFiles
+ if len(outputPaths) > 1 {
+ // TODO(cparsons): This is actually expected behavior for static libraries with no srcs.
+ // We should support this.
+ ctx.ModuleErrorf("expected at most one output file for '%s', but got %s", label, objPaths)
+ return false
+ } else if len(outputPaths) == 0 {
+ handler.module.outputFile = android.OptionalPath{}
+ return true
+ }
+ outputFilePath := android.PathForBazelOut(ctx, outputPaths[0])
+ handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
+
+ objFiles := make(android.Paths, len(objPaths))
+ for i, objPath := range objPaths {
+ objFiles[i] = android.PathForBazelOut(ctx, objPath)
+ }
+ objects := Objects{
+ objFiles: objFiles,
+ }
+
+ ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
+ StaticLibrary: outputFilePath,
+ ReuseObjects: objects,
+ Objects: objects,
+
+ // TODO(cparsons): Include transitive static libraries in this provider to support
+ // static libraries with deps.
+ TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL).
+ Direct(outputFilePath).
+ Build(),
+ })
+ if i, ok := handler.module.linker.(snapshotLibraryInterface); ok {
+ // Dependencies on this library will expect collectedSnapshotHeaders to
+ // be set, otherwise validation will fail. For now, set this to an empty
+ // list.
+ // TODO(cparsons): More closely mirror the collectHeadersForSnapshot
+ // implementation.
+ i.(*libraryDecorator).collectedSnapshotHeaders = android.Paths{}
+ }
+
+ return ok
+}
+
// collectHeadersForSnapshot collects all exported headers from library.
// It globs header files in the source tree for exported include directories,
// and tracks generated header files separately.
@@ -450,23 +581,33 @@
}
continue
}
- exts := headerExts
- // Glob all files under this special directory, because of C++ headers.
- if strings.HasPrefix(dir, "external/libcxx/include") {
- exts = []string{""}
+ glob, err := ctx.GlobWithDeps(dir+"/**/*", nil)
+ if err != nil {
+ ctx.ModuleErrorf("glob failed: %#v", err)
+ return
}
- for _, ext := range exts {
- glob, err := ctx.GlobWithDeps(dir+"/**/*"+ext, nil)
- if err != nil {
- ctx.ModuleErrorf("glob failed: %#v", err)
- return
- }
- for _, header := range glob {
+ isLibcxx := strings.HasPrefix(dir, "external/libcxx/include")
+ for _, header := range glob {
+ if isLibcxx {
+ // Glob all files under this special directory, because of C++ headers with no
+ // extension.
if strings.HasSuffix(header, "/") {
continue
}
- ret = append(ret, android.PathForSource(ctx, header))
+ } else {
+ // Filter out only the files with extensions that are headers.
+ found := false
+ for _, ext := range headerExts {
+ if strings.HasSuffix(header, ext) {
+ found = true
+ break
+ }
+ }
+ if !found {
+ continue
+ }
}
+ ret = append(ret, android.PathForSource(ctx, header))
}
}
@@ -633,6 +774,11 @@
return objs
}
if library.buildStubs() {
+ symbolFile := String(library.Properties.Stubs.Symbol_file)
+ if symbolFile != "" && !strings.HasSuffix(symbolFile, ".map.txt") {
+ ctx.PropertyErrorf("symbol_file", "%q doesn't have .map.txt suffix", symbolFile)
+ return Objects{}
+ }
objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Stubs.Symbol_file), library.MutatedProperties.StubsVersion, "--apex")
library.versionScriptPath = android.OptionalPathForPath(versionScript)
return objs
@@ -712,7 +858,7 @@
type versionedInterface interface {
buildStubs() bool
- setBuildStubs()
+ setBuildStubs(isLatest bool)
hasStubsVariants() bool
setStubsVersion(string)
stubsVersion() string
@@ -1293,9 +1439,8 @@
dir := android.PathForModuleGen(ctx, "aidl")
library.reexportDirs(dir)
- // TODO: restrict to aidl deps
- library.reexportDeps(library.baseCompiler.pathDeps...)
- library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...)
+ library.reexportDeps(library.baseCompiler.aidlOrderOnlyDeps...)
+ library.addExportedGeneratedHeaders(library.baseCompiler.aidlHeaders...)
}
}
@@ -1309,9 +1454,8 @@
includes = append(includes, flags.proto.Dir)
library.reexportDirs(includes...)
- // TODO: restrict to proto deps
- library.reexportDeps(library.baseCompiler.pathDeps...)
- library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...)
+ library.reexportDeps(library.baseCompiler.protoOrderOnlyDeps...)
+ library.addExportedGeneratedHeaders(library.baseCompiler.protoHeaders...)
}
}
@@ -1319,25 +1463,27 @@
if library.baseCompiler.hasSrcExt(".sysprop") {
dir := android.PathForModuleGen(ctx, "sysprop", "include")
if library.Properties.Sysprop.Platform != nil {
- isClientProduct := ctx.ProductSpecific() && !ctx.useVndk()
- isClientVendor := ctx.useVndk()
isOwnerPlatform := Bool(library.Properties.Sysprop.Platform)
// If the owner is different from the user, expose public header. That is,
// 1) if the user is product (as owner can only be platform / vendor)
- // 2) if one is platform and the other is vendor
- // Exceptions are ramdisk and recovery. They are not enforced at all. So
- // they always use internal header.
- if !ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() &&
- (isClientProduct || (isOwnerPlatform == isClientVendor)) {
+ // 2) if the owner is platform and the client is vendor
+ // We don't care Platform -> Vendor dependency as it's already forbidden.
+ if ctx.Device() && (ctx.ProductSpecific() || (isOwnerPlatform && ctx.inVendor())) {
dir = android.PathForModuleGen(ctx, "sysprop/public", "include")
}
}
+ // Make sure to only export headers which are within the include directory.
+ _, headers := android.FilterPathListPredicate(library.baseCompiler.syspropHeaders, func(path android.Path) bool {
+ _, isRel := android.MaybeRel(ctx, dir.String(), path.String())
+ return isRel
+ })
+
// Add sysprop-related directories to the exported directories of this library.
library.reexportDirs(dir)
- library.reexportDeps(library.baseCompiler.pathDeps...)
- library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...)
+ library.reexportDeps(library.baseCompiler.syspropOrderOnlyDeps...)
+ library.addExportedGeneratedHeaders(headers...)
}
// Add stub-related flags if this library is a stub library.
@@ -1352,8 +1498,11 @@
func (library *libraryDecorator) exportVersioningMacroIfNeeded(ctx android.BaseModuleContext) {
if library.buildStubs() && library.stubsVersion() != "" && !library.skipAPIDefine {
name := versioningMacroName(ctx.Module().(*Module).ImplementationModuleName(ctx))
- ver := library.stubsVersion()
- library.reexportFlags("-D" + name + "=" + ver)
+ apiLevel, err := android.ApiLevelFromUser(ctx, library.stubsVersion())
+ if err != nil {
+ ctx.ModuleErrorf("Can't export version macro: %s", err.Error())
+ }
+ library.reexportFlags("-D" + name + "=" + strconv.Itoa(apiLevel.FinalOrPreviewInt()))
}
}
@@ -1427,7 +1576,7 @@
if ctx.isVndk() && !ctx.IsVndkExt() {
return
}
- } else if len(library.Properties.Stubs.Versions) > 0 && !ctx.Host() && ctx.directlyInAnyApex() {
+ } else if library.hasStubsVariants() && !ctx.Host() && ctx.directlyInAnyApex() {
// 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.
@@ -1543,11 +1692,29 @@
}
func (library *libraryDecorator) hasStubsVariants() bool {
- return len(library.Properties.Stubs.Versions) > 0
+ // Just having stubs.symbol_file is enough to create a stub variant. In that case
+ // the stub for the future API level is created.
+ return library.Properties.Stubs.Symbol_file != nil ||
+ len(library.Properties.Stubs.Versions) > 0
}
func (library *libraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
- return library.Properties.Stubs.Versions
+ if !library.hasStubsVariants() {
+ return nil
+ }
+
+ // Future API level is implicitly added if there isn't
+ vers := library.Properties.Stubs.Versions
+ if inList(android.FutureApiLevel.String(), vers) {
+ return vers
+ }
+ // In some cases, people use the raw value "10000" in the versions property.
+ // We shouldn't add the future API level in that case, otherwise there will
+ // be two identical versions.
+ if inList(strconv.Itoa(android.FutureApiLevel.FinalOrFutureInt()), vers) {
+ return vers
+ }
+ return append(vers, android.FutureApiLevel.String())
}
func (library *libraryDecorator) setStubsVersion(version string) {
@@ -1558,8 +1725,9 @@
return library.MutatedProperties.StubsVersion
}
-func (library *libraryDecorator) setBuildStubs() {
+func (library *libraryDecorator) setBuildStubs(isLatest bool) {
library.MutatedProperties.BuildStubs = true
+ library.MutatedProperties.IsLatestVersion = isLatest
}
func (library *libraryDecorator) setAllStubsVersions(versions []string) {
@@ -1571,8 +1739,7 @@
}
func (library *libraryDecorator) isLatestStubVersion() bool {
- versions := library.Properties.Stubs.Versions
- return versions[len(versions)-1] == library.stubsVersion()
+ return library.MutatedProperties.IsLatestVersion
}
func (library *libraryDecorator) availableFor(what string) bool {
@@ -1739,12 +1906,9 @@
isLLNDK := false
if m, ok := mctx.Module().(*Module); ok {
- isLLNDK = m.IsLlndk()
// Don't count the vestigial llndk_library module as isLLNDK, it needs a static
// variant so that a cc_library_prebuilt can depend on it.
- if _, ok := m.linker.(*llndkStubDecorator); ok {
- isLLNDK = false
- }
+ isLLNDK = m.IsLlndk() && !isVestigialLLNDKModule(m)
}
buildStatic := library.BuildStaticVariant() && !isLLNDK
buildShared := library.BuildSharedVariant()
@@ -1818,7 +1982,8 @@
c.stl = nil
c.Properties.PreventInstall = true
lib := moduleLibraryInterface(m)
- lib.setBuildStubs()
+ isLatest := i == (len(versions) - 1)
+ lib.setBuildStubs(isLatest)
if variants[i] != "" {
// A non-LLNDK stubs module is hidden from make and has a dependency from the
@@ -1849,6 +2014,7 @@
for i, module := range modules {
module.(*Module).Properties.Sdk_version = StringPtr(versionStrs[i])
+ module.(*Module).Properties.Min_sdk_version = StringPtr(versionStrs[i])
}
}
@@ -1856,9 +2022,8 @@
Host() bool
InRamdisk() bool
InVendorRamdisk() bool
- InRecovery() bool
}) bool {
- return !module.Host() && !module.InRamdisk() && !module.InVendorRamdisk() && !module.InRecovery()
+ return !module.Host() && !module.InRamdisk() && !module.InVendorRamdisk()
}
func CanBeVersionVariant(module interface {
@@ -1960,3 +2125,118 @@
return outputFile
}
+
+type bazelCcLibraryStaticAttributes struct {
+ Copts bazel.StringListAttribute
+ Srcs bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Linkopts bazel.StringListAttribute
+ Linkstatic bool
+ Includes bazel.StringListAttribute
+ Hdrs bazel.LabelListAttribute
+}
+
+type bazelCcLibraryStatic struct {
+ android.BazelTargetModuleBase
+ bazelCcLibraryStaticAttributes
+}
+
+func BazelCcLibraryStaticFactory() android.Module {
+ module := &bazelCcLibraryStatic{}
+ module.AddProperties(&module.bazelCcLibraryStaticAttributes)
+ android.InitBazelTargetModule(module)
+ return module
+}
+
+func CcLibraryStaticBp2Build(ctx android.TopDownMutatorContext) {
+ module, ok := ctx.Module().(*Module)
+ if !ok {
+ // Not a cc module
+ return
+ }
+ if !module.ConvertWithBp2build(ctx) {
+ return
+ }
+ if ctx.ModuleType() != "cc_library_static" {
+ return
+ }
+
+ compilerAttrs := bp2BuildParseCompilerProps(ctx, module)
+
+ var includeDirs []string
+ var localIncludeDirs []string
+ for _, props := range module.compiler.compilerProps() {
+ if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+ // TODO: these should be arch and os specific.
+ includeDirs = bp2BuildMakePathsRelativeToModule(ctx, baseCompilerProps.Include_dirs)
+ localIncludeDirs = bp2BuildMakePathsRelativeToModule(ctx, baseCompilerProps.Local_include_dirs)
+ break
+ }
+ }
+
+ // Soong implicitly includes headers from the module's directory.
+ // For Bazel builds to work we have to make these header includes explicit.
+ if module.compiler.(*libraryDecorator).includeBuildDirectory() {
+ localIncludeDirs = append(localIncludeDirs, ".")
+ }
+
+ // For Bazel, be more explicit about headers - list all header files in include dirs as srcs
+ for _, includeDir := range includeDirs {
+ compilerAttrs.srcs.Value.Append(bp2BuildListHeadersInDir(ctx, includeDir))
+ }
+ for _, localIncludeDir := range localIncludeDirs {
+ compilerAttrs.srcs.Value.Append(bp2BuildListHeadersInDir(ctx, localIncludeDir))
+ }
+
+ var staticLibs []string
+ var wholeStaticLibs []string
+ for _, props := range module.linker.linkerProps() {
+ // TODO: move this into bp2buildParseLinkerProps
+ if baseLinkerProperties, ok := props.(*BaseLinkerProperties); ok {
+ staticLibs = baseLinkerProperties.Static_libs
+ wholeStaticLibs = baseLinkerProperties.Whole_static_libs
+ break
+ }
+ }
+
+ // FIXME: Treat Static_libs and Whole_static_libs differently?
+ allDeps := staticLibs
+ allDeps = append(allDeps, wholeStaticLibs...)
+
+ depsLabels := android.BazelLabelForModuleDeps(ctx, allDeps)
+
+ exportedIncludes, exportedIncludesHeaders := bp2BuildParseExportedIncludes(ctx, module)
+
+ // FIXME: Unify absolute vs relative paths
+ // FIXME: Use -I copts instead of setting includes= ?
+ allIncludes := exportedIncludes
+ allIncludes.Value = append(allIncludes.Value, includeDirs...)
+ allIncludes.Value = append(allIncludes.Value, localIncludeDirs...)
+
+ compilerAttrs.hdrs.Append(exportedIncludesHeaders)
+
+ linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
+ depsLabels.Append(linkerAttrs.deps.Value)
+
+ attrs := &bazelCcLibraryStaticAttributes{
+ Copts: compilerAttrs.copts,
+ Srcs: compilerAttrs.srcs,
+ Deps: bazel.MakeLabelListAttribute(depsLabels),
+ Linkstatic: true,
+ Includes: allIncludes,
+ Hdrs: compilerAttrs.hdrs,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_library_static",
+ Bzl_load_location: "//build/bazel/rules:cc_library_static.bzl",
+ }
+
+ ctx.CreateBazelTargetModule(BazelCcLibraryStaticFactory, module.Name(), props, attrs)
+}
+
+func (m *bazelCcLibraryStatic) Name() string {
+ return m.BaseModuleName()
+}
+
+func (m *bazelCcLibraryStatic) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 8b3dbeb..076ce80 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -14,13 +14,18 @@
package cc
-import "android/soong/android"
+import (
+ "android/soong/android"
+ "android/soong/bazel"
+)
func init() {
RegisterLibraryHeadersBuildComponents(android.InitRegistrationContext)
// Register sdk member types.
android.RegisterSdkMemberType(headersLibrarySdkMemberType)
+
+ android.RegisterBp2BuildMutator("cc_library_headers", CcLibraryHeadersBp2Build)
}
var headersLibrarySdkMemberType = &librarySdkMemberType{
@@ -55,3 +60,62 @@
library.HeaderOnly()
return module.Init()
}
+
+type bazelCcLibraryHeadersAttributes struct {
+ Copts bazel.StringListAttribute
+ Hdrs bazel.LabelListAttribute
+ Includes bazel.StringListAttribute
+ Deps bazel.LabelListAttribute
+}
+
+type bazelCcLibraryHeaders struct {
+ android.BazelTargetModuleBase
+ bazelCcLibraryHeadersAttributes
+}
+
+func BazelCcLibraryHeadersFactory() android.Module {
+ module := &bazelCcLibraryHeaders{}
+ module.AddProperties(&module.bazelCcLibraryHeadersAttributes)
+ android.InitBazelTargetModule(module)
+ return module
+}
+
+func CcLibraryHeadersBp2Build(ctx android.TopDownMutatorContext) {
+ module, ok := ctx.Module().(*Module)
+ if !ok {
+ // Not a cc module
+ return
+ }
+
+ if !module.ConvertWithBp2build(ctx) {
+ return
+ }
+
+ if ctx.ModuleType() != "cc_library_headers" {
+ return
+ }
+
+ exportedIncludes, exportedIncludesHeaders := bp2BuildParseExportedIncludes(ctx, module)
+ compilerAttrs := bp2BuildParseCompilerProps(ctx, module)
+ linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
+
+ attrs := &bazelCcLibraryHeadersAttributes{
+ Copts: compilerAttrs.copts,
+ Includes: exportedIncludes,
+ Hdrs: exportedIncludesHeaders,
+ Deps: linkerAttrs.deps,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_library_headers",
+ Bzl_load_location: "//build/bazel/rules:cc_library_headers.bzl",
+ }
+
+ ctx.CreateBazelTargetModule(BazelCcLibraryHeadersFactory, module.Name(), props, attrs)
+}
+
+func (m *bazelCcLibraryHeaders) Name() string {
+ return m.BaseModuleName()
+}
+
+func (m *bazelCcLibraryHeaders) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 3f2cf49..d5f2adf 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -130,6 +130,10 @@
pbm.AddProperty("vendor_available", true)
}
+ if proptools.Bool(ccModule.VendorProperties.Odm_available) {
+ pbm.AddProperty("odm_available", true)
+ }
+
if proptools.Bool(ccModule.VendorProperties.Product_available) {
pbm.AddProperty("product_available", true)
}
@@ -202,22 +206,22 @@
dirs: true,
},
{
- // exportedGeneratedIncludeDirs lists directories that contains some header files
- // that are explicitly listed in the exportedGeneratedHeaders property. So, the contents
+ // ExportedGeneratedIncludeDirs lists directories that contains some header files
+ // that are explicitly listed in the ExportedGeneratedHeaders property. So, the contents
// of these directories do not need to be copied, but these directories do need adding to
// the export_include_dirs property in the prebuilt module in the snapshot.
- pathsGetter: func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.exportedGeneratedIncludeDirs },
+ pathsGetter: func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.ExportedGeneratedIncludeDirs },
propertyName: "export_include_dirs",
snapshotDir: nativeGeneratedIncludeDir,
copy: false,
dirs: true,
},
{
- // exportedGeneratedHeaders lists header files that are in one of the directories
- // specified in exportedGeneratedIncludeDirs must be copied into the snapshot.
- // As they are in a directory in exportedGeneratedIncludeDirs they do not need adding to a
+ // ExportedGeneratedHeaders lists header files that are in one of the directories
+ // specified in ExportedGeneratedIncludeDirs must be copied into the snapshot.
+ // As they are in a directory in ExportedGeneratedIncludeDirs they do not need adding to a
// property in the prebuilt module in the snapshot.
- pathsGetter: func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.exportedGeneratedHeaders },
+ pathsGetter: func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.ExportedGeneratedHeaders },
propertyName: "",
snapshotDir: nativeGeneratedIncludeDir,
copy: true,
@@ -254,42 +258,52 @@
// values where necessary.
for _, propertyInfo := range includeDirProperties {
// Calculate the base directory in the snapshot into which the files will be copied.
- // lib.ArchType is "" for common properties.
+ // lib.archType is "" for common properties.
targetDir := filepath.Join(libInfo.OsPrefix(), libInfo.archType, propertyInfo.snapshotDir)
propertyName := propertyInfo.propertyName
// Iterate over each path in one of the include directory properties.
for _, path := range propertyInfo.pathsGetter(libInfo) {
+ inputPath := path.String()
+
+ // Map the input path to a snapshot relative path. The mapping is independent of the module
+ // that references them so that if multiple modules within the same snapshot export the same
+ // header files they end up in the same place in the snapshot and so do not get duplicated.
+ targetRelativePath := inputPath
+ if isGeneratedHeaderDirectory(path) {
+ // Remove everything up to the .intermediates/ from the generated output directory to
+ // leave a module relative path.
+ base := android.PathForIntermediates(sdkModuleContext, "")
+ targetRelativePath = android.Rel(sdkModuleContext, base.String(), inputPath)
+ }
+
+ snapshotRelativePath := filepath.Join(targetDir, targetRelativePath)
// Copy the files/directories when necessary.
if propertyInfo.copy {
if propertyInfo.dirs {
// When copying a directory glob and copy all the headers within it.
// TODO(jiyong) copy headers having other suffixes
- headers, _ := sdkModuleContext.GlobWithDeps(path.String()+"/**/*.h", nil)
+ headers, _ := sdkModuleContext.GlobWithDeps(inputPath+"/**/*.h", nil)
for _, file := range headers {
src := android.PathForSource(sdkModuleContext, file)
- dest := filepath.Join(targetDir, file)
+
+ // The destination path in the snapshot is constructed from the snapshot relative path
+ // of the input directory and the input directory relative path of the header file.
+ inputRelativePath := android.Rel(sdkModuleContext, inputPath, file)
+ dest := filepath.Join(snapshotRelativePath, inputRelativePath)
builder.CopyToSnapshot(src, dest)
}
} else {
- // Otherwise, just copy the files.
- dest := filepath.Join(targetDir, libInfo.name, path.Rel())
- builder.CopyToSnapshot(path, dest)
+ // Otherwise, just copy the file to its snapshot relative path.
+ builder.CopyToSnapshot(path, snapshotRelativePath)
}
}
// Only directories are added to a property.
if propertyInfo.dirs {
- var snapshotPath string
- if isGeneratedHeaderDirectory(path) {
- snapshotPath = filepath.Join(targetDir, libInfo.name)
- } else {
- snapshotPath = filepath.Join(targetDir, path.String())
- }
-
- includeDirs[propertyName] = append(includeDirs[propertyName], snapshotPath)
+ includeDirs[propertyName] = append(includeDirs[propertyName], snapshotRelativePath)
}
}
}
@@ -326,9 +340,6 @@
memberType *librarySdkMemberType
- // The name of the library, is not exported as this must not be changed during optimization.
- name string
-
// archType is not exported as if set (to a non default value) it is always arch specific.
// This is "" for common properties.
archType string
@@ -340,13 +351,13 @@
// The list of arch specific exported generated include dirs.
//
- // This field is not exported as its contents are always arch specific.
- exportedGeneratedIncludeDirs android.Paths
+ // This field is exported as its contents may not be arch specific, e.g. protos.
+ ExportedGeneratedIncludeDirs android.Paths `android:"arch_variant"`
// The list of arch specific exported generated header files.
//
- // This field is not exported as its contents are is always arch specific.
- exportedGeneratedHeaders android.Paths
+ // This field is exported as its contents may not be arch specific, e.g. protos.
+ ExportedGeneratedHeaders android.Paths `android:"arch_variant"`
// The list of possibly common exported system include dirs.
//
@@ -415,12 +426,11 @@
exportedIncludeDirs, exportedGeneratedIncludeDirs := android.FilterPathListPredicate(
exportedInfo.IncludeDirs, isGeneratedHeaderDirectory)
- p.name = variant.Name()
p.archType = ccModule.Target().Arch.ArchType.String()
// Make sure that the include directories are unique.
p.ExportedIncludeDirs = android.FirstUniquePaths(exportedIncludeDirs)
- p.exportedGeneratedIncludeDirs = android.FirstUniquePaths(exportedGeneratedIncludeDirs)
+ p.ExportedGeneratedIncludeDirs = android.FirstUniquePaths(exportedGeneratedIncludeDirs)
// Take a copy before filtering out duplicates to avoid changing the slice owned by the
// ccModule.
@@ -446,7 +456,7 @@
}
p.SystemSharedLibs = specifiedDeps.systemSharedLibs
}
- p.exportedGeneratedHeaders = exportedInfo.GeneratedHeaders
+ p.ExportedGeneratedHeaders = exportedInfo.GeneratedHeaders
if !p.memberType.noOutputFiles && addOutputFile {
p.outputFile = getRequiredMemberOutputFile(ctx, ccModule)
diff --git a/cc/library_test.go b/cc/library_test.go
index 49838b4..7975275 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -199,7 +199,7 @@
},
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.Platform_version_active_codenames = []string{"R"}
ctx := testCcWithConfig(t, config)
@@ -222,7 +222,7 @@
},
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.Platform_version_active_codenames = []string{"R"}
testCcErrorWithConfig(t, `"libfoo" .*: versions: not sorted`, config)
}
diff --git a/cc/linkable.go b/cc/linkable.go
index 489063f..571a3bb 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -6,6 +6,59 @@
"github.com/google/blueprint"
)
+// PlatformSanitizeable is an interface for sanitizing platform modules.
+type PlatformSanitizeable interface {
+ LinkableInterface
+
+ // SanitizePropDefined returns whether the Sanitizer properties struct for this module is defined.
+ SanitizePropDefined() bool
+
+ // IsDependencyRoot returns whether a module is of a type which cannot be a linkage dependency
+ // of another module. For example, cc_binary and rust_binary represent dependency roots as other
+ // modules cannot have linkage dependencies against these types.
+ IsDependencyRoot() bool
+
+ // IsSanitizerEnabled returns whether a sanitizer is enabled.
+ IsSanitizerEnabled(t SanitizerType) bool
+
+ // IsSanitizerExplicitlyDisabled returns whether a sanitizer has been explicitly disabled (set to false) rather
+ // than left undefined.
+ IsSanitizerExplicitlyDisabled(t SanitizerType) bool
+
+ // SanitizeDep returns the value of the SanitizeDep flag, which is set if a module is a dependency of a
+ // sanitized module.
+ SanitizeDep() bool
+
+ // SetSanitizer enables or disables the specified sanitizer type if it's supported, otherwise this should panic.
+ SetSanitizer(t SanitizerType, b bool)
+
+ // SetSanitizerDep returns true if the module is statically linked.
+ SetSanitizeDep(b bool)
+
+ // StaticallyLinked returns true if the module is statically linked.
+ StaticallyLinked() bool
+
+ // SetInSanitizerDir sets the module installation to the sanitizer directory.
+ SetInSanitizerDir()
+
+ // SanitizeNever returns true if this module should never be sanitized.
+ SanitizeNever() bool
+
+ // SanitizerSupported returns true if a sanitizer type is supported by this modules compiler.
+ SanitizerSupported(t SanitizerType) bool
+
+ // SanitizableDepTagChecker returns a SantizableDependencyTagChecker function type.
+ SanitizableDepTagChecker() SantizableDependencyTagChecker
+}
+
+// SantizableDependencyTagChecker functions check whether or not a dependency
+// tag can be sanitized. These functions should return true if the tag can be
+// sanitized, otherwise they should return false. These functions should also
+// handle all possible dependency tags in the dependency tree. For example,
+// Rust modules can depend on both Rust and CC libraries, so the Rust module
+// implementation should handle tags from both.
+type SantizableDependencyTagChecker func(tag blueprint.DependencyTag) bool
+
// LinkableInterface is an interface for a type of module that is linkable in a C++ library.
type LinkableInterface interface {
android.Module
@@ -27,6 +80,8 @@
SetShared()
Static() bool
Shared() bool
+ Header() bool
+ IsPrebuilt() bool
Toc() android.OptionalPath
Host() bool
@@ -40,22 +95,53 @@
InRecovery() bool
OnlyInRecovery() bool
+ InVendor() bool
+
UseSdk() bool
+
+ // IsLlndk returns true for both LLNDK (public) and LLNDK-private libs.
+ IsLlndk() bool
+
+ // IsLlndkPublic returns true only for LLNDK (public) libs.
+ IsLlndkPublic() bool
+
+ // IsLlndkHeaders returns true if this module is an LLNDK headers module.
+ IsLlndkHeaders() bool
+
+ // IsLlndkLibrary returns true if this module is an LLNDK library module.
+ IsLlndkLibrary() bool
+
+ // HasLlndkStubs returns true if this module has LLNDK stubs.
+ HasLlndkStubs() bool
+
UseVndk() bool
MustUseVendorVariant() bool
- IsLlndk() bool
- IsLlndkPublic() bool
IsVndk() bool
IsVndkExt() bool
IsVndkPrivate() bool
HasVendorVariant() bool
+ HasProductVariant() bool
+ HasNonSystemVariants() bool
InProduct() bool
+ // SubName returns the modules SubName, used for image and NDK/SDK variations.
+ SubName() string
+
SdkVersion() string
+ MinSdkVersion() string
AlwaysSdk() bool
IsSdkVariant() bool
SplitPerApiLevel() bool
+
+ // SetPreventInstall sets the PreventInstall property to 'true' for this module.
+ SetPreventInstall()
+ // SetHideFromMake sets the HideFromMake property to 'true' for this module.
+ SetHideFromMake()
+
+ // KernelHeadersDecorator returns true if this is a kernel headers decorator module.
+ // This is specific to cc and should always return false for all other packages.
+ KernelHeadersDecorator() bool
}
var (
@@ -67,14 +153,51 @@
CoverageDepTag = dependencyTag{name: "coverage"}
)
+// GetImageVariantType returns the ImageVariantType string value for the given module
+// (these are defined in cc/image.go).
+func GetImageVariantType(c LinkableInterface) ImageVariantType {
+ if c.Host() {
+ return hostImageVariant
+ } else if c.InVendor() {
+ return vendorImageVariant
+ } else if c.InProduct() {
+ return productImageVariant
+ } else if c.InRamdisk() {
+ return ramdiskImageVariant
+ } else if c.InVendorRamdisk() {
+ return vendorRamdiskImageVariant
+ } else if c.InRecovery() {
+ return recoveryImageVariant
+ } else {
+ return coreImageVariant
+ }
+}
+
+// DepTagMakeSuffix returns the makeSuffix value of a particular library dependency tag.
+// Returns an empty string if not a library dependency tag.
+func DepTagMakeSuffix(depTag blueprint.DependencyTag) string {
+ if libDepTag, ok := depTag.(libraryDependencyTag); ok {
+ return libDepTag.makeSuffix
+ }
+ return ""
+}
+
// SharedDepTag returns the dependency tag for any C++ shared libraries.
func SharedDepTag() blueprint.DependencyTag {
return libraryDependencyTag{Kind: sharedLibraryDependency}
}
// StaticDepTag returns the dependency tag for any C++ static libraries.
-func StaticDepTag() blueprint.DependencyTag {
- return libraryDependencyTag{Kind: staticLibraryDependency}
+func StaticDepTag(wholeStatic bool) blueprint.DependencyTag {
+ return libraryDependencyTag{Kind: staticLibraryDependency, wholeStatic: wholeStatic}
+}
+
+// IsWholeStaticLib whether a dependency tag is a whole static library dependency.
+func IsWholeStaticLib(depTag blueprint.DependencyTag) bool {
+ if tag, ok := depTag.(libraryDependencyTag); ok {
+ return tag.wholeStatic
+ }
+ return false
}
// HeaderDepTag returns the dependency tag for any C++ "header-only" libraries.
diff --git a/cc/linker.go b/cc/linker.go
index ff07224..ae33356 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -321,14 +321,15 @@
}
if ctx.toolchain().Bionic() {
- // libclang_rt.builtins and libatomic have to be last on the command line
+ // libclang_rt.builtins has to be last on the command line
if !Bool(linker.Properties.No_libcrt) {
deps.LateStaticLibs = append(deps.LateStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
- deps.LateStaticLibs = append(deps.LateStaticLibs, "libatomic")
}
deps.SystemSharedLibs = linker.Properties.System_shared_libs
- if deps.SystemSharedLibs == nil {
+ // In Bazel conversion mode, variations have not been specified, so SystemSharedLibs may
+ // inaccuarately appear unset, which can cause issues with circular dependencies.
+ if deps.SystemSharedLibs == nil && !ctx.BazelConversionMode() {
// Provide a default system_shared_libs if it is unspecified. Note: If an
// empty list [] is specified, it implies that the module declines the
// default system_shared_libs.
@@ -597,21 +598,20 @@
_ = pctx.SourcePathVariable("genSortedBssSymbolsPath", "build/soong/scripts/gen_sorted_bss_symbols.sh")
genSortedBssSymbols = pctx.AndroidStaticRule("gen_sorted_bss_symbols",
blueprint.RuleParams{
- Command: "CROSS_COMPILE=$crossCompile $genSortedBssSymbolsPath ${in} ${out}",
- CommandDeps: []string{"$genSortedBssSymbolsPath", "${crossCompile}nm"},
+ Command: "CLANG_BIN=${clangBin} $genSortedBssSymbolsPath ${in} ${out}",
+ CommandDeps: []string{"$genSortedBssSymbolsPath", "${clangBin}/llvm-nm"},
},
- "crossCompile")
+ "clangBin")
)
func (linker *baseLinker) sortBssSymbolsBySize(ctx ModuleContext, in android.Path, symbolOrderingFile android.ModuleOutPath, flags builderFlags) string {
- crossCompile := gccCmd(flags.toolchain, "")
ctx.Build(pctx, android.BuildParams{
Rule: genSortedBssSymbols,
Description: "generate bss symbol order " + symbolOrderingFile.Base(),
Output: symbolOrderingFile,
Input: in,
Args: map[string]string{
- "crossCompile": crossCompile,
+ "clangBin": "${config.ClangBin}",
},
})
return "-Wl,--symbol-ordering-file," + symbolOrderingFile.String()
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index d0fbc48..a46b31c 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -55,13 +55,6 @@
// Whether the system library uses symbol versions.
Unversioned *bool
- // whether this module can be directly depended upon by libs that are installed
- // to /vendor and /product.
- // When set to false, this module can only be depended on by VNDK libraries, not
- // vendor nor product libraries. This effectively hides this module from
- // non-system modules. Default value is true.
- Vendor_available *bool
-
// list of llndk headers to re-export include directories from.
Export_llndk_headers []string `android:"arch_variant"`
@@ -100,6 +93,11 @@
return name + llndkLibrarySuffix
}
+func (stub *llndkStubDecorator) linkerProps() []interface{} {
+ props := stub.libraryDecorator.linkerProps()
+ return append(props, &stub.Properties)
+}
+
func (stub *llndkStubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
stub.libraryDecorator.libName = stub.implementationModuleName(ctx.ModuleName())
return stub.libraryDecorator.linkerFlags(ctx, flags)
@@ -131,18 +129,11 @@
stub := &llndkStubDecorator{
libraryDecorator: library,
}
- stub.Properties.Vendor_available = BoolPtr(true)
module.compiler = stub
module.linker = stub
module.installer = nil
module.library = stub
- module.AddProperties(
- &module.Properties,
- &stub.Properties,
- &library.MutatedProperties,
- &library.flagExporter.Properties)
-
return module
}
@@ -156,14 +147,27 @@
// }
func LlndkLibraryFactory() android.Module {
module := NewLLndkStubLibrary()
- android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
- return module
+ return module.Init()
+}
+
+// isVestigialLLNDKModule returns true if m is a vestigial llndk_library module used to provide
+// properties to the LLNDK variant of a cc_library.
+func isVestigialLLNDKModule(m *Module) bool {
+ _, ok := m.linker.(*llndkStubDecorator)
+ return ok
}
type llndkHeadersDecorator struct {
*libraryDecorator
}
+func (llndk *llndkHeadersDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
+ deps.HeaderLibs = append(deps.HeaderLibs, llndk.Properties.Llndk.Export_llndk_headers...)
+ deps.ReexportHeaderLibHeaders = append(deps.ReexportHeaderLibHeaders,
+ llndk.Properties.Llndk.Export_llndk_headers...)
+ return deps
+}
+
// llndk_headers contains a set of c/c++ llndk headers files which are imported
// by other soongs cc modules.
func llndkHeadersFactory() android.Module {
@@ -181,11 +185,6 @@
module.installer = nil
module.library = decorator
- module.AddProperties(
- &module.Properties,
- &library.MutatedProperties,
- &library.flagExporter.Properties)
-
module.Init()
return module
diff --git a/cc/makevars.go b/cc/makevars.go
index bd8aab5..48d5636 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -99,6 +99,7 @@
ctx.Strict("GLOBAL_CLANG_CPPFLAGS_NO_OVERRIDE", "")
ctx.Strict("BOARD_VNDK_VERSION", ctx.DeviceConfig().VndkVersion())
+ ctx.Strict("RECOVERY_SNAPSHOT_VERSION", ctx.DeviceConfig().RecoverySnapshotVersion())
// Filter vendor_public_library that are exported to make
exportedVendorPublicLibraries := []string{}
@@ -153,16 +154,6 @@
ctx.Strict("SOONG_STRIP_PATH", "${stripPath}")
ctx.Strict("XZ", "${xzCmd}")
- nativeHelperIncludeFlags, err := ctx.Eval("${config.CommonNativehelperInclude}")
- if err != nil {
- panic(err)
- }
- nativeHelperIncludes, nativeHelperSystemIncludes := splitSystemIncludes(ctx, nativeHelperIncludeFlags)
- if len(nativeHelperSystemIncludes) > 0 {
- panic("native helper may not have any system includes")
- }
- ctx.Strict("JNI_H_INCLUDE", strings.Join(nativeHelperIncludes, " "))
-
includeFlags, err := ctx.Eval("${config.CommonGlobalIncludes}")
if err != nil {
panic(err)
diff --git a/cc/ndk_api_coverage_parser/Android.bp b/cc/ndk_api_coverage_parser/Android.bp
index 8d9827c..b119e90 100644
--- a/cc/ndk_api_coverage_parser/Android.bp
+++ b/cc/ndk_api_coverage_parser/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
python_library_host {
name: "ndk_api_coverage_parser_lib",
pkg_path: "ndk_api_coverage_parser",
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 60f931d..56fd5fc 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -75,11 +75,6 @@
// Path to the NOTICE file associated with the headers.
License *string `android:"path"`
-
- // True if this API is not yet ready to be shipped in the NDK. It will be
- // available in the platform for testing, but will be excluded from the
- // sysroot provided to the NDK proper.
- Draft bool
}
type headerModule struct {
@@ -184,11 +179,6 @@
// Path to the NOTICE file associated with the headers.
License *string
-
- // True if this API is not yet ready to be shipped in the NDK. It will be
- // available in the platform for testing, but will be excluded from the
- // sysroot provided to the NDK proper.
- Draft bool
}
// Like ndk_headers, but preprocesses the headers with the bionic versioner:
@@ -311,11 +301,6 @@
// Path to the NOTICE file associated with the headers.
License *string
-
- // True if this API is not yet ready to be shipped in the NDK. It will be
- // available in the platform for testing, but will be excluded from the
- // sysroot provided to the NDK proper.
- Draft bool
}
type preprocessedHeadersModule struct {
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index a5c43fe..95d8477 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -20,6 +20,7 @@
"sync"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
"android/soong/android"
)
@@ -78,11 +79,6 @@
// used. This is only needed to work around platform bugs like
// https://github.com/android-ndk/ndk/issues/265.
Unversioned_until *string
-
- // True if this API is not yet ready to be shipped in the NDK. It will be
- // available in the platform for testing, but will be excluded from the
- // sysroot provided to the NDK proper.
- Draft bool
}
type stubDecorator struct {
@@ -147,8 +143,8 @@
return false
}
- this.unversionedUntil, err = nativeApiLevelFromUserWithDefault(ctx,
- String(this.properties.Unversioned_until), "minimum")
+ str := proptools.StringDefault(this.properties.Unversioned_until, "minimum")
+ this.unversionedUntil, err = nativeApiLevelFromUser(ctx, str)
if err != nil {
ctx.PropertyErrorf("unversioned_until", err.Error())
return false
@@ -354,6 +350,5 @@
func NdkLibraryFactory() android.Module {
module := newStubLibrary()
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
- module.ModuleBase.EnableNativeBridgeSupportByDefault()
return module
}
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index 793ab37..8d522d0 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -70,7 +70,6 @@
// ./prebuilts/ndk/current/platforms/android-<sdk_version>/arch-$(HOST_ARCH)/usr/lib/<NAME>.o.
func NdkPrebuiltObjectFactory() android.Module {
module := newBaseModule(android.DeviceSupported, android.MultilibBoth)
- module.ModuleBase.EnableNativeBridgeSupportByDefault()
module.linker = &ndkPrebuiltObjectLinker{
objectLinker: objectLinker{
baseLinker: NewBaseLinker(nil),
@@ -150,7 +149,6 @@
module.Properties.AlwaysSdk = true
module.Properties.Sdk_version = StringPtr("current")
module.stl.Properties.Stl = StringPtr("none")
- module.ModuleBase.EnableNativeBridgeSupportByDefault()
return module.Init()
}
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index 70b15c1..d8c500e 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -104,38 +104,22 @@
}
if m, ok := module.(*headerModule); ok {
- if ctx.Config().ExcludeDraftNdkApis() && m.properties.Draft {
- return
- }
-
installPaths = append(installPaths, m.installPaths...)
licensePaths = append(licensePaths, m.licensePath)
}
if m, ok := module.(*versionedHeaderModule); ok {
- if ctx.Config().ExcludeDraftNdkApis() && m.properties.Draft {
- return
- }
-
installPaths = append(installPaths, m.installPaths...)
licensePaths = append(licensePaths, m.licensePath)
}
if m, ok := module.(*preprocessedHeadersModule); ok {
- if ctx.Config().ExcludeDraftNdkApis() && m.properties.Draft {
- return
- }
-
installPaths = append(installPaths, m.installPaths...)
licensePaths = append(licensePaths, m.licensePath)
}
if m, ok := module.(*Module); ok {
if installer, ok := m.installer.(*stubDecorator); ok && m.library.buildStubs() {
- if ctx.Config().ExcludeDraftNdkApis() &&
- installer.properties.Draft {
- return
- }
installPaths = append(installPaths, installer.installPath)
}
diff --git a/cc/ndkstubgen/Android.bp b/cc/ndkstubgen/Android.bp
index 85dfaee..782c124 100644
--- a/cc/ndkstubgen/Android.bp
+++ b/cc/ndkstubgen/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
python_binary_host {
name: "ndkstubgen",
pkg_path: "ndkstubgen",
diff --git a/cc/object.go b/cc/object.go
index 3ce7676..9bb279a 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -18,6 +18,7 @@
"fmt"
"android/soong/android"
+ "android/soong/bazel"
)
//
@@ -27,6 +28,8 @@
func init() {
android.RegisterModuleType("cc_object", ObjectFactory)
android.RegisterSdkMemberType(ccObjectSdkMemberType)
+
+ android.RegisterBp2BuildMutator("cc_object", ObjectBp2Build)
}
var ccObjectSdkMemberType = &librarySdkMemberType{
@@ -43,6 +46,26 @@
Properties ObjectLinkerProperties
}
+type objectBazelHandler struct {
+ bazelHandler
+
+ module *Module
+}
+
+func (handler *objectBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+ bazelCtx := ctx.Config().BazelContext
+ objPaths, ok := bazelCtx.GetOutputFiles(label, ctx.Arch().ArchType)
+ if ok {
+ if len(objPaths) != 1 {
+ ctx.ModuleErrorf("expected exactly one object file for '%s', but got %s", label, objPaths)
+ return false
+ }
+
+ handler.module.outputFile = android.OptionalPathForPath(android.PathForBazelOut(ctx, objPaths[0]))
+ }
+ return ok
+}
+
type ObjectLinkerProperties struct {
// list of modules that should only provide headers for this module.
Header_libs []string `android:"arch_variant,variant_prepend"`
@@ -77,14 +100,119 @@
baseLinker: NewBaseLinker(module.sanitize),
}
module.compiler = NewBaseCompiler()
+ module.bazelHandler = &objectBazelHandler{module: module}
// Clang's address-significance tables are incompatible with ld -r.
module.compiler.appendCflags([]string{"-fno-addrsig"})
module.sdkMemberTypes = []android.SdkMemberType{ccObjectSdkMemberType}
+
return module.Init()
}
+// For bp2build conversion.
+type bazelObjectAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Hdrs bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Copts bazel.StringListAttribute
+ Asflags []string
+ Local_include_dirs []string
+}
+
+type bazelObject struct {
+ android.BazelTargetModuleBase
+ bazelObjectAttributes
+}
+
+func (m *bazelObject) Name() string {
+ return m.BaseModuleName()
+}
+
+func (m *bazelObject) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+
+func BazelObjectFactory() android.Module {
+ module := &bazelObject{}
+ module.AddProperties(&module.bazelObjectAttributes)
+ android.InitBazelTargetModule(module)
+ return module
+}
+
+// ObjectBp2Build is the bp2build converter from cc_object modules to the
+// Bazel equivalent target, plus any necessary include deps for the cc_object.
+func ObjectBp2Build(ctx android.TopDownMutatorContext) {
+ m, ok := ctx.Module().(*Module)
+ if !ok || !m.ConvertWithBp2build(ctx) {
+ return
+ }
+
+ // a Module can be something other than a cc_object.
+ if ctx.ModuleType() != "cc_object" {
+ return
+ }
+
+ if m.compiler == nil {
+ // a cc_object must have access to the compiler decorator for its props.
+ ctx.ModuleErrorf("compiler must not be nil for a cc_object module")
+ }
+
+ // Set arch-specific configurable attributes
+ compilerAttrs := bp2BuildParseCompilerProps(ctx, m)
+ var localIncludeDirs []string
+ var asFlags []string
+ for _, props := range m.compiler.compilerProps() {
+ if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+ localIncludeDirs = baseCompilerProps.Local_include_dirs
+ break
+ }
+ }
+
+ if c, ok := m.compiler.(*baseCompiler); ok && c.includeBuildDirectory() {
+ localIncludeDirs = append(localIncludeDirs, ".")
+ }
+
+ var deps bazel.LabelListAttribute
+ for _, props := range m.linker.linkerProps() {
+ if objectLinkerProps, ok := props.(*ObjectLinkerProperties); ok {
+ deps = bazel.MakeLabelListAttribute(
+ android.BazelLabelForModuleDeps(ctx, objectLinkerProps.Objs))
+ }
+ }
+
+ productVariableProps := android.ProductVariableProperties(ctx)
+ if props, exists := productVariableProps["Asflags"]; exists {
+ // TODO(b/183595873): consider deduplicating handling of product variable properties
+ for _, prop := range props {
+ flags, ok := prop.Property.([]string)
+ if !ok {
+ ctx.ModuleErrorf("Could not convert product variable asflag property")
+ return
+ }
+ // TODO(b/183595873) handle other product variable usages -- as selects?
+ if newFlags, subbed := bazel.TryVariableSubstitutions(flags, prop.ProductConfigVariable); subbed {
+ asFlags = append(asFlags, newFlags...)
+ }
+ }
+ }
+ // TODO(b/183595872) warn/error if we're not handling product variables
+
+ attrs := &bazelObjectAttributes{
+ Srcs: compilerAttrs.srcs,
+ Hdrs: compilerAttrs.hdrs,
+ Deps: deps,
+ Copts: compilerAttrs.copts,
+ Asflags: asFlags,
+ Local_include_dirs: localIncludeDirs,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_object",
+ Bzl_load_location: "//build/bazel/rules:cc_object.bzl",
+ }
+
+ ctx.CreateBazelTargetModule(BazelObjectFactory, m.Name(), props, attrs)
+}
+
func (object *objectLinker) appendLdflags(flags []string) {
panic(fmt.Errorf("appendLdflags on objectLinker not supported"))
}
diff --git a/cc/object_test.go b/cc/object_test.go
index 6ff8a00..f82d544 100644
--- a/cc/object_test.go
+++ b/cc/object_test.go
@@ -15,6 +15,7 @@
package cc
import (
+ "android/soong/android"
"testing"
)
@@ -27,5 +28,28 @@
linker_script: "foo.lds",
}`)
})
+}
+func TestCcObjectWithBazel(t *testing.T) {
+ bp := `
+cc_object {
+ name: "foo",
+ srcs: ["baz.o"],
+ bazel_module: { label: "//foo/bar:bar" },
+}`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToOutputFiles: map[string][]string{
+ "//foo/bar:bar": []string{"bazel_out.o"}}}
+ ctx := testCcWithConfig(t, config)
+
+ module := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon").Module()
+ outputFiles, err := module.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+
+ expectedOutputFiles := []string{"outputbase/execroot/__main__/bazel_out.o"}
+ android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
}
diff --git a/cc/pgo.go b/cc/pgo.go
index ada694b..95c9c2e 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -22,7 +22,6 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
- "android/soong/cc/config"
)
var (
@@ -271,14 +270,6 @@
}
}
-func (pgo *pgo) deps(ctx BaseModuleContext, deps Deps) Deps {
- if pgo.Properties.ShouldProfileModule {
- runtimeLibrary := config.ProfileRuntimeLibrary(ctx.toolchain())
- deps.LateStaticLibs = append(deps.LateStaticLibs, runtimeLibrary)
- }
- return deps
-}
-
func (pgo *pgo) flags(ctx ModuleContext, flags Flags) Flags {
if ctx.Host() {
return flags
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 2cd18cb..c19b1ff 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -246,7 +246,7 @@
module.AddProperties(&prebuilt.properties)
- srcsSupplier := func(ctx android.BaseModuleContext) []string {
+ srcsSupplier := func(ctx android.BaseModuleContext, _ android.Module) []string {
return prebuilt.prebuiltSrcs(ctx)
}
@@ -305,6 +305,7 @@
func NewPrebuiltStaticLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
module, library := NewPrebuiltLibrary(hod)
library.BuildOnlyStatic()
+ module.bazelHandler = &prebuiltStaticLibraryBazelHandler{module: module, library: library}
return module, library
}
@@ -319,6 +320,56 @@
properties prebuiltObjectProperties
}
+type prebuiltStaticLibraryBazelHandler struct {
+ bazelHandler
+
+ module *Module
+ library *libraryDecorator
+}
+
+func (h *prebuiltStaticLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+ bazelCtx := ctx.Config().BazelContext
+ ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+ if err != nil {
+ ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
+ }
+ if !ok {
+ return false
+ }
+ staticLibs := ccInfo.CcStaticLibraryFiles
+ if len(staticLibs) > 1 {
+ ctx.ModuleErrorf("expected 1 static library from bazel target %q, got %s", label, staticLibs)
+ return false
+ }
+
+ // TODO(b/184543518): cc_prebuilt_library_static may have properties for re-exporting flags
+
+ // TODO(eakammer):Add stub-related flags if this library is a stub library.
+ // h.library.exportVersioningMacroIfNeeded(ctx)
+
+ // Dependencies on this library will expect collectedSnapshotHeaders to be set, otherwise
+ // validation will fail. For now, set this to an empty list.
+ // TODO(cparsons): More closely mirror the collectHeadersForSnapshot implementation.
+ h.library.collectedSnapshotHeaders = android.Paths{}
+
+ if len(staticLibs) == 0 {
+ h.module.outputFile = android.OptionalPath{}
+ return true
+ }
+
+ out := android.PathForBazelOut(ctx, staticLibs[0])
+ h.module.outputFile = android.OptionalPathForPath(out)
+
+ depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(out).Build()
+ ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
+ StaticLibrary: out,
+
+ TransitiveStaticLibrariesForOrdering: depSet,
+ })
+
+ return true
+}
+
func (p *prebuiltObjectLinker) prebuilt() *android.Prebuilt {
return &p.Prebuilt
}
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index ee4de6e..fa6dd87 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -15,7 +15,6 @@
package cc
import (
- "path/filepath"
"testing"
"android/soong/android"
@@ -23,27 +22,19 @@
"github.com/google/blueprint"
)
-func testPrebuilt(t *testing.T, bp string, fs map[string][]byte, handlers ...configCustomizer) *android.TestContext {
- config := TestConfig(buildDir, android.Android, nil, bp, fs)
- ctx := CreateTestContext(config)
+var prepareForPrebuiltTest = android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.PrepareForTestWithAndroidMk,
+)
- // Enable androidmk support.
- // * Register the singleton
- // * Configure that we are inside make
- // * Add CommonOS to ensure that androidmk processing works.
- android.RegisterAndroidMkBuildComponents(ctx)
- android.SetKatiEnabledForTests(config)
+func testPrebuilt(t *testing.T, bp string, fs android.MockFS, handlers ...android.FixturePreparer) *android.TestContext {
+ result := android.GroupFixturePreparers(
+ prepareForPrebuiltTest,
+ fs.AddToFixture(),
+ android.GroupFixturePreparers(handlers...),
+ ).RunTestWithBp(t, bp)
- for _, handler := range handlers {
- handler(config)
- }
-
- ctx.Register()
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
- return ctx
+ return result.TestContext
}
type configCustomizer func(config android.Config)
@@ -313,8 +304,7 @@
})
fooRule := ctx.ModuleForTests("foo", "linux_glibc_x86_64").Rule("Symlink")
- assertString(t, fooRule.Output.String(),
- filepath.Join(buildDir, ".intermediates/foo/linux_glibc_x86_64/foo"))
+ assertString(t, fooRule.Output.String(), "out/soong/.intermediates/foo/linux_glibc_x86_64/foo")
assertString(t, fooRule.Args["fromPath"], "$$PWD/linux_glibc_x86_64/bin/foo")
var libfooDep android.Path
@@ -324,8 +314,7 @@
break
}
}
- assertString(t, libfooDep.String(),
- filepath.Join(buildDir, ".intermediates/libfoo/linux_glibc_x86_64_shared/libfoo.so"))
+ assertString(t, libfooDep.String(), "out/soong/.intermediates/libfoo/linux_glibc_x86_64_shared/libfoo.so")
}
func TestPrebuiltLibrarySanitized(t *testing.T) {
@@ -370,9 +359,11 @@
assertString(t, static2.OutputFile().Path().Base(), "libf.a")
// With SANITIZE_TARGET=hwaddress
- ctx = testPrebuilt(t, bp, fs, func(config android.Config) {
- config.TestProductVariables.SanitizeDevice = []string{"hwaddress"}
- })
+ ctx = testPrebuilt(t, bp, fs,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.SanitizeDevice = []string{"hwaddress"}
+ }),
+ )
shared_rule = ctx.ModuleForTests("libtest", "android_arm64_armv8-a_shared_hwasan").Rule("android/soong/cc.strip")
assertString(t, shared_rule.Input.String(), "hwasan/libf.so")
diff --git a/cc/proto_test.go b/cc/proto_test.go
index f8bbd26..b9c89c7 100644
--- a/cc/proto_test.go
+++ b/cc/proto_test.go
@@ -61,7 +61,7 @@
t.Errorf("expected %q in %q", w, cmd)
}
- foobarPath := foobar.Module().(android.HostToolProvider).HostToolPath().String()
+ foobarPath := foobar.Module().(android.HostToolProvider).HostToolPath().RelativeToTop().String()
if w := "--plugin=protoc-gen-foobar=" + foobarPath; !strings.Contains(cmd, w) {
t.Errorf("expected %q in %q", w, cmd)
diff --git a/cc/sabi.go b/cc/sabi.go
index 4a1ba3c..c0eb57c 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -141,7 +141,7 @@
}
// Don't create ABI dump for prebuilts.
- if m.Prebuilt() != nil || m.isSnapshotPrebuilt() {
+ if m.Prebuilt() != nil || m.IsSnapshotPrebuilt() {
return false
}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 0e2d01a..397121e 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -71,7 +71,7 @@
"export_memory_stats=0", "max_malloc_fill_size=0"}
)
-type sanitizerType int
+type SanitizerType int
func boolPtr(v bool) *bool {
if v {
@@ -82,21 +82,22 @@
}
const (
- asan sanitizerType = iota + 1
- hwasan
+ Asan SanitizerType = iota + 1
+ Hwasan
tsan
intOverflow
cfi
scs
- fuzzer
+ Fuzzer
+ memtag_heap
)
// Name of the sanitizer variation for this sanitizer type
-func (t sanitizerType) variationName() string {
+func (t SanitizerType) variationName() string {
switch t {
- case asan:
+ case Asan:
return "asan"
- case hwasan:
+ case Hwasan:
return "hwasan"
case tsan:
return "tsan"
@@ -106,20 +107,24 @@
return "cfi"
case scs:
return "scs"
- case fuzzer:
+ case memtag_heap:
+ return "memtag_heap"
+ case Fuzzer:
return "fuzzer"
default:
- panic(fmt.Errorf("unknown sanitizerType %d", t))
+ panic(fmt.Errorf("unknown SanitizerType %d", t))
}
}
// This is the sanitizer names in SANITIZE_[TARGET|HOST]
-func (t sanitizerType) name() string {
+func (t SanitizerType) name() string {
switch t {
- case asan:
+ case Asan:
return "address"
- case hwasan:
+ case Hwasan:
return "hwaddress"
+ case memtag_heap:
+ return "memtag_heap"
case tsan:
return "thread"
case intOverflow:
@@ -128,15 +133,37 @@
return "cfi"
case scs:
return "shadow-call-stack"
- case fuzzer:
+ case Fuzzer:
return "fuzzer"
default:
- panic(fmt.Errorf("unknown sanitizerType %d", t))
+ panic(fmt.Errorf("unknown SanitizerType %d", t))
}
}
-func (t sanitizerType) incompatibleWithCfi() bool {
- return t == asan || t == fuzzer || t == hwasan
+func (*Module) SanitizerSupported(t SanitizerType) bool {
+ switch t {
+ case Asan:
+ return true
+ case Hwasan:
+ return true
+ case tsan:
+ return true
+ case intOverflow:
+ return true
+ case cfi:
+ return true
+ case scs:
+ return true
+ case Fuzzer:
+ return true
+ default:
+ return false
+ }
+}
+
+// incompatibleWithCfi returns true if a sanitizer is incompatible with CFI.
+func (t SanitizerType) incompatibleWithCfi() bool {
+ return t == Asan || t == Fuzzer || t == Hwasan
}
type SanitizeUserProps struct {
@@ -157,6 +184,7 @@
Integer_overflow *bool `android:"arch_variant"`
Scudo *bool `android:"arch_variant"`
Scs *bool `android:"arch_variant"`
+ Memtag_heap *bool `android:"arch_variant"`
// A modifier for ASAN and HWASAN for write only instrumentation
Writeonly *bool `android:"arch_variant"`
@@ -168,6 +196,7 @@
Undefined *bool `android:"arch_variant"`
Cfi *bool `android:"arch_variant"`
Integer_overflow *bool `android:"arch_variant"`
+ Memtag_heap *bool `android:"arch_variant"`
Misc_undefined []string `android:"arch_variant"`
No_recover []string `android:"arch_variant"`
} `android:"arch_variant"`
@@ -238,6 +267,12 @@
return
}
+ // cc_test targets default to SYNC MemTag unless explicitly set to ASYNC (via diag: {memtag_heap}).
+ if ctx.testBinary() && s.Memtag_heap == nil {
+ s.Memtag_heap = boolPtr(true)
+ s.Diag.Memtag_heap = boolPtr(true)
+ }
+
var globalSanitizers []string
var globalSanitizersDiag []string
@@ -308,6 +343,11 @@
}
s.Writeonly = boolPtr(true)
}
+ if found, globalSanitizers = removeFromList("memtag_heap", globalSanitizers); found && s.Memtag_heap == nil {
+ if !ctx.Config().MemtagHeapDisabledForPath(ctx.ModuleDir()) {
+ s.Memtag_heap = boolPtr(true)
+ }
+ }
if len(globalSanitizers) > 0 {
ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
@@ -324,11 +364,32 @@
s.Diag.Cfi = boolPtr(true)
}
+ if found, globalSanitizersDiag = removeFromList("memtag_heap", globalSanitizersDiag); found &&
+ s.Diag.Memtag_heap == nil && Bool(s.Memtag_heap) {
+ s.Diag.Memtag_heap = boolPtr(true)
+ }
+
if len(globalSanitizersDiag) > 0 {
ctx.ModuleErrorf("unknown global sanitizer diagnostics option %s", globalSanitizersDiag[0])
}
}
+ // Enable Memtag for all components in the include paths (for Aarch64 only)
+ if ctx.Arch().ArchType == android.Arm64 {
+ if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
+ if s.Memtag_heap == nil {
+ s.Memtag_heap = boolPtr(true)
+ }
+ if s.Diag.Memtag_heap == nil {
+ s.Diag.Memtag_heap = boolPtr(true)
+ }
+ } else if ctx.Config().MemtagHeapAsyncEnabledForPath(ctx.ModuleDir()) {
+ if s.Memtag_heap == nil {
+ s.Memtag_heap = boolPtr(true)
+ }
+ }
+ }
+
// Enable CFI for all components in the include paths (for Aarch64 only)
if s.Cfi == nil && ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) && ctx.Arch().ArchType == android.Arm64 {
s.Cfi = boolPtr(true)
@@ -343,12 +404,6 @@
s.Diag.Cfi = boolPtr(false)
}
- // Also disable CFI for arm32 until b/35157333 is fixed.
- if ctx.Arch().ArchType == android.Arm {
- s.Cfi = boolPtr(false)
- s.Diag.Cfi = boolPtr(false)
- }
-
// HWASan requires AArch64 hardware feature (top-byte-ignore).
if ctx.Arch().ArchType != android.Arm64 {
s.Hwaddress = nil
@@ -359,6 +414,11 @@
s.Scs = nil
}
+ // memtag_heap is only implemented on AArch64.
+ if ctx.Arch().ArchType != android.Arm64 {
+ s.Memtag_heap = nil
+ }
+
// Also disable CFI if ASAN is enabled.
if Bool(s.Address) || Bool(s.Hwaddress) {
s.Cfi = boolPtr(false)
@@ -413,7 +473,7 @@
if ctx.Os() != android.Windows && (Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) || Bool(s.Thread) ||
Bool(s.Fuzzer) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0 ||
- Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs)) {
+ Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs) || Bool(s.Memtag_heap)) {
sanitize.Properties.SanitizerEnabled = true
}
@@ -458,6 +518,22 @@
return false
}
+func toDisableUnsignedShiftBaseChange(flags []string) bool {
+ // Returns true if any flag is fsanitize*integer, and there is
+ // no explicit flag about sanitize=unsigned-shift-base.
+ for _, f := range flags {
+ if strings.Contains(f, "sanitize=unsigned-shift-base") {
+ return false
+ }
+ }
+ for _, f := range flags {
+ if strings.HasPrefix(f, "-fsanitize") && strings.Contains(f, "integer") {
+ return true
+ }
+ }
+ return false
+}
+
func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
minimalRuntimeLib := config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(ctx.toolchain()) + ".a"
minimalRuntimePath := "${config.ClangAsanLibDir}/" + minimalRuntimeLib
@@ -614,6 +690,10 @@
if toDisableImplicitIntegerChange(flags.Local.CFlags) {
flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=implicit-integer-sign-change")
}
+ // http://b/171275751, Android doesn't build with this sanitizer yet.
+ if toDisableUnsignedShiftBaseChange(flags.Local.CFlags) {
+ flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=unsigned-shift-base")
+ }
}
if len(sanitize.Properties.DiagSanitizers) > 0 {
@@ -660,11 +740,12 @@
return sanitize.Properties.InSanitizerDir
}
-func (sanitize *sanitize) getSanitizerBoolPtr(t sanitizerType) *bool {
+// getSanitizerBoolPtr returns the SanitizerTypes associated bool pointer from SanitizeProperties.
+func (sanitize *sanitize) getSanitizerBoolPtr(t SanitizerType) *bool {
switch t {
- case asan:
+ case Asan:
return sanitize.Properties.Sanitize.Address
- case hwasan:
+ case Hwasan:
return sanitize.Properties.Sanitize.Hwaddress
case tsan:
return sanitize.Properties.Sanitize.Thread
@@ -674,34 +755,39 @@
return sanitize.Properties.Sanitize.Cfi
case scs:
return sanitize.Properties.Sanitize.Scs
- case fuzzer:
+ case memtag_heap:
+ return sanitize.Properties.Sanitize.Memtag_heap
+ case Fuzzer:
return sanitize.Properties.Sanitize.Fuzzer
default:
- panic(fmt.Errorf("unknown sanitizerType %d", t))
+ panic(fmt.Errorf("unknown SanitizerType %d", t))
}
}
+// isUnsanitizedVariant returns true if no sanitizers are enabled.
func (sanitize *sanitize) isUnsanitizedVariant() bool {
- return !sanitize.isSanitizerEnabled(asan) &&
- !sanitize.isSanitizerEnabled(hwasan) &&
+ return !sanitize.isSanitizerEnabled(Asan) &&
+ !sanitize.isSanitizerEnabled(Hwasan) &&
!sanitize.isSanitizerEnabled(tsan) &&
!sanitize.isSanitizerEnabled(cfi) &&
!sanitize.isSanitizerEnabled(scs) &&
- !sanitize.isSanitizerEnabled(fuzzer)
+ !sanitize.isSanitizerEnabled(memtag_heap) &&
+ !sanitize.isSanitizerEnabled(Fuzzer)
}
+// isVariantOnProductionDevice returns true if variant is for production devices (no non-production sanitizers enabled).
func (sanitize *sanitize) isVariantOnProductionDevice() bool {
- return !sanitize.isSanitizerEnabled(asan) &&
- !sanitize.isSanitizerEnabled(hwasan) &&
+ return !sanitize.isSanitizerEnabled(Asan) &&
+ !sanitize.isSanitizerEnabled(Hwasan) &&
!sanitize.isSanitizerEnabled(tsan) &&
- !sanitize.isSanitizerEnabled(fuzzer)
+ !sanitize.isSanitizerEnabled(Fuzzer)
}
-func (sanitize *sanitize) SetSanitizer(t sanitizerType, b bool) {
+func (sanitize *sanitize) SetSanitizer(t SanitizerType, b bool) {
switch t {
- case asan:
+ case Asan:
sanitize.Properties.Sanitize.Address = boolPtr(b)
- case hwasan:
+ case Hwasan:
sanitize.Properties.Sanitize.Hwaddress = boolPtr(b)
case tsan:
sanitize.Properties.Sanitize.Thread = boolPtr(b)
@@ -711,10 +797,12 @@
sanitize.Properties.Sanitize.Cfi = boolPtr(b)
case scs:
sanitize.Properties.Sanitize.Scs = boolPtr(b)
- case fuzzer:
+ case memtag_heap:
+ sanitize.Properties.Sanitize.Memtag_heap = boolPtr(b)
+ case Fuzzer:
sanitize.Properties.Sanitize.Fuzzer = boolPtr(b)
default:
- panic(fmt.Errorf("unknown sanitizerType %d", t))
+ panic(fmt.Errorf("unknown SanitizerType %d", t))
}
if b {
sanitize.Properties.SanitizerEnabled = true
@@ -723,7 +811,7 @@
// Check if the sanitizer is explicitly disabled (as opposed to nil by
// virtue of not being set).
-func (sanitize *sanitize) isSanitizerExplicitlyDisabled(t sanitizerType) bool {
+func (sanitize *sanitize) isSanitizerExplicitlyDisabled(t SanitizerType) bool {
if sanitize == nil {
return false
}
@@ -737,7 +825,7 @@
// indirectly (via a mutator) sets the bool ptr to true, and you can't
// distinguish between the cases. It isn't needed though - both cases can be
// treated identically.
-func (sanitize *sanitize) isSanitizerEnabled(t sanitizerType) bool {
+func (sanitize *sanitize) isSanitizerEnabled(t SanitizerType) bool {
if sanitize == nil {
return false
}
@@ -746,7 +834,8 @@
return sanitizerVal != nil && *sanitizerVal == true
}
-func isSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
+// IsSanitizableDependencyTag returns true if the dependency tag is sanitizable.
+func IsSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
switch t := tag.(type) {
case dependencyTag:
return t == reuseObjTag || t == objDepTag
@@ -757,6 +846,10 @@
}
}
+func (m *Module) SanitizableDepTagChecker() SantizableDependencyTagChecker {
+ return IsSanitizableDependencyTag
+}
+
// Determines if the current module is a static library going to be captured
// as vendor snapshot. Such modules must create both cfi and non-cfi variants,
// except for ones which explicitly disable cfi.
@@ -765,51 +858,58 @@
return false
}
- c := mctx.Module().(*Module)
+ c := mctx.Module().(PlatformSanitizeable)
- if !c.inVendor() {
+ if !c.InVendor() {
return false
}
- if !c.static() {
+ if !c.StaticallyLinked() {
return false
}
- if c.Prebuilt() != nil {
+ if c.IsPrebuilt() {
return false
}
- return c.sanitize != nil &&
- !Bool(c.sanitize.Properties.Sanitize.Never) &&
- !c.sanitize.isSanitizerExplicitlyDisabled(cfi)
+ if !c.SanitizerSupported(cfi) {
+ return false
+ }
+
+ return c.SanitizePropDefined() &&
+ !c.SanitizeNever() &&
+ !c.IsSanitizerExplicitlyDisabled(cfi)
}
// Propagate sanitizer requirements down from binaries
-func sanitizerDepsMutator(t sanitizerType) func(android.TopDownMutatorContext) {
+func sanitizerDepsMutator(t SanitizerType) func(android.TopDownMutatorContext) {
return func(mctx android.TopDownMutatorContext) {
- if c, ok := mctx.Module().(*Module); ok {
- enabled := c.sanitize.isSanitizerEnabled(t)
+ if c, ok := mctx.Module().(PlatformSanitizeable); ok {
+ enabled := c.IsSanitizerEnabled(t)
if t == cfi && needsCfiForVendorSnapshot(mctx) {
// We shouldn't change the result of isSanitizerEnabled(cfi) to correctly
// determine defaultVariation in sanitizerMutator below.
// Instead, just mark SanitizeDep to forcefully create cfi variant.
enabled = true
- c.sanitize.Properties.SanitizeDep = true
+ c.SetSanitizeDep(true)
}
if enabled {
+ isSanitizableDependencyTag := c.SanitizableDepTagChecker()
mctx.WalkDeps(func(child, parent android.Module) bool {
if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
return false
}
- if d, ok := child.(*Module); ok && d.sanitize != nil &&
- !Bool(d.sanitize.Properties.Sanitize.Never) &&
- !d.sanitize.isSanitizerExplicitlyDisabled(t) {
- if t == cfi || t == hwasan || t == scs {
- if d.static() {
- d.sanitize.Properties.SanitizeDep = true
+ if d, ok := child.(PlatformSanitizeable); ok && d.SanitizePropDefined() &&
+ !d.SanitizeNever() &&
+ !d.IsSanitizerExplicitlyDisabled(t) {
+ if t == cfi || t == Hwasan || t == scs {
+ if d.StaticallyLinked() && d.SanitizerSupported(t) {
+ // Rust does not support some of these sanitizers, so we need to check if it's
+ // supported before setting this true.
+ d.SetSanitizeDep(true)
}
} else {
- d.sanitize.Properties.SanitizeDep = true
+ d.SetSanitizeDep(true)
}
}
return true
@@ -827,9 +927,19 @@
}
}
+func (c *Module) SanitizeNever() bool {
+ return Bool(c.sanitize.Properties.Sanitize.Never)
+}
+
+func (c *Module) IsSanitizerExplicitlyDisabled(t SanitizerType) bool {
+ return c.sanitize.isSanitizerExplicitlyDisabled(t)
+}
+
// Propagate the ubsan minimal runtime dependency when there are integer overflow sanitized static dependencies.
func sanitizerRuntimeDepsMutator(mctx android.TopDownMutatorContext) {
+ // Change this to PlatformSanitizable when/if non-cc modules support ubsan sanitizers.
if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
+ isSanitizableDependencyTag := c.SanitizableDepTagChecker()
mctx.WalkDeps(func(child, parent android.Module) bool {
if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
return false
@@ -965,6 +1075,26 @@
sanitizers = append(sanitizers, "shadow-call-stack")
}
+ if Bool(c.sanitize.Properties.Sanitize.Memtag_heap) && c.binary() {
+ noteDep := "note_memtag_heap_async"
+ if Bool(c.sanitize.Properties.Sanitize.Diag.Memtag_heap) {
+ noteDep = "note_memtag_heap_sync"
+ }
+ // If we're using snapshots, redirect to snapshot whenever possible
+ // TODO(b/178470649): clean manual snapshot redirections
+ snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
+ if lib, ok := snapshot.StaticLibs[noteDep]; ok {
+ noteDep = lib
+ }
+ depTag := libraryDependencyTag{Kind: staticLibraryDependency, wholeStatic: true}
+ variations := append(mctx.Target().Variations(),
+ blueprint.Variation{Mutator: "link", Variation: "static"})
+ if c.Device() {
+ variations = append(variations, c.ImageVariation())
+ }
+ mctx.AddFarVariationDependencies(variations, depTag, noteDep)
+ }
+
if Bool(c.sanitize.Properties.Sanitize.Fuzzer) {
sanitizers = append(sanitizers, "fuzzer-no-link")
}
@@ -1005,6 +1135,9 @@
Bool(c.sanitize.Properties.Sanitize.Undefined) ||
Bool(c.sanitize.Properties.Sanitize.All_undefined) {
runtimeLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain)
+ if c.staticBinary() {
+ runtimeLibrary += ".static"
+ }
}
if runtimeLibrary != "" && (toolchain.Bionic() || c.sanitize.Properties.UbsanRuntimeDep) {
@@ -1019,13 +1152,11 @@
// added to libFlags and LOCAL_SHARED_LIBRARIES by cc.Module
if c.staticBinary() {
deps := append(extraStaticDeps, runtimeLibrary)
- // If we're using snapshots and in vendor, redirect to snapshot whenever possible
- if c.VndkVersion() == mctx.DeviceConfig().VndkVersion() {
- snapshots := vendorSnapshotStaticLibs(mctx.Config())
- for idx, dep := range deps {
- if lib, ok := snapshots.get(dep, mctx.Arch().ArchType); ok {
- deps[idx] = lib
- }
+ // If we're using snapshots, redirect to snapshot whenever possible
+ snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
+ for idx, dep := range deps {
+ if lib, ok := snapshot.StaticLibs[dep]; ok {
+ deps[idx] = lib
}
}
@@ -1037,14 +1168,13 @@
variations = append(variations, c.ImageVariation())
}
mctx.AddFarVariationDependencies(variations, depTag, deps...)
- } else if !c.static() && !c.header() {
- // If we're using snapshots and in vendor, redirect to snapshot whenever possible
- if c.VndkVersion() == mctx.DeviceConfig().VndkVersion() {
- snapshots := vendorSnapshotSharedLibs(mctx.Config())
- if lib, ok := snapshots.get(runtimeLibrary, mctx.Arch().ArchType); ok {
- runtimeLibrary = lib
- }
+ } else if !c.static() && !c.Header() {
+ // If we're using snapshots, redirect to snapshot whenever possible
+ snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
+ if lib, ok := snapshot.SharedLibs[runtimeLibrary]; ok {
+ runtimeLibrary = lib
}
+
// Skip apex dependency check for sharedLibraryDependency
// when sanitizer diags are enabled. Skipping the check will allow
// building with diag libraries without having to list the
@@ -1078,16 +1208,52 @@
AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string)
}
+func (c *Module) SanitizePropDefined() bool {
+ return c.sanitize != nil
+}
+
+func (c *Module) IsSanitizerEnabled(t SanitizerType) bool {
+ return c.sanitize.isSanitizerEnabled(t)
+}
+
+func (c *Module) SanitizeDep() bool {
+ return c.sanitize.Properties.SanitizeDep
+}
+
+func (c *Module) StaticallyLinked() bool {
+ return c.static()
+}
+
+func (c *Module) SetInSanitizerDir() {
+ if c.sanitize != nil {
+ c.sanitize.Properties.InSanitizerDir = true
+ }
+}
+
+func (c *Module) SetSanitizer(t SanitizerType, b bool) {
+ if c.sanitize != nil {
+ c.sanitize.SetSanitizer(t, b)
+ }
+}
+
+func (c *Module) SetSanitizeDep(b bool) {
+ if c.sanitize != nil {
+ c.sanitize.Properties.SanitizeDep = b
+ }
+}
+
+var _ PlatformSanitizeable = (*Module)(nil)
+
// Create sanitized variants for modules that need them
-func sanitizerMutator(t sanitizerType) func(android.BottomUpMutatorContext) {
+func sanitizerMutator(t SanitizerType) func(android.BottomUpMutatorContext) {
return func(mctx android.BottomUpMutatorContext) {
- if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
- if c.isDependencyRoot() && c.sanitize.isSanitizerEnabled(t) {
+ if c, ok := mctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() {
+ if c.IsDependencyRoot() && c.IsSanitizerEnabled(t) {
modules := mctx.CreateVariations(t.variationName())
- modules[0].(*Module).sanitize.SetSanitizer(t, true)
- } else if c.sanitize.isSanitizerEnabled(t) || c.sanitize.Properties.SanitizeDep {
- isSanitizerEnabled := c.sanitize.isSanitizerEnabled(t)
- if c.static() || c.header() || t == asan || t == fuzzer {
+ modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
+ } else if c.IsSanitizerEnabled(t) || c.SanitizeDep() {
+ isSanitizerEnabled := c.IsSanitizerEnabled(t)
+ if c.StaticallyLinked() || c.Header() || t == Asan || t == Fuzzer {
// Static and header libs are split into non-sanitized and sanitized variants.
// Shared libs are not split. However, for asan and fuzzer, we split even for shared
// libs because a library sanitized for asan/fuzzer can't be linked from a library
@@ -1101,64 +1267,69 @@
// module. By setting it to the name of the sanitized variation, the dangling dependency
// is redirected to the sanitized variant of the dependent module.
defaultVariation := t.variationName()
+ // Not all PlatformSanitizeable modules support the CFI sanitizer
+ cfiSupported := mctx.Module().(PlatformSanitizeable).SanitizerSupported(cfi)
mctx.SetDefaultDependencyVariation(&defaultVariation)
- modules := mctx.CreateVariations("", t.variationName())
- modules[0].(*Module).sanitize.SetSanitizer(t, false)
- modules[1].(*Module).sanitize.SetSanitizer(t, true)
- modules[0].(*Module).sanitize.Properties.SanitizeDep = false
- modules[1].(*Module).sanitize.Properties.SanitizeDep = false
- if mctx.Device() && t.incompatibleWithCfi() {
+ modules := mctx.CreateVariations("", t.variationName())
+ modules[0].(PlatformSanitizeable).SetSanitizer(t, false)
+ modules[1].(PlatformSanitizeable).SetSanitizer(t, true)
+ modules[0].(PlatformSanitizeable).SetSanitizeDep(false)
+ modules[1].(PlatformSanitizeable).SetSanitizeDep(false)
+
+ if mctx.Device() && t.incompatibleWithCfi() && cfiSupported {
// TODO: Make sure that cfi mutator runs "after" any of the sanitizers that
// are incompatible with cfi
- modules[1].(*Module).sanitize.SetSanitizer(cfi, false)
+ modules[1].(PlatformSanitizeable).SetSanitizer(cfi, false)
}
// For cfi/scs/hwasan, we can export both sanitized and un-sanitized variants
// to Make, because the sanitized version has a different suffix in name.
// For other types of sanitizers, suppress the variation that is disabled.
- if t != cfi && t != scs && t != hwasan {
+ if t != cfi && t != scs && t != Hwasan {
if isSanitizerEnabled {
- modules[0].(*Module).Properties.PreventInstall = true
- modules[0].(*Module).Properties.HideFromMake = true
+ modules[0].(PlatformSanitizeable).SetPreventInstall()
+ modules[0].(PlatformSanitizeable).SetHideFromMake()
} else {
- modules[1].(*Module).Properties.PreventInstall = true
- modules[1].(*Module).Properties.HideFromMake = true
+ modules[1].(PlatformSanitizeable).SetPreventInstall()
+ modules[1].(PlatformSanitizeable).SetHideFromMake()
}
}
// Export the static lib name to make
- if c.static() && c.ExportedToMake() {
+ if c.StaticallyLinked() && c.ExportedToMake() {
if t == cfi {
- cfiStaticLibs(mctx.Config()).add(c, c.Name())
- } else if t == hwasan {
- hwasanStaticLibs(mctx.Config()).add(c, c.Name())
+ cfiStaticLibs(mctx.Config()).add(c, c.Module().Name())
+ } else if t == Hwasan {
+ hwasanStaticLibs(mctx.Config()).add(c, c.Module().Name())
}
}
} else {
// Shared libs are not split. Only the sanitized variant is created.
modules := mctx.CreateVariations(t.variationName())
- modules[0].(*Module).sanitize.SetSanitizer(t, true)
- modules[0].(*Module).sanitize.Properties.SanitizeDep = false
+ modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
+ modules[0].(PlatformSanitizeable).SetSanitizeDep(false)
// locate the asan libraries under /data/asan
- if mctx.Device() && t == asan && isSanitizerEnabled {
- modules[0].(*Module).sanitize.Properties.InSanitizerDir = true
+ if mctx.Device() && t == Asan && isSanitizerEnabled {
+ modules[0].(PlatformSanitizeable).SetInSanitizerDir()
}
if mctx.Device() && t.incompatibleWithCfi() {
// TODO: Make sure that cfi mutator runs "after" any of the sanitizers that
// are incompatible with cfi
- modules[0].(*Module).sanitize.SetSanitizer(cfi, false)
+ modules[0].(PlatformSanitizeable).SetSanitizer(cfi, false)
}
}
}
- c.sanitize.Properties.SanitizeDep = false
+ c.SetSanitizeDep(false)
} else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok && sanitizeable.IsSanitizerEnabled(mctx, t.name()) {
// APEX modules fall here
sanitizeable.AddSanitizerDependencies(mctx, t.name())
mctx.CreateVariations(t.variationName())
} else if c, ok := mctx.Module().(*Module); ok {
+ //TODO: When Rust modules have vendor support, enable this path for PlatformSanitizeable
+
// Check if it's a snapshot module supporting sanitizer
if s, ok := c.linker.(snapshotSanitizer); ok && s.isSanitizerEnabled(t) {
// Set default variation as above.
@@ -1183,23 +1354,23 @@
type sanitizerStaticLibsMap struct {
// libsMap contains one list of modules per each image and each arch.
// e.g. libs[vendor]["arm"] contains arm modules installed to vendor
- libsMap map[imageVariantType]map[string][]string
+ libsMap map[ImageVariantType]map[string][]string
libsMapLock sync.Mutex
- sanitizerType sanitizerType
+ sanitizerType SanitizerType
}
-func newSanitizerStaticLibsMap(t sanitizerType) *sanitizerStaticLibsMap {
+func newSanitizerStaticLibsMap(t SanitizerType) *sanitizerStaticLibsMap {
return &sanitizerStaticLibsMap{
sanitizerType: t,
- libsMap: make(map[imageVariantType]map[string][]string),
+ libsMap: make(map[ImageVariantType]map[string][]string),
}
}
// Add the current module to sanitizer static libs maps
// Each module should pass its exported name as names of Make and Soong can differ.
-func (s *sanitizerStaticLibsMap) add(c *Module, name string) {
- image := c.getImageVariantType()
- arch := c.Arch().ArchType.String()
+func (s *sanitizerStaticLibsMap) add(c LinkableInterface, name string) {
+ image := GetImageVariantType(c)
+ arch := c.Module().Target().Arch.ArchType.String()
s.libsMapLock.Lock()
defer s.libsMapLock.Unlock()
@@ -1218,7 +1389,7 @@
// See build/make/core/binary.mk for more details.
func (s *sanitizerStaticLibsMap) exportToMake(ctx android.MakeVarsContext) {
for _, image := range android.SortedStringKeys(s.libsMap) {
- archMap := s.libsMap[imageVariantType(image)]
+ archMap := s.libsMap[ImageVariantType(image)]
for _, arch := range android.SortedStringKeys(archMap) {
libs := archMap[arch]
sort.Strings(libs)
@@ -1246,7 +1417,7 @@
func hwasanStaticLibs(config android.Config) *sanitizerStaticLibsMap {
return config.Once(hwasanStaticLibsKey, func() interface{} {
- return newSanitizerStaticLibsMap(hwasan)
+ return newSanitizerStaticLibsMap(Hwasan)
}).(*sanitizerStaticLibsMap)
}
diff --git a/cc/sdk.go b/cc/sdk.go
index ec57f06..aec950b 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -38,18 +38,31 @@
ctx.CreateVariations("sdk")
} else if m.UseSdk() || m.SplitPerApiLevel() {
modules := ctx.CreateVariations("", "sdk")
+
+ // Clear the sdk_version property for the platform (non-SDK) variant so later code
+ // doesn't get confused by it.
modules[0].(*Module).Properties.Sdk_version = nil
+
+ // Mark the SDK variant.
modules[1].(*Module).Properties.IsSdkVariant = true
if ctx.Config().UnbundledBuildApps() {
+ // For an unbundled apps build, hide the platform variant from Make.
modules[0].(*Module).Properties.HideFromMake = true
modules[0].(*Module).Properties.PreventInstall = true
} else {
+ // For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
+ // exposed to Make.
modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
modules[1].(*Module).Properties.PreventInstall = true
}
ctx.AliasVariation("")
} else {
+ if m, ok := ctx.Module().(*Module); ok {
+ // Clear the sdk_version property for modules that don't have an SDK variant so
+ // later code doesn't get confused by it.
+ m.Properties.Sdk_version = nil
+ }
ctx.CreateVariations("")
ctx.AliasVariation("")
}
@@ -62,5 +75,7 @@
}
ctx.AliasVariation("")
}
+ case *snapshot:
+ ctx.CreateVariations("")
}
}
diff --git a/cc/sdk_test.go b/cc/sdk_test.go
index 5a3c181..61925e3 100644
--- a/cc/sdk_test.go
+++ b/cc/sdk_test.go
@@ -66,6 +66,7 @@
} else {
toFile = m.outputFile.Path()
}
+ toFile = toFile.RelativeToTop()
rule := from.Description("link")
for _, dep := range rule.Implicits {
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 4c4e9b6..6d48aed 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -18,35 +18,37 @@
// snapshot mutators and snapshot information maps which are also defined in this file.
import (
+ "path/filepath"
"strings"
- "sync"
"android/soong/android"
+
+ "github.com/google/blueprint"
)
// Defines the specifics of different images to which the snapshot process is applicable, e.g.,
// vendor, recovery, ramdisk.
type snapshotImage interface {
- // Used to register callbacks with the build system.
- init()
+ // Returns true if a snapshot should be generated for this image.
+ shouldGenerateSnapshot(ctx android.SingletonContext) bool
// Function that returns true if the module is included in this image.
// Using a function return instead of a value to prevent early
// evalution of a function that may be not be defined.
inImage(m *Module) func() bool
- // Returns the value of the "available" property for a given module for
- // and snapshot, e.g., "vendor_available", "recovery_available", etc.
- // or nil if the property is not defined.
- available(m *Module) *bool
+ // Returns true if the module is private and must not be included in the
+ // snapshot. For example VNDK-private modules must return true for the
+ // vendor snapshots. But false for the recovery snapshots.
+ private(m *Module) bool
// Returns true if a dir under source tree is an SoC-owned proprietary
// directory, such as device/, vendor/, etc.
//
// For a given snapshot (e.g., vendor, recovery, etc.) if
- // isProprietaryPath(dir) returns true, then the module in dir will be
- // built from sources.
- isProprietaryPath(dir string) bool
+ // isProprietaryPath(dir, deviceConfig) returns true, then the module in dir
+ // will be built from sources.
+ isProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool
// Whether to include VNDK in the snapshot for this image.
includeVndk() bool
@@ -55,30 +57,101 @@
// snapshot, e.g., using the exclude_from_vendor_snapshot or
// exclude_from_recovery_snapshot properties.
excludeFromSnapshot(m *Module) bool
+
+ // Returns true if the build is using a snapshot for this image.
+ isUsingSnapshot(cfg android.DeviceConfig) bool
+
+ // Returns a version of which the snapshot should be used in this target.
+ // This will only be meaningful when isUsingSnapshot is true.
+ targetSnapshotVersion(cfg android.DeviceConfig) string
+
+ // Whether to exclude a given module from the directed snapshot or not.
+ // If the makefile variable DIRECTED_{IMAGE}_SNAPSHOT is true, directed snapshot is turned on,
+ // and only modules listed in {IMAGE}_SNAPSHOT_MODULES will be captured.
+ excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool
+
+ // The image variant name for this snapshot image.
+ // For example, recovery snapshot image will return "recovery", and vendor snapshot image will
+ // return "vendor." + version.
+ imageVariantName(cfg android.DeviceConfig) string
+
+ // The variant suffix for snapshot modules. For example, vendor snapshot modules will have
+ // ".vendor" as their suffix.
+ moduleNameSuffix() string
}
type vendorSnapshotImage struct{}
type recoverySnapshotImage struct{}
-func (vendorSnapshotImage) init() {
- android.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
- android.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
- android.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
- android.RegisterModuleType("vendor_snapshot_header", VendorSnapshotHeaderFactory)
- android.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
- android.RegisterModuleType("vendor_snapshot_object", VendorSnapshotObjectFactory)
+type directoryMap map[string]bool
+
+var (
+ // Modules under following directories are ignored. They are OEM's and vendor's
+ // proprietary modules(device/, kernel/, vendor/, and hardware/).
+ defaultDirectoryExcludedMap = directoryMap{
+ "device": true,
+ "hardware": true,
+ "kernel": true,
+ "vendor": true,
+ }
+
+ // Modules under following directories are included as they are in AOSP,
+ // although hardware/ and kernel/ are normally for vendor's own.
+ defaultDirectoryIncludedMap = directoryMap{
+ "kernel/configs": true,
+ "kernel/prebuilts": true,
+ "kernel/tests": true,
+ "hardware/interfaces": true,
+ "hardware/libhardware": true,
+ "hardware/libhardware_legacy": true,
+ "hardware/ril": true,
+ }
+)
+
+func (vendorSnapshotImage) init(ctx android.RegistrationContext) {
+ ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
+ ctx.RegisterModuleType("vendor_snapshot", vendorSnapshotFactory)
+ ctx.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
+ ctx.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
+ ctx.RegisterModuleType("vendor_snapshot_header", VendorSnapshotHeaderFactory)
+ ctx.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
+ ctx.RegisterModuleType("vendor_snapshot_object", VendorSnapshotObjectFactory)
+
+ ctx.RegisterSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
+}
+
+func (vendorSnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) bool {
+ // BOARD_VNDK_VERSION must be set to 'current' in order to generate a snapshot.
+ return ctx.DeviceConfig().VndkVersion() == "current"
}
func (vendorSnapshotImage) inImage(m *Module) func() bool {
- return m.inVendor
+ return m.InVendor
}
-func (vendorSnapshotImage) available(m *Module) *bool {
- return m.VendorProperties.Vendor_available
+func (vendorSnapshotImage) private(m *Module) bool {
+ return m.IsVndkPrivate()
}
-func (vendorSnapshotImage) isProprietaryPath(dir string) bool {
- return isVendorProprietaryPath(dir)
+func isDirectoryExcluded(dir string, excludedMap directoryMap, includedMap directoryMap) bool {
+ if dir == "." || dir == "/" {
+ return false
+ }
+ if includedMap[dir] {
+ return false
+ } else if excludedMap[dir] {
+ return true
+ } else if defaultDirectoryIncludedMap[dir] {
+ return false
+ } else if defaultDirectoryExcludedMap[dir] {
+ return true
+ } else {
+ return isDirectoryExcluded(filepath.Dir(dir), excludedMap, includedMap)
+ }
+}
+
+func (vendorSnapshotImage) isProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
+ return isDirectoryExcluded(dir, deviceConfig.VendorSnapshotDirsExcludedMap(), deviceConfig.VendorSnapshotDirsIncludedMap())
}
// vendor snapshot includes static/header libraries with vndk: {enabled: true}.
@@ -90,25 +163,60 @@
return m.ExcludeFromVendorSnapshot()
}
-func (recoverySnapshotImage) init() {
- android.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
- android.RegisterModuleType("recovery_snapshot_shared", RecoverySnapshotSharedFactory)
- android.RegisterModuleType("recovery_snapshot_static", RecoverySnapshotStaticFactory)
- android.RegisterModuleType("recovery_snapshot_header", RecoverySnapshotHeaderFactory)
- android.RegisterModuleType("recovery_snapshot_binary", RecoverySnapshotBinaryFactory)
- android.RegisterModuleType("recovery_snapshot_object", RecoverySnapshotObjectFactory)
+func (vendorSnapshotImage) isUsingSnapshot(cfg android.DeviceConfig) bool {
+ vndkVersion := cfg.VndkVersion()
+ return vndkVersion != "current" && vndkVersion != ""
+}
+
+func (vendorSnapshotImage) targetSnapshotVersion(cfg android.DeviceConfig) string {
+ return cfg.VndkVersion()
+}
+
+// returns true iff a given module SHOULD BE EXCLUDED, false if included
+func (vendorSnapshotImage) excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
+ // If we're using full snapshot, not directed snapshot, capture every module
+ if !cfg.DirectedVendorSnapshot() {
+ return false
+ }
+ // Else, checks if name is in VENDOR_SNAPSHOT_MODULES.
+ return !cfg.VendorSnapshotModules()[name]
+}
+
+func (vendorSnapshotImage) imageVariantName(cfg android.DeviceConfig) string {
+ return VendorVariationPrefix + cfg.VndkVersion()
+}
+
+func (vendorSnapshotImage) moduleNameSuffix() string {
+ return VendorSuffix
+}
+
+func (recoverySnapshotImage) init(ctx android.RegistrationContext) {
+ ctx.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
+ ctx.RegisterModuleType("recovery_snapshot", recoverySnapshotFactory)
+ ctx.RegisterModuleType("recovery_snapshot_shared", RecoverySnapshotSharedFactory)
+ ctx.RegisterModuleType("recovery_snapshot_static", RecoverySnapshotStaticFactory)
+ ctx.RegisterModuleType("recovery_snapshot_header", RecoverySnapshotHeaderFactory)
+ ctx.RegisterModuleType("recovery_snapshot_binary", RecoverySnapshotBinaryFactory)
+ ctx.RegisterModuleType("recovery_snapshot_object", RecoverySnapshotObjectFactory)
+}
+
+func (recoverySnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) bool {
+ // RECOVERY_SNAPSHOT_VERSION must be set to 'current' in order to generate a
+ // snapshot.
+ return ctx.DeviceConfig().RecoverySnapshotVersion() == "current"
}
func (recoverySnapshotImage) inImage(m *Module) func() bool {
return m.InRecovery
}
-func (recoverySnapshotImage) available(m *Module) *bool {
- return m.Properties.Recovery_available
+// recovery snapshot does not have private libraries.
+func (recoverySnapshotImage) private(m *Module) bool {
+ return false
}
-func (recoverySnapshotImage) isProprietaryPath(dir string) bool {
- return isRecoveryProprietaryPath(dir)
+func (recoverySnapshotImage) isProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
+ return isDirectoryExcluded(dir, deviceConfig.RecoverySnapshotDirsExcludedMap(), deviceConfig.RecoverySnapshotDirsIncludedMap())
}
// recovery snapshot does NOT treat vndk specially.
@@ -120,84 +228,169 @@
return m.ExcludeFromRecoverySnapshot()
}
+func (recoverySnapshotImage) isUsingSnapshot(cfg android.DeviceConfig) bool {
+ recoverySnapshotVersion := cfg.RecoverySnapshotVersion()
+ return recoverySnapshotVersion != "current" && recoverySnapshotVersion != ""
+}
+
+func (recoverySnapshotImage) targetSnapshotVersion(cfg android.DeviceConfig) string {
+ return cfg.RecoverySnapshotVersion()
+}
+
+func (recoverySnapshotImage) excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
+ // If we're using full snapshot, not directed snapshot, capture every module
+ if !cfg.DirectedRecoverySnapshot() {
+ return false
+ }
+ // Else, checks if name is in RECOVERY_SNAPSHOT_MODULES.
+ return !cfg.RecoverySnapshotModules()[name]
+}
+
+func (recoverySnapshotImage) imageVariantName(cfg android.DeviceConfig) string {
+ return android.RecoveryVariation
+}
+
+func (recoverySnapshotImage) moduleNameSuffix() string {
+ return recoverySuffix
+}
+
var vendorSnapshotImageSingleton vendorSnapshotImage
var recoverySnapshotImageSingleton recoverySnapshotImage
func init() {
- vendorSnapshotImageSingleton.init()
- recoverySnapshotImageSingleton.init()
+ vendorSnapshotImageSingleton.init(android.InitRegistrationContext)
+ recoverySnapshotImageSingleton.init(android.InitRegistrationContext)
}
const (
- vendorSnapshotHeaderSuffix = ".vendor_header."
- vendorSnapshotSharedSuffix = ".vendor_shared."
- vendorSnapshotStaticSuffix = ".vendor_static."
- vendorSnapshotBinarySuffix = ".vendor_binary."
- vendorSnapshotObjectSuffix = ".vendor_object."
+ snapshotHeaderSuffix = "_header."
+ snapshotSharedSuffix = "_shared."
+ snapshotStaticSuffix = "_static."
+ snapshotBinarySuffix = "_binary."
+ snapshotObjectSuffix = "_object."
)
-const (
- recoverySnapshotHeaderSuffix = ".recovery_header."
- recoverySnapshotSharedSuffix = ".recovery_shared."
- recoverySnapshotStaticSuffix = ".recovery_static."
- recoverySnapshotBinarySuffix = ".recovery_binary."
- recoverySnapshotObjectSuffix = ".recovery_object."
-)
-
-var (
- vendorSnapshotsLock sync.Mutex
- vendorSuffixModulesKey = android.NewOnceKey("vendorSuffixModules")
- vendorSnapshotHeaderLibsKey = android.NewOnceKey("vendorSnapshotHeaderLibs")
- vendorSnapshotStaticLibsKey = android.NewOnceKey("vendorSnapshotStaticLibs")
- vendorSnapshotSharedLibsKey = android.NewOnceKey("vendorSnapshotSharedLibs")
- vendorSnapshotBinariesKey = android.NewOnceKey("vendorSnapshotBinaries")
- vendorSnapshotObjectsKey = android.NewOnceKey("vendorSnapshotObjects")
-)
-
-// vendorSuffixModules holds names of modules whose vendor variants should have the vendor suffix.
-// This is determined by source modules, and then this will be used when exporting snapshot modules
-// to Makefile.
-//
-// For example, if libbase has "vendor_available: true", the name of core variant will be "libbase"
-// while the name of vendor variant will be "libbase.vendor". In such cases, the vendor snapshot of
-// "libbase" should be exported with the name "libbase.vendor".
-//
-// Refer to VendorSnapshotSourceMutator and makeLibName which use this.
-func vendorSuffixModules(config android.Config) map[string]bool {
- return config.Once(vendorSuffixModulesKey, func() interface{} {
- return make(map[string]bool)
- }).(map[string]bool)
+type SnapshotProperties struct {
+ Header_libs []string `android:"arch_variant"`
+ Static_libs []string `android:"arch_variant"`
+ Shared_libs []string `android:"arch_variant"`
+ Vndk_libs []string `android:"arch_variant"`
+ Binaries []string `android:"arch_variant"`
+ Objects []string `android:"arch_variant"`
}
-// these are vendor snapshot maps holding names of vendor snapshot modules
-func vendorSnapshotHeaderLibs(config android.Config) *snapshotMap {
- return config.Once(vendorSnapshotHeaderLibsKey, func() interface{} {
- return newSnapshotMap()
- }).(*snapshotMap)
+type snapshot struct {
+ android.ModuleBase
+
+ properties SnapshotProperties
+
+ baseSnapshot baseSnapshotDecorator
+
+ image snapshotImage
}
-func vendorSnapshotSharedLibs(config android.Config) *snapshotMap {
- return config.Once(vendorSnapshotSharedLibsKey, func() interface{} {
- return newSnapshotMap()
- }).(*snapshotMap)
+func (s *snapshot) ImageMutatorBegin(ctx android.BaseModuleContext) {
+ cfg := ctx.DeviceConfig()
+ if !s.image.isUsingSnapshot(cfg) || s.image.targetSnapshotVersion(cfg) != s.baseSnapshot.version() {
+ s.Disable()
+ }
}
-func vendorSnapshotStaticLibs(config android.Config) *snapshotMap {
- return config.Once(vendorSnapshotStaticLibsKey, func() interface{} {
- return newSnapshotMap()
- }).(*snapshotMap)
+func (s *snapshot) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
}
-func vendorSnapshotBinaries(config android.Config) *snapshotMap {
- return config.Once(vendorSnapshotBinariesKey, func() interface{} {
- return newSnapshotMap()
- }).(*snapshotMap)
+func (s *snapshot) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
}
-func vendorSnapshotObjects(config android.Config) *snapshotMap {
- return config.Once(vendorSnapshotObjectsKey, func() interface{} {
- return newSnapshotMap()
- }).(*snapshotMap)
+func (s *snapshot) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (s *snapshot) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (s *snapshot) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (s *snapshot) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+ return []string{s.image.imageVariantName(ctx.DeviceConfig())}
+}
+
+func (s *snapshot) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+}
+
+func (s *snapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // Nothing, the snapshot module is only used to forward dependency information in DepsMutator.
+}
+
+func getSnapshotNameSuffix(moduleSuffix, version, arch string) string {
+ versionSuffix := version
+ if arch != "" {
+ versionSuffix += "." + arch
+ }
+ return moduleSuffix + versionSuffix
+}
+
+func (s *snapshot) DepsMutator(ctx android.BottomUpMutatorContext) {
+ collectSnapshotMap := func(names []string, snapshotSuffix, moduleSuffix string) map[string]string {
+ snapshotMap := make(map[string]string)
+ for _, name := range names {
+ snapshotMap[name] = name +
+ getSnapshotNameSuffix(snapshotSuffix+moduleSuffix,
+ s.baseSnapshot.version(),
+ ctx.DeviceConfig().Arches()[0].ArchType.String())
+ }
+ return snapshotMap
+ }
+
+ snapshotSuffix := s.image.moduleNameSuffix()
+ headers := collectSnapshotMap(s.properties.Header_libs, snapshotSuffix, snapshotHeaderSuffix)
+ binaries := collectSnapshotMap(s.properties.Binaries, snapshotSuffix, snapshotBinarySuffix)
+ objects := collectSnapshotMap(s.properties.Objects, snapshotSuffix, snapshotObjectSuffix)
+ staticLibs := collectSnapshotMap(s.properties.Static_libs, snapshotSuffix, snapshotStaticSuffix)
+ sharedLibs := collectSnapshotMap(s.properties.Shared_libs, snapshotSuffix, snapshotSharedSuffix)
+ vndkLibs := collectSnapshotMap(s.properties.Vndk_libs, "", vndkSuffix)
+ for k, v := range vndkLibs {
+ sharedLibs[k] = v
+ }
+
+ ctx.SetProvider(SnapshotInfoProvider, SnapshotInfo{
+ HeaderLibs: headers,
+ Binaries: binaries,
+ Objects: objects,
+ StaticLibs: staticLibs,
+ SharedLibs: sharedLibs,
+ })
+}
+
+type SnapshotInfo struct {
+ HeaderLibs, Binaries, Objects, StaticLibs, SharedLibs map[string]string
+}
+
+var SnapshotInfoProvider = blueprint.NewMutatorProvider(SnapshotInfo{}, "deps")
+
+var _ android.ImageInterface = (*snapshot)(nil)
+
+func vendorSnapshotFactory() android.Module {
+ return snapshotFactory(vendorSnapshotImageSingleton)
+}
+
+func recoverySnapshotFactory() android.Module {
+ return snapshotFactory(recoverySnapshotImageSingleton)
+}
+
+func snapshotFactory(image snapshotImage) android.Module {
+ snapshot := &snapshot{}
+ snapshot.image = image
+ snapshot.AddProperties(
+ &snapshot.properties,
+ &snapshot.baseSnapshot.baseProperties)
+ android.InitAndroidArchModule(snapshot, android.DeviceSupported, android.MultilibBoth)
+ return snapshot
}
type baseSnapshotDecoratorProperties struct {
@@ -206,6 +399,13 @@
// Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64')
Target_arch string
+
+ // Suffix to be added to the module name when exporting to Android.mk, e.g. ".vendor".
+ Androidmk_suffix string `blueprint:"mutated"`
+
+ // Suffix to be added to the module name, e.g., vendor_shared,
+ // recovery_shared, etc.
+ ModuleSuffix string `blueprint:"mutated"`
}
// baseSnapshotDecorator provides common basic functions for all snapshot modules, such as snapshot
@@ -222,7 +422,7 @@
// will be seen as "libbase.vendor_static.30.arm64" by Soong.
type baseSnapshotDecorator struct {
baseProperties baseSnapshotDecoratorProperties
- moduleSuffix string
+ image snapshotImage
}
func (p *baseSnapshotDecorator) Name(name string) string {
@@ -230,12 +430,7 @@
}
func (p *baseSnapshotDecorator) NameSuffix() string {
- versionSuffix := p.version()
- if p.arch() != "" {
- versionSuffix += "." + p.arch()
- }
-
- return p.moduleSuffix + versionSuffix
+ return getSnapshotNameSuffix(p.moduleSuffix(), p.version(), p.arch())
}
func (p *baseSnapshotDecorator) version() string {
@@ -246,14 +441,33 @@
return p.baseProperties.Target_arch
}
+func (p *baseSnapshotDecorator) moduleSuffix() string {
+ return p.baseProperties.ModuleSuffix
+}
+
func (p *baseSnapshotDecorator) isSnapshotPrebuilt() bool {
return true
}
+func (p *baseSnapshotDecorator) snapshotAndroidMkSuffix() string {
+ return p.baseProperties.Androidmk_suffix
+}
+
+func (p *baseSnapshotDecorator) setSnapshotAndroidMkSuffix(ctx android.ModuleContext) {
+ if ctx.OtherModuleDependencyVariantExists([]blueprint.Variation{
+ {Mutator: "image", Variation: android.CoreVariation},
+ }, ctx.Module().(*Module).BaseModuleName()) {
+ p.baseProperties.Androidmk_suffix = p.image.moduleNameSuffix()
+ } else {
+ p.baseProperties.Androidmk_suffix = ""
+ }
+}
+
// Call this with a module suffix after creating a snapshot module, such as
// vendorSnapshotSharedSuffix, recoverySnapshotBinarySuffix, etc.
-func (p *baseSnapshotDecorator) init(m *Module, suffix string) {
- p.moduleSuffix = suffix
+func (p *baseSnapshotDecorator) init(m *Module, image snapshotImage, moduleSuffix string) {
+ p.image = image
+ p.baseProperties.ModuleSuffix = image.moduleNameSuffix() + moduleSuffix
m.AddProperties(&p.baseProperties)
android.AddLoadHook(m, func(ctx android.LoadHookContext) {
vendorSnapshotLoadHook(ctx, p)
@@ -299,8 +513,8 @@
}
type snapshotSanitizer interface {
- isSanitizerEnabled(t sanitizerType) bool
- setSanitizerVariation(t sanitizerType, enabled bool)
+ isSanitizerEnabled(t SanitizerType) bool
+ setSanitizerVariation(t SanitizerType, enabled bool)
}
type snapshotLibraryDecorator struct {
@@ -313,7 +527,6 @@
// Library flags for cfi variant.
Cfi snapshotLibraryProperties `android:"arch_variant"`
}
- androidMkVendorSuffix bool
}
func (p *snapshotLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
@@ -336,8 +549,7 @@
// As snapshots are prebuilts, this just returns the prebuilt binary after doing things which are
// done by normal library decorator, e.g. exporting flags.
func (p *snapshotLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
- m := ctx.Module().(*Module)
- p.androidMkVendorSuffix = vendorSuffixModules(ctx.Config())[m.BaseModuleName()]
+ p.setSnapshotAndroidMkSuffix(ctx)
if p.header() {
return p.libraryDecorator.link(ctx, flags, deps, objs)
@@ -351,10 +563,18 @@
return nil
}
+ // Flags specified directly to this module.
p.libraryDecorator.reexportDirs(android.PathsForModuleSrc(ctx, p.properties.Export_include_dirs)...)
p.libraryDecorator.reexportSystemDirs(android.PathsForModuleSrc(ctx, p.properties.Export_system_include_dirs)...)
p.libraryDecorator.reexportFlags(p.properties.Export_flags...)
+ // Flags reexported from dependencies. (e.g. vndk_prebuilt_shared)
+ p.libraryDecorator.reexportDirs(deps.ReexportedDirs...)
+ p.libraryDecorator.reexportSystemDirs(deps.ReexportedSystemDirs...)
+ p.libraryDecorator.reexportFlags(deps.ReexportedFlags...)
+ p.libraryDecorator.reexportDeps(deps.ReexportedDeps...)
+ p.libraryDecorator.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
+
in := android.PathForModuleSrc(ctx, *p.properties.Src)
p.unstrippedOutputFile = in
@@ -400,7 +620,7 @@
return false
}
-func (p *snapshotLibraryDecorator) isSanitizerEnabled(t sanitizerType) bool {
+func (p *snapshotLibraryDecorator) isSanitizerEnabled(t SanitizerType) bool {
switch t {
case cfi:
return p.sanitizerProperties.Cfi.Src != nil
@@ -409,7 +629,7 @@
}
}
-func (p *snapshotLibraryDecorator) setSanitizerVariation(t sanitizerType, enabled bool) {
+func (p *snapshotLibraryDecorator) setSanitizerVariation(t SanitizerType, enabled bool) {
if !enabled {
return
}
@@ -421,7 +641,7 @@
}
}
-func snapshotLibraryFactory(suffix string) (*Module, *snapshotLibraryDecorator) {
+func snapshotLibraryFactory(image snapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
module, library := NewLibrary(android.DeviceSupported)
module.stl = nil
@@ -444,7 +664,7 @@
module.linker = prebuilt
module.installer = prebuilt
- prebuilt.init(module, suffix)
+ prebuilt.init(module, image, moduleSuffix)
module.AddProperties(
&prebuilt.properties,
&prebuilt.sanitizerProperties,
@@ -458,7 +678,7 @@
// overrides the vendor variant of the cc shared library with the same name, if BOARD_VNDK_VERSION
// is set.
func VendorSnapshotSharedFactory() android.Module {
- module, prebuilt := snapshotLibraryFactory(vendorSnapshotSharedSuffix)
+ module, prebuilt := snapshotLibraryFactory(vendorSnapshotImageSingleton, snapshotSharedSuffix)
prebuilt.libraryDecorator.BuildOnlyShared()
return module.Init()
}
@@ -468,7 +688,7 @@
// overrides the recovery variant of the cc shared library with the same name, if BOARD_VNDK_VERSION
// is set.
func RecoverySnapshotSharedFactory() android.Module {
- module, prebuilt := snapshotLibraryFactory(recoverySnapshotSharedSuffix)
+ module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, snapshotSharedSuffix)
prebuilt.libraryDecorator.BuildOnlyShared()
return module.Init()
}
@@ -478,7 +698,7 @@
// overrides the vendor variant of the cc static library with the same name, if BOARD_VNDK_VERSION
// is set.
func VendorSnapshotStaticFactory() android.Module {
- module, prebuilt := snapshotLibraryFactory(vendorSnapshotStaticSuffix)
+ module, prebuilt := snapshotLibraryFactory(vendorSnapshotImageSingleton, snapshotStaticSuffix)
prebuilt.libraryDecorator.BuildOnlyStatic()
return module.Init()
}
@@ -488,7 +708,7 @@
// overrides the recovery variant of the cc static library with the same name, if BOARD_VNDK_VERSION
// is set.
func RecoverySnapshotStaticFactory() android.Module {
- module, prebuilt := snapshotLibraryFactory(recoverySnapshotStaticSuffix)
+ module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, snapshotStaticSuffix)
prebuilt.libraryDecorator.BuildOnlyStatic()
return module.Init()
}
@@ -498,7 +718,7 @@
// overrides the vendor variant of the cc header library with the same name, if BOARD_VNDK_VERSION
// is set.
func VendorSnapshotHeaderFactory() android.Module {
- module, prebuilt := snapshotLibraryFactory(vendorSnapshotHeaderSuffix)
+ module, prebuilt := snapshotLibraryFactory(vendorSnapshotImageSingleton, snapshotHeaderSuffix)
prebuilt.libraryDecorator.HeaderOnly()
return module.Init()
}
@@ -508,7 +728,7 @@
// overrides the recovery variant of the cc header library with the same name, if BOARD_VNDK_VERSION
// is set.
func RecoverySnapshotHeaderFactory() android.Module {
- module, prebuilt := snapshotLibraryFactory(recoverySnapshotHeaderSuffix)
+ module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, snapshotHeaderSuffix)
prebuilt.libraryDecorator.HeaderOnly()
return module.Init()
}
@@ -530,8 +750,7 @@
type snapshotBinaryDecorator struct {
baseSnapshotDecorator
*binaryDecorator
- properties snapshotBinaryProperties
- androidMkVendorSuffix bool
+ properties snapshotBinaryProperties
}
func (p *snapshotBinaryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
@@ -547,6 +766,8 @@
// cc modules' link functions are to link compiled objects into final binaries.
// As snapshots are prebuilts, this just returns the prebuilt binary
func (p *snapshotBinaryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
+ p.setSnapshotAndroidMkSuffix(ctx)
+
if !p.matchesWithDevice(ctx.DeviceConfig()) {
return nil
}
@@ -555,9 +776,6 @@
p.unstrippedOutputFile = in
binName := in.Base()
- m := ctx.Module().(*Module)
- p.androidMkVendorSuffix = vendorSuffixModules(ctx.Config())[m.BaseModuleName()]
-
// use cpExecutable to make it executable
outputFile := android.PathForModuleOut(ctx, binName)
ctx.Build(pctx, android.BuildParams{
@@ -578,17 +796,17 @@
// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_binary
// overrides the vendor variant of the cc binary with the same name, if BOARD_VNDK_VERSION is set.
func VendorSnapshotBinaryFactory() android.Module {
- return snapshotBinaryFactory(vendorSnapshotBinarySuffix)
+ return snapshotBinaryFactory(vendorSnapshotImageSingleton, snapshotBinarySuffix)
}
// recovery_snapshot_binary is a special prebuilt executable binary which is auto-generated by
// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_binary
// overrides the recovery variant of the cc binary with the same name, if BOARD_VNDK_VERSION is set.
func RecoverySnapshotBinaryFactory() android.Module {
- return snapshotBinaryFactory(recoverySnapshotBinarySuffix)
+ return snapshotBinaryFactory(recoverySnapshotImageSingleton, snapshotBinarySuffix)
}
-func snapshotBinaryFactory(suffix string) android.Module {
+func snapshotBinaryFactory(image snapshotImage, moduleSuffix string) android.Module {
module, binary := NewBinary(android.DeviceSupported)
binary.baseLinker.Properties.No_libcrt = BoolPtr(true)
binary.baseLinker.Properties.Nocrt = BoolPtr(true)
@@ -607,7 +825,7 @@
module.stl = nil
module.linker = prebuilt
- prebuilt.init(module, suffix)
+ prebuilt.init(module, image, moduleSuffix)
module.AddProperties(&prebuilt.properties)
return module.Init()
}
@@ -627,8 +845,7 @@
type snapshotObjectLinker struct {
baseSnapshotDecorator
objectLinker
- properties vendorSnapshotObjectProperties
- androidMkVendorSuffix bool
+ properties vendorSnapshotObjectProperties
}
func (p *snapshotObjectLinker) matchesWithDevice(config android.DeviceConfig) bool {
@@ -644,13 +861,12 @@
// cc modules' link functions are to link compiled objects into final binaries.
// As snapshots are prebuilts, this just returns the prebuilt binary
func (p *snapshotObjectLinker) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
+ p.setSnapshotAndroidMkSuffix(ctx)
+
if !p.matchesWithDevice(ctx.DeviceConfig()) {
return nil
}
- m := ctx.Module().(*Module)
- p.androidMkVendorSuffix = vendorSuffixModules(ctx.Config())[m.BaseModuleName()]
-
return android.PathForModuleSrc(ctx, *p.properties.Src)
}
@@ -671,7 +887,7 @@
}
module.linker = prebuilt
- prebuilt.init(module, vendorSnapshotObjectSuffix)
+ prebuilt.init(module, vendorSnapshotImageSingleton, snapshotObjectSuffix)
module.AddProperties(&prebuilt.properties)
return module.Init()
}
@@ -689,156 +905,19 @@
}
module.linker = prebuilt
- prebuilt.init(module, recoverySnapshotObjectSuffix)
+ prebuilt.init(module, recoverySnapshotImageSingleton, snapshotObjectSuffix)
module.AddProperties(&prebuilt.properties)
return module.Init()
}
type snapshotInterface interface {
matchesWithDevice(config android.DeviceConfig) bool
+ isSnapshotPrebuilt() bool
+ version() string
+ snapshotAndroidMkSuffix() string
}
var _ snapshotInterface = (*vndkPrebuiltLibraryDecorator)(nil)
var _ snapshotInterface = (*snapshotLibraryDecorator)(nil)
var _ snapshotInterface = (*snapshotBinaryDecorator)(nil)
var _ snapshotInterface = (*snapshotObjectLinker)(nil)
-
-//
-// Mutators that helps vendor snapshot modules override source modules.
-//
-
-// VendorSnapshotMutator gathers all snapshots for vendor, and disable all snapshots which don't
-// match with device, e.g.
-// - snapshot version is different with BOARD_VNDK_VERSION
-// - snapshot arch is different with device's arch (e.g. arm vs x86)
-//
-// This also handles vndk_prebuilt_shared, except for they won't be disabled in any cases, given
-// that any versions of VNDK might be packed into vndk APEX.
-//
-// TODO(b/145966707): remove mutator and utilize android.Prebuilt to override source modules
-func VendorSnapshotMutator(ctx android.BottomUpMutatorContext) {
- vndkVersion := ctx.DeviceConfig().VndkVersion()
- // don't need snapshot if current
- if vndkVersion == "current" || vndkVersion == "" {
- return
- }
-
- module, ok := ctx.Module().(*Module)
- if !ok || !module.Enabled() || module.VndkVersion() != vndkVersion {
- return
- }
-
- if !module.isSnapshotPrebuilt() {
- return
- }
-
- // isSnapshotPrebuilt ensures snapshotInterface
- if !module.linker.(snapshotInterface).matchesWithDevice(ctx.DeviceConfig()) {
- // Disable unnecessary snapshot module, but do not disable
- // vndk_prebuilt_shared because they might be packed into vndk APEX
- if !module.IsVndk() {
- module.Disable()
- }
- return
- }
-
- var snapshotMap *snapshotMap
-
- if lib, ok := module.linker.(libraryInterface); ok {
- if lib.static() {
- snapshotMap = vendorSnapshotStaticLibs(ctx.Config())
- } else if lib.shared() {
- snapshotMap = vendorSnapshotSharedLibs(ctx.Config())
- } else {
- // header
- snapshotMap = vendorSnapshotHeaderLibs(ctx.Config())
- }
- } else if _, ok := module.linker.(*snapshotBinaryDecorator); ok {
- snapshotMap = vendorSnapshotBinaries(ctx.Config())
- } else if _, ok := module.linker.(*snapshotObjectLinker); ok {
- snapshotMap = vendorSnapshotObjects(ctx.Config())
- } else {
- return
- }
-
- vendorSnapshotsLock.Lock()
- defer vendorSnapshotsLock.Unlock()
- snapshotMap.add(module.BaseModuleName(), ctx.Arch().ArchType, ctx.ModuleName())
-}
-
-// VendorSnapshotSourceMutator disables source modules which have corresponding snapshots.
-func VendorSnapshotSourceMutator(ctx android.BottomUpMutatorContext) {
- if !ctx.Device() {
- return
- }
-
- vndkVersion := ctx.DeviceConfig().VndkVersion()
- // don't need snapshot if current
- if vndkVersion == "current" || vndkVersion == "" {
- return
- }
-
- module, ok := ctx.Module().(*Module)
- if !ok {
- return
- }
-
- // vendor suffix should be added to snapshots if the source module isn't vendor: true.
- if !module.SocSpecific() {
- // But we can't just check SocSpecific() since we already passed the image mutator.
- // Check ramdisk and recovery to see if we are real "vendor: true" module.
- ramdiskAvailable := module.InRamdisk() && !module.OnlyInRamdisk()
- vendorRamdiskAvailable := module.InVendorRamdisk() && !module.OnlyInVendorRamdisk()
- recoveryAvailable := module.InRecovery() && !module.OnlyInRecovery()
-
- if !ramdiskAvailable && !recoveryAvailable && !vendorRamdiskAvailable {
- vendorSnapshotsLock.Lock()
- defer vendorSnapshotsLock.Unlock()
-
- vendorSuffixModules(ctx.Config())[ctx.ModuleName()] = true
- }
- }
-
- if module.isSnapshotPrebuilt() || module.VndkVersion() != ctx.DeviceConfig().VndkVersion() {
- // only non-snapshot modules with BOARD_VNDK_VERSION
- return
- }
-
- // .. and also filter out llndk library
- if module.IsLlndk() {
- return
- }
-
- var snapshotMap *snapshotMap
-
- if lib, ok := module.linker.(libraryInterface); ok {
- if lib.static() {
- snapshotMap = vendorSnapshotStaticLibs(ctx.Config())
- } else if lib.shared() {
- snapshotMap = vendorSnapshotSharedLibs(ctx.Config())
- } else {
- // header
- snapshotMap = vendorSnapshotHeaderLibs(ctx.Config())
- }
- } else if module.binary() {
- snapshotMap = vendorSnapshotBinaries(ctx.Config())
- } else if module.object() {
- snapshotMap = vendorSnapshotObjects(ctx.Config())
- } else {
- return
- }
-
- if _, ok := snapshotMap.get(ctx.ModuleName(), ctx.Arch().ArchType); !ok {
- // Corresponding snapshot doesn't exist
- return
- }
-
- // Disables source modules if corresponding snapshot exists.
- if lib, ok := module.linker.(libraryInterface); ok && lib.buildStatic() && lib.buildShared() {
- // But do not disable because the shared variant depends on the static variant.
- module.HideFromMake()
- module.Properties.HideFromMake = true
- } else {
- module.Disable()
- }
-}
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index e841a54..c32fa36 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -71,11 +71,18 @@
// shouldCollectHeadersForSnapshot determines if the module is a possible candidate for snapshot.
// If it's true, collectHeadersForSnapshot will be called in GenerateAndroidBuildActions.
func shouldCollectHeadersForSnapshot(ctx android.ModuleContext, m *Module, apexInfo android.ApexInfo) bool {
+ if ctx.DeviceConfig().VndkVersion() != "current" &&
+ ctx.DeviceConfig().RecoverySnapshotVersion() != "current" {
+ return false
+ }
if _, _, ok := isVndkSnapshotAware(ctx.DeviceConfig(), m, apexInfo); ok {
return ctx.Config().VndkSnapshotBuildArtifacts()
- } else if isVendorSnapshotAware(m, isVendorProprietaryPath(ctx.ModuleDir()), apexInfo) ||
- isRecoverySnapshotAware(m, isRecoveryProprietaryPath(ctx.ModuleDir()), apexInfo) {
- return true
+ }
+
+ for _, image := range []snapshotImage{vendorSnapshotImageSingleton, recoverySnapshotImageSingleton} {
+ if isSnapshotAware(ctx.DeviceConfig(), m, image.isProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()), apexInfo, image) {
+ return true
+ }
}
return false
}
diff --git a/cc/stl.go b/cc/stl.go
index 406fa3a..4f8865f 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -140,11 +140,18 @@
}
func staticUnwinder(ctx android.BaseModuleContext) string {
- if ctx.Arch().ArchType == android.Arm {
- return "libunwind_llvm"
- } else {
- return "libgcc_stripped"
+ vndkVersion := ctx.Module().(*Module).VndkVersion()
+
+ // Modules using R vndk use different unwinder
+ if vndkVersion == "30" {
+ if ctx.Arch().ArchType == android.Arm {
+ return "libunwind_llvm"
+ } else {
+ return "libgcc_stripped"
+ }
}
+
+ return "libunwind"
}
func (stl *stl) deps(ctx BaseModuleContext, deps Deps) Deps {
@@ -192,11 +199,7 @@
if needsLibAndroidSupport(ctx) {
deps.StaticLibs = append(deps.StaticLibs, "ndk_libandroid_support")
}
- if ctx.Arch().ArchType == android.Arm {
- deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind")
- } else {
- deps.StaticLibs = append(deps.StaticLibs, "libgcc_stripped")
- }
+ deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind")
default:
panic(fmt.Errorf("Unknown stl: %q", stl.Properties.SelectedStl))
}
@@ -231,10 +234,6 @@
// Use Win32 threads in libc++.
"-D_LIBCPP_HAS_THREAD_API_WIN32")
}
- } else {
- if ctx.Arch().ArchType == android.Arm {
- flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,libunwind_llvm.a")
- }
}
case "libstdc++":
// Nothing
diff --git a/cc/symbolfile/Android.bp b/cc/symbolfile/Android.bp
index 5b43309..6722110 100644
--- a/cc/symbolfile/Android.bp
+++ b/cc/symbolfile/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
python_library_host {
name: "symbolfile",
pkg_path: "symbolfile",
diff --git a/cc/test.go b/cc/test.go
index 4ff5bf6..9b77e45 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -19,6 +19,8 @@
"strconv"
"strings"
+ "github.com/google/blueprint/proptools"
+
"android/soong/android"
"android/soong/tradefed"
)
@@ -85,14 +87,10 @@
// Add RunCommandTargetPreparer to stop framework before the test and start it after the test.
Disable_framework *bool
- // Add MinApiLevelModuleController to auto generated test config. If the device property of
- // "ro.product.first_api_level" < Test_min_api_level, then skip this module.
+ // Add ShippingApiLevelModuleController to auto generated test config. If the device properties
+ // for the shipping api level is less than the test_min_api_level, skip this module.
Test_min_api_level *int64
- // Add MinApiLevelModuleController to auto generated test config. If the device property of
- // "ro.build.version.sdk" < Test_min_sdk_version, then skip this module.
- Test_min_sdk_version *int64
-
// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
// explicitly.
@@ -236,6 +234,10 @@
return BoolDefault(test.Properties.Gtest, true)
}
+func (test *testDecorator) testBinary() bool {
+ return true
+}
+
func (test *testDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
if !test.gtest() {
return flags
@@ -369,9 +371,7 @@
}
})
- var apiLevelProp string
var configs []tradefed.Config
- var minLevel string
for _, module := range test.Properties.Test_mainline_modules {
configs = append(configs, tradefed.Option{Name: "config-descriptor:metadata", Key: "mainline-param", Value: module})
}
@@ -395,20 +395,10 @@
for _, tag := range test.Properties.Test_options.Test_suite_tag {
configs = append(configs, tradefed.Option{Name: "test-suite-tag", Value: tag})
}
- if test.Properties.Test_min_api_level != nil && test.Properties.Test_min_sdk_version != nil {
- ctx.PropertyErrorf("test_min_api_level", "'test_min_api_level' and 'test_min_sdk_version' should not be set at the same time.")
- } else if test.Properties.Test_min_api_level != nil {
- apiLevelProp = "ro.product.first_api_level"
- minLevel = strconv.FormatInt(int64(*test.Properties.Test_min_api_level), 10)
- } else if test.Properties.Test_min_sdk_version != nil {
- apiLevelProp = "ro.build.version.sdk"
- minLevel = strconv.FormatInt(int64(*test.Properties.Test_min_sdk_version), 10)
- }
- if apiLevelProp != "" {
+ if test.Properties.Test_min_api_level != nil {
var options []tradefed.Option
- options = append(options, tradefed.Option{Name: "min-api-level", Value: minLevel})
- options = append(options, tradefed.Option{Name: "api-level-prop", Value: apiLevelProp})
- configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.MinApiLevelModuleController", options})
+ options = append(options, tradefed.Option{Name: "min-api-level", Value: strconv.FormatInt(int64(*test.Properties.Test_min_api_level), 10)})
+ configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController", options})
}
test.testConfig = tradefed.AutoGenNativeTestConfig(ctx, test.Properties.Test_config,
@@ -425,6 +415,9 @@
ctx.PropertyErrorf("no_named_install_directory", "Module install directory may only be disabled if relative_install_path is set")
}
+ if ctx.Host() && test.gtest() && test.Properties.Test_options.Unit_test == nil {
+ test.Properties.Test_options.Unit_test = proptools.BoolPtr(true)
+ }
test.binaryDecorator.baseInstaller.install(ctx, file)
}
diff --git a/cc/testing.go b/cc/testing.go
index fc5b030..ff32bff 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -25,7 +25,6 @@
RegisterBinaryBuildComponents(ctx)
RegisterLibraryBuildComponents(ctx)
RegisterLibraryHeadersBuildComponents(ctx)
- genrule.RegisterGenruleBuildComponents(ctx)
ctx.RegisterModuleType("toolchain_library", ToolchainLibraryFactory)
ctx.RegisterModuleType("llndk_library", LlndkLibraryFactory)
@@ -38,20 +37,35 @@
}
func GatherRequiredDepsForTest(oses ...android.OsType) string {
- ret := `
- toolchain_library {
- name: "libatomic",
- defaults: ["linux_bionic_supported"],
- vendor_available: true,
- product_available: true,
- recovery_available: true,
- native_bridge_supported: true,
- src: "",
- }
+ ret := commonDefaultModules()
+ supportLinuxBionic := false
+ for _, os := range oses {
+ if os == android.Fuchsia {
+ ret += withFuchsiaModules()
+ }
+ if os == android.Windows {
+ ret += withWindowsModules()
+ }
+ if os == android.LinuxBionic {
+ supportLinuxBionic = true
+ ret += withLinuxBionic()
+ }
+ }
+
+ if !supportLinuxBionic {
+ ret += withoutLinuxBionic()
+ }
+
+ return ret
+}
+
+func commonDefaultModules() string {
+ return `
toolchain_library {
name: "libcompiler_rt-extras",
vendor_available: true,
+ vendor_ramdisk_available: true,
product_available: true,
recovery_available: true,
src: "",
@@ -60,6 +74,7 @@
toolchain_library {
name: "libclang_rt.builtins-arm-android",
vendor_available: true,
+ vendor_ramdisk_available: true,
product_available: true,
recovery_available: true,
native_bridge_supported: true,
@@ -69,6 +84,7 @@
toolchain_library {
name: "libclang_rt.builtins-aarch64-android",
vendor_available: true,
+ vendor_ramdisk_available: true,
product_available: true,
recovery_available: true,
native_bridge_supported: true,
@@ -93,6 +109,7 @@
toolchain_library {
name: "libclang_rt.builtins-i686-android",
vendor_available: true,
+ vendor_ramdisk_available: true,
product_available: true,
recovery_available: true,
native_bridge_supported: true,
@@ -103,6 +120,18 @@
name: "libclang_rt.builtins-x86_64-android",
defaults: ["linux_bionic_supported"],
vendor_available: true,
+ vendor_ramdisk_available: true,
+ product_available: true,
+ recovery_available: true,
+ native_bridge_supported: true,
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libunwind",
+ defaults: ["linux_bionic_supported"],
+ vendor_available: true,
+ vendor_ramdisk_available: true,
product_available: true,
recovery_available: true,
native_bridge_supported: true,
@@ -160,29 +189,6 @@
srcs: [""],
}
- toolchain_library {
- name: "libgcc",
- defaults: ["linux_bionic_supported"],
- vendor_available: true,
- product_available: true,
- recovery_available: true,
- src: "",
- apex_available: [
- "//apex_available:platform",
- "//apex_available:anyapex",
- ],
- }
-
- toolchain_library {
- name: "libgcc_stripped",
- defaults: ["linux_bionic_supported"],
- vendor_available: true,
- product_available: true,
- recovery_available: true,
- sdk_version: "current",
- src: "",
- }
-
cc_library {
name: "libc",
defaults: ["linux_bionic_supported"],
@@ -228,6 +234,7 @@
cc_library {
name: "libprofile-extras",
vendor_available: true,
+ vendor_ramdisk_available: true,
product_available: true,
recovery_available: true,
native_coverage: false,
@@ -238,6 +245,7 @@
cc_library {
name: "libprofile-clang-extras",
vendor_available: true,
+ vendor_ramdisk_available: true,
product_available: true,
recovery_available: true,
native_coverage: false,
@@ -299,7 +307,7 @@
llndk_library {
name: "libft2.llndk",
symbol_file: "",
- vendor_available: false,
+ private: true,
sdk_version: "current",
}
cc_library {
@@ -309,6 +317,7 @@
system_shared_libs: [],
stl: "none",
vendor_available: true,
+ vendor_ramdisk_available: true,
product_available: true,
recovery_available: true,
host_supported: true,
@@ -346,6 +355,7 @@
stl: "none",
host_supported: false,
vendor_available: true,
+ vendor_ramdisk_available: true,
product_available: true,
recovery_available: true,
min_sdk_version: "29",
@@ -354,22 +364,13 @@
"//apex_available:anyapex",
],
}
- cc_library {
- name: "libunwind_llvm",
- no_libcrt: true,
- nocrt: true,
- system_shared_libs: [],
- stl: "none",
- vendor_available: true,
- product_available: true,
- recovery_available: true,
- }
cc_defaults {
name: "crt_defaults",
defaults: ["linux_bionic_supported"],
recovery_available: true,
vendor_available: true,
+ vendor_ramdisk_available: true,
product_available: true,
native_bridge_supported: true,
stl: "none",
@@ -445,24 +446,19 @@
stl: "none",
system_shared_libs: [],
}
- `
- supportLinuxBionic := false
- for _, os := range oses {
- if os == android.Fuchsia {
- ret += `
- cc_library {
- name: "libbioniccompat",
- stl: "none",
+ cc_library_static {
+ name: "note_memtag_heap_async",
}
- cc_library {
- name: "libcompiler_rt",
- stl: "none",
+
+ cc_library_static {
+ name: "note_memtag_heap_sync",
}
- `
- }
- if os == android.Windows {
- ret += `
+ `
+}
+
+func withWindowsModules() string {
+ return `
toolchain_library {
name: "libwinpthread",
host_supported: true,
@@ -475,10 +471,23 @@
src: "",
}
`
+}
+
+func withFuchsiaModules() string {
+ return `
+ cc_library {
+ name: "libbioniccompat",
+ stl: "none",
}
- if os == android.LinuxBionic {
- supportLinuxBionic = true
- ret += `
+ cc_library {
+ name: "libcompiler_rt",
+ stl: "none",
+ }
+ `
+}
+
+func withLinuxBionic() string {
+ return `
cc_binary {
name: "linker",
defaults: ["linux_bionic_supported"],
@@ -518,23 +527,130 @@
},
}
`
- }
- }
+}
- if !supportLinuxBionic {
- ret += `
+func withoutLinuxBionic() string {
+ return `
cc_defaults {
name: "linux_bionic_supported",
}
`
- }
-
- return ret
}
func GatherRequiredFilesForTest(fs map[string][]byte) {
}
+// The directory in which cc linux bionic default modules will be defined.
+//
+// Placing them here ensures that their location does not conflict with default test modules
+// defined by other packages.
+const linuxBionicDefaultsPath = "defaults/cc/linux-bionic/Android.bp"
+
+// The directory in which the default cc common test modules will be defined.
+//
+// Placing them here ensures that their location does not conflict with default test modules
+// defined by other packages.
+const DefaultCcCommonTestModulesDir = "defaults/cc/common/"
+
+// Test fixture preparer that will register most cc build components.
+//
+// Singletons and mutators should only be added here if they are needed for a majority of cc
+// module types, otherwise they should be added under a separate preparer to allow them to be
+// selected only when needed to reduce test execution time.
+//
+// Module types do not have much of an overhead unless they are used so this should include as many
+// module types as possible. The exceptions are those module types that require mutators and/or
+// singletons in order to function in which case they should be kept together in a separate
+// preparer.
+var PrepareForTestWithCcBuildComponents = android.GroupFixturePreparers(
+ android.PrepareForTestWithAndroidBuildComponents,
+ android.FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
+ android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
+ ctx.RegisterModuleType("cc_test", TestFactory)
+ ctx.RegisterModuleType("cc_test_library", TestLibraryFactory)
+ ctx.RegisterModuleType("llndk_headers", llndkHeadersFactory)
+ ctx.RegisterModuleType("vendor_public_library", vendorPublicLibraryFactory)
+ ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
+
+ RegisterVndkLibraryTxtTypes(ctx)
+ }),
+
+ // Additional files needed in tests that disallow non-existent source files.
+ // This includes files that are needed by all, or at least most, instances of a cc module type.
+ android.MockFS{
+ // Needed for ndk_prebuilt_(shared|static)_stl.
+ "prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs": nil,
+ }.AddToFixture(),
+)
+
+// Preparer that will define default cc modules, e.g. standard prebuilt modules.
+var PrepareForTestWithCcDefaultModules = android.GroupFixturePreparers(
+ PrepareForTestWithCcBuildComponents,
+
+ // Additional files needed in tests that disallow non-existent source.
+ android.MockFS{
+ "defaults/cc/common/libc.map.txt": nil,
+ "defaults/cc/common/libdl.map.txt": nil,
+ "defaults/cc/common/libm.map.txt": nil,
+ }.AddToFixture(),
+
+ // Place the default cc test modules that are common to all platforms in a location that will not
+ // conflict with default test modules defined by other packages.
+ android.FixtureAddTextFile(DefaultCcCommonTestModulesDir+"Android.bp", commonDefaultModules()),
+ // Disable linux bionic by default.
+ android.FixtureAddTextFile(linuxBionicDefaultsPath, withoutLinuxBionic()),
+)
+
+// Prepare a fixture to use all cc module types, mutators and singletons fully.
+//
+// This should only be used by tests that want to run with as much of the build enabled as possible.
+var PrepareForIntegrationTestWithCc = android.GroupFixturePreparers(
+ android.PrepareForIntegrationTestWithAndroid,
+ genrule.PrepareForIntegrationTestWithGenrule,
+ PrepareForTestWithCcDefaultModules,
+)
+
+// The preparer to include if running a cc related test for windows.
+var PrepareForTestOnWindows = android.GroupFixturePreparers(
+ // Place the default cc test modules for windows platforms in a location that will not conflict
+ // with default test modules defined by other packages.
+ android.FixtureAddTextFile("defaults/cc/windows/Android.bp", withWindowsModules()),
+)
+
+// The preparer to include if running a cc related test for linux bionic.
+var PrepareForTestOnLinuxBionic = android.GroupFixturePreparers(
+ // Enable linux bionic
+ //
+ // Can be used after PrepareForTestWithCcDefaultModules to override its default behavior of
+ // disabling linux bionic, hence why this uses FixtureOverrideTextFile.
+ android.FixtureOverrideTextFile(linuxBionicDefaultsPath, withLinuxBionic()),
+)
+
+// The preparer to include if running a cc related test for fuchsia.
+var PrepareForTestOnFuchsia = android.GroupFixturePreparers(
+ // Place the default cc test modules for fuschia in a location that will not conflict with default
+ // test modules defined by other packages.
+ android.FixtureAddTextFile("defaults/cc/fuschia/Android.bp", withFuchsiaModules()),
+ android.PrepareForTestSetDeviceToFuchsia,
+)
+
+// This adds some additional modules and singletons which might negatively impact the performance
+// of tests so they are not included in the PrepareForIntegrationTestWithCc.
+var PrepareForTestWithCcIncludeVndk = android.GroupFixturePreparers(
+ PrepareForIntegrationTestWithCc,
+ android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+ vendorSnapshotImageSingleton.init(ctx)
+ recoverySnapshotImageSingleton.init(ctx)
+ ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
+ }),
+)
+
+// TestConfig is the legacy way of creating a test Config for testing cc modules.
+//
+// See testCc for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
func TestConfig(buildDir string, os android.OsType, env map[string]string,
bp string, fs map[string][]byte) android.Config {
@@ -551,7 +667,7 @@
var config android.Config
if os == android.Fuchsia {
- config = android.TestArchConfigFuchsia(buildDir, env, bp, mockFS)
+ panic("Fuchsia not supported use test fixture instead")
} else {
config = android.TestArchConfig(buildDir, env, bp, mockFS)
}
@@ -559,8 +675,14 @@
return config
}
+// CreateTestContext is the legacy way of creating a TestContext for testing cc modules.
+//
+// See testCc for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
func CreateTestContext(config android.Config) *android.TestContext {
ctx := android.NewTestArchContext(config)
+ genrule.RegisterGenruleBuildComponents(ctx)
ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
ctx.RegisterModuleType("cc_test", TestFactory)
ctx.RegisterModuleType("cc_test_library", TestLibraryFactory)
@@ -568,16 +690,15 @@
ctx.RegisterModuleType("vendor_public_library", vendorPublicLibraryFactory)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
- ctx.RegisterModuleType("vndk_libraries_txt", VndkLibrariesTxtFactory)
- ctx.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
- ctx.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
- ctx.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
+
+ vendorSnapshotImageSingleton.init(ctx)
+ recoverySnapshotImageSingleton.init(ctx)
+ ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
+ RegisterVndkLibraryTxtTypes(ctx)
+
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
android.RegisterPrebuiltMutators(ctx)
RegisterRequiredBuildComponentsForTest(ctx)
- ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
- ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
- ctx.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
return ctx
}
diff --git a/cc/tidy.go b/cc/tidy.go
index 17471e6..616cf8a 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -15,10 +15,12 @@
package cc
import (
+ "regexp"
"strings"
"github.com/google/blueprint/proptools"
+ "android/soong/android"
"android/soong/cc/config"
)
@@ -40,6 +42,21 @@
Properties TidyProperties
}
+var quotedFlagRegexp, _ = regexp.Compile(`^-?-[^=]+=('|").*('|")$`)
+
+// When passing flag -name=value, if user add quotes around 'value',
+// the quotation marks will be preserved by NinjaAndShellEscapeList
+// and the 'value' string with quotes won't work like the intended value.
+// So here we report an error if -*='*' is found.
+func checkNinjaAndShellEscapeList(ctx ModuleContext, prop string, slice []string) []string {
+ for _, s := range slice {
+ if quotedFlagRegexp.MatchString(s) {
+ ctx.PropertyErrorf(prop, "Extra quotes in: %s", s)
+ }
+ }
+ return proptools.NinjaAndShellEscapeList(slice)
+}
+
func (tidy *tidyFeature) props() []interface{} {
return []interface{}{&tidy.Properties}
}
@@ -72,11 +89,19 @@
if len(withTidyFlags) > 0 {
flags.TidyFlags = append(flags.TidyFlags, withTidyFlags)
}
- esc := proptools.NinjaAndShellEscapeList
- flags.TidyFlags = append(flags.TidyFlags, esc(tidy.Properties.Tidy_flags)...)
- // If TidyFlags is empty, add default header filter.
- if len(flags.TidyFlags) == 0 {
- headerFilter := "-header-filter=\"(" + ctx.ModuleDir() + "|${config.TidyDefaultHeaderDirs})\""
+ esc := checkNinjaAndShellEscapeList
+ flags.TidyFlags = append(flags.TidyFlags, esc(ctx, "tidy_flags", tidy.Properties.Tidy_flags)...)
+ // If TidyFlags does not contain -header-filter, add default header filter.
+ // Find the substring because the flag could also appear as --header-filter=...
+ // and with or without single or double quotes.
+ if !android.SubstringInList(flags.TidyFlags, "-header-filter=") {
+ defaultDirs := ctx.Config().Getenv("DEFAULT_TIDY_HEADER_DIRS")
+ headerFilter := "-header-filter="
+ if defaultDirs == "" {
+ headerFilter += ctx.ModuleDir() + "/"
+ } else {
+ headerFilter += "\"(" + ctx.ModuleDir() + "/|" + defaultDirs + ")\""
+ }
flags.TidyFlags = append(flags.TidyFlags, headerFilter)
}
@@ -109,7 +134,7 @@
tidyChecks += config.TidyChecksForDir(ctx.ModuleDir())
}
if len(tidy.Properties.Tidy_checks) > 0 {
- tidyChecks = tidyChecks + "," + strings.Join(esc(
+ tidyChecks = tidyChecks + "," + strings.Join(esc(ctx, "tidy_checks",
config.ClangRewriteTidyChecks(tidy.Properties.Tidy_checks)), ",")
}
if ctx.Windows() {
@@ -130,8 +155,32 @@
tidyChecks = tidyChecks + ",-bugprone-branch-clone"
flags.TidyFlags = append(flags.TidyFlags, tidyChecks)
- if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
- tidyChecksAsErrors := "-warnings-as-errors=" + strings.Join(esc(tidy.Properties.Tidy_checks_as_errors), ",")
+ if ctx.Config().IsEnvTrue("WITH_TIDY") {
+ // WITH_TIDY=1 enables clang-tidy globally. There could be many unexpected
+ // warnings from new checks and many local tidy_checks_as_errors and
+ // -warnings-as-errors can break a global build.
+ // So allow all clang-tidy warnings.
+ inserted := false
+ for i, s := range flags.TidyFlags {
+ if strings.Contains(s, "-warnings-as-errors=") {
+ // clang-tidy accepts only one -warnings-as-errors
+ // replace the old one
+ re := regexp.MustCompile(`'?-?-warnings-as-errors=[^ ]* *`)
+ newFlag := re.ReplaceAllString(s, "")
+ if newFlag == "" {
+ flags.TidyFlags[i] = "-warnings-as-errors=-*"
+ } else {
+ flags.TidyFlags[i] = newFlag + " -warnings-as-errors=-*"
+ }
+ inserted = true
+ break
+ }
+ }
+ if !inserted {
+ flags.TidyFlags = append(flags.TidyFlags, "-warnings-as-errors=-*")
+ }
+ } else if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
+ tidyChecksAsErrors := "-warnings-as-errors=" + strings.Join(esc(ctx, "tidy_checks_as_errors", tidy.Properties.Tidy_checks_as_errors), ",")
flags.TidyFlags = append(flags.TidyFlags, tidyChecksAsErrors)
}
return flags
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index d2c29d6..2f68cca 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -23,8 +23,6 @@
"sort"
"strings"
- "github.com/google/blueprint/proptools"
-
"android/soong/android"
)
@@ -34,6 +32,16 @@
android.OptionalPath{},
true,
vendorSnapshotImageSingleton,
+ false, /* fake */
+}
+
+var vendorFakeSnapshotSingleton = snapshotSingleton{
+ "vendor",
+ "SOONG_VENDOR_FAKE_SNAPSHOT_ZIP",
+ android.OptionalPath{},
+ true,
+ vendorSnapshotImageSingleton,
+ true, /* fake */
}
var recoverySnapshotSingleton = snapshotSingleton{
@@ -42,12 +50,17 @@
android.OptionalPath{},
false,
recoverySnapshotImageSingleton,
+ false, /* fake */
}
func VendorSnapshotSingleton() android.Singleton {
return &vendorSnapshotSingleton
}
+func VendorFakeSnapshotSingleton() android.Singleton {
+ return &vendorFakeSnapshotSingleton
+}
+
func RecoverySnapshotSingleton() android.Singleton {
return &recoverySnapshotSingleton
}
@@ -70,76 +83,31 @@
// associated with this snapshot (e.g., specific to the vendor image,
// recovery image, etc.).
image snapshotImage
+
+ // Whether this singleton is for fake snapshot or not.
+ // Fake snapshot is a snapshot whose prebuilt binaries and headers are empty.
+ // It is much faster to generate, and can be used to inspect dependencies.
+ fake bool
}
-var (
- // Modules under following directories are ignored. They are OEM's and vendor's
- // proprietary modules(device/, kernel/, vendor/, and hardware/).
- vendorProprietaryDirs = []string{
- "device",
- "kernel",
- "vendor",
- "hardware",
- }
-
- // Modules under following directories are ignored. They are OEM's and vendor's
- // proprietary modules(device/, kernel/, vendor/, and hardware/).
- recoveryProprietaryDirs = []string{
- "bootable/recovery",
- "device",
- "hardware",
- "kernel",
- "vendor",
- }
-
- // Modules under following directories are included as they are in AOSP,
- // although hardware/ and kernel/ are normally for vendor's own.
- aospDirsUnderProprietary = []string{
- "kernel/configs",
- "kernel/prebuilts",
- "kernel/tests",
- "hardware/interfaces",
- "hardware/libhardware",
- "hardware/libhardware_legacy",
- "hardware/ril",
- }
-)
-
-// Determine if a dir under source tree is an SoC-owned proprietary directory, such as
-// device/, vendor/, etc.
-func isVendorProprietaryPath(dir string) bool {
- return isProprietaryPath(dir, vendorProprietaryDirs)
+// Determine if a dir under source tree is an SoC-owned proprietary directory based
+// on vendor snapshot configuration
+// Examples: device/, vendor/
+func isVendorProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
+ return VendorSnapshotSingleton().(*snapshotSingleton).image.isProprietaryPath(dir, deviceConfig)
}
-func isRecoveryProprietaryPath(dir string) bool {
- return isProprietaryPath(dir, recoveryProprietaryDirs)
-}
-
-// Determine if a dir under source tree is an SoC-owned proprietary directory, such as
-// device/, vendor/, etc.
-func isProprietaryPath(dir string, proprietaryDirs []string) bool {
- for _, p := range proprietaryDirs {
- if strings.HasPrefix(dir, p) {
- // filter out AOSP defined directories, e.g. hardware/interfaces/
- aosp := false
- for _, p := range aospDirsUnderProprietary {
- if strings.HasPrefix(dir, p) {
- aosp = true
- break
- }
- }
- if !aosp {
- return true
- }
- }
- }
- return false
+// Determine if a dir under source tree is an SoC-owned proprietary directory based
+// on recovery snapshot configuration
+// Examples: device/, vendor/
+func isRecoveryProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
+ return RecoverySnapshotSingleton().(*snapshotSingleton).image.isProprietaryPath(dir, deviceConfig)
}
func isVendorProprietaryModule(ctx android.BaseModuleContext) bool {
// Any module in a vendor proprietary path is a vendor proprietary
// module.
- if isVendorProprietaryPath(ctx.ModuleDir()) {
+ if isVendorProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) {
return true
}
@@ -156,27 +124,30 @@
return false
}
-// Determine if a module is going to be included in vendor snapshot or not.
-//
-// Targets of vendor snapshot are "vendor: true" or "vendor_available: true" modules in
-// AOSP. They are not guaranteed to be compatible with older vendor images. (e.g. might
-// depend on newer VNDK) So they are captured as vendor snapshot To build older vendor
-// image and newer system image altogether.
-func isVendorSnapshotAware(m *Module, inVendorProprietaryPath bool, apexInfo android.ApexInfo) bool {
- return isSnapshotAware(m, inVendorProprietaryPath, apexInfo, vendorSnapshotImageSingleton)
-}
+func isRecoveryProprietaryModule(ctx android.BaseModuleContext) bool {
-// Determine if a module is going to be included in recovery snapshot or not.
-//
-// Targets of recovery snapshot are "recovery: true" or "recovery_available: true"
-// modules in AOSP. They are not guaranteed to be compatible with older recovery images.
-// So they are captured as recovery snapshot To build older recovery image.
-func isRecoverySnapshotAware(m *Module, inRecoveryProprietaryPath bool, apexInfo android.ApexInfo) bool {
- return isSnapshotAware(m, inRecoveryProprietaryPath, apexInfo, recoverySnapshotImageSingleton)
+ // Any module in a recovery proprietary path is a recovery proprietary
+ // module.
+ if isRecoveryProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) {
+ return true
+ }
+
+ // However if the module is not in a recovery proprietary path, it may
+ // still be a recovery proprietary module. This happens for cc modules
+ // that are excluded from the recovery snapshot, and it means that the
+ // vendor has assumed control of the framework-provided module.
+
+ if c, ok := ctx.Module().(*Module); ok {
+ if c.ExcludeFromRecoverySnapshot() {
+ return true
+ }
+ }
+
+ return false
}
// Determines if the module is a candidate for snapshot.
-func isSnapshotAware(m *Module, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshotImage) bool {
+func isSnapshotAware(cfg android.DeviceConfig, m *Module, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshotImage) bool {
if !m.Enabled() || m.Properties.HideFromMake {
return false
}
@@ -192,7 +163,7 @@
}
// If the module would be included based on its path, check to see if
// the module is marked to be excluded. If so, skip it.
- if m.ExcludeFromVendorSnapshot() {
+ if image.excludeFromSnapshot(m) {
return false
}
if m.Target().Os.Class != android.Device {
@@ -202,7 +173,7 @@
return false
}
// the module must be installed in target image
- if !apexInfo.IsForPlatform() || m.isSnapshotPrebuilt() || !image.inImage(m)() {
+ if !apexInfo.IsForPlatform() || m.IsSnapshotPrebuilt() || !image.inImage(m)() {
return false
}
// skip kernel_headers which always depend on vendor
@@ -225,7 +196,7 @@
if m.sanitize != nil {
// scs and hwasan export both sanitized and unsanitized variants for static and header
// Always use unsanitized variants of them.
- for _, t := range []sanitizerType{scs, hwasan} {
+ for _, t := range []SanitizerType{scs, Hwasan} {
if !l.shared() && m.sanitize.isSanitizerEnabled(t) {
return false
}
@@ -238,7 +209,7 @@
}
}
if l.static() {
- return m.outputFile.Valid() && proptools.BoolDefault(image.available(m), true)
+ return m.outputFile.Valid() && !image.private(m)
}
if l.shared() {
if !m.outputFile.Valid() {
@@ -256,7 +227,7 @@
// Binaries and Objects
if m.binary() || m.object() {
- return m.outputFile.Valid() && proptools.BoolDefault(image.available(m), true)
+ return m.outputFile.Valid()
}
return false
@@ -290,8 +261,7 @@
}
func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
- // BOARD_VNDK_VERSION must be set to 'current' in order to generate a vendor snapshot.
- if ctx.DeviceConfig().VndkVersion() != "current" {
+ if !c.image.shouldGenerateSnapshot(ctx) {
return
}
@@ -331,6 +301,11 @@
*/
snapshotDir := c.name + "-snapshot"
+ if c.fake {
+ // If this is a fake snapshot singleton, place all files under fake/ subdirectory to avoid
+ // collision with real snapshot files
+ snapshotDir = filepath.Join("fake", snapshotDir)
+ }
snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
includeDir := filepath.Join(snapshotArchDir, "include")
@@ -342,9 +317,19 @@
var headers android.Paths
+ copyFile := func(ctx android.SingletonContext, path android.Path, out string, fake bool) android.OutputPath {
+ if fake {
+ // All prebuilt binaries and headers are installed by copyFile function. This makes a fake
+ // snapshot just touch prebuilts and headers, rather than installing real files.
+ return writeStringToFileRule(ctx, "", out)
+ } else {
+ return copyFileRule(ctx, path, out)
+ }
+ }
+
// installSnapshot function copies prebuilt file (.so, .a, or executable) and json flag file.
// For executables, init_rc and vintf_fragments files are also copied.
- installSnapshot := func(m *Module) android.Paths {
+ installSnapshot := func(m *Module, fake bool) android.Paths {
targetArch := "arch-" + m.Target().Arch.ArchType.String()
if m.Target().Arch.ArchVariant != "" {
targetArch += "-" + m.Target().Arch.ArchVariant
@@ -380,7 +365,7 @@
out := filepath.Join(configsDir, path.Base())
if !installedConfigs[out] {
installedConfigs[out] = true
- ret = append(ret, copyFileRule(ctx, path, out))
+ ret = append(ret, copyFile(ctx, path, out, fake))
}
}
@@ -431,7 +416,7 @@
prop.ModuleName += ".cfi"
}
snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, stem)
- ret = append(ret, copyFileRule(ctx, libPath, snapshotLibOut))
+ ret = append(ret, copyFile(ctx, libPath, snapshotLibOut, fake))
} else {
stem = ctx.ModuleName(m)
}
@@ -445,7 +430,7 @@
// install bin
binPath := m.outputFile.Path()
snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base())
- ret = append(ret, copyFileRule(ctx, binPath, snapshotBinOut))
+ ret = append(ret, copyFile(ctx, binPath, snapshotBinOut, fake))
propOut = snapshotBinOut + ".json"
} else if m.object() {
// object files aren't installed to the device, so their names can conflict.
@@ -453,7 +438,7 @@
objPath := m.outputFile.Path()
snapshotObjOut := filepath.Join(snapshotArchDir, targetArch, "object",
ctx.ModuleName(m)+filepath.Ext(objPath.Base()))
- ret = append(ret, copyFileRule(ctx, objPath, snapshotObjOut))
+ ret = append(ret, copyFile(ctx, objPath, snapshotObjOut, fake))
propOut = snapshotObjOut + ".json"
} else {
ctx.Errorf("unknown module %q in vendor snapshot", m.String())
@@ -477,36 +462,33 @@
}
moduleDir := ctx.ModuleDir(module)
- inProprietaryPath := c.image.isProprietaryPath(moduleDir)
+ inProprietaryPath := c.image.isProprietaryPath(moduleDir, ctx.DeviceConfig())
apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
- if m.ExcludeFromVendorSnapshot() {
+ if c.image.excludeFromSnapshot(m) {
if inProprietaryPath {
// Error: exclude_from_vendor_snapshot applies
// to framework-path modules only.
ctx.Errorf("module %q in vendor proprietary path %q may not use \"exclude_from_vendor_snapshot: true\"", m.String(), moduleDir)
return
}
- if Bool(c.image.available(m)) {
- // Error: may not combine "vendor_available:
- // true" with "exclude_from_vendor_snapshot:
- // true".
- ctx.Errorf(
- "module %q may not use both \""+
- c.name+
- "_available: true\" and \"exclude_from_vendor_snapshot: true\"",
- m.String())
- return
- }
}
- if !isSnapshotAware(m, inProprietaryPath, apexInfo, c.image) {
+ if !isSnapshotAware(ctx.DeviceConfig(), m, inProprietaryPath, apexInfo, c.image) {
return
}
- // installSnapshot installs prebuilts and json flag files
- snapshotOutputs = append(snapshotOutputs, installSnapshot(m)...)
+ // If we are using directed snapshot and a module is not included in the
+ // list, we will still include the module as if it was a fake module.
+ // The reason is that soong needs all the dependencies to be present, even
+ // if they are not using during the build.
+ installAsFake := c.fake
+ if c.image.excludeFromDirectedSnapshot(ctx.DeviceConfig(), m.BaseModuleName()) {
+ installAsFake = true
+ }
+ // installSnapshot installs prebuilts and json flag files
+ snapshotOutputs = append(snapshotOutputs, installSnapshot(m, installAsFake)...)
// just gather headers and notice files here, because they are to be deduplicated
if l, ok := m.linker.(snapshotLibraryInterface); ok {
headers = append(headers, l.snapshotHeaders()...)
@@ -518,16 +500,14 @@
// skip already copied notice file
if !installedNotices[noticeOut] {
installedNotices[noticeOut] = true
- snapshotOutputs = append(snapshotOutputs, combineNoticesRule(
- ctx, m.NoticeFiles(), noticeOut))
+ snapshotOutputs = append(snapshotOutputs, combineNoticesRule(ctx, m.NoticeFiles(), noticeOut))
}
}
})
// install all headers after removing duplicates
for _, header := range android.FirstUniquePaths(headers) {
- snapshotOutputs = append(snapshotOutputs, copyFileRule(
- ctx, header, filepath.Join(includeDir, header.String())))
+ snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String()), c.fake))
}
// All artifacts are ready. Sort them to normalize ninja and then zip.
@@ -546,10 +526,11 @@
ctx,
snapshotDir,
c.name+"-"+ctx.Config().DeviceName()+"_list")
+ rspFile := snapshotOutputList.ReplaceExtension(ctx, "rsp")
zipRule.Command().
Text("tr").
FlagWithArg("-d ", "\\'").
- FlagWithRspFileInputList("< ", snapshotOutputs).
+ FlagWithRspFileInputList("< ", rspFile, snapshotOutputs).
FlagWithOutput("> ", snapshotOutputList)
zipRule.Temporary(snapshotOutputList)
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
new file mode 100644
index 0000000..66396f7
--- /dev/null
+++ b/cc/vendor_snapshot_test.go
@@ -0,0 +1,1478 @@
+// Copyright 2021 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"
+ "fmt"
+ "path/filepath"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func TestVendorSnapshotCapture(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "libvndk",
+ vendor_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ },
+ nocrt: true,
+ }
+
+ cc_library {
+ name: "libvendor",
+ vendor: true,
+ nocrt: true,
+ }
+
+ cc_library {
+ name: "libvendor_available",
+ vendor_available: true,
+ nocrt: true,
+ }
+
+ cc_library_headers {
+ name: "libvendor_headers",
+ vendor_available: true,
+ nocrt: true,
+ }
+
+ cc_binary {
+ name: "vendor_bin",
+ vendor: true,
+ nocrt: true,
+ }
+
+ cc_binary {
+ name: "vendor_available_bin",
+ vendor_available: true,
+ nocrt: true,
+ }
+
+ toolchain_library {
+ name: "libb",
+ vendor_available: true,
+ src: "libb.a",
+ }
+
+ cc_object {
+ name: "obj",
+ vendor_available: true,
+ }
+
+ cc_library {
+ name: "libllndk",
+ llndk_stubs: "libllndk.llndk",
+ }
+
+ llndk_library {
+ name: "libllndk.llndk",
+ symbol_file: "",
+ }
+`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
+ ctx := testCcWithConfig(t, config)
+
+ // Check Vendor snapshot output.
+
+ snapshotDir := "vendor-snapshot"
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+
+ var jsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ []string{"arm", "armv7-a-neon"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ // For shared libraries, only non-VNDK vendor_available modules are captured
+ sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+ checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(sharedDir, "libvendor.so.json"),
+ filepath.Join(sharedDir, "libvendor_available.so.json"))
+
+ // LLNDK modules are not captured
+ checkSnapshotExclude(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", sharedDir, sharedVariant)
+
+ // For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
+ // Also cfi variants are captured, except for prebuilts like toolchain_library
+ staticVariant := fmt.Sprintf("android_vendor.29_%s_%s_static", archType, archVariant)
+ staticCfiVariant := fmt.Sprintf("android_vendor.29_%s_%s_static_cfi", archType, archVariant)
+ staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
+ checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.a", staticDir, staticVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.cfi.a", staticDir, staticCfiVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.a", staticDir, staticVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.cfi.a", staticDir, staticCfiVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.a", staticDir, staticVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.cfi.a", staticDir, staticCfiVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(staticDir, "libb.a.json"),
+ filepath.Join(staticDir, "libvndk.a.json"),
+ filepath.Join(staticDir, "libvndk.cfi.a.json"),
+ filepath.Join(staticDir, "libvendor.a.json"),
+ filepath.Join(staticDir, "libvendor.cfi.a.json"),
+ filepath.Join(staticDir, "libvendor_available.a.json"),
+ filepath.Join(staticDir, "libvendor_available.cfi.a.json"))
+
+ // For binary executables, all vendor:true and vendor_available modules are captured.
+ if archType == "arm64" {
+ binaryVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant)
+ binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
+ checkSnapshot(t, ctx, snapshotSingleton, "vendor_bin", "vendor_bin", binaryDir, binaryVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "vendor_available_bin", "vendor_available_bin", binaryDir, binaryVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(binaryDir, "vendor_bin.json"),
+ filepath.Join(binaryDir, "vendor_available_bin.json"))
+ }
+
+ // For header libraries, all vendor:true and vendor_available modules are captured.
+ headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
+ jsonFiles = append(jsonFiles, filepath.Join(headerDir, "libvendor_headers.json"))
+
+ // For object modules, all vendor:true and vendor_available modules are captured.
+ objectVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant)
+ objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
+ checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
+ jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
+ }
+
+ for _, jsonFile := range jsonFiles {
+ // verify all json files exist
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+ t.Errorf("%q expected but not found", jsonFile)
+ }
+ }
+
+ // fake snapshot should have all outputs in the normal snapshot.
+ fakeSnapshotSingleton := ctx.SingletonForTests("vendor-fake-snapshot")
+ for _, output := range snapshotSingleton.AllOutputs() {
+ fakeOutput := strings.Replace(output, "/vendor-snapshot/", "/fake/vendor-snapshot/", 1)
+ if fakeSnapshotSingleton.MaybeOutput(fakeOutput).Rule == nil {
+ t.Errorf("%q expected but not found", fakeOutput)
+ }
+ }
+}
+
+func TestVendorSnapshotDirected(t *testing.T) {
+ bp := `
+ cc_library_shared {
+ name: "libvendor",
+ vendor: true,
+ nocrt: true,
+ }
+
+ cc_library_shared {
+ name: "libvendor_available",
+ vendor_available: true,
+ nocrt: true,
+ }
+
+ genrule {
+ name: "libfoo_gen",
+ cmd: "",
+ out: ["libfoo.so"],
+ }
+
+ cc_prebuilt_library_shared {
+ name: "libfoo",
+ vendor: true,
+ prefer: true,
+ srcs: [":libfoo_gen"],
+ }
+
+ cc_library_shared {
+ name: "libfoo",
+ vendor: true,
+ nocrt: true,
+ }
+`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
+ config.TestProductVariables.DirectedVendorSnapshot = true
+ config.TestProductVariables.VendorSnapshotModules = make(map[string]bool)
+ config.TestProductVariables.VendorSnapshotModules["libvendor"] = true
+ config.TestProductVariables.VendorSnapshotModules["libfoo"] = true
+ ctx := testCcWithConfig(t, config)
+
+ // Check Vendor snapshot output.
+
+ snapshotDir := "vendor-snapshot"
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+
+ var includeJsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ []string{"arm", "armv7-a-neon"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+
+ // Included modules
+ checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libvendor.so.json"))
+ // Check that snapshot captures "prefer: true" prebuilt
+ checkSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json"))
+
+ // Excluded modules. Modules not included in the directed vendor snapshot
+ // are still include as fake modules.
+ checkSnapshotRule(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libvendor_available.so.json"))
+ }
+
+ // Verify that each json file for an included module has a rule.
+ for _, jsonFile := range includeJsonFiles {
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+ t.Errorf("include json file %q not found", jsonFile)
+ }
+ }
+}
+
+func TestVendorSnapshotUse(t *testing.T) {
+ frameworkBp := `
+ cc_library {
+ name: "libvndk",
+ vendor_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ },
+ nocrt: true,
+ }
+
+ cc_library {
+ name: "libvendor",
+ vendor: true,
+ nocrt: true,
+ no_libcrt: true,
+ stl: "none",
+ system_shared_libs: [],
+ }
+
+ cc_library {
+ name: "libvendor_available",
+ vendor_available: true,
+ nocrt: true,
+ no_libcrt: true,
+ stl: "none",
+ system_shared_libs: [],
+ }
+
+ cc_library {
+ name: "lib32",
+ vendor: true,
+ nocrt: true,
+ no_libcrt: true,
+ stl: "none",
+ system_shared_libs: [],
+ compile_multilib: "32",
+ }
+
+ cc_library {
+ name: "lib64",
+ vendor: true,
+ nocrt: true,
+ no_libcrt: true,
+ stl: "none",
+ system_shared_libs: [],
+ compile_multilib: "64",
+ }
+
+ cc_binary {
+ name: "bin",
+ vendor: true,
+ nocrt: true,
+ no_libcrt: true,
+ stl: "none",
+ system_shared_libs: [],
+ }
+
+ cc_binary {
+ name: "bin32",
+ vendor: true,
+ nocrt: true,
+ no_libcrt: true,
+ stl: "none",
+ system_shared_libs: [],
+ compile_multilib: "32",
+ }
+`
+
+ vndkBp := `
+ vndk_prebuilt_shared {
+ name: "libvndk",
+ version: "30",
+ target_arch: "arm64",
+ vendor_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ },
+ arch: {
+ arm64: {
+ srcs: ["libvndk.so"],
+ export_include_dirs: ["include/libvndk"],
+ },
+ arm: {
+ srcs: ["libvndk.so"],
+ export_include_dirs: ["include/libvndk"],
+ },
+ },
+ }
+
+ // old snapshot module which has to be ignored
+ vndk_prebuilt_shared {
+ name: "libvndk",
+ version: "26",
+ target_arch: "arm64",
+ vendor_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ },
+ arch: {
+ arm64: {
+ srcs: ["libvndk.so"],
+ export_include_dirs: ["include/libvndk"],
+ },
+ arm: {
+ srcs: ["libvndk.so"],
+ export_include_dirs: ["include/libvndk"],
+ },
+ },
+ }
+
+ // different arch snapshot which has to be ignored
+ vndk_prebuilt_shared {
+ name: "libvndk",
+ version: "30",
+ target_arch: "arm",
+ vendor_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ },
+ arch: {
+ arm: {
+ srcs: ["libvndk.so"],
+ export_include_dirs: ["include/libvndk"],
+ },
+ },
+ }
+`
+
+ vendorProprietaryBp := `
+ cc_library {
+ name: "libvendor_without_snapshot",
+ vendor: true,
+ nocrt: true,
+ no_libcrt: true,
+ stl: "none",
+ system_shared_libs: [],
+ }
+
+ cc_library_shared {
+ name: "libclient",
+ vendor: true,
+ nocrt: true,
+ no_libcrt: true,
+ stl: "none",
+ system_shared_libs: [],
+ shared_libs: ["libvndk", "libvendor_available"],
+ static_libs: ["libvendor", "libvendor_without_snapshot"],
+ arch: {
+ arm64: {
+ shared_libs: ["lib64"],
+ },
+ arm: {
+ shared_libs: ["lib32"],
+ },
+ },
+ srcs: ["client.cpp"],
+ }
+
+ cc_library_shared {
+ name: "libclient_cfi",
+ vendor: true,
+ nocrt: true,
+ no_libcrt: true,
+ stl: "none",
+ system_shared_libs: [],
+ static_libs: ["libvendor"],
+ sanitize: {
+ cfi: true,
+ },
+ srcs: ["client.cpp"],
+ }
+
+ cc_binary {
+ name: "bin_without_snapshot",
+ vendor: true,
+ nocrt: true,
+ no_libcrt: true,
+ stl: "libc++_static",
+ system_shared_libs: [],
+ static_libs: ["libvndk"],
+ srcs: ["bin.cpp"],
+ }
+
+ vendor_snapshot {
+ name: "vendor_snapshot",
+ version: "30",
+ arch: {
+ arm64: {
+ vndk_libs: [
+ "libvndk",
+ ],
+ static_libs: [
+ "libc++_static",
+ "libc++demangle",
+ "libgcc_stripped",
+ "libvendor",
+ "libvendor_available",
+ "libvndk",
+ "lib64",
+ ],
+ shared_libs: [
+ "libvendor",
+ "libvendor_available",
+ "lib64",
+ ],
+ binaries: [
+ "bin",
+ ],
+ },
+ arm: {
+ vndk_libs: [
+ "libvndk",
+ ],
+ static_libs: [
+ "libvendor",
+ "libvendor_available",
+ "libvndk",
+ "lib32",
+ ],
+ shared_libs: [
+ "libvendor",
+ "libvendor_available",
+ "lib32",
+ ],
+ binaries: [
+ "bin32",
+ ],
+ },
+ }
+ }
+
+ vendor_snapshot_static {
+ name: "libvndk",
+ version: "30",
+ target_arch: "arm64",
+ compile_multilib: "both",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "libvndk.a",
+ },
+ arm: {
+ src: "libvndk.a",
+ },
+ },
+ shared_libs: ["libvndk"],
+ export_shared_lib_headers: ["libvndk"],
+ }
+
+ vendor_snapshot_shared {
+ name: "libvendor",
+ version: "30",
+ target_arch: "arm64",
+ compile_multilib: "both",
+ vendor: true,
+ shared_libs: [
+ "libvendor_without_snapshot",
+ "libvendor_available",
+ "libvndk",
+ ],
+ arch: {
+ arm64: {
+ src: "libvendor.so",
+ export_include_dirs: ["include/libvendor"],
+ },
+ arm: {
+ src: "libvendor.so",
+ export_include_dirs: ["include/libvendor"],
+ },
+ },
+ }
+
+ vendor_snapshot_static {
+ name: "lib32",
+ version: "30",
+ target_arch: "arm64",
+ compile_multilib: "32",
+ vendor: true,
+ arch: {
+ arm: {
+ src: "lib32.a",
+ },
+ },
+ }
+
+ vendor_snapshot_shared {
+ name: "lib32",
+ version: "30",
+ target_arch: "arm64",
+ compile_multilib: "32",
+ vendor: true,
+ arch: {
+ arm: {
+ src: "lib32.so",
+ },
+ },
+ }
+
+ vendor_snapshot_static {
+ name: "lib64",
+ version: "30",
+ target_arch: "arm64",
+ compile_multilib: "64",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "lib64.a",
+ },
+ },
+ }
+
+ vendor_snapshot_shared {
+ name: "lib64",
+ version: "30",
+ target_arch: "arm64",
+ compile_multilib: "64",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "lib64.so",
+ },
+ },
+ }
+
+ vendor_snapshot_static {
+ name: "libvendor",
+ version: "30",
+ target_arch: "arm64",
+ compile_multilib: "both",
+ vendor: true,
+ arch: {
+ arm64: {
+ cfi: {
+ src: "libvendor.cfi.a",
+ export_include_dirs: ["include/libvendor_cfi"],
+ },
+ src: "libvendor.a",
+ export_include_dirs: ["include/libvendor"],
+ },
+ arm: {
+ cfi: {
+ src: "libvendor.cfi.a",
+ export_include_dirs: ["include/libvendor_cfi"],
+ },
+ src: "libvendor.a",
+ export_include_dirs: ["include/libvendor"],
+ },
+ },
+ }
+
+ vendor_snapshot_shared {
+ name: "libvendor_available",
+ version: "30",
+ target_arch: "arm64",
+ compile_multilib: "both",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "libvendor_available.so",
+ export_include_dirs: ["include/libvendor"],
+ },
+ arm: {
+ src: "libvendor_available.so",
+ export_include_dirs: ["include/libvendor"],
+ },
+ },
+ }
+
+ vendor_snapshot_static {
+ name: "libvendor_available",
+ version: "30",
+ target_arch: "arm64",
+ compile_multilib: "both",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "libvendor_available.a",
+ export_include_dirs: ["include/libvendor"],
+ },
+ arm: {
+ src: "libvendor_available.so",
+ export_include_dirs: ["include/libvendor"],
+ },
+ },
+ }
+
+ vendor_snapshot_static {
+ name: "libc++_static",
+ version: "30",
+ target_arch: "arm64",
+ compile_multilib: "64",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "libc++_static.a",
+ },
+ },
+ }
+
+ vendor_snapshot_static {
+ name: "libc++demangle",
+ version: "30",
+ target_arch: "arm64",
+ compile_multilib: "64",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "libc++demangle.a",
+ },
+ },
+ }
+
+ vendor_snapshot_static {
+ name: "libgcc_stripped",
+ version: "30",
+ target_arch: "arm64",
+ compile_multilib: "64",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "libgcc_stripped.a",
+ },
+ },
+ }
+
+ vendor_snapshot_binary {
+ name: "bin",
+ version: "30",
+ target_arch: "arm64",
+ compile_multilib: "64",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "bin",
+ },
+ },
+ }
+
+ vendor_snapshot_binary {
+ name: "bin32",
+ version: "30",
+ target_arch: "arm64",
+ compile_multilib: "32",
+ vendor: true,
+ arch: {
+ arm: {
+ src: "bin32",
+ },
+ },
+ }
+
+ // old snapshot module which has to be ignored
+ vendor_snapshot_binary {
+ name: "bin",
+ version: "26",
+ target_arch: "arm64",
+ compile_multilib: "first",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "bin",
+ },
+ },
+ }
+
+ // different arch snapshot which has to be ignored
+ vendor_snapshot_binary {
+ name: "bin",
+ version: "30",
+ target_arch: "arm",
+ compile_multilib: "first",
+ vendor: true,
+ arch: {
+ arm64: {
+ src: "bin",
+ },
+ },
+ }
+`
+ depsBp := GatherRequiredDepsForTest(android.Android)
+
+ mockFS := map[string][]byte{
+ "deps/Android.bp": []byte(depsBp),
+ "framework/Android.bp": []byte(frameworkBp),
+ "framework/symbol.txt": nil,
+ "vendor/Android.bp": []byte(vendorProprietaryBp),
+ "vendor/bin": nil,
+ "vendor/bin32": nil,
+ "vendor/bin.cpp": nil,
+ "vendor/client.cpp": nil,
+ "vendor/include/libvndk/a.h": nil,
+ "vendor/include/libvendor/b.h": nil,
+ "vendor/include/libvendor_cfi/c.h": nil,
+ "vendor/libc++_static.a": nil,
+ "vendor/libc++demangle.a": nil,
+ "vendor/libgcc_striped.a": nil,
+ "vendor/libvndk.a": nil,
+ "vendor/libvendor.a": nil,
+ "vendor/libvendor.cfi.a": nil,
+ "vendor/libvendor.so": nil,
+ "vendor/lib32.a": nil,
+ "vendor/lib32.so": nil,
+ "vendor/lib64.a": nil,
+ "vendor/lib64.so": nil,
+ "vndk/Android.bp": []byte(vndkBp),
+ "vndk/include/libvndk/a.h": nil,
+ "vndk/libvndk.so": nil,
+ }
+
+ config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
+ config.TestProductVariables.DeviceVndkVersion = StringPtr("30")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("31")
+ ctx := CreateTestContext(config)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "vendor/Android.bp", "vndk/Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+
+ sharedVariant := "android_vendor.30_arm64_armv8-a_shared"
+ staticVariant := "android_vendor.30_arm64_armv8-a_static"
+ binaryVariant := "android_vendor.30_arm64_armv8-a"
+
+ sharedCfiVariant := "android_vendor.30_arm64_armv8-a_shared_cfi"
+ staticCfiVariant := "android_vendor.30_arm64_armv8-a_static_cfi"
+
+ shared32Variant := "android_vendor.30_arm_armv7-a-neon_shared"
+ binary32Variant := "android_vendor.30_arm_armv7-a-neon"
+
+ // libclient uses libvndk.vndk.30.arm64, libvendor.vendor_static.30.arm64, libvendor_without_snapshot
+ libclientCcFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("cc").Args["cFlags"]
+ for _, includeFlags := range []string{
+ "-Ivndk/include/libvndk", // libvndk
+ "-Ivendor/include/libvendor", // libvendor
+ } {
+ if !strings.Contains(libclientCcFlags, includeFlags) {
+ t.Errorf("flags for libclient must contain %#v, but was %#v.",
+ includeFlags, libclientCcFlags)
+ }
+ }
+
+ libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("ld").Args["libFlags"]
+ for _, input := range [][]string{
+ []string{sharedVariant, "libvndk.vndk.30.arm64"},
+ []string{staticVariant, "libvendor.vendor_static.30.arm64"},
+ []string{staticVariant, "libvendor_without_snapshot"},
+ } {
+ outputPaths := getOutputPaths(ctx, input[0] /* variant */, []string{input[1]} /* module name */)
+ if !strings.Contains(libclientLdFlags, outputPaths[0].String()) {
+ t.Errorf("libflags for libclient must contain %#v, but was %#v", outputPaths[0], libclientLdFlags)
+ }
+ }
+
+ libclientAndroidMkSharedLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkSharedLibs
+ if g, w := libclientAndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "lib64"}; !reflect.DeepEqual(g, w) {
+ t.Errorf("wanted libclient AndroidMkSharedLibs %q, got %q", w, g)
+ }
+
+ libclientAndroidMkStaticLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkStaticLibs
+ if g, w := libclientAndroidMkStaticLibs, []string{"libvendor", "libvendor_without_snapshot"}; !reflect.DeepEqual(g, w) {
+ t.Errorf("wanted libclient AndroidMkStaticLibs %q, got %q", w, g)
+ }
+
+ libclient32AndroidMkSharedLibs := ctx.ModuleForTests("libclient", shared32Variant).Module().(*Module).Properties.AndroidMkSharedLibs
+ if g, w := libclient32AndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "lib32"}; !reflect.DeepEqual(g, w) {
+ t.Errorf("wanted libclient32 AndroidMkSharedLibs %q, got %q", w, g)
+ }
+
+ // libclient_cfi uses libvendor.vendor_static.30.arm64's cfi variant
+ libclientCfiCcFlags := ctx.ModuleForTests("libclient_cfi", sharedCfiVariant).Rule("cc").Args["cFlags"]
+ if !strings.Contains(libclientCfiCcFlags, "-Ivendor/include/libvendor_cfi") {
+ t.Errorf("flags for libclient_cfi must contain %#v, but was %#v.",
+ "-Ivendor/include/libvendor_cfi", libclientCfiCcFlags)
+ }
+
+ libclientCfiLdFlags := ctx.ModuleForTests("libclient_cfi", sharedCfiVariant).Rule("ld").Args["libFlags"]
+ libvendorCfiOutputPaths := getOutputPaths(ctx, staticCfiVariant, []string{"libvendor.vendor_static.30.arm64"})
+ if !strings.Contains(libclientCfiLdFlags, libvendorCfiOutputPaths[0].String()) {
+ t.Errorf("libflags for libclientCfi must contain %#v, but was %#v", libvendorCfiOutputPaths[0], libclientCfiLdFlags)
+ }
+
+ // bin_without_snapshot uses libvndk.vendor_static.30.arm64 (which reexports vndk's exported headers)
+ binWithoutSnapshotCcFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("cc").Args["cFlags"]
+ if !strings.Contains(binWithoutSnapshotCcFlags, "-Ivndk/include/libvndk") {
+ t.Errorf("flags for bin_without_snapshot must contain %#v, but was %#v.",
+ "-Ivendor/include/libvndk", binWithoutSnapshotCcFlags)
+ }
+
+ binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("ld").Args["libFlags"]
+ libVndkStaticOutputPaths := getOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.30.arm64"})
+ if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) {
+ t.Errorf("libflags for bin_without_snapshot must contain %#v, but was %#v",
+ libVndkStaticOutputPaths[0], binWithoutSnapshotLdFlags)
+ }
+
+ // libvendor.so is installed by libvendor.vendor_shared.30.arm64
+ ctx.ModuleForTests("libvendor.vendor_shared.30.arm64", sharedVariant).Output("libvendor.so")
+
+ // lib64.so is installed by lib64.vendor_shared.30.arm64
+ ctx.ModuleForTests("lib64.vendor_shared.30.arm64", sharedVariant).Output("lib64.so")
+
+ // lib32.so is installed by lib32.vendor_shared.30.arm64
+ ctx.ModuleForTests("lib32.vendor_shared.30.arm64", shared32Variant).Output("lib32.so")
+
+ // libvendor_available.so is installed by libvendor_available.vendor_shared.30.arm64
+ ctx.ModuleForTests("libvendor_available.vendor_shared.30.arm64", sharedVariant).Output("libvendor_available.so")
+
+ // libvendor_without_snapshot.so is installed by libvendor_without_snapshot
+ ctx.ModuleForTests("libvendor_without_snapshot", sharedVariant).Output("libvendor_without_snapshot.so")
+
+ // bin is installed by bin.vendor_binary.30.arm64
+ ctx.ModuleForTests("bin.vendor_binary.30.arm64", binaryVariant).Output("bin")
+
+ // bin32 is installed by bin32.vendor_binary.30.arm64
+ ctx.ModuleForTests("bin32.vendor_binary.30.arm64", binary32Variant).Output("bin32")
+
+ // bin_without_snapshot is installed by bin_without_snapshot
+ ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Output("bin_without_snapshot")
+
+ // libvendor, libvendor_available and bin don't have vendor.30 variant
+ libvendorVariants := ctx.ModuleVariantsForTests("libvendor")
+ if inList(sharedVariant, libvendorVariants) {
+ t.Errorf("libvendor must not have variant %#v, but it does", sharedVariant)
+ }
+
+ libvendorAvailableVariants := ctx.ModuleVariantsForTests("libvendor_available")
+ if inList(sharedVariant, libvendorAvailableVariants) {
+ t.Errorf("libvendor_available must not have variant %#v, but it does", sharedVariant)
+ }
+
+ binVariants := ctx.ModuleVariantsForTests("bin")
+ if inList(binaryVariant, binVariants) {
+ t.Errorf("bin must not have variant %#v, but it does", sharedVariant)
+ }
+}
+
+func TestVendorSnapshotSanitizer(t *testing.T) {
+ bp := `
+ vendor_snapshot {
+ name: "vendor_snapshot",
+ version: "28",
+ arch: {
+ arm64: {
+ static_libs: [
+ "libsnapshot",
+ "note_memtag_heap_sync",
+ ],
+ },
+ },
+ }
+ vendor_snapshot_static {
+ name: "libsnapshot",
+ vendor: true,
+ target_arch: "arm64",
+ version: "28",
+ arch: {
+ arm64: {
+ src: "libsnapshot.a",
+ cfi: {
+ src: "libsnapshot.cfi.a",
+ }
+ },
+ },
+ }
+
+ vendor_snapshot_static {
+ name: "note_memtag_heap_sync",
+ vendor: true,
+ target_arch: "arm64",
+ version: "28",
+ arch: {
+ arm64: {
+ src: "note_memtag_heap_sync.a",
+ },
+ },
+ }
+
+ cc_test {
+ name: "vstest",
+ gtest: false,
+ vendor: true,
+ compile_multilib: "64",
+ nocrt: true,
+ no_libcrt: true,
+ stl: "none",
+ static_libs: ["libsnapshot"],
+ system_shared_libs: [],
+ }
+`
+
+ mockFS := map[string][]byte{
+ "vendor/Android.bp": []byte(bp),
+ "vendor/libc++demangle.a": nil,
+ "vendor/libsnapshot.a": nil,
+ "vendor/libsnapshot.cfi.a": nil,
+ "vendor/note_memtag_heap_sync.a": nil,
+ }
+
+ config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
+ config.TestProductVariables.DeviceVndkVersion = StringPtr("28")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
+ ctx := testCcWithConfig(t, config)
+
+ // Check non-cfi and cfi variant.
+ staticVariant := "android_vendor.28_arm64_armv8-a_static"
+ staticCfiVariant := "android_vendor.28_arm64_armv8-a_static_cfi"
+
+ staticModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticVariant).Module().(*Module)
+ assertString(t, staticModule.outputFile.Path().Base(), "libsnapshot.a")
+
+ staticCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticCfiVariant).Module().(*Module)
+ assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a")
+}
+
+func assertExcludeFromVendorSnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool) {
+ t.Helper()
+ m := ctx.ModuleForTests(name, vendorVariant).Module().(*Module)
+ if m.ExcludeFromVendorSnapshot() != expected {
+ t.Errorf("expected %q ExcludeFromVendorSnapshot to be %t", m.String(), expected)
+ }
+}
+
+func assertExcludeFromRecoverySnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool) {
+ t.Helper()
+ m := ctx.ModuleForTests(name, recoveryVariant).Module().(*Module)
+ if m.ExcludeFromRecoverySnapshot() != expected {
+ t.Errorf("expected %q ExcludeFromRecoverySnapshot to be %t", m.String(), expected)
+ }
+}
+
+func TestVendorSnapshotExclude(t *testing.T) {
+
+ // This test verifies that the exclude_from_vendor_snapshot property
+ // makes its way from the Android.bp source file into the module data
+ // structure. It also verifies that modules are correctly included or
+ // excluded in the vendor snapshot based on their path (framework or
+ // vendor) and the exclude_from_vendor_snapshot property.
+
+ frameworkBp := `
+ cc_library_shared {
+ name: "libinclude",
+ srcs: ["src/include.cpp"],
+ vendor_available: true,
+ }
+ cc_library_shared {
+ name: "libexclude",
+ srcs: ["src/exclude.cpp"],
+ vendor: true,
+ exclude_from_vendor_snapshot: true,
+ }
+ cc_library_shared {
+ name: "libavailable_exclude",
+ srcs: ["src/exclude.cpp"],
+ vendor_available: true,
+ exclude_from_vendor_snapshot: true,
+ }
+ `
+
+ vendorProprietaryBp := `
+ cc_library_shared {
+ name: "libvendor",
+ srcs: ["vendor.cpp"],
+ vendor: true,
+ }
+ `
+
+ depsBp := GatherRequiredDepsForTest(android.Android)
+
+ mockFS := map[string][]byte{
+ "deps/Android.bp": []byte(depsBp),
+ "framework/Android.bp": []byte(frameworkBp),
+ "framework/include.cpp": nil,
+ "framework/exclude.cpp": nil,
+ "device/Android.bp": []byte(vendorProprietaryBp),
+ "device/vendor.cpp": nil,
+ }
+
+ config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
+ config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
+ ctx := CreateTestContext(config)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "device/Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+
+ // Test an include and exclude framework module.
+ assertExcludeFromVendorSnapshotIs(t, ctx, "libinclude", false)
+ assertExcludeFromVendorSnapshotIs(t, ctx, "libexclude", true)
+ assertExcludeFromVendorSnapshotIs(t, ctx, "libavailable_exclude", true)
+
+ // A vendor module is excluded, but by its path, not the
+ // exclude_from_vendor_snapshot property.
+ assertExcludeFromVendorSnapshotIs(t, ctx, "libvendor", false)
+
+ // Verify the content of the vendor snapshot.
+
+ snapshotDir := "vendor-snapshot"
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+
+ var includeJsonFiles []string
+ var excludeJsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ []string{"arm", "armv7-a-neon"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+
+ // Included modules
+ checkSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
+
+ // Excluded modules
+ checkSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
+ checkSnapshotExclude(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libvendor.so.json"))
+ checkSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
+ }
+
+ // Verify that each json file for an included module has a rule.
+ for _, jsonFile := range includeJsonFiles {
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+ t.Errorf("include json file %q not found", jsonFile)
+ }
+ }
+
+ // Verify that each json file for an excluded module has no rule.
+ for _, jsonFile := range excludeJsonFiles {
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
+ t.Errorf("exclude json file %q found", jsonFile)
+ }
+ }
+}
+
+func TestVendorSnapshotExcludeInVendorProprietaryPathErrors(t *testing.T) {
+
+ // This test verifies that using the exclude_from_vendor_snapshot
+ // property on a module in a vendor proprietary path generates an
+ // error. These modules are already excluded, so we prohibit using the
+ // property in this way, which could add to confusion.
+
+ vendorProprietaryBp := `
+ cc_library_shared {
+ name: "libvendor",
+ srcs: ["vendor.cpp"],
+ vendor: true,
+ exclude_from_vendor_snapshot: true,
+ }
+ `
+
+ depsBp := GatherRequiredDepsForTest(android.Android)
+
+ mockFS := map[string][]byte{
+ "deps/Android.bp": []byte(depsBp),
+ "device/Android.bp": []byte(vendorProprietaryBp),
+ "device/vendor.cpp": nil,
+ }
+
+ config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
+ config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
+ ctx := CreateTestContext(config)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "device/Android.bp"})
+ android.FailIfErrored(t, errs)
+
+ _, errs = ctx.PrepareBuildActions(config)
+ android.CheckErrorsAgainstExpectations(t, errs, []string{
+ `module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+ `module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+ `module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+ `module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+ `module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+ `module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+ })
+}
+
+func TestRecoverySnapshotCapture(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "libvndk",
+ vendor_available: true,
+ recovery_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ },
+ nocrt: true,
+ }
+
+ cc_library {
+ name: "librecovery",
+ recovery: true,
+ nocrt: true,
+ }
+
+ cc_library {
+ name: "librecovery_available",
+ recovery_available: true,
+ nocrt: true,
+ }
+
+ cc_library_headers {
+ name: "librecovery_headers",
+ recovery_available: true,
+ nocrt: true,
+ }
+
+ cc_binary {
+ name: "recovery_bin",
+ recovery: true,
+ nocrt: true,
+ }
+
+ cc_binary {
+ name: "recovery_available_bin",
+ recovery_available: true,
+ nocrt: true,
+ }
+
+ toolchain_library {
+ name: "libb",
+ recovery_available: true,
+ src: "libb.a",
+ }
+
+ cc_object {
+ name: "obj",
+ recovery_available: true,
+ }
+`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
+ ctx := testCcWithConfig(t, config)
+
+ // Check Recovery snapshot output.
+
+ snapshotDir := "recovery-snapshot"
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+ var jsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ // For shared libraries, only recovery_available modules are captured.
+ sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+ checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", sharedDir, sharedVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(sharedDir, "libvndk.so.json"),
+ filepath.Join(sharedDir, "librecovery.so.json"),
+ filepath.Join(sharedDir, "librecovery_available.so.json"))
+
+ // For static libraries, all recovery:true and recovery_available modules are captured.
+ staticVariant := fmt.Sprintf("android_recovery_%s_%s_static", archType, archVariant)
+ staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
+ checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.a", staticDir, staticVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.a", staticDir, staticVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(staticDir, "libb.a.json"),
+ filepath.Join(staticDir, "librecovery.a.json"),
+ filepath.Join(staticDir, "librecovery_available.a.json"))
+
+ // For binary executables, all recovery:true and recovery_available modules are captured.
+ if archType == "arm64" {
+ binaryVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
+ binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
+ checkSnapshot(t, ctx, snapshotSingleton, "recovery_bin", "recovery_bin", binaryDir, binaryVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "recovery_available_bin", "recovery_available_bin", binaryDir, binaryVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(binaryDir, "recovery_bin.json"),
+ filepath.Join(binaryDir, "recovery_available_bin.json"))
+ }
+
+ // For header libraries, all vendor:true and vendor_available modules are captured.
+ headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
+ jsonFiles = append(jsonFiles, filepath.Join(headerDir, "librecovery_headers.json"))
+
+ // For object modules, all vendor:true and vendor_available modules are captured.
+ objectVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
+ objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
+ checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
+ jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
+ }
+
+ for _, jsonFile := range jsonFiles {
+ // verify all json files exist
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+ t.Errorf("%q expected but not found", jsonFile)
+ }
+ }
+}
+
+func TestRecoverySnapshotExclude(t *testing.T) {
+ // This test verifies that the exclude_from_recovery_snapshot property
+ // makes its way from the Android.bp source file into the module data
+ // structure. It also verifies that modules are correctly included or
+ // excluded in the recovery snapshot based on their path (framework or
+ // vendor) and the exclude_from_recovery_snapshot property.
+
+ frameworkBp := `
+ cc_library_shared {
+ name: "libinclude",
+ srcs: ["src/include.cpp"],
+ recovery_available: true,
+ }
+ cc_library_shared {
+ name: "libexclude",
+ srcs: ["src/exclude.cpp"],
+ recovery: true,
+ exclude_from_recovery_snapshot: true,
+ }
+ cc_library_shared {
+ name: "libavailable_exclude",
+ srcs: ["src/exclude.cpp"],
+ recovery_available: true,
+ exclude_from_recovery_snapshot: true,
+ }
+ `
+
+ vendorProprietaryBp := `
+ cc_library_shared {
+ name: "librecovery",
+ srcs: ["recovery.cpp"],
+ recovery: true,
+ }
+ `
+
+ depsBp := GatherRequiredDepsForTest(android.Android)
+
+ mockFS := map[string][]byte{
+ "deps/Android.bp": []byte(depsBp),
+ "framework/Android.bp": []byte(frameworkBp),
+ "framework/include.cpp": nil,
+ "framework/exclude.cpp": nil,
+ "device/Android.bp": []byte(vendorProprietaryBp),
+ "device/recovery.cpp": nil,
+ }
+
+ config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
+ config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
+ ctx := CreateTestContext(config)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "device/Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+
+ // Test an include and exclude framework module.
+ assertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude", false)
+ assertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude", true)
+ assertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude", true)
+
+ // A recovery module is excluded, but by its path, not the
+ // exclude_from_recovery_snapshot property.
+ assertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery", false)
+
+ // Verify the content of the recovery snapshot.
+
+ snapshotDir := "recovery-snapshot"
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+ var includeJsonFiles []string
+ var excludeJsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+
+ // Included modules
+ checkSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
+
+ // Excluded modules
+ checkSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
+ checkSnapshotExclude(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
+ checkSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
+ }
+
+ // Verify that each json file for an included module has a rule.
+ for _, jsonFile := range includeJsonFiles {
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+ t.Errorf("include json file %q not found", jsonFile)
+ }
+ }
+
+ // Verify that each json file for an excluded module has no rule.
+ for _, jsonFile := range excludeJsonFiles {
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
+ t.Errorf("exclude json file %q found", jsonFile)
+ }
+ }
+}
+
+func TestRecoverySnapshotDirected(t *testing.T) {
+ bp := `
+ cc_library_shared {
+ name: "librecovery",
+ recovery: true,
+ nocrt: true,
+ }
+
+ cc_library_shared {
+ name: "librecovery_available",
+ recovery_available: true,
+ nocrt: true,
+ }
+
+ genrule {
+ name: "libfoo_gen",
+ cmd: "",
+ out: ["libfoo.so"],
+ }
+
+ cc_prebuilt_library_shared {
+ name: "libfoo",
+ recovery: true,
+ prefer: true,
+ srcs: [":libfoo_gen"],
+ }
+
+ cc_library_shared {
+ name: "libfoo",
+ recovery: true,
+ nocrt: true,
+ }
+`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+ config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("29")
+ config.TestProductVariables.DirectedRecoverySnapshot = true
+ config.TestProductVariables.RecoverySnapshotModules = make(map[string]bool)
+ config.TestProductVariables.RecoverySnapshotModules["librecovery"] = true
+ config.TestProductVariables.RecoverySnapshotModules["libfoo"] = true
+ ctx := testCcWithConfig(t, config)
+
+ // Check recovery snapshot output.
+
+ snapshotDir := "recovery-snapshot"
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+ var includeJsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+
+ // Included modules
+ checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
+ // Check that snapshot captures "prefer: true" prebuilt
+ checkSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json"))
+
+ // Excluded modules. Modules not included in the directed recovery snapshot
+ // are still include as fake modules.
+ checkSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery_available.so.json"))
+ }
+
+ // Verify that each json file for an included module has a rule.
+ for _, jsonFile := range includeJsonFiles {
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+ t.Errorf("include json file %q not found", jsonFile)
+ }
+ }
+}
diff --git a/cc/vndk.go b/cc/vndk.go
index 6bc7131..1a8a454 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -21,7 +21,6 @@
"path/filepath"
"sort"
"strings"
- "sync"
"android/soong/android"
"android/soong/cc/config"
@@ -35,6 +34,7 @@
vndkCoreLibrariesTxt = "vndkcore.libraries.txt"
vndkSpLibrariesTxt = "vndksp.libraries.txt"
vndkPrivateLibrariesTxt = "vndkprivate.libraries.txt"
+ vndkProductLibrariesTxt = "vndkproduct.libraries.txt"
vndkUsingCoreVariantLibrariesTxt = "vndkcorevariant.libraries.txt"
)
@@ -45,6 +45,7 @@
vndkCoreLibrariesTxt,
vndkSpLibrariesTxt,
vndkPrivateLibrariesTxt,
+ vndkProductLibrariesTxt,
}
}
// Snapshot vndks have their own *.libraries.VER.txt files.
@@ -54,6 +55,7 @@
insertVndkVersion(vndkCoreLibrariesTxt, vndkVersion),
insertVndkVersion(vndkSpLibrariesTxt, vndkVersion),
insertVndkVersion(vndkPrivateLibrariesTxt, vndkVersion),
+ insertVndkVersion(vndkProductLibrariesTxt, vndkVersion),
}
}
@@ -228,45 +230,59 @@
return nil
}
+type moduleListerFunc func(ctx android.SingletonContext) (moduleNames, fileNames []string)
+
var (
- vndkCoreLibrariesKey = android.NewOnceKey("vndkCoreLibrarires")
- vndkSpLibrariesKey = android.NewOnceKey("vndkSpLibrarires")
- llndkLibrariesKey = android.NewOnceKey("llndkLibrarires")
- vndkPrivateLibrariesKey = android.NewOnceKey("vndkPrivateLibrarires")
- vndkUsingCoreVariantLibrariesKey = android.NewOnceKey("vndkUsingCoreVariantLibraries")
- vndkMustUseVendorVariantListKey = android.NewOnceKey("vndkMustUseVendorVariantListKey")
- vndkLibrariesLock sync.Mutex
+ llndkLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsLLNDK && !isVestigialLLNDKModule(m) })
+ llndkLibrariesWithoutHWASAN = vndkModuleListRemover(llndkLibraries, "libclang_rt.hwasan-")
+ vndkSPLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKSP })
+ vndkCoreLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKCore })
+ vndkPrivateLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKPrivate && !isVestigialLLNDKModule(m) })
+ vndkProductLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKProduct })
+ vndkUsingCoreVariantLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKUsingCoreVariant })
)
-func vndkCoreLibraries(config android.Config) map[string]string {
- return config.Once(vndkCoreLibrariesKey, func() interface{} {
- return make(map[string]string)
- }).(map[string]string)
+// vndkModuleLister takes a predicate that operates on a Module and returns a moduleListerFunc
+// that produces a list of module names and output file names for which the predicate returns true.
+func vndkModuleLister(predicate func(*Module) bool) moduleListerFunc {
+ return func(ctx android.SingletonContext) (moduleNames, fileNames []string) {
+ ctx.VisitAllModules(func(m android.Module) {
+ if c, ok := m.(*Module); ok && predicate(c) {
+ filename, err := getVndkFileName(c)
+ if err != nil {
+ ctx.ModuleErrorf(m, "%s", err)
+ }
+ moduleNames = append(moduleNames, ctx.ModuleName(m))
+ fileNames = append(fileNames, filename)
+ }
+ })
+ moduleNames = android.SortedUniqueStrings(moduleNames)
+ fileNames = android.SortedUniqueStrings(fileNames)
+ return
+ }
}
-func vndkSpLibraries(config android.Config) map[string]string {
- return config.Once(vndkSpLibrariesKey, func() interface{} {
- return make(map[string]string)
- }).(map[string]string)
+// vndkModuleListRemover takes a moduleListerFunc and a prefix and returns a moduleListerFunc
+// that returns the same lists as the input moduleListerFunc, but with modules with the
+// given prefix removed.
+func vndkModuleListRemover(lister moduleListerFunc, prefix string) moduleListerFunc {
+ return func(ctx android.SingletonContext) (moduleNames, fileNames []string) {
+ moduleNames, fileNames = lister(ctx)
+ filter := func(in []string) []string {
+ out := make([]string, 0, len(in))
+ for _, lib := range in {
+ if strings.HasPrefix(lib, prefix) {
+ continue
+ }
+ out = append(out, lib)
+ }
+ return out
+ }
+ return filter(moduleNames), filter(fileNames)
+ }
}
-func llndkLibraries(config android.Config) map[string]string {
- return config.Once(llndkLibrariesKey, func() interface{} {
- return make(map[string]string)
- }).(map[string]string)
-}
-
-func vndkPrivateLibraries(config android.Config) map[string]string {
- return config.Once(vndkPrivateLibrariesKey, func() interface{} {
- return make(map[string]string)
- }).(map[string]string)
-}
-
-func vndkUsingCoreVariantLibraries(config android.Config) map[string]string {
- return config.Once(vndkUsingCoreVariantLibrariesKey, func() interface{} {
- return make(map[string]string)
- }).(map[string]string)
-}
+var vndkMustUseVendorVariantListKey = android.NewOnceKey("vndkMustUseVendorVariantListKey")
func vndkMustUseVendorVariantList(cfg android.Config) []string {
return cfg.Once(vndkMustUseVendorVariantListKey, func() interface{} {
@@ -284,27 +300,22 @@
func processLlndkLibrary(mctx android.BottomUpMutatorContext, m *Module) {
lib := m.linker.(*llndkStubDecorator)
- name := m.ImplementationModuleName(mctx)
- filename := name + ".so"
- vndkLibrariesLock.Lock()
- defer vndkLibrariesLock.Unlock()
-
- llndkLibraries(mctx.Config())[name] = filename
m.VendorProperties.IsLLNDK = true
- if !Bool(lib.Properties.Vendor_available) {
- vndkPrivateLibraries(mctx.Config())[name] = filename
- m.VendorProperties.IsLLNDKPrivate = true
+ if Bool(lib.Properties.Private) {
+ m.VendorProperties.IsVNDKPrivate = true
}
}
func processVndkLibrary(mctx android.BottomUpMutatorContext, m *Module) {
- name := m.BaseModuleName()
- filename, err := getVndkFileName(m)
- if err != nil {
- panic(err)
+ if m.InProduct() {
+ // We may skip the steps for the product variants because they
+ // are already covered by the vendor variants.
+ return
}
+ name := m.BaseModuleName()
+
if lib := m.library; lib != nil && lib.hasStubsVariants() && name != "libz" {
// b/155456180 libz is the ONLY exception here. We don't want to make
// libz an LLNDK library because we in general can't guarantee that
@@ -315,29 +326,23 @@
mctx.PropertyErrorf("vndk.enabled", "This library provides stubs. Shouldn't be VNDK. Consider making it as LLNDK")
}
- vndkLibrariesLock.Lock()
- defer vndkLibrariesLock.Unlock()
-
- if m.InProduct() {
- // We may skip the other steps for the product variants because they
- // are already covered by the vendor variants.
- return
- }
-
if inList(name, vndkMustUseVendorVariantList(mctx.Config())) {
m.Properties.MustUseVendorVariant = true
}
if mctx.DeviceConfig().VndkUseCoreVariant() && !m.Properties.MustUseVendorVariant {
- vndkUsingCoreVariantLibraries(mctx.Config())[name] = filename
+ m.VendorProperties.IsVNDKUsingCoreVariant = true
}
if m.vndkdep.isVndkSp() {
- vndkSpLibraries(mctx.Config())[name] = filename
+ m.VendorProperties.IsVNDKSP = true
} else {
- vndkCoreLibraries(mctx.Config())[name] = filename
+ m.VendorProperties.IsVNDKCore = true
}
if m.IsVndkPrivate() {
- vndkPrivateLibraries(mctx.Config())[name] = filename
+ m.VendorProperties.IsVNDKPrivate = true
+ }
+ if Bool(m.VendorProperties.Product_available) {
+ m.VendorProperties.IsVNDKProduct = true
}
}
@@ -381,7 +386,7 @@
useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() &&
mctx.DeviceConfig().VndkUseCoreVariant() && !m.MustUseVendorVariant()
- return lib.shared() && m.inVendor() && m.IsVndk() && !m.IsVndkExt() && !useCoreVariant
+ return lib.shared() && m.InVendor() && m.IsVndk() && !m.IsVndkExt() && !useCoreVariant
}
return false
}
@@ -411,7 +416,7 @@
flagExporter.Properties = llndkLib.(*llndkStubDecorator).flagExporter.Properties
m.VendorProperties.IsLLNDK = llndk.VendorProperties.IsLLNDK
- m.VendorProperties.IsLLNDKPrivate = llndk.VendorProperties.IsLLNDKPrivate
+ m.VendorProperties.IsVNDKPrivate = llndk.VendorProperties.IsVNDKPrivate
}
}
@@ -436,29 +441,91 @@
}
func init() {
- android.RegisterModuleType("vndk_libraries_txt", VndkLibrariesTxtFactory)
+ RegisterVndkLibraryTxtTypes(android.InitRegistrationContext)
android.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
}
+func RegisterVndkLibraryTxtTypes(ctx android.RegistrationContext) {
+ // Make uses LLNDK_LIBRARIES to determine which libraries to install.
+ // HWASAN is only part of the LL-NDK in builds in which libc depends on HWASAN.
+ // Therefore, by removing the library here, we cause it to only be installed if libc
+ // depends on it.
+ ctx.RegisterSingletonModuleType("llndk_libraries_txt", llndkLibrariesTxtFactory)
+ ctx.RegisterSingletonModuleType("vndksp_libraries_txt", vndkSPLibrariesTxtFactory)
+ ctx.RegisterSingletonModuleType("vndkcore_libraries_txt", vndkCoreLibrariesTxtFactory)
+ ctx.RegisterSingletonModuleType("vndkprivate_libraries_txt", vndkPrivateLibrariesTxtFactory)
+ ctx.RegisterSingletonModuleType("vndkproduct_libraries_txt", vndkProductLibrariesTxtFactory)
+ ctx.RegisterSingletonModuleType("vndkcorevariant_libraries_txt", vndkUsingCoreVariantLibrariesTxtFactory)
+}
+
type vndkLibrariesTxt struct {
- android.ModuleBase
- outputFile android.OutputPath
+ android.SingletonModuleBase
+
+ lister moduleListerFunc
+ makeVarName string
+
+ properties VndkLibrariesTxtProperties
+
+ outputFile android.OutputPath
+ moduleNames []string
+ fileNames []string
+}
+
+type VndkLibrariesTxtProperties struct {
+ Insert_vndk_version *bool
}
var _ etc.PrebuiltEtcModule = &vndkLibrariesTxt{}
var _ android.OutputFileProducer = &vndkLibrariesTxt{}
-// vndk_libraries_txt is a special kind of module type in that it name is one of
-// - llndk.libraries.txt
-// - vndkcore.libraries.txt
-// - vndksp.libraries.txt
-// - vndkprivate.libraries.txt
-// - vndkcorevariant.libraries.txt
-// A module behaves like a prebuilt_etc but its content is generated by soong.
-// By being a soong module, these files can be referenced by other soong modules.
+// llndk_libraries_txt is a singleton module whose content is a list of LLNDK libraries
+// generated by Soong but can be referenced by other modules.
// For example, apex_vndk can depend on these files as prebuilt.
-func VndkLibrariesTxtFactory() android.Module {
- m := &vndkLibrariesTxt{}
+func llndkLibrariesTxtFactory() android.SingletonModule {
+ return newVndkLibrariesTxt(llndkLibrariesWithoutHWASAN, "LLNDK_LIBRARIES")
+}
+
+// vndksp_libraries_txt is a singleton module whose content is a list of VNDKSP libraries
+// generated by Soong but can be referenced by other modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func vndkSPLibrariesTxtFactory() android.SingletonModule {
+ return newVndkLibrariesTxt(vndkSPLibraries, "VNDK_SAMEPROCESS_LIBRARIES")
+}
+
+// vndkcore_libraries_txt is a singleton module whose content is a list of VNDK core libraries
+// generated by Soong but can be referenced by other modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func vndkCoreLibrariesTxtFactory() android.SingletonModule {
+ return newVndkLibrariesTxt(vndkCoreLibraries, "VNDK_CORE_LIBRARIES")
+}
+
+// vndkprivate_libraries_txt is a singleton module whose content is a list of VNDK private libraries
+// generated by Soong but can be referenced by other modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func vndkPrivateLibrariesTxtFactory() android.SingletonModule {
+ return newVndkLibrariesTxt(vndkPrivateLibraries, "VNDK_PRIVATE_LIBRARIES")
+}
+
+// vndkproduct_libraries_txt is a singleton module whose content is a list of VNDK product libraries
+// generated by Soong but can be referenced by other modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func vndkProductLibrariesTxtFactory() android.SingletonModule {
+ return newVndkLibrariesTxt(vndkProductLibraries, "VNDK_PRODUCT_LIBRARIES")
+}
+
+// vndkcorevariant_libraries_txt is a singleton module whose content is a list of VNDK libraries
+// that are using the core variant, generated by Soong but can be referenced by other modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func vndkUsingCoreVariantLibrariesTxtFactory() android.SingletonModule {
+ return newVndkLibrariesTxt(vndkUsingCoreVariantLibraries, "VNDK_USING_CORE_VARIANT_LIBRARIES")
+}
+
+func newVndkLibrariesTxt(lister moduleListerFunc, makeVarName string) android.SingletonModule {
+ m := &vndkLibrariesTxt{
+ lister: lister,
+ makeVarName: makeVarName,
+ }
+ m.AddProperties(&m.properties)
android.InitAndroidModule(m)
return m
}
@@ -471,54 +538,41 @@
}
func (txt *vndkLibrariesTxt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- var list []string
- switch txt.Name() {
- case llndkLibrariesTxt:
- for _, filename := range android.SortedStringMapValues(llndkLibraries(ctx.Config())) {
- if strings.HasPrefix(filename, "libclang_rt.hwasan-") {
- continue
- }
- list = append(list, filename)
- }
- case vndkCoreLibrariesTxt:
- list = android.SortedStringMapValues(vndkCoreLibraries(ctx.Config()))
- case vndkSpLibrariesTxt:
- list = android.SortedStringMapValues(vndkSpLibraries(ctx.Config()))
- case vndkPrivateLibrariesTxt:
- list = android.SortedStringMapValues(vndkPrivateLibraries(ctx.Config()))
- case vndkUsingCoreVariantLibrariesTxt:
- list = android.SortedStringMapValues(vndkUsingCoreVariantLibraries(ctx.Config()))
- default:
- ctx.ModuleErrorf("name(%s) is unknown.", txt.Name())
- return
- }
-
var filename string
- if txt.Name() != vndkUsingCoreVariantLibrariesTxt {
+ if BoolDefault(txt.properties.Insert_vndk_version, true) {
filename = insertVndkVersion(txt.Name(), ctx.DeviceConfig().PlatformVndkVersion())
} else {
filename = txt.Name()
}
txt.outputFile = android.PathForModuleOut(ctx, filename).OutputPath
- android.WriteFileRule(ctx, txt.outputFile, strings.Join(list, "\n"))
installPath := android.PathForModuleInstall(ctx, "etc")
ctx.InstallFile(installPath, filename, txt.outputFile)
}
+func (txt *vndkLibrariesTxt) GenerateSingletonBuildActions(ctx android.SingletonContext) {
+ txt.moduleNames, txt.fileNames = txt.lister(ctx)
+ android.WriteFileRule(ctx, txt.outputFile, strings.Join(txt.fileNames, "\n"))
+}
+
func (txt *vndkLibrariesTxt) AndroidMkEntries() []android.AndroidMkEntries {
return []android.AndroidMkEntries{android.AndroidMkEntries{
Class: "ETC",
OutputFile: android.OptionalPathForPath(txt.outputFile),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_STEM", txt.outputFile.Base())
},
},
}}
}
+func (txt *vndkLibrariesTxt) MakeVars(ctx android.MakeVarsContext) {
+ ctx.Strict(txt.makeVarName, strings.Join(txt.moduleNames, " "))
+
+}
+
// PrebuiltEtcModule interface
func (txt *vndkLibrariesTxt) OutputFile() android.OutputPath {
return txt.outputFile
@@ -555,8 +609,8 @@
}
// !inVendor: There's product/vendor variants for VNDK libs. We only care about vendor variants.
// !installable: Snapshot only cares about "installable" modules.
- // isSnapshotPrebuilt: Snapshotting a snapshot doesn't make sense.
- if !m.inVendor() || !m.installable(apexInfo) || m.isSnapshotPrebuilt() {
+ // IsSnapshotPrebuilt: Snapshotting a snapshot doesn't make sense.
+ if !m.InVendor() || !m.installable(apexInfo) || m.IsSnapshotPrebuilt() {
return nil, "", false
}
l, ok := m.linker.(snapshotLibraryInterface)
@@ -773,10 +827,11 @@
// filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr
snapshotOutputList := android.PathForOutput(ctx, snapshotDir, "android-vndk-"+ctx.DeviceConfig().DeviceArch()+"_list")
+ rspFile := snapshotOutputList.ReplaceExtension(ctx, "rsp")
zipRule.Command().
Text("tr").
FlagWithArg("-d ", "\\'").
- FlagWithRspFileInputList("< ", snapshotOutputs).
+ FlagWithRspFileInputList("< ", rspFile, snapshotOutputs).
FlagWithOutput("> ", snapshotOutputList)
zipRule.Temporary(snapshotOutputList)
@@ -799,31 +854,27 @@
if prebuilt, ok := m.linker.(*prebuiltLibraryLinker); ok {
return prebuilt.libraryDecorator.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
}
+ if library, ok := m.linker.(*llndkStubDecorator); ok {
+ return library.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
+ }
return "", fmt.Errorf("VNDK library should have libraryDecorator or prebuiltLibraryLinker as linker: %T", m.linker)
}
func (c *vndkSnapshotSingleton) buildVndkLibrariesTxtFiles(ctx android.SingletonContext) {
- llndk := android.SortedStringMapValues(llndkLibraries(ctx.Config()))
- vndkcore := android.SortedStringMapValues(vndkCoreLibraries(ctx.Config()))
- vndksp := android.SortedStringMapValues(vndkSpLibraries(ctx.Config()))
- vndkprivate := android.SortedStringMapValues(vndkPrivateLibraries(ctx.Config()))
-
// Build list of vndk libs as merged & tagged & filter-out(libclang_rt):
// Since each target have different set of libclang_rt.* files,
// keep the common set of files in vndk.libraries.txt
+ _, llndk := vndkModuleListRemover(llndkLibraries, "libclang_rt.")(ctx)
+ _, vndkcore := vndkModuleListRemover(vndkCoreLibraries, "libclang_rt.")(ctx)
+ _, vndksp := vndkSPLibraries(ctx)
+ _, vndkprivate := vndkPrivateLibraries(ctx)
+ _, vndkproduct := vndkModuleListRemover(vndkProductLibraries, "libclang_rt.")(ctx)
var merged []string
- filterOutLibClangRt := func(libList []string) (filtered []string) {
- for _, lib := range libList {
- if !strings.HasPrefix(lib, "libclang_rt.") {
- filtered = append(filtered, lib)
- }
- }
- return
- }
- merged = append(merged, addPrefix(filterOutLibClangRt(llndk), "LLNDK: ")...)
+ merged = append(merged, addPrefix(llndk, "LLNDK: ")...)
merged = append(merged, addPrefix(vndksp, "VNDK-SP: ")...)
- merged = append(merged, addPrefix(filterOutLibClangRt(vndkcore), "VNDK-core: ")...)
+ merged = append(merged, addPrefix(vndkcore, "VNDK-core: ")...)
merged = append(merged, addPrefix(vndkprivate, "VNDK-private: ")...)
+ merged = append(merged, addPrefix(vndkproduct, "VNDK-product: ")...)
c.vndkLibrariesFile = android.PathForOutput(ctx, "vndk", "vndk.libraries.txt")
android.WriteFileRule(ctx, c.vndkLibrariesFile, strings.Join(merged, "\n"))
}
@@ -845,25 +896,6 @@
ctx.Strict("LLNDK_MOVED_TO_APEX_LIBRARIES",
strings.Join(android.SortedStringKeys(movedToApexLlndkLibraries), " "))
- // Make uses LLNDK_LIBRARIES to determine which libraries to install.
- // HWASAN is only part of the LL-NDK in builds in which libc depends on HWASAN.
- // Therefore, by removing the library here, we cause it to only be installed if libc
- // depends on it.
- installedLlndkLibraries := []string{}
- for lib := range llndkLibraries(ctx.Config()) {
- if strings.HasPrefix(lib, "libclang_rt.hwasan-") {
- continue
- }
- installedLlndkLibraries = append(installedLlndkLibraries, lib)
- }
- sort.Strings(installedLlndkLibraries)
- ctx.Strict("LLNDK_LIBRARIES", strings.Join(installedLlndkLibraries, " "))
-
- ctx.Strict("VNDK_CORE_LIBRARIES", strings.Join(android.SortedStringKeys(vndkCoreLibraries(ctx.Config())), " "))
- ctx.Strict("VNDK_SAMEPROCESS_LIBRARIES", strings.Join(android.SortedStringKeys(vndkSpLibraries(ctx.Config())), " "))
- ctx.Strict("VNDK_PRIVATE_LIBRARIES", strings.Join(android.SortedStringKeys(vndkPrivateLibraries(ctx.Config())), " "))
- ctx.Strict("VNDK_USING_CORE_VARIANT_LIBRARIES", strings.Join(android.SortedStringKeys(vndkUsingCoreVariantLibraries(ctx.Config())), " "))
-
ctx.Strict("VNDK_LIBRARIES_FILE", c.vndkLibrariesFile.String())
ctx.Strict("SOONG_VNDK_SNAPSHOT_ZIP", c.vndkSnapshotZipFile.String())
}
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index 04162cd..71e6427 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -107,6 +107,10 @@
return "64"
}
+func (p *vndkPrebuiltLibraryDecorator) snapshotAndroidMkSuffix() string {
+ return ".vendor"
+}
+
func (p *vndkPrebuiltLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
p.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), p.NameSuffix())
return p.libraryDecorator.linkerFlags(ctx, flags)
diff --git a/cmd/dep_fixer/Android.bp b/cmd/dep_fixer/Android.bp
index 97364d5..818fd28 100644
--- a/cmd/dep_fixer/Android.bp
+++ b/cmd/dep_fixer/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "dep_fixer",
deps: ["soong-makedeps"],
diff --git a/cmd/diff_target_files/Android.bp b/cmd/diff_target_files/Android.bp
index bc6b068..ae8c329 100644
--- a/cmd/diff_target_files/Android.bp
+++ b/cmd/diff_target_files/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "diff_target_files",
srcs: [
diff --git a/cmd/extract_apks/Android.bp b/cmd/extract_apks/Android.bp
index f8fe864..8a4ed63 100644
--- a/cmd/extract_apks/Android.bp
+++ b/cmd/extract_apks/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "extract_apks",
srcs: ["main.go"],
diff --git a/cmd/extract_jar_packages/Android.bp b/cmd/extract_jar_packages/Android.bp
index 4ea8798..ab33504 100644
--- a/cmd/extract_jar_packages/Android.bp
+++ b/cmd/extract_jar_packages/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "extract_jar_packages",
deps: [
diff --git a/cmd/extract_linker/Android.bp b/cmd/extract_linker/Android.bp
index 690c4fa..d40d250 100644
--- a/cmd/extract_linker/Android.bp
+++ b/cmd/extract_linker/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "extract_linker",
srcs: ["main.go"],
diff --git a/cmd/fileslist/Android.bp b/cmd/fileslist/Android.bp
index cbf939a..3c6f675 100644
--- a/cmd/fileslist/Android.bp
+++ b/cmd/fileslist/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "fileslist",
srcs: [
diff --git a/cmd/host_bionic_inject/Android.bp b/cmd/host_bionic_inject/Android.bp
index 5994103..16bc179 100644
--- a/cmd/host_bionic_inject/Android.bp
+++ b/cmd/host_bionic_inject/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "host_bionic_inject",
deps: ["soong-symbol_inject"],
diff --git a/cmd/javac_wrapper/Android.bp b/cmd/javac_wrapper/Android.bp
index c00f4bd..e441567 100644
--- a/cmd/javac_wrapper/Android.bp
+++ b/cmd/javac_wrapper/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "soong_javac_wrapper",
srcs: [
diff --git a/cmd/merge_zips/Android.bp b/cmd/merge_zips/Android.bp
index 8c97b6d..c516f99 100644
--- a/cmd/merge_zips/Android.bp
+++ b/cmd/merge_zips/Android.bp
@@ -12,13 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "merge_zips",
deps: [
"android-archive-zip",
"blueprint-pathtools",
"soong-jar",
- "soong-zip",
+ "soong-response",
],
srcs: [
"merge_zips.go",
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index 274c8ee..712c7fc 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -25,12 +25,14 @@
"os"
"path/filepath"
"sort"
+ "strings"
+
+ "android/soong/response"
"github.com/google/blueprint/pathtools"
"android/soong/jar"
"android/soong/third_party/zip"
- soongZip "android/soong/zip"
)
// Input zip: we can open it, close it, and obtain an array of entries
@@ -690,15 +692,20 @@
inputs := make([]string, 0)
for _, input := range args[1:] {
if input[0] == '@' {
- bytes, err := ioutil.ReadFile(input[1:])
+ f, err := os.Open(strings.TrimPrefix(input[1:], "@"))
if err != nil {
log.Fatal(err)
}
- inputs = append(inputs, soongZip.ReadRespFile(bytes)...)
- continue
+
+ rspInputs, err := response.ReadRspFile(f)
+ f.Close()
+ if err != nil {
+ log.Fatal(err)
+ }
+ inputs = append(inputs, rspInputs...)
+ } else {
+ inputs = append(inputs, input)
}
- inputs = append(inputs, input)
- continue
}
log.SetFlags(log.Lshortfile)
diff --git a/cmd/multiproduct_kati/Android.bp b/cmd/multiproduct_kati/Android.bp
index d34f8c3..21d8e21 100644
--- a/cmd/multiproduct_kati/Android.bp
+++ b/cmd/multiproduct_kati/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "multiproduct_kati",
deps: [
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 7dd50f6..e0f8caa 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -244,7 +244,7 @@
jobs = runtime.NumCPU() / 4
ramGb := int(config.TotalRAM() / 1024 / 1024 / 1024)
- if ramJobs := ramGb / 20; ramGb > 0 && jobs > ramJobs {
+ if ramJobs := ramGb / 25; ramGb > 0 && jobs > ramJobs {
jobs = ramJobs
}
diff --git a/cmd/path_interposer/Android.bp b/cmd/path_interposer/Android.bp
index 41a219f..875cd72 100644
--- a/cmd/path_interposer/Android.bp
+++ b/cmd/path_interposer/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "path_interposer",
deps: ["soong-ui-build-paths"],
diff --git a/cmd/pom2bp/Android.bp b/cmd/pom2bp/Android.bp
index 0b2b7b5..0dfed8b 100644
--- a/cmd/pom2bp/Android.bp
+++ b/cmd/pom2bp/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "pom2bp",
deps: [
diff --git a/cmd/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go
index d341b8c..d31489e 100644
--- a/cmd/pom2bp/pom2bp.go
+++ b/cmd/pom2bp/pom2bp.go
@@ -144,6 +144,7 @@
var hostAndDeviceModuleNames = HostAndDeviceModuleNames{}
var sdkVersion string
+var defaultMinSdkVersion string
var useVersion string
var staticDeps bool
var jetifier bool
@@ -286,6 +287,10 @@
return sdkVersion
}
+func (p Pom) DefaultMinSdkVersion() string {
+ return defaultMinSdkVersion
+}
+
func (p Pom) Jetifier() bool {
return jetifier
}
@@ -396,6 +401,8 @@
{{- end}}
],
{{- end}}
+ {{- else if not .IsHostOnly}}
+ min_sdk_version: "{{.DefaultMinSdkVersion}}",
{{- end}}
}
`))
@@ -437,6 +444,8 @@
{{- end}}
],
{{- end}}
+ {{- else if not .IsHostOnly}}
+ min_sdk_version: "{{.DefaultMinSdkVersion}}",
{{- end}}
}
@@ -457,7 +466,7 @@
min_sdk_version: "{{.MinSdkVersion}}",
manifest: "manifests/{{.BpName}}/AndroidManifest.xml",
{{- else if not .IsHostOnly}}
- min_sdk_version: "24",
+ min_sdk_version: "{{.DefaultMinSdkVersion}}",
{{- end}}
{{- end}}
static_libs: [
@@ -598,6 +607,8 @@
This may be specified multiple times to declare these dependencies.
-sdk-version <version>
Sets sdk_version: "<version>" for all modules.
+ -default-min-sdk-version
+ The default min_sdk_version to use for a module if one cannot be mined from AndroidManifest.xml
-use-version <version>
If the maven directory contains multiple versions of artifacts and their pom files,
-use-version can be used to only write Android.bp files for a specific version of those artifacts.
@@ -622,6 +633,7 @@
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(&defaultMinSdkVersion, "default-min-sdk-version", "24", "Default min_sdk_version to use, if one is not available from AndroidManifest.xml. Default: 24")
flag.StringVar(&useVersion, "use-version", "", "Only read artifacts of a specific version")
flag.BoolVar(&staticDeps, "static-deps", false, "Statically include direct dependencies")
flag.BoolVar(&jetifier, "jetifier", false, "Sets jetifier: true on all modules")
diff --git a/cmd/pom2mk/Android.bp b/cmd/pom2mk/Android.bp
index 54422b1..cc9dacc 100644
--- a/cmd/pom2mk/Android.bp
+++ b/cmd/pom2mk/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "pom2mk",
deps: ["blueprint-proptools"],
diff --git a/cmd/sbox/Android.bp b/cmd/sbox/Android.bp
index f5e87c0..b8d75ed 100644
--- a/cmd/sbox/Android.bp
+++ b/cmd/sbox/Android.bp
@@ -12,11 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "sbox",
deps: [
"sbox_proto",
"soong-makedeps",
+ "soong-response",
],
srcs: [
"sbox.go",
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index f8919a4..7bd0868 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -32,6 +32,7 @@
"android/soong/cmd/sbox/sbox_proto"
"android/soong/makedeps"
+ "android/soong/response"
"github.com/golang/protobuf/proto"
)
@@ -218,6 +219,11 @@
return "", fmt.Errorf("command is required")
}
+ pathToTempDirInSbox := tempDir
+ if command.GetChdir() {
+ pathToTempDirInSbox = "."
+ }
+
err = os.MkdirAll(tempDir, 0777)
if err != nil {
return "", fmt.Errorf("failed to create %q: %w", tempDir, err)
@@ -228,14 +234,18 @@
if err != nil {
return "", err
}
+ err = copyRspFiles(command.RspFiles, tempDir, pathToTempDirInSbox)
+ if err != nil {
+ return "", err
+ }
if strings.Contains(rawCommand, depFilePlaceholder) {
- depFile = filepath.Join(tempDir, "deps.d")
+ depFile = filepath.Join(pathToTempDirInSbox, "deps.d")
rawCommand = strings.Replace(rawCommand, depFilePlaceholder, depFile, -1)
}
if strings.Contains(rawCommand, sandboxDirPlaceholder) {
- rawCommand = strings.Replace(rawCommand, sandboxDirPlaceholder, tempDir, -1)
+ rawCommand = strings.Replace(rawCommand, sandboxDirPlaceholder, pathToTempDirInSbox, -1)
}
// Emulate ninja's behavior of creating the directories for any output files before
@@ -254,6 +264,15 @@
if command.GetChdir() {
cmd.Dir = tempDir
+ path := os.Getenv("PATH")
+ absPath, err := makeAbsPathEnv(path)
+ if err != nil {
+ return "", err
+ }
+ err = os.Setenv("PATH", absPath)
+ if err != nil {
+ return "", fmt.Errorf("Failed to update PATH: %w", err)
+ }
}
err = cmd.Run()
@@ -368,6 +387,14 @@
}
defer in.Close()
+ // Remove the target before copying. In most cases the file won't exist, but if there are
+ // duplicate copy rules for a file and the source file was read-only the second copy could
+ // fail.
+ err = os.Remove(to)
+ if err != nil && !os.IsNotExist(err) {
+ return err
+ }
+
out, err := os.Create(to)
if err != nil {
return err
@@ -395,6 +422,83 @@
return nil
}
+// copyRspFiles copies rsp files into the sandbox with path mappings, and also copies the files
+// listed into the sandbox.
+func copyRspFiles(rspFiles []*sbox_proto.RspFile, toDir, toDirInSandbox string) error {
+ for _, rspFile := range rspFiles {
+ err := copyOneRspFile(rspFile, toDir, toDirInSandbox)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// copyOneRspFiles copies an rsp file into the sandbox with path mappings, and also copies the files
+// listed into the sandbox.
+func copyOneRspFile(rspFile *sbox_proto.RspFile, toDir, toDirInSandbox string) error {
+ in, err := os.Open(rspFile.GetFile())
+ if err != nil {
+ return err
+ }
+ defer in.Close()
+
+ files, err := response.ReadRspFile(in)
+ if err != nil {
+ return err
+ }
+
+ for i, from := range files {
+ // Convert the real path of the input file into the path inside the sandbox using the
+ // path mappings.
+ to := applyPathMappings(rspFile.PathMappings, from)
+
+ // Copy the file into the sandbox.
+ err := copyOneFile(from, joinPath(toDir, to), false)
+ if err != nil {
+ return err
+ }
+
+ // Rewrite the name in the list of files to be relative to the sandbox directory.
+ files[i] = joinPath(toDirInSandbox, to)
+ }
+
+ // Convert the real path of the rsp file into the path inside the sandbox using the path
+ // mappings.
+ outRspFile := joinPath(toDir, applyPathMappings(rspFile.PathMappings, rspFile.GetFile()))
+
+ err = os.MkdirAll(filepath.Dir(outRspFile), 0777)
+ if err != nil {
+ return err
+ }
+
+ out, err := os.Create(outRspFile)
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+
+ // Write the rsp file with converted paths into the sandbox.
+ err = response.WriteRspFile(out, files)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// applyPathMappings takes a list of path mappings and a path, and returns the path with the first
+// matching path mapping applied. If the path does not match any of the path mappings then it is
+// returned unmodified.
+func applyPathMappings(pathMappings []*sbox_proto.PathMapping, path string) string {
+ for _, mapping := range pathMappings {
+ if strings.HasPrefix(path, mapping.GetFrom()+"/") {
+ return joinPath(mapping.GetTo()+"/", strings.TrimPrefix(path, mapping.GetFrom()+"/"))
+ }
+ }
+ return path
+}
+
// moveFiles moves files specified by a set of copy rules. It uses os.Rename, so it is restricted
// to moving files where the source and destination are in the same filesystem. This is OK for
// sbox because the temporary directory is inside the out directory. It updates the timestamp
@@ -466,3 +570,17 @@
}
return filepath.Join(dir, file)
}
+
+func makeAbsPathEnv(pathEnv string) (string, error) {
+ pathEnvElements := filepath.SplitList(pathEnv)
+ for i, p := range pathEnvElements {
+ if !filepath.IsAbs(p) {
+ absPath, err := filepath.Abs(p)
+ if err != nil {
+ return "", fmt.Errorf("failed to make PATH entry %q absolute: %w", p, err)
+ }
+ pathEnvElements[i] = absPath
+ }
+ }
+ return strings.Join(pathEnvElements, string(filepath.ListSeparator)), nil
+}
diff --git a/cmd/sbox/sbox_proto/sbox.pb.go b/cmd/sbox/sbox_proto/sbox.pb.go
index 79bb90c..b996481 100644
--- a/cmd/sbox/sbox_proto/sbox.pb.go
+++ b/cmd/sbox/sbox_proto/sbox.pb.go
@@ -86,10 +86,13 @@
CopyAfter []*Copy `protobuf:"bytes,4,rep,name=copy_after,json=copyAfter" json:"copy_after,omitempty"`
// An optional hash of the input files to ensure the textproto files and the sbox rule reruns
// when the lists of inputs changes, even if the inputs are not on the command line.
- InputHash *string `protobuf:"bytes,5,opt,name=input_hash,json=inputHash" json:"input_hash,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ InputHash *string `protobuf:"bytes,5,opt,name=input_hash,json=inputHash" json:"input_hash,omitempty"`
+ // A list of files that will be copied before the sandboxed command, and whose contents should be
+ // copied as if they were listed in copy_before.
+ RspFiles []*RspFile `protobuf:"bytes,6,rep,name=rsp_files,json=rspFiles" json:"rsp_files,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
}
func (m *Command) Reset() { *m = Command{} }
@@ -152,6 +155,13 @@
return ""
}
+func (m *Command) GetRspFiles() []*RspFile {
+ if m != nil {
+ return m.RspFiles
+ }
+ return nil
+}
+
// Copy describes a from-to pair of files to copy. The paths may be relative, the root that they
// are relative to is specific to the context the Copy is used in and will be different for
// from and to.
@@ -211,10 +221,110 @@
return false
}
+// RspFile describes an rspfile that should be copied into the sandbox directory.
+type RspFile struct {
+ // The path to the rsp file.
+ File *string `protobuf:"bytes,1,req,name=file" json:"file,omitempty"`
+ // A list of path mappings that should be applied to each file listed in the rsp file.
+ PathMappings []*PathMapping `protobuf:"bytes,2,rep,name=path_mappings,json=pathMappings" json:"path_mappings,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *RspFile) Reset() { *m = RspFile{} }
+func (m *RspFile) String() string { return proto.CompactTextString(m) }
+func (*RspFile) ProtoMessage() {}
+func (*RspFile) Descriptor() ([]byte, []int) {
+ return fileDescriptor_9d0425bf0de86ed1, []int{3}
+}
+
+func (m *RspFile) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_RspFile.Unmarshal(m, b)
+}
+func (m *RspFile) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_RspFile.Marshal(b, m, deterministic)
+}
+func (m *RspFile) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_RspFile.Merge(m, src)
+}
+func (m *RspFile) XXX_Size() int {
+ return xxx_messageInfo_RspFile.Size(m)
+}
+func (m *RspFile) XXX_DiscardUnknown() {
+ xxx_messageInfo_RspFile.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_RspFile proto.InternalMessageInfo
+
+func (m *RspFile) GetFile() string {
+ if m != nil && m.File != nil {
+ return *m.File
+ }
+ return ""
+}
+
+func (m *RspFile) GetPathMappings() []*PathMapping {
+ if m != nil {
+ return m.PathMappings
+ }
+ return nil
+}
+
+// PathMapping describes a mapping from a path outside the sandbox to the path inside the sandbox.
+type PathMapping struct {
+ From *string `protobuf:"bytes,1,req,name=from" json:"from,omitempty"`
+ To *string `protobuf:"bytes,2,req,name=to" json:"to,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *PathMapping) Reset() { *m = PathMapping{} }
+func (m *PathMapping) String() string { return proto.CompactTextString(m) }
+func (*PathMapping) ProtoMessage() {}
+func (*PathMapping) Descriptor() ([]byte, []int) {
+ return fileDescriptor_9d0425bf0de86ed1, []int{4}
+}
+
+func (m *PathMapping) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_PathMapping.Unmarshal(m, b)
+}
+func (m *PathMapping) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_PathMapping.Marshal(b, m, deterministic)
+}
+func (m *PathMapping) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_PathMapping.Merge(m, src)
+}
+func (m *PathMapping) XXX_Size() int {
+ return xxx_messageInfo_PathMapping.Size(m)
+}
+func (m *PathMapping) XXX_DiscardUnknown() {
+ xxx_messageInfo_PathMapping.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_PathMapping proto.InternalMessageInfo
+
+func (m *PathMapping) GetFrom() string {
+ if m != nil && m.From != nil {
+ return *m.From
+ }
+ return ""
+}
+
+func (m *PathMapping) GetTo() string {
+ if m != nil && m.To != nil {
+ return *m.To
+ }
+ return ""
+}
+
func init() {
proto.RegisterType((*Manifest)(nil), "sbox.Manifest")
proto.RegisterType((*Command)(nil), "sbox.Command")
proto.RegisterType((*Copy)(nil), "sbox.Copy")
+ proto.RegisterType((*RspFile)(nil), "sbox.RspFile")
+ proto.RegisterType((*PathMapping)(nil), "sbox.PathMapping")
}
func init() {
@@ -222,22 +332,27 @@
}
var fileDescriptor_9d0425bf0de86ed1 = []byte{
- // 268 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0x4f, 0x4b, 0xc3, 0x40,
- 0x10, 0xc5, 0xc9, 0x9f, 0xd2, 0x64, 0x6a, 0x7b, 0x18, 0x3c, 0xec, 0x45, 0x09, 0x01, 0x21, 0x45,
- 0xe8, 0xc1, 0x6f, 0x60, 0xf5, 0x20, 0x82, 0x97, 0x1c, 0x45, 0x08, 0x9b, 0x64, 0x43, 0x02, 0x4d,
- 0x26, 0xec, 0x6e, 0xa0, 0xfd, 0x56, 0x7e, 0x44, 0xd9, 0x49, 0x2a, 0x82, 0xb7, 0x99, 0xdf, 0xe3,
- 0xcd, 0x7b, 0x0c, 0x80, 0x29, 0xe9, 0x7c, 0x18, 0x35, 0x59, 0xc2, 0xd0, 0xcd, 0xe9, 0x17, 0x44,
- 0x1f, 0x72, 0xe8, 0x1a, 0x65, 0x2c, 0xee, 0x21, 0xaa, 0xa8, 0xef, 0xe5, 0x50, 0x1b, 0xe1, 0x25,
- 0x41, 0xb6, 0x79, 0xda, 0x1e, 0xd8, 0xf0, 0x32, 0xd3, 0xfc, 0x57, 0xc6, 0x07, 0xd8, 0xd1, 0x64,
- 0xc7, 0xc9, 0x16, 0xb5, 0x1a, 0x9b, 0xee, 0xa4, 0x84, 0x9f, 0x78, 0x59, 0x9c, 0x6f, 0x67, 0xfa,
- 0x3a, 0xc3, 0xf4, 0xdb, 0x83, 0xf5, 0x62, 0xc6, 0x47, 0xd8, 0x54, 0x34, 0x5e, 0x8a, 0x52, 0x35,
- 0xa4, 0xd5, 0x12, 0x00, 0xd7, 0x80, 0xf1, 0x92, 0x83, 0x93, 0x8f, 0xac, 0xe2, 0x2d, 0xac, 0xaa,
- 0xb6, 0xee, 0x34, 0x9f, 0x8d, 0xf2, 0x79, 0x41, 0x01, 0xeb, 0xa5, 0x81, 0x08, 0x12, 0x3f, 0x8b,
- 0xf3, 0xeb, 0x8a, 0x7b, 0x60, 0x77, 0x21, 0x1b, 0xab, 0xb4, 0x08, 0xff, 0xdd, 0x8e, 0x9d, 0xfa,
- 0xec, 0x44, 0xbc, 0x03, 0xe8, 0x06, 0xd7, 0xbc, 0x95, 0xa6, 0x15, 0x2b, 0xae, 0x1d, 0x33, 0x79,
- 0x93, 0xa6, 0x4d, 0xdf, 0x21, 0x74, 0x0e, 0x44, 0x08, 0x1b, 0x4d, 0xbd, 0xf0, 0x38, 0x88, 0x67,
- 0xdc, 0x81, 0x6f, 0x49, 0xf8, 0x4c, 0x7c, 0x4b, 0x78, 0x0f, 0xa0, 0xce, 0xaa, 0x9a, 0xac, 0x2c,
- 0x4f, 0x4a, 0x04, 0x5c, 0xf5, 0x0f, 0x39, 0xde, 0x7c, 0xf2, 0xc3, 0x0b, 0x7e, 0xf8, 0x4f, 0x00,
- 0x00, 0x00, 0xff, 0xff, 0x78, 0x37, 0x3e, 0x6a, 0x7d, 0x01, 0x00, 0x00,
+ // 342 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x5f, 0x4b, 0xc3, 0x30,
+ 0x14, 0xc5, 0x69, 0xd7, 0xb9, 0xf6, 0xee, 0x0f, 0x18, 0x7c, 0xc8, 0x8b, 0x32, 0x0a, 0xc2, 0xa6,
+ 0x30, 0xd0, 0x07, 0xdf, 0x9d, 0x22, 0x22, 0x0c, 0x24, 0xe0, 0x8b, 0x08, 0x25, 0xeb, 0x52, 0x5b,
+ 0x58, 0x9b, 0x90, 0x64, 0xb0, 0x7d, 0x57, 0x3f, 0x8c, 0xe4, 0xa6, 0xd3, 0x82, 0x2f, 0xbe, 0xdd,
+ 0x7b, 0x0e, 0xf7, 0xdc, 0x5f, 0xc2, 0x05, 0x30, 0x6b, 0xb9, 0x5f, 0x28, 0x2d, 0xad, 0x24, 0x91,
+ 0xab, 0xd3, 0x0f, 0x88, 0x57, 0xbc, 0xa9, 0x0a, 0x61, 0x2c, 0x99, 0x43, 0x9c, 0xcb, 0xba, 0xe6,
+ 0xcd, 0xc6, 0xd0, 0x60, 0xda, 0x9b, 0x0d, 0x6f, 0xc7, 0x0b, 0x1c, 0x78, 0xf0, 0x2a, 0xfb, 0xb1,
+ 0xc9, 0x25, 0x4c, 0xe4, 0xce, 0xaa, 0x9d, 0xcd, 0x36, 0x42, 0x15, 0xd5, 0x56, 0xd0, 0x70, 0x1a,
+ 0xcc, 0x12, 0x36, 0xf6, 0xea, 0xa3, 0x17, 0xd3, 0xaf, 0x00, 0x06, 0xed, 0x30, 0xb9, 0x86, 0x61,
+ 0x2e, 0xd5, 0x21, 0x5b, 0x8b, 0x42, 0x6a, 0xd1, 0x2e, 0x80, 0xe3, 0x02, 0x75, 0x60, 0xe0, 0xec,
+ 0x25, 0xba, 0xe4, 0x0c, 0xfa, 0x79, 0xb9, 0xa9, 0x34, 0xc6, 0xc6, 0xcc, 0x37, 0x84, 0xc2, 0xa0,
+ 0x25, 0xa0, 0xbd, 0x69, 0x38, 0x4b, 0xd8, 0xb1, 0x25, 0x73, 0xc0, 0xe9, 0x8c, 0x17, 0x56, 0x68,
+ 0x1a, 0xfd, 0xc9, 0x4e, 0x9c, 0x7b, 0xef, 0x4c, 0x72, 0x0e, 0x50, 0x35, 0x8e, 0xbc, 0xe4, 0xa6,
+ 0xa4, 0x7d, 0xc4, 0x4e, 0x50, 0x79, 0xe6, 0xa6, 0x24, 0x57, 0x90, 0x68, 0xa3, 0x32, 0x87, 0x6f,
+ 0xe8, 0x49, 0xf7, 0x17, 0x98, 0x51, 0x4f, 0xd5, 0x56, 0xb0, 0x58, 0xfb, 0xc2, 0xa4, 0x2f, 0x10,
+ 0xb9, 0x74, 0x42, 0x20, 0x2a, 0xb4, 0xac, 0x69, 0x80, 0x50, 0x58, 0x93, 0x09, 0x84, 0x56, 0xd2,
+ 0x10, 0x95, 0xd0, 0x4a, 0x72, 0x01, 0x20, 0xf6, 0x22, 0xdf, 0x59, 0xbe, 0xde, 0x0a, 0xda, 0xc3,
+ 0x67, 0x75, 0x94, 0xf4, 0x0d, 0x06, 0xed, 0x02, 0x8c, 0x73, 0x5f, 0x7a, 0x8c, 0x73, 0xda, 0x1d,
+ 0x8c, 0x15, 0xb7, 0x65, 0x56, 0x73, 0xa5, 0xaa, 0xe6, 0xd3, 0xd0, 0x10, 0xd1, 0x4e, 0x3d, 0xda,
+ 0x2b, 0xb7, 0xe5, 0xca, 0x3b, 0x6c, 0xa4, 0x7e, 0x1b, 0x93, 0xde, 0xc0, 0xb0, 0x63, 0xfe, 0x87,
+ 0x74, 0x39, 0x7a, 0xc7, 0x33, 0xc9, 0xf0, 0x4c, 0xbe, 0x03, 0x00, 0x00, 0xff, 0xff, 0x83, 0x82,
+ 0xb0, 0xc3, 0x33, 0x02, 0x00, 0x00,
}
diff --git a/cmd/sbox/sbox_proto/sbox.proto b/cmd/sbox/sbox_proto/sbox.proto
index 695b0e8..bdf92c6 100644
--- a/cmd/sbox/sbox_proto/sbox.proto
+++ b/cmd/sbox/sbox_proto/sbox.proto
@@ -47,6 +47,10 @@
// An optional hash of the input files to ensure the textproto files and the sbox rule reruns
// when the lists of inputs changes, even if the inputs are not on the command line.
optional string input_hash = 5;
+
+ // A list of files that will be copied before the sandboxed command, and whose contents should be
+ // copied as if they were listed in copy_before.
+ repeated RspFile rsp_files = 6;
}
// Copy describes a from-to pair of files to copy. The paths may be relative, the root that they
@@ -58,4 +62,19 @@
// If true, make the file executable after copying it.
optional bool executable = 3;
-}
\ No newline at end of file
+}
+
+// RspFile describes an rspfile that should be copied into the sandbox directory.
+message RspFile {
+ // The path to the rsp file.
+ required string file = 1;
+
+ // A list of path mappings that should be applied to each file listed in the rsp file.
+ repeated PathMapping path_mappings = 2;
+}
+
+// PathMapping describes a mapping from a path outside the sandbox to the path inside the sandbox.
+message PathMapping {
+ required string from = 1;
+ required string to = 2;
+}
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index 441ea0d..9f09224 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_binary {
name: "soong_build",
deps: [
@@ -20,17 +24,13 @@
"golang-protobuf-proto",
"soong",
"soong-android",
- "soong-env",
+ "soong-bp2build",
"soong-ui-metrics_proto",
],
srcs: [
"main.go",
"writedocs.go",
"queryview.go",
- "queryview_templates.go",
- ],
- testSrcs: [
- "queryview_test.go",
],
primaryBuilder: true,
}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 907bed3..1e796ec 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -17,22 +17,49 @@
import (
"flag"
"fmt"
+ "io/ioutil"
"os"
"path/filepath"
+ "strings"
+ "time"
+ "android/soong/bp2build"
+ "android/soong/shared"
"github.com/google/blueprint/bootstrap"
+ "github.com/google/blueprint/deptools"
"android/soong/android"
)
var (
+ topDir string
+ outDir string
+ availableEnvFile string
+ usedEnvFile string
+
+ delveListen string
+ delvePath string
+
docFile string
bazelQueryViewDir string
+ bp2buildMarker string
)
func init() {
+ // Flags that make sense in every mode
+ flag.StringVar(&topDir, "top", "", "Top directory of the Android source tree")
+ flag.StringVar(&outDir, "out", "", "Soong output directory (usually $TOP/out/soong)")
+ flag.StringVar(&availableEnvFile, "available_env", "", "File containing available environment variables")
+ flag.StringVar(&usedEnvFile, "used_env", "", "File containing used environment variables")
+
+ // Debug flags
+ flag.StringVar(&delveListen, "delve_listen", "", "Delve port to listen on for debugging")
+ flag.StringVar(&delvePath, "delve_path", "", "Path to Delve. Only used if --delve_listen is set")
+
+ // Flags representing various modes soong_build can run in
flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
- flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory")
+ flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top")
+ flag.StringVar(&bp2buildMarker, "bp2build_marker", "", "If set, run bp2build, touch the specified marker file then exit")
}
func newNameResolver(config android.Config) *android.NameResolver {
@@ -51,22 +78,10 @@
return android.NewNameResolver(exportFilter)
}
-// bazelConversionRequested checks that the user is intending to convert
-// Blueprint to Bazel BUILD files.
-func bazelConversionRequested(configuration android.Config) bool {
- return configuration.IsEnvTrue("CONVERT_TO_BAZEL")
-}
-
-func newContext(srcDir string, configuration android.Config) *android.Context {
+func newContext(configuration android.Config, prepareBuildActions bool) *android.Context {
ctx := android.NewContext(configuration)
- if bazelConversionRequested(configuration) {
- // Register an alternate set of singletons and mutators for bazel
- // conversion for Bazel conversion.
- ctx.RegisterForBazelConversion()
- } else {
- ctx.Register()
- }
- if !shouldPrepareBuildActions(configuration) {
+ ctx.Register()
+ if !prepareBuildActions {
configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
}
ctx.SetNameInterface(newNameResolver(configuration))
@@ -74,8 +89,8 @@
return ctx
}
-func newConfig(srcDir string) android.Config {
- configuration, err := android.NewConfig(srcDir, bootstrap.BuildDir, bootstrap.ModuleListFile)
+func newConfig(srcDir, outDir string, availableEnv map[string]string) android.Config {
+ configuration, err := android.NewConfig(srcDir, outDir, bootstrap.CmdlineArgs.ModuleListFile, availableEnv)
if err != nil {
fmt.Fprintf(os.Stderr, "%s", err)
os.Exit(1)
@@ -83,90 +98,322 @@
return configuration
}
+// Bazel-enabled mode. Soong runs in two passes.
+// First pass: Analyze the build tree, but only store all bazel commands
+// needed to correctly evaluate the tree in the second pass.
+// TODO(cparsons): Don't output any ninja file, as the second pass will overwrite
+// the incorrect results from the first pass, and file I/O is expensive.
+func runMixedModeBuild(configuration android.Config, firstCtx *android.Context, extraNinjaDeps []string) {
+ var firstArgs, secondArgs bootstrap.Args
+
+ firstArgs = bootstrap.CmdlineArgs
+ configuration.SetStopBefore(bootstrap.StopBeforeWriteNinja)
+ bootstrap.RunBlueprint(firstArgs, firstCtx.Context, configuration, extraNinjaDeps...)
+
+ // Invoke bazel commands and save results for second pass.
+ if err := configuration.BazelContext.InvokeBazel(); err != nil {
+ fmt.Fprintf(os.Stderr, "%s", err)
+ os.Exit(1)
+ }
+ // Second pass: Full analysis, using the bazel command results. Output ninja file.
+ secondConfig, err := android.ConfigForAdditionalRun(configuration)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s", err)
+ os.Exit(1)
+ }
+ secondCtx := newContext(secondConfig, true)
+ secondArgs = bootstrap.CmdlineArgs
+ ninjaDeps := bootstrap.RunBlueprint(secondArgs, secondCtx.Context, secondConfig, extraNinjaDeps...)
+ err = deptools.WriteDepFile(shared.JoinPath(topDir, secondArgs.DepFile), secondArgs.OutFile, ninjaDeps)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error writing depfile '%s': %s\n", secondArgs.DepFile, err)
+ os.Exit(1)
+ }
+}
+
+// Run the code-generation phase to convert BazelTargetModules to BUILD files.
+func runQueryView(configuration android.Config, ctx *android.Context) {
+ codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView)
+ absoluteQueryViewDir := shared.JoinPath(topDir, bazelQueryViewDir)
+ if err := createBazelQueryView(codegenContext, absoluteQueryViewDir); err != nil {
+ fmt.Fprintf(os.Stderr, "%s", err)
+ os.Exit(1)
+ }
+}
+
+func runSoongDocs(configuration android.Config, extraNinjaDeps []string) {
+ ctx := newContext(configuration, false)
+ soongDocsArgs := bootstrap.CmdlineArgs
+ bootstrap.RunBlueprint(soongDocsArgs, ctx.Context, configuration, extraNinjaDeps...)
+ if err := writeDocs(ctx, configuration, docFile); err != nil {
+ fmt.Fprintf(os.Stderr, "%s", err)
+ os.Exit(1)
+ }
+}
+
+func writeMetrics(configuration android.Config) {
+ metricsFile := filepath.Join(configuration.BuildDir(), "soong_build_metrics.pb")
+ err := android.WriteMetrics(configuration, metricsFile)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error writing soong_build metrics %s: %s", metricsFile, err)
+ os.Exit(1)
+ }
+}
+
+func writeJsonModuleGraph(configuration android.Config, ctx *android.Context, path string, extraNinjaDeps []string) {
+ f, err := os.Create(path)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s", err)
+ os.Exit(1)
+ }
+
+ defer f.Close()
+ ctx.Context.PrintJSONGraph(f)
+ writeFakeNinjaFile(extraNinjaDeps, configuration.BuildDir())
+}
+
+func doChosenActivity(configuration android.Config, extraNinjaDeps []string) string {
+ bazelConversionRequested := configuration.IsEnvTrue("GENERATE_BAZEL_FILES") || bp2buildMarker != ""
+ mixedModeBuild := configuration.BazelContext.BazelEnabled()
+ generateQueryView := bazelQueryViewDir != ""
+ jsonModuleFile := configuration.Getenv("SOONG_DUMP_JSON_MODULE_GRAPH")
+
+ blueprintArgs := bootstrap.CmdlineArgs
+ prepareBuildActions := !generateQueryView && jsonModuleFile == ""
+ if bazelConversionRequested {
+ // Run the alternate pipeline of bp2build mutators and singleton to convert
+ // Blueprint to BUILD files before everything else.
+ runBp2Build(configuration, extraNinjaDeps)
+ if bp2buildMarker != "" {
+ return bp2buildMarker
+ } else {
+ return bootstrap.CmdlineArgs.OutFile
+ }
+ }
+
+ ctx := newContext(configuration, prepareBuildActions)
+ if mixedModeBuild {
+ runMixedModeBuild(configuration, ctx, extraNinjaDeps)
+ } else {
+ ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, ctx.Context, configuration, extraNinjaDeps...)
+ err := deptools.WriteDepFile(shared.JoinPath(topDir, blueprintArgs.DepFile), blueprintArgs.OutFile, ninjaDeps)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error writing depfile '%s': %s\n", blueprintArgs.DepFile, err)
+ os.Exit(1)
+ }
+ }
+
+ // Convert the Soong module graph into Bazel BUILD files.
+ if generateQueryView {
+ runQueryView(configuration, ctx)
+ return bootstrap.CmdlineArgs.OutFile // TODO: This is a lie
+ }
+
+ if jsonModuleFile != "" {
+ writeJsonModuleGraph(configuration, ctx, jsonModuleFile, extraNinjaDeps)
+ return bootstrap.CmdlineArgs.OutFile // TODO: This is a lie
+ }
+
+ writeMetrics(configuration)
+ return bootstrap.CmdlineArgs.OutFile
+}
+
+// soong_ui dumps the available environment variables to
+// soong.environment.available . Then soong_build itself is run with an empty
+// environment so that the only way environment variables can be accessed is
+// using Config, which tracks access to them.
+
+// At the end of the build, a file called soong.environment.used is written
+// containing the current value of all used environment variables. The next
+// time soong_ui is run, it checks whether any environment variables that was
+// used had changed and if so, it deletes soong.environment.used to cause a
+// rebuild.
+//
+// The dependency of build.ninja on soong.environment.used is declared in
+// build.ninja.d
+func parseAvailableEnv() map[string]string {
+ if availableEnvFile == "" {
+ fmt.Fprintf(os.Stderr, "--available_env not set\n")
+ os.Exit(1)
+ }
+
+ result, err := shared.EnvFromFile(shared.JoinPath(topDir, availableEnvFile))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error reading available environment file '%s': %s\n", availableEnvFile, err)
+ os.Exit(1)
+ }
+
+ return result
+}
+
func main() {
- android.ReexecWithDelveMaybe()
flag.Parse()
+ shared.ReexecWithDelveMaybe(delveListen, delvePath)
+ android.InitSandbox(topDir)
+
+ availableEnv := parseAvailableEnv()
+
// The top-level Blueprints file is passed as the first argument.
srcDir := filepath.Dir(flag.Arg(0))
- var ctx *android.Context
- configuration := newConfig(srcDir)
- extraNinjaDeps := []string{configuration.ProductVariablesFileName}
+ configuration := newConfig(srcDir, outDir, availableEnv)
+ extraNinjaDeps := []string{
+ configuration.ProductVariablesFileName,
+ usedEnvFile,
+ }
- // 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 != "" {
+ if configuration.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
+ configuration.SetAllowMissingDependencies()
+ }
+
+ if shared.IsDebugging() {
// 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"))
}
- if configuration.BazelContext.BazelEnabled() {
- // Bazel-enabled mode. Soong runs in two passes.
- // First pass: Analyze the build tree, but only store all bazel commands
- // needed to correctly evaluate the tree in the second pass.
- // TODO(cparsons): Don't output any ninja file, as the second pass will overwrite
- // the incorrect results from the first pass, and file I/O is expensive.
- firstCtx := newContext(srcDir, configuration)
- configuration.SetStopBefore(bootstrap.StopBeforeWriteNinja)
- bootstrap.Main(firstCtx.Context, configuration, extraNinjaDeps...)
- // Invoke bazel commands and save results for second pass.
- if err := configuration.BazelContext.InvokeBazel(); err != nil {
- fmt.Fprintf(os.Stderr, "%s", err)
- os.Exit(1)
- }
- // Second pass: Full analysis, using the bazel command results. Output ninja file.
- secondPassConfig, err := android.ConfigForAdditionalRun(configuration)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%s", err)
- os.Exit(1)
- }
- ctx = newContext(srcDir, secondPassConfig)
- bootstrap.Main(ctx.Context, secondPassConfig, extraNinjaDeps...)
- } else {
- ctx = newContext(srcDir, configuration)
- bootstrap.Main(ctx.Context, configuration, extraNinjaDeps...)
- }
-
- // Convert the Soong module graph into Bazel BUILD files.
- if bazelQueryViewDir != "" {
- if err := createBazelQueryView(ctx, bazelQueryViewDir); err != nil {
- fmt.Fprintf(os.Stderr, "%s", err)
- os.Exit(1)
- }
- }
if docFile != "" {
- if err := writeDocs(ctx, docFile); err != nil {
- fmt.Fprintf(os.Stderr, "%s", err)
- os.Exit(1)
- }
+ // We don't write an used variables file when generating documentation
+ // because that is done from within the actual builds as a Ninja action and
+ // thus it would overwrite the actual used variables file so this is
+ // special-cased.
+ // TODO: Fix this by not passing --used_env to the soong_docs invocation
+ runSoongDocs(configuration, extraNinjaDeps)
+ return
}
- // TODO(ccross): make this a command line argument. Requires plumbing through blueprint
- // to affect the command line of the primary builder.
- if shouldPrepareBuildActions(configuration) {
- metricsFile := filepath.Join(bootstrap.BuildDir, "soong_build_metrics.pb")
- err := android.WriteMetrics(configuration, metricsFile)
- if err != nil {
- fmt.Fprintf(os.Stderr, "error writing soong_build metrics %s: %s", metricsFile, err)
- os.Exit(1)
- }
+ finalOutputFile := doChosenActivity(configuration, extraNinjaDeps)
+ writeUsedEnvironmentFile(configuration, finalOutputFile)
+}
+
+func writeUsedEnvironmentFile(configuration android.Config, finalOutputFile string) {
+ if usedEnvFile == "" {
+ return
+ }
+
+ path := shared.JoinPath(topDir, usedEnvFile)
+ data, err := shared.EnvFileContents(configuration.EnvDeps())
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error writing used environment file '%s': %s\n", usedEnvFile, err)
+ os.Exit(1)
+ }
+
+ err = ioutil.WriteFile(path, data, 0666)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error writing used environment file '%s': %s\n", usedEnvFile, err)
+ os.Exit(1)
+ }
+
+ // Touch the output file so that it's not older than the file we just
+ // wrote. We can't write the environment file earlier because one an access
+ // new environment variables while writing it.
+ touch(shared.JoinPath(topDir, finalOutputFile))
+}
+
+// Workarounds to support running bp2build in a clean AOSP checkout with no
+// prior builds, and exiting early as soon as the BUILD files get generated,
+// therefore not creating build.ninja files that soong_ui and callers of
+// soong_build expects.
+//
+// These files are: build.ninja and build.ninja.d. Since Kati hasn't been
+// ran as well, and `nothing` is defined in a .mk file, there isn't a ninja
+// target called `nothing`, so we manually create it here.
+func writeFakeNinjaFile(extraNinjaDeps []string, buildDir string) {
+ extraNinjaDepsString := strings.Join(extraNinjaDeps, " \\\n ")
+
+ ninjaFileName := "build.ninja"
+ ninjaFile := shared.JoinPath(topDir, buildDir, ninjaFileName)
+ ninjaFileD := shared.JoinPath(topDir, buildDir, ninjaFileName)
+ // A workaround to create the 'nothing' ninja target so `m nothing` works,
+ // since bp2build runs without Kati, and the 'nothing' target is declared in
+ // a Makefile.
+ ioutil.WriteFile(ninjaFile, []byte("build nothing: phony\n phony_output = true\n"), 0666)
+ ioutil.WriteFile(ninjaFileD,
+ []byte(fmt.Sprintf("%s: \\\n %s\n", ninjaFileName, extraNinjaDepsString)),
+ 0666)
+}
+
+func touch(path string) {
+ f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error touching '%s': %s\n", path, err)
+ os.Exit(1)
+ }
+
+ err = f.Close()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error touching '%s': %s\n", path, err)
+ os.Exit(1)
+ }
+
+ currentTime := time.Now().Local()
+ err = os.Chtimes(path, currentTime, currentTime)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error touching '%s': %s\n", path, err)
+ os.Exit(1)
}
}
-// shouldPrepareBuildActions reads configuration and flags if build actions
-// should be generated.
-func shouldPrepareBuildActions(configuration android.Config) bool {
- // Generating Soong docs
- if docFile != "" {
- return false
+// Run Soong in the bp2build mode. This creates a standalone context that registers
+// an alternate pipeline of mutators and singletons specifically for generating
+// Bazel BUILD files instead of Ninja files.
+func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
+ // Register an alternate set of singletons and mutators for bazel
+ // conversion for Bazel conversion.
+ bp2buildCtx := android.NewContext(configuration)
+
+ // Propagate "allow misssing dependencies" bit. This is normally set in
+ // newContext(), but we create bp2buildCtx without calling that method.
+ bp2buildCtx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
+ bp2buildCtx.SetNameInterface(newNameResolver(configuration))
+ bp2buildCtx.RegisterForBazelConversion()
+
+ // The bp2build process is a purely functional process that only depends on
+ // Android.bp files. It must not depend on the values of per-build product
+ // configurations or variables, since those will generate different BUILD
+ // files based on how the user has configured their tree.
+ bp2buildCtx.SetModuleListFile(bootstrap.CmdlineArgs.ModuleListFile)
+ modulePaths, err := bp2buildCtx.ListModulePaths(configuration.SrcDir())
+ if err != nil {
+ panic(err)
}
- // Generating a directory for Soong query (queryview)
- if bazelQueryViewDir != "" {
- return false
+ extraNinjaDeps = append(extraNinjaDeps, modulePaths...)
+
+ // No need to generate Ninja build rules/statements from Modules and Singletons.
+ configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
+
+ // Run the loading and analysis pipeline to prepare the graph of regular
+ // Modules parsed from Android.bp files, and the BazelTargetModules mapped
+ // from the regular Modules.
+ blueprintArgs := bootstrap.CmdlineArgs
+ ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bp2buildCtx.Context, configuration, extraNinjaDeps...)
+
+ for _, globPath := range bp2buildCtx.Globs() {
+ ninjaDeps = append(ninjaDeps, globPath.FileListFile(configuration.BuildDir()))
}
- // Generating a directory for converted Bazel BUILD files
- return !bazelConversionRequested(configuration)
+ depFile := bp2buildMarker + ".d"
+ err = deptools.WriteDepFile(shared.JoinPath(topDir, depFile), bp2buildMarker, ninjaDeps)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot write depfile '%s': %s\n", depFile, err)
+ os.Exit(1)
+ }
+
+ // Run the code-generation phase to convert BazelTargetModules to BUILD files
+ // and print conversion metrics to the user.
+ codegenContext := bp2build.NewCodegenContext(configuration, *bp2buildCtx, bp2build.Bp2Build)
+ metrics := bp2build.Codegen(codegenContext)
+
+ // Only report metrics when in bp2build mode. The metrics aren't relevant
+ // for queryview, since that's a total repo-wide conversion and there's a
+ // 1:1 mapping for each module.
+ metrics.Print()
+
+ extraNinjaDeps = append(extraNinjaDeps, codegenContext.AdditionalNinjaDeps()...)
+ if bp2buildMarker != "" {
+ touch(shared.JoinPath(topDir, bp2buildMarker))
+ } else {
+ writeFakeNinjaFile(extraNinjaDeps, codegenContext.Config().BuildDir())
+ }
}
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index f5aa685..edc8a42 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -16,512 +16,48 @@
import (
"android/soong/android"
- "fmt"
+ "android/soong/bp2build"
"io/ioutil"
"os"
"path/filepath"
- "reflect"
- "strings"
-
- "github.com/google/blueprint"
- "github.com/google/blueprint/bootstrap/bpdoc"
- "github.com/google/blueprint/proptools"
)
-var (
- // An allowlist of prop types that are surfaced from module props to rule
- // attributes. (nested) dictionaries are notably absent here, because while
- // Soong supports multi value typed and nested dictionaries, Bazel's rule
- // attr() API supports only single-level string_dicts.
- allowedPropTypes = map[string]bool{
- "int": true, // e.g. 42
- "bool": true, // e.g. True
- "string_list": true, // e.g. ["a", "b"]
- "string": true, // e.g. "a"
- }
+func createBazelQueryView(ctx *bp2build.CodegenContext, bazelQueryViewDir string) error {
+ ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
- // Certain module property names are blocklisted/ignored here, for the reasons commented.
- ignoredPropNames = map[string]bool{
- "name": true, // redundant, since this is explicitly generated for every target
- "from": true, // reserved keyword
- "in": true, // reserved keyword
- "arch": true, // interface prop type is not supported yet.
- "multilib": true, // interface prop type is not supported yet.
- "target": true, // interface prop type is not supported yet.
- "visibility": true, // Bazel has native visibility semantics. Handle later.
- "features": true, // There is already a built-in attribute 'features' which cannot be overridden.
- }
-)
+ // Ignore metrics reporting for queryview, since queryview is already a full-repo
+ // conversion and can use data from bazel query directly.
+ buildToTargets, _ := bp2build.GenerateBazelTargets(ctx)
-func targetNameWithVariant(c *blueprint.Context, logicModule blueprint.Module) string {
- name := ""
- if c.ModuleSubDir(logicModule) != "" {
- // TODO(b/162720883): Figure out a way to drop the "--" variant suffixes.
- name = c.ModuleName(logicModule) + "--" + c.ModuleSubDir(logicModule)
- } else {
- name = c.ModuleName(logicModule)
- }
-
- return strings.Replace(name, "//", "", 1)
-}
-
-func qualifiedTargetLabel(c *blueprint.Context, logicModule blueprint.Module) string {
- return "//" +
- packagePath(c, logicModule) +
- ":" +
- targetNameWithVariant(c, logicModule)
-}
-
-func packagePath(c *blueprint.Context, logicModule blueprint.Module) string {
- return filepath.Dir(c.BlueprintFile(logicModule))
-}
-
-func escapeString(s string) string {
- s = strings.ReplaceAll(s, "\\", "\\\\")
- return strings.ReplaceAll(s, "\"", "\\\"")
-}
-
-func makeIndent(indent int) string {
- if indent < 0 {
- panic(fmt.Errorf("indent column cannot be less than 0, but got %d", indent))
- }
- return strings.Repeat(" ", indent)
-}
-
-// prettyPrint a property value into the equivalent Starlark representation
-// recursively.
-func prettyPrint(propertyValue reflect.Value, indent int) (string, error) {
- if isZero(propertyValue) {
- // A property value being set or unset actually matters -- Soong does set default
- // values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
- // https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
- //
- // In Bazel-parlance, we would use "attr.<type>(default = <default value>)" to set the default
- // value of unset attributes.
- return "", nil
- }
-
- var ret string
- switch propertyValue.Kind() {
- case reflect.String:
- ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
- case reflect.Bool:
- ret = strings.Title(fmt.Sprintf("%v", propertyValue.Interface()))
- case reflect.Int, reflect.Uint, reflect.Int64:
- ret = fmt.Sprintf("%v", propertyValue.Interface())
- case reflect.Ptr:
- return prettyPrint(propertyValue.Elem(), indent)
- case reflect.Slice:
- ret = "[\n"
- for i := 0; i < propertyValue.Len(); i++ {
- indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1)
- if err != nil {
- return "", err
- }
-
- if indexedValue != "" {
- ret += makeIndent(indent + 1)
- ret += indexedValue
- ret += ",\n"
- }
- }
- ret += makeIndent(indent)
- ret += "]"
- case reflect.Struct:
- ret = "{\n"
- // Sort and print the struct props by the key.
- structProps := extractStructProperties(propertyValue, indent)
- for _, k := range android.SortedStringKeys(structProps) {
- ret += makeIndent(indent + 1)
- ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
- }
- ret += makeIndent(indent)
- ret += "}"
- case reflect.Interface:
- // TODO(b/164227191): implement pretty print for interfaces.
- // Interfaces are used for for arch, multilib and target properties.
- return "", nil
- default:
- return "", fmt.Errorf(
- "unexpected kind for property struct field: %s", propertyValue.Kind())
- }
- return ret, nil
-}
-
-// Converts a reflected property struct value into a map of property names and property values,
-// which each property value correctly pretty-printed and indented at the right nest level,
-// since property structs can be nested. In Starlark, nested structs are represented as nested
-// dicts: https://docs.bazel.build/skylark/lib/dict.html
-func extractStructProperties(structValue reflect.Value, indent int) map[string]string {
- if structValue.Kind() != reflect.Struct {
- panic(fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind()))
- }
-
- ret := map[string]string{}
- structType := structValue.Type()
- for i := 0; i < structValue.NumField(); i++ {
- field := structType.Field(i)
- if field.PkgPath != "" {
- // Skip unexported fields. Some properties are
- // internal to Soong only, and these fields do not have PkgPath.
- continue
- }
- if proptools.HasTag(field, "blueprint", "mutated") {
- continue
- }
-
- fieldValue := structValue.Field(i)
- if isZero(fieldValue) {
- // Ignore zero-valued fields
- continue
- }
-
- propertyName := proptools.PropertyNameForField(field.Name)
- prettyPrintedValue, err := prettyPrint(fieldValue, indent+1)
- if err != nil {
- panic(
- fmt.Errorf(
- "Error while parsing property: %q. %s",
- propertyName,
- err))
- }
- if prettyPrintedValue != "" {
- ret[propertyName] = prettyPrintedValue
- }
- }
-
- return ret
-}
-
-func isStructPtr(t reflect.Type) bool {
- return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
-}
-
-// Generically extract module properties and types into a map, keyed by the module property name.
-func extractModuleProperties(aModule android.Module) map[string]string {
- ret := map[string]string{}
-
- // Iterate over this android.Module's property structs.
- for _, properties := range aModule.GetProperties() {
- propertiesValue := reflect.ValueOf(properties)
- // Check that propertiesValue is a pointer to the Properties struct, like
- // *cc.BaseLinkerProperties or *java.CompilerProperties.
- //
- // propertiesValue can also be type-asserted to the structs to
- // manipulate internal props, if needed.
- if isStructPtr(propertiesValue.Type()) {
- structValue := propertiesValue.Elem()
- for k, v := range extractStructProperties(structValue, 0) {
- ret[k] = v
- }
- } else {
- panic(fmt.Errorf(
- "properties must be a pointer to a struct, got %T",
- propertiesValue.Interface()))
- }
-
- }
-
- return ret
-}
-
-// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
-// testonly = True, forcing other rules that depend on _test rules to also be
-// marked as testonly = True. This semantic constraint is not present in Soong.
-// To work around, rename "*_test" rules to "*_test_".
-func canonicalizeModuleType(moduleName string) string {
- if strings.HasSuffix(moduleName, "_test") {
- return moduleName + "_"
- }
-
- return moduleName
-}
-
-type RuleShim struct {
- // The rule class shims contained in a bzl file. e.g. ["cc_object", "cc_library", ..]
- rules []string
-
- // The generated string content of the bzl file.
- content string
-}
-
-// Create <module>.bzl containing Bazel rule shims for every module type available in Soong and
-// user-specified Go plugins.
-//
-// This function reuses documentation generation APIs to ensure parity between modules-as-docs
-// and modules-as-code, including the names and types of module properties.
-func createRuleShims(packages []*bpdoc.Package) (map[string]RuleShim, error) {
- var propToAttr func(prop bpdoc.Property, propName string) string
- propToAttr = func(prop bpdoc.Property, propName string) string {
- // dots are not allowed in Starlark attribute names. Substitute them with double underscores.
- propName = strings.ReplaceAll(propName, ".", "__")
- if !shouldGenerateAttribute(propName) {
- return ""
- }
-
- // Canonicalize and normalize module property types to Bazel attribute types
- starlarkAttrType := prop.Type
- if starlarkAttrType == "list of string" {
- starlarkAttrType = "string_list"
- } else if starlarkAttrType == "int64" {
- starlarkAttrType = "int"
- } else if starlarkAttrType == "" {
- var attr string
- for _, nestedProp := range prop.Properties {
- nestedAttr := propToAttr(nestedProp, propName+"__"+nestedProp.Name)
- if nestedAttr != "" {
- // TODO(b/167662930): Fix nested props resulting in too many attributes.
- // Let's still generate these, but comment them out.
- attr += "# " + nestedAttr
- }
- }
- return attr
- }
-
- if !allowedPropTypes[starlarkAttrType] {
- return ""
- }
-
- return fmt.Sprintf(" %q: attr.%s(),\n", propName, starlarkAttrType)
- }
-
- ruleShims := map[string]RuleShim{}
- for _, pkg := range packages {
- content := "load(\"//build/bazel/queryview_rules:providers.bzl\", \"SoongModuleInfo\")\n"
-
- bzlFileName := strings.ReplaceAll(pkg.Path, "android/soong/", "")
- bzlFileName = strings.ReplaceAll(bzlFileName, ".", "_")
- bzlFileName = strings.ReplaceAll(bzlFileName, "/", "_")
-
- rules := []string{}
-
- for _, moduleTypeTemplate := range moduleTypeDocsToTemplates(pkg.ModuleTypes) {
- attrs := `{
- "module_name": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
-`
- for _, prop := range moduleTypeTemplate.Properties {
- attrs += propToAttr(prop, prop.Name)
- }
-
- moduleTypeName := moduleTypeTemplate.Name
-
- // Certain SDK-related module types dynamically inject properties, instead of declaring
- // them as structs. These properties are registered in an SdkMemberTypesRegistry. If
- // the module type name matches, add these properties into the rule definition.
- var registeredTypes []android.SdkMemberType
- if moduleTypeName == "module_exports" || moduleTypeName == "module_exports_snapshot" {
- registeredTypes = android.ModuleExportsMemberTypes.RegisteredTypes()
- } else if moduleTypeName == "sdk" || moduleTypeName == "sdk_snapshot" {
- registeredTypes = android.SdkMemberTypes.RegisteredTypes()
- }
- for _, memberType := range registeredTypes {
- attrs += fmt.Sprintf(" %q: attr.string_list(),\n", memberType.SdkPropertyName())
- }
-
- attrs += " },"
-
- rule := canonicalizeModuleType(moduleTypeTemplate.Name)
- content += fmt.Sprintf(moduleRuleShim, rule, attrs)
- rules = append(rules, rule)
- }
-
- ruleShims[bzlFileName] = RuleShim{content: content, rules: rules}
- }
- return ruleShims, nil
-}
-
-func createBazelQueryView(ctx *android.Context, bazelQueryViewDir string) error {
- blueprintCtx := ctx.Context
- blueprintCtx.VisitAllModules(func(module blueprint.Module) {
- buildFile, err := buildFileForModule(blueprintCtx, module, bazelQueryViewDir)
- if err != nil {
- panic(err)
- }
-
- buildFile.Write([]byte(generateSoongModuleTarget(blueprintCtx, module) + "\n\n"))
- buildFile.Close()
- })
- var err error
-
- // Write top level files: WORKSPACE and BUILD. These files are empty.
- if err = writeReadOnlyFile(bazelQueryViewDir, "WORKSPACE", ""); err != nil {
- return err
- }
-
- // Used to denote that the top level directory is a package.
- if err = writeReadOnlyFile(bazelQueryViewDir, "BUILD", ""); err != nil {
- return err
- }
-
- packages, err := getPackages(ctx)
- if err != nil {
- return err
- }
- ruleShims, err := createRuleShims(packages)
- if err != nil {
- return err
- }
-
- // Write .bzl Starlark files into the bazel_rules top level directory (provider and rule definitions)
- bazelRulesDir := bazelQueryViewDir + "/build/bazel/queryview_rules"
- if err = writeReadOnlyFile(bazelRulesDir, "BUILD", ""); err != nil {
- return err
- }
- if err = writeReadOnlyFile(bazelRulesDir, "providers.bzl", providersBzl); err != nil {
- return err
- }
-
- for bzlFileName, ruleShim := range ruleShims {
- if err = writeReadOnlyFile(bazelRulesDir, bzlFileName+".bzl", ruleShim.content); err != nil {
+ filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets, bp2build.QueryView)
+ for _, f := range filesToWrite {
+ if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil {
return err
}
}
- return writeReadOnlyFile(bazelRulesDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims))
+ return nil
}
-// Generate the content of soong_module.bzl with the rule shim load statements
-// and mapping of module_type to rule shim map for every module type in Soong.
-func generateSoongModuleBzl(bzlLoads map[string]RuleShim) string {
- var loadStmts string
- var moduleRuleMap string
- for bzlFileName, ruleShim := range bzlLoads {
- loadStmt := "load(\"//build/bazel/queryview_rules:"
- loadStmt += bzlFileName
- loadStmt += ".bzl\""
- for _, rule := range ruleShim.rules {
- loadStmt += fmt.Sprintf(", %q", rule)
- moduleRuleMap += " \"" + rule + "\": " + rule + ",\n"
- }
- loadStmt += ")\n"
- loadStmts += loadStmt
- }
-
- return fmt.Sprintf(soongModuleBzl, loadStmts, moduleRuleMap)
-}
-
-func shouldGenerateAttribute(prop string) bool {
- return !ignoredPropNames[prop]
-}
-
-// props is an unsorted map. This function ensures that
-// the generated attributes are sorted to ensure determinism.
-func propsToAttributes(props map[string]string) string {
- var attributes string
- for _, propName := range android.SortedStringKeys(props) {
- if shouldGenerateAttribute(propName) {
- attributes += fmt.Sprintf(" %s = %s,\n", propName, props[propName])
- }
- }
- return attributes
-}
-
-// Convert a module and its deps and props into a Bazel macro/rule
-// representation in the BUILD file.
-func generateSoongModuleTarget(
- blueprintCtx *blueprint.Context,
- module blueprint.Module) string {
-
- var props map[string]string
- if aModule, ok := module.(android.Module); ok {
- props = extractModuleProperties(aModule)
- }
- attributes := propsToAttributes(props)
-
- // TODO(b/163018919): DirectDeps can have duplicate (module, variant)
- // items, if the modules are added using different DependencyTag. Figure
- // out the implications of that.
- depLabels := map[string]bool{}
- blueprintCtx.VisitDirectDeps(module, func(depModule blueprint.Module) {
- depLabels[qualifiedTargetLabel(blueprintCtx, depModule)] = true
- })
-
- depLabelList := "[\n"
- for depLabel, _ := range depLabels {
- depLabelList += fmt.Sprintf(" %q,\n", depLabel)
- }
- depLabelList += " ]"
-
- return fmt.Sprintf(
- soongModuleTarget,
- targetNameWithVariant(blueprintCtx, module),
- blueprintCtx.ModuleName(module),
- canonicalizeModuleType(blueprintCtx.ModuleType(module)),
- blueprintCtx.ModuleSubDir(module),
- depLabelList,
- attributes)
-}
-
-func buildFileForModule(
- ctx *blueprint.Context, module blueprint.Module, bazelQueryViewDir string) (*os.File, error) {
- // Create nested directories for the BUILD file
- dirPath := filepath.Join(bazelQueryViewDir, packagePath(ctx, module))
- createDirectoryIfNonexistent(dirPath)
- // Open the file for appending, and create it if it doesn't exist
- f, err := os.OpenFile(
- filepath.Join(dirPath, "BUILD.bazel"),
- os.O_APPEND|os.O_CREATE|os.O_WRONLY,
- 0644)
- if err != nil {
- return nil, err
- }
-
- // If the file is empty, add the load statement for the `soong_module` rule
- fi, err := f.Stat()
- if err != nil {
- return nil, err
- }
- if fi.Size() == 0 {
- f.Write([]byte(soongModuleLoad + "\n"))
- }
-
- return f, nil
-}
-
-func createDirectoryIfNonexistent(dir string) {
- if _, err := os.Stat(dir); os.IsNotExist(err) {
- os.MkdirAll(dir, os.ModePerm)
- }
-}
-
-// The QueryView directory should be read-only, sufficient for bazel query. The files
+// The auto-conversion directory should be read-only, sufficient for bazel query. The files
// are not intended to be edited by end users.
-func writeReadOnlyFile(dir string, baseName string, content string) error {
- createDirectoryIfNonexistent(dir)
- pathToFile := filepath.Join(dir, baseName)
+func writeReadOnlyFile(dir string, f bp2build.BazelFile) error {
+ dir = filepath.Join(dir, f.Dir)
+ if err := createDirectoryIfNonexistent(dir); err != nil {
+ return err
+ }
+ pathToFile := filepath.Join(dir, f.Basename)
+
// 0444 is read-only
- return ioutil.WriteFile(pathToFile, []byte(content), 0444)
+ err := ioutil.WriteFile(pathToFile, []byte(f.Contents), 0444)
+
+ return err
}
-func isZero(value reflect.Value) bool {
- switch value.Kind() {
- case reflect.Func, reflect.Map, reflect.Slice:
- return value.IsNil()
- case reflect.Array:
- valueIsZero := true
- for i := 0; i < value.Len(); i++ {
- valueIsZero = valueIsZero && isZero(value.Index(i))
- }
- return valueIsZero
- case reflect.Struct:
- valueIsZero := true
- for i := 0; i < value.NumField(); i++ {
- if value.Field(i).CanSet() {
- valueIsZero = valueIsZero && isZero(value.Field(i))
- }
- }
- return valueIsZero
- case reflect.Ptr:
- if !value.IsNil() {
- return isZero(reflect.Indirect(value))
- } else {
- return true
- }
- default:
- zeroValue := reflect.Zero(value.Type())
- result := value.Interface() == zeroValue.Interface()
- return result
+func createDirectoryIfNonexistent(dir string) error {
+ if _, err := os.Stat(dir); os.IsNotExist(err) {
+ return os.MkdirAll(dir, os.ModePerm)
+ } else {
+ return err
}
}
diff --git a/cmd/soong_build/queryview_test.go b/cmd/soong_build/queryview_test.go
deleted file mode 100644
index 9471a91..0000000
--- a/cmd/soong_build/queryview_test.go
+++ /dev/null
@@ -1,470 +0,0 @@
-// Copyright 2020 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
- "android/soong/android"
- "io/ioutil"
- "os"
- "strings"
- "testing"
-
- "github.com/google/blueprint/bootstrap/bpdoc"
-)
-
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "bazel_queryview_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
-func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
-}
-
-type customModule struct {
- android.ModuleBase
-}
-
-// OutputFiles is needed because some instances of this module use dist with a
-// tag property which requires the module implements OutputFileProducer.
-func (m *customModule) OutputFiles(tag string) (android.Paths, error) {
- return android.PathsForTesting("path" + tag), nil
-}
-
-func (m *customModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- // nothing for now.
-}
-
-func customModuleFactory() android.Module {
- module := &customModule{}
- android.InitAndroidModule(module)
- return module
-}
-
-func TestGenerateBazelQueryViewFromBlueprint(t *testing.T) {
- testCases := []struct {
- bp string
- expectedBazelTarget string
- }{
- {
- bp: `custom {
- name: "foo",
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- ramdisk: true,
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- ramdisk = True,
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- owner: "a_string_with\"quotes\"_and_\\backslashes\\\\",
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- owner = "a_string_with\"quotes\"_and_\\backslashes\\\\",
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- required: ["bar"],
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- required = [
- "bar",
- ],
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- target_required: ["qux", "bazqux"],
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- target_required = [
- "qux",
- "bazqux",
- ],
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- dist: {
- targets: ["goal_foo"],
- tag: ".foo",
- },
- dists: [
- {
- targets: ["goal_bar"],
- tag: ".bar",
- },
- ],
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- dist = {
- "tag": ".foo",
- "targets": [
- "goal_foo",
- ],
- },
- dists = [
- {
- "tag": ".bar",
- "targets": [
- "goal_bar",
- ],
- },
- ],
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- required: ["bar"],
- target_required: ["qux", "bazqux"],
- ramdisk: true,
- owner: "custom_owner",
- dists: [
- {
- tag: ".tag",
- targets: ["my_goal"],
- },
- ],
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- dists = [
- {
- "tag": ".tag",
- "targets": [
- "my_goal",
- ],
- },
- ],
- owner = "custom_owner",
- ramdisk = True,
- required = [
- "bar",
- ],
- target_required = [
- "qux",
- "bazqux",
- ],
-)`,
- },
- }
-
- for _, testCase := range testCases {
- config := android.TestConfig(buildDir, nil, testCase.bp, nil)
- ctx := android.NewTestContext(config)
- ctx.RegisterModuleType("custom", customModuleFactory)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
-
- module := ctx.ModuleForTests("foo", "").Module().(*customModule)
- blueprintCtx := ctx.Context.Context
-
- actualBazelTarget := generateSoongModuleTarget(blueprintCtx, module)
- if actualBazelTarget != testCase.expectedBazelTarget {
- t.Errorf(
- "Expected generated Bazel target to be '%s', got '%s'",
- testCase.expectedBazelTarget,
- actualBazelTarget,
- )
- }
- }
-}
-
-func createPackageFixtures() []*bpdoc.Package {
- properties := []bpdoc.Property{
- bpdoc.Property{
- Name: "int64_prop",
- Type: "int64",
- },
- bpdoc.Property{
- Name: "int_prop",
- Type: "int",
- },
- bpdoc.Property{
- Name: "bool_prop",
- Type: "bool",
- },
- bpdoc.Property{
- Name: "string_prop",
- Type: "string",
- },
- bpdoc.Property{
- Name: "string_list_prop",
- Type: "list of string",
- },
- bpdoc.Property{
- Name: "nested_prop",
- Type: "",
- Properties: []bpdoc.Property{
- bpdoc.Property{
- Name: "int_prop",
- Type: "int",
- },
- bpdoc.Property{
- Name: "bool_prop",
- Type: "bool",
- },
- bpdoc.Property{
- Name: "string_prop",
- Type: "string",
- },
- },
- },
- bpdoc.Property{
- Name: "unknown_type",
- Type: "unknown",
- },
- }
-
- fooPropertyStruct := &bpdoc.PropertyStruct{
- Name: "FooProperties",
- Properties: properties,
- }
-
- moduleTypes := []*bpdoc.ModuleType{
- &bpdoc.ModuleType{
- Name: "foo_library",
- PropertyStructs: []*bpdoc.PropertyStruct{
- fooPropertyStruct,
- },
- },
-
- &bpdoc.ModuleType{
- Name: "foo_binary",
- PropertyStructs: []*bpdoc.PropertyStruct{
- fooPropertyStruct,
- },
- },
- &bpdoc.ModuleType{
- Name: "foo_test",
- PropertyStructs: []*bpdoc.PropertyStruct{
- fooPropertyStruct,
- },
- },
- }
-
- return [](*bpdoc.Package){
- &bpdoc.Package{
- Name: "foo_language",
- Path: "android/soong/foo",
- ModuleTypes: moduleTypes,
- },
- }
-}
-
-func TestGenerateModuleRuleShims(t *testing.T) {
- ruleShims, err := createRuleShims(createPackageFixtures())
- if err != nil {
- panic(err)
- }
-
- if len(ruleShims) != 1 {
- t.Errorf("Expected to generate 1 rule shim, but got %d", len(ruleShims))
- }
-
- fooRuleShim := ruleShims["foo"]
- expectedRules := []string{"foo_binary", "foo_library", "foo_test_"}
-
- if len(fooRuleShim.rules) != 3 {
- t.Errorf("Expected 3 rules, but got %d", len(fooRuleShim.rules))
- }
-
- for i, rule := range fooRuleShim.rules {
- if rule != expectedRules[i] {
- t.Errorf("Expected rule shim to contain %s, but got %s", expectedRules[i], rule)
- }
- }
-
- expectedBzl := `load("//build/bazel/queryview_rules:providers.bzl", "SoongModuleInfo")
-
-def _foo_binary_impl(ctx):
- return [SoongModuleInfo()]
-
-foo_binary = rule(
- implementation = _foo_binary_impl,
- attrs = {
- "module_name": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
- "bool_prop": attr.bool(),
- "int64_prop": attr.int(),
- "int_prop": attr.int(),
-# "nested_prop__int_prop": attr.int(),
-# "nested_prop__bool_prop": attr.bool(),
-# "nested_prop__string_prop": attr.string(),
- "string_list_prop": attr.string_list(),
- "string_prop": attr.string(),
- },
-)
-
-def _foo_library_impl(ctx):
- return [SoongModuleInfo()]
-
-foo_library = rule(
- implementation = _foo_library_impl,
- attrs = {
- "module_name": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
- "bool_prop": attr.bool(),
- "int64_prop": attr.int(),
- "int_prop": attr.int(),
-# "nested_prop__int_prop": attr.int(),
-# "nested_prop__bool_prop": attr.bool(),
-# "nested_prop__string_prop": attr.string(),
- "string_list_prop": attr.string_list(),
- "string_prop": attr.string(),
- },
-)
-
-def _foo_test__impl(ctx):
- return [SoongModuleInfo()]
-
-foo_test_ = rule(
- implementation = _foo_test__impl,
- attrs = {
- "module_name": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
- "bool_prop": attr.bool(),
- "int64_prop": attr.int(),
- "int_prop": attr.int(),
-# "nested_prop__int_prop": attr.int(),
-# "nested_prop__bool_prop": attr.bool(),
-# "nested_prop__string_prop": attr.string(),
- "string_list_prop": attr.string_list(),
- "string_prop": attr.string(),
- },
-)
-`
-
- if fooRuleShim.content != expectedBzl {
- t.Errorf(
- "Expected the generated rule shim bzl to be:\n%s\nbut got:\n%s",
- expectedBzl,
- fooRuleShim.content)
- }
-}
-
-func TestGenerateSoongModuleBzl(t *testing.T) {
- ruleShims, err := createRuleShims(createPackageFixtures())
- if err != nil {
- panic(err)
- }
- actualSoongModuleBzl := generateSoongModuleBzl(ruleShims)
-
- expectedLoad := "load(\"//build/bazel/queryview_rules:foo.bzl\", \"foo_binary\", \"foo_library\", \"foo_test_\")"
- expectedRuleMap := `soong_module_rule_map = {
- "foo_binary": foo_binary,
- "foo_library": foo_library,
- "foo_test_": foo_test_,
-}`
- if !strings.Contains(actualSoongModuleBzl, expectedLoad) {
- t.Errorf(
- "Generated soong_module.bzl:\n\n%s\n\n"+
- "Could not find the load statement in the generated soong_module.bzl:\n%s",
- actualSoongModuleBzl,
- expectedLoad)
- }
-
- if !strings.Contains(actualSoongModuleBzl, expectedRuleMap) {
- t.Errorf(
- "Generated soong_module.bzl:\n\n%s\n\n"+
- "Could not find the module -> rule map in the generated soong_module.bzl:\n%s",
- actualSoongModuleBzl,
- expectedRuleMap)
- }
-}
diff --git a/cmd/soong_build/writedocs.go b/cmd/soong_build/writedocs.go
index 253979e..a69de6a 100644
--- a/cmd/soong_build/writedocs.go
+++ b/cmd/soong_build/writedocs.go
@@ -20,7 +20,6 @@
"html/template"
"io/ioutil"
"path/filepath"
- "reflect"
"sort"
"github.com/google/blueprint/bootstrap"
@@ -96,17 +95,13 @@
return result
}
-func getPackages(ctx *android.Context) ([]*bpdoc.Package, error) {
- moduleTypeFactories := android.ModuleTypeFactories()
- bpModuleTypeFactories := make(map[string]reflect.Value)
- for moduleType, factory := range moduleTypeFactories {
- bpModuleTypeFactories[moduleType] = reflect.ValueOf(factory)
- }
- return bootstrap.ModuleTypeDocs(ctx.Context, bpModuleTypeFactories)
+func getPackages(ctx *android.Context, config interface{}) ([]*bpdoc.Package, error) {
+ moduleTypeFactories := android.ModuleTypeFactoriesForDocs()
+ return bootstrap.ModuleTypeDocs(ctx.Context, config, moduleTypeFactories)
}
-func writeDocs(ctx *android.Context, filename string) error {
- packages, err := getPackages(ctx)
+func writeDocs(ctx *android.Context, config interface{}, filename string) error {
+ packages, err := getPackages(ctx, config)
if err != nil {
return err
}
diff --git a/cmd/soong_env/soong_env.go b/cmd/soong_env/soong_env.go
deleted file mode 100644
index 8020b17..0000000
--- a/cmd/soong_env/soong_env.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2015 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.
-
-// soong_env determines if the given soong environment file (usually ".soong.environment") is stale
-// by comparing its contents to the current corresponding environment variable values.
-// It fails if the file cannot be opened or corrupted, or its contents differ from the current
-// values.
-
-package main
-
-import (
- "flag"
- "fmt"
- "os"
-
- "android/soong/env"
-)
-
-func usage() {
- fmt.Fprintf(os.Stderr, "usage: soong_env env_file\n")
- fmt.Fprintf(os.Stderr, "exits with success if the environment varibles in env_file match\n")
- fmt.Fprintf(os.Stderr, "the current environment\n")
- flag.PrintDefaults()
- os.Exit(2)
-}
-
-// This is a simple executable packaging, and the real work happens in env.StaleEnvFile.
-func main() {
- flag.Parse()
-
- if flag.NArg() != 1 {
- usage()
- }
-
- stale, err := env.StaleEnvFile(flag.Arg(0))
- if err != nil {
- fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
- os.Exit(1)
- }
-
- if stale {
- os.Exit(1)
- }
-
- os.Exit(0)
-}
diff --git a/cmd/soong_ui/Android.bp b/cmd/soong_ui/Android.bp
index 4e57bef..4f5eea9 100644
--- a/cmd/soong_ui/Android.bp
+++ b/cmd/soong_ui/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "soong_ui",
deps: [
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 74ede68..390a9ec 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -25,6 +25,7 @@
"strings"
"time"
+ "android/soong/shared"
"android/soong/ui/build"
"android/soong/ui/logger"
"android/soong/ui/metrics"
@@ -69,7 +70,7 @@
return build.NewConfig(ctx, args...)
},
stdio: stdio,
- run: make,
+ run: runMake,
}, {
flag: "--dumpvar-mode",
description: "print the value of the legacy make variable VAR to stdout",
@@ -91,7 +92,7 @@
description: "build modules based on the specified build action",
config: buildActionConfig,
stdio: stdio,
- run: make,
+ run: runMake,
},
}
@@ -118,6 +119,8 @@
// Command is the type of soong_ui execution. Only one type of
// execution is specified. The args are specific to the command.
func main() {
+ shared.ReexecWithDelveMaybe(os.Getenv("SOONG_UI_DELVE"), shared.ResolveDelveBinary())
+
buildStarted := time.Now()
c, args, err := getCommand(os.Args)
@@ -475,7 +478,7 @@
return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
}
-func make(ctx build.Context, config build.Config, _ []string, logsDir string) {
+func runMake(ctx build.Context, config build.Config, _ []string, logsDir string) {
if config.IsVerbose() {
writer := ctx.Writer
fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
diff --git a/cmd/zip2zip/Android.bp b/cmd/zip2zip/Android.bp
index 2c4cd82..3ef7668 100644
--- a/cmd/zip2zip/Android.bp
+++ b/cmd/zip2zip/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "zip2zip",
deps: [
diff --git a/cmd/zipsync/Android.bp b/cmd/zipsync/Android.bp
index 49b5f3e..0dcdd5c 100644
--- a/cmd/zipsync/Android.bp
+++ b/cmd/zipsync/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "zipsync",
deps: [
diff --git a/cuj/Android.bp b/cuj/Android.bp
index 21d667f..a2da6e6 100644
--- a/cuj/Android.bp
+++ b/cuj/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "cuj_tests",
deps: [
diff --git a/dexpreopt/Android.bp b/dexpreopt/Android.bp
index 35f90df..679d066 100644
--- a/dexpreopt/Android.bp
+++ b/dexpreopt/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-dexpreopt",
pkgPath: "android/soong/dexpreopt",
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index ab789aa..ad52b00 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -255,24 +255,13 @@
// Add class loader context for the given library to the map entry for the given SDK version.
func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
- hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) error {
-
- // If missing dependencies are allowed, the build shouldn't fail when a <uses-library> is
- // not found. However, this is likely to result is disabling dexpreopt, as it won't be
- // possible to construct class loader context without on-host and on-device library paths.
- strict = strict && !ctx.Config().AllowMissingDependencies()
-
- if hostPath == nil && strict {
- return fmt.Errorf("unknown build path to <uses-library> \"%s\"", lib)
- }
+ hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) error {
devicePath := UnknownInstallLibraryPath
if installPath == nil {
if android.InList(lib, CompatUsesLibs) || android.InList(lib, OptionalCompatUsesLibs) {
// Assume that compatibility libraries are installed in /system/framework.
installPath = android.PathForModuleInstall(ctx, "framework", lib+".jar")
- } else if strict {
- return fmt.Errorf("unknown install path to <uses-library> \"%s\"", lib)
} else {
// For some stub libraries the only known thing is the name of their implementation
// library, but the library itself is unavailable (missing or part of a prebuilt). In
@@ -310,40 +299,17 @@
return nil
}
-// Wrapper around addContext that reports errors.
-func (clcMap ClassLoaderContextMap) addContextOrReportError(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
- hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) {
-
- err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, strict, nestedClcMap)
- if err != nil {
- ctx.ModuleErrorf(err.Error())
- }
-}
-
-// Add class loader context. Fail on unknown build/install paths.
-func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, lib string,
- hostPath, installPath android.Path) {
-
- clcMap.addContextOrReportError(ctx, AnySdkVersion, lib, hostPath, installPath, true, nil)
-}
-
-// Add class loader context if the library exists. Don't fail on unknown build/install paths.
-func (clcMap ClassLoaderContextMap) MaybeAddContext(ctx android.ModuleInstallPathContext, lib *string,
- hostPath, installPath android.Path) {
-
- if lib != nil {
- clcMap.addContextOrReportError(ctx, AnySdkVersion, *lib, hostPath, installPath, false, nil)
- }
-}
-
// Add class loader context for the given SDK version. Don't fail on unknown build/install paths, as
// libraries with unknown paths still need to be processed by manifest_fixer (which doesn't care
// about paths). For the subset of libraries that are used in dexpreopt, their build/install paths
// are validated later before CLC is used (in validateClassLoaderContext).
-func (clcMap ClassLoaderContextMap) AddContextForSdk(ctx android.ModuleInstallPathContext, sdkVer int,
+func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, sdkVer int,
lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
- clcMap.addContextOrReportError(ctx, sdkVer, lib, hostPath, installPath, false, nestedClcMap)
+ err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, nestedClcMap)
+ if err != nil {
+ ctx.ModuleErrorf(err.Error())
+ }
}
// Merge the other class loader context map into this one, do not override existing entries.
@@ -522,25 +488,22 @@
return clcHost, clcTarget, paths
}
-// JSON representation of <uses-library> paths on host and on device.
-type jsonLibraryPath struct {
- Host string
- Device string
+// Class loader contexts that come from Make via JSON dexpreopt.config. JSON CLC representation is
+// the same as Soong representation except that SDK versions and paths are represented with strings.
+type jsonClassLoaderContext struct {
+ Name string
+ Host string
+ Device string
+ Subcontexts []*jsonClassLoaderContext
}
-// Class loader contexts that come from Make (via JSON dexpreopt.config) files have simpler
-// structure than Soong class loader contexts: they are flat maps from a <uses-library> name to its
-// on-host and on-device paths. There are no nested subcontexts. It is a limitation of the current
-// Make implementation.
-type jsonClassLoaderContext map[string]jsonLibraryPath
+// A map from SDK version (represented with a JSON string) to JSON CLCs.
+type jsonClassLoaderContextMap map[string][]*jsonClassLoaderContext
-// A map from SDK version (represented with a JSON string) to JSON class loader context.
-type jsonClassLoaderContextMap map[string]jsonClassLoaderContext
-
-// Convert JSON class loader context map to ClassLoaderContextMap.
+// Convert JSON CLC map to Soong represenation.
func fromJsonClassLoaderContext(ctx android.PathContext, jClcMap jsonClassLoaderContextMap) ClassLoaderContextMap {
clcMap := make(ClassLoaderContextMap)
- for sdkVerStr, clc := range jClcMap {
+ for sdkVerStr, clcs := range jClcMap {
sdkVer, ok := strconv.Atoi(sdkVerStr)
if ok != nil {
if sdkVerStr == "any" {
@@ -549,14 +512,45 @@
android.ReportPathErrorf(ctx, "failed to parse SDK version in dexpreopt.config: '%s'", sdkVerStr)
}
}
- for lib, path := range clc {
- clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
- Name: lib,
- Host: constructPath(ctx, path.Host),
- Device: path.Device,
- Subcontexts: nil,
- })
- }
+ clcMap[sdkVer] = fromJsonClassLoaderContextRec(ctx, clcs)
}
return clcMap
}
+
+// Recursive helper for fromJsonClassLoaderContext.
+func fromJsonClassLoaderContextRec(ctx android.PathContext, jClcs []*jsonClassLoaderContext) []*ClassLoaderContext {
+ clcs := make([]*ClassLoaderContext, 0, len(jClcs))
+ for _, clc := range jClcs {
+ clcs = append(clcs, &ClassLoaderContext{
+ Name: clc.Name,
+ Host: constructPath(ctx, clc.Host),
+ Device: clc.Device,
+ Subcontexts: fromJsonClassLoaderContextRec(ctx, clc.Subcontexts),
+ })
+ }
+ return clcs
+}
+
+// Convert Soong CLC map to JSON representation for Make.
+func toJsonClassLoaderContext(clcMap ClassLoaderContextMap) jsonClassLoaderContextMap {
+ jClcMap := make(jsonClassLoaderContextMap)
+ for sdkVer, clcs := range clcMap {
+ sdkVerStr := fmt.Sprintf("%d", sdkVer)
+ jClcMap[sdkVerStr] = toJsonClassLoaderContextRec(clcs)
+ }
+ return jClcMap
+}
+
+// Recursive helper for toJsonClassLoaderContext.
+func toJsonClassLoaderContextRec(clcs []*ClassLoaderContext) []*jsonClassLoaderContext {
+ jClcs := make([]*jsonClassLoaderContext, len(clcs))
+ for _, clc := range clcs {
+ jClcs = append(jClcs, &jsonClassLoaderContext{
+ Name: clc.Name,
+ Host: clc.Host.String(),
+ Device: clc.Device,
+ Subcontexts: toJsonClassLoaderContextRec(clc.Subcontexts),
+ })
+ }
+ return jClcs
+}
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
index 6b6b162..86f7871 100644
--- a/dexpreopt/class_loader_context_test.go
+++ b/dexpreopt/class_loader_context_test.go
@@ -18,6 +18,7 @@
// For class loader context tests involving .bp files, see TestUsesLibraries in java package.
import (
+ "fmt"
"reflect"
"strings"
"testing"
@@ -50,36 +51,30 @@
m := make(ClassLoaderContextMap)
- m.AddContext(ctx, "a", buildPath(ctx, "a"), installPath(ctx, "a"))
- m.AddContext(ctx, "b", buildPath(ctx, "b"), installPath(ctx, "b"))
-
- // "Maybe" variant in the good case: add as usual.
- c := "c"
- m.MaybeAddContext(ctx, &c, buildPath(ctx, "c"), installPath(ctx, "c"))
-
- // "Maybe" variant in the bad case: don't add library with unknown name, keep going.
- m.MaybeAddContext(ctx, nil, nil, nil)
+ m.AddContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m.AddContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+ m.AddContext(ctx, AnySdkVersion, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
// Add some libraries with nested subcontexts.
m1 := make(ClassLoaderContextMap)
- m1.AddContext(ctx, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"))
- m1.AddContext(ctx, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"))
+ m1.AddContext(ctx, AnySdkVersion, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"), nil)
+ m1.AddContext(ctx, AnySdkVersion, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"), nil)
m2 := make(ClassLoaderContextMap)
- m2.AddContext(ctx, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"))
- m2.AddContext(ctx, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"))
- m2.AddContextForSdk(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
+ m2.AddContext(ctx, AnySdkVersion, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"), nil)
+ m2.AddContext(ctx, AnySdkVersion, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"), nil)
+ m2.AddContext(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
m3 := make(ClassLoaderContextMap)
- m3.AddContext(ctx, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"))
- m3.AddContext(ctx, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"))
+ m3.AddContext(ctx, AnySdkVersion, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"), nil)
+ m3.AddContext(ctx, AnySdkVersion, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"), nil)
- m.AddContextForSdk(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2)
+ m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2)
// When the same library is both in conditional and unconditional context, it should be removed
// from conditional context.
- m.AddContextForSdk(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
- m.AddContextForSdk(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+ m.AddContext(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+ m.AddContext(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
// Merge map with implicit root library that is among toplevel contexts => does nothing.
m.AddContextMap(m1, "c")
@@ -88,12 +83,12 @@
m.AddContextMap(m3, "m_g")
// Compatibility libraries with unknown install paths get default paths.
- m.AddContextForSdk(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil)
- m.AddContextForSdk(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil)
+ m.AddContext(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil)
+ m.AddContext(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil)
// Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only
// needed as a compatibility library if "android.test.runner" is in CLC as well.
- m.AddContextForSdk(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil)
+ m.AddContext(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil)
valid, validationError := validateClassLoaderContext(m)
@@ -160,31 +155,19 @@
})
}
-// Test that an unexpected unknown build path causes immediate error.
-func TestCLCUnknownBuildPath(t *testing.T) {
- ctx := testContext()
- m := make(ClassLoaderContextMap)
- err := m.addContext(ctx, AnySdkVersion, "a", nil, nil, true, nil)
- checkError(t, err, "unknown build path to <uses-library> \"a\"")
-}
-
-// Test that an unexpected unknown install path causes immediate error.
-func TestCLCUnknownInstallPath(t *testing.T) {
- ctx := testContext()
- m := make(ClassLoaderContextMap)
- err := m.addContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, true, nil)
- checkError(t, err, "unknown install path to <uses-library> \"a\"")
-}
-
-func TestCLCMaybeAdd(t *testing.T) {
+// Test that unknown library paths cause a validation error.
+func testCLCUnknownPath(t *testing.T, whichPath string) {
ctx := testContext()
m := make(ClassLoaderContextMap)
- a := "a"
- m.MaybeAddContext(ctx, &a, nil, nil)
+ if whichPath == "build" {
+ m.AddContext(ctx, AnySdkVersion, "a", nil, nil, nil)
+ } else {
+ m.AddContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, nil)
+ }
// The library should be added to <uses-library> tags by the manifest_fixer.
- t.Run("maybe add", func(t *testing.T) {
+ t.Run("uses libs", func(t *testing.T) {
haveUsesLibs := m.UsesLibs()
wantUsesLibs := []string{"a"}
if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
@@ -192,20 +175,28 @@
}
})
- // But class loader context in such cases should raise an error on validation.
- t.Run("validate", func(t *testing.T) {
- _, err := validateClassLoaderContext(m)
- checkError(t, err, "invalid build path for <uses-library> \"a\"")
- })
+ // But CLC cannot be constructed: there is a validation error.
+ _, err := validateClassLoaderContext(m)
+ checkError(t, err, fmt.Sprintf("invalid %s path for <uses-library> \"a\"", whichPath))
+}
+
+// Test that unknown build path is an error.
+func TestCLCUnknownBuildPath(t *testing.T) {
+ testCLCUnknownPath(t, "build")
+}
+
+// Test that unknown install path is an error.
+func TestCLCUnknownInstallPath(t *testing.T) {
+ testCLCUnknownPath(t, "install")
}
// An attempt to add conditional nested subcontext should fail.
func TestCLCNestedConditional(t *testing.T) {
ctx := testContext()
m1 := make(ClassLoaderContextMap)
- m1.AddContextForSdk(ctx, 42, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m1.AddContext(ctx, 42, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
m := make(ClassLoaderContextMap)
- err := m.addContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), true, m1)
+ err := m.addContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), m1)
checkError(t, err, "nested class loader context shouldn't have conditional part")
}
@@ -214,10 +205,10 @@
func TestCLCSdkVersionOrder(t *testing.T) {
ctx := testContext()
m := make(ClassLoaderContextMap)
- m.AddContextForSdk(ctx, 28, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
- m.AddContextForSdk(ctx, 29, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
- m.AddContextForSdk(ctx, 30, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
- m.AddContextForSdk(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), nil)
+ m.AddContext(ctx, 28, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m.AddContext(ctx, 29, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+ m.AddContext(ctx, 30, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+ m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), nil)
valid, validationError := validateClassLoaderContext(m)
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index f52ecb4..26ff5ba 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -27,11 +27,14 @@
// GlobalConfig stores the configuration for dex preopting. The fields are set
// from product variables via dex_preopt_config.mk.
type GlobalConfig struct {
- DisablePreopt bool // disable preopt for all modules
- DisablePreoptModules []string // modules with preopt disabled by product-specific config
+ DisablePreopt bool // disable preopt for all modules (excluding boot images)
+ DisablePreoptBootImages bool // disable prepot for boot images
+ DisablePreoptModules []string // modules with preopt disabled by product-specific config
OnlyPreoptBootImageAndSystemServer bool // only preopt jars in the boot image or system server
+ PreoptWithUpdatableBcp bool // If updatable boot jars are included in dexpreopt or not.
+
UseArtImage bool // use the art image (use other boot class path dex files without image)
HasSystemOther bool // store odex files that match PatternsOnSystemOther on the system_other partition
@@ -79,12 +82,19 @@
CpuVariant map[android.ArchType]string // cpu variant for each architecture
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
- 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
+ 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
+
+ // If true, downgrade the compiler filter of dexpreopt to "verify" when verify_uses_libraries
+ // check fails, instead of failing the build. This will disable any AOT-compilation.
+ //
+ // The intended use case for this flag is to have a smoother migration path for the Java
+ // modules that need to add <uses-library> information in their build files. The flag allows to
+ // quickly silence build errors. This flag should be used with caution and only as a temporary
+ // measure, as it masks real errors and affects performance.
+ RelaxUsesLibraryCheck bool
}
// GlobalSoongConfig contains the global config that is generated from Soong,
@@ -105,7 +115,7 @@
DexLocation string // dex location on device
BuildPath android.OutputPath
DexPath android.Path
- ManifestPath android.Path
+ ManifestPath android.OptionalPath
UncompressedDex bool
HasApkLibraries bool
PreoptFlags []string
@@ -114,8 +124,10 @@
ProfileIsTextListing bool
ProfileBootListing android.OptionalPath
- EnforceUsesLibraries bool
- ClassLoaderContexts ClassLoaderContextMap
+ EnforceUsesLibraries bool // turn on build-time verify_uses_libraries check
+ EnforceUsesLibrariesStatusFile android.Path // a file with verify_uses_libraries errors (if any)
+ ProvidesUsesLibrary string // library name (usually the same as module name)
+ ClassLoaderContexts ClassLoaderContextMap
Archs []android.ArchType
DexPreoptImages []android.Path
@@ -178,7 +190,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
BootImageProfiles []string
}
@@ -189,7 +200,6 @@
}
// Construct paths that require a PathContext.
- config.GlobalConfig.DirtyImageObjects = android.OptionalPathForPath(constructPath(ctx, config.DirtyImageObjects))
config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles)
return config.GlobalConfig, nil
@@ -234,8 +244,9 @@
return ctx.Config().Once(testGlobalConfigOnceKey, func() interface{} {
// Nope, return a config with preopting disabled
return globalConfigAndRaw{&GlobalConfig{
- DisablePreopt: true,
- DisableGenerateProfile: true,
+ DisablePreopt: true,
+ DisablePreoptBootImages: true,
+ DisableGenerateProfile: true,
}, nil}
})
}).(globalConfigAndRaw)
@@ -259,14 +270,15 @@
// Copies of entries in ModuleConfig that are not constructable without extra parameters. They will be
// used to construct the real value manually below.
- BuildPath string
- DexPath string
- ManifestPath string
- ProfileClassListing string
- ClassLoaderContexts jsonClassLoaderContextMap
- DexPreoptImages []string
- DexPreoptImageLocations []string
- PreoptBootClassPathDexFiles []string
+ BuildPath string
+ DexPath string
+ ManifestPath string
+ ProfileClassListing string
+ EnforceUsesLibrariesStatusFile string
+ ClassLoaderContexts jsonClassLoaderContextMap
+ DexPreoptImages []string
+ DexPreoptImageLocations []string
+ PreoptBootClassPathDexFiles []string
}
config := ModuleJSONConfig{}
@@ -279,8 +291,9 @@
// Construct paths that require a PathContext.
config.ModuleConfig.BuildPath = constructPath(ctx, config.BuildPath).(android.OutputPath)
config.ModuleConfig.DexPath = constructPath(ctx, config.DexPath)
- config.ModuleConfig.ManifestPath = constructPath(ctx, config.ManifestPath)
+ config.ModuleConfig.ManifestPath = android.OptionalPathForPath(constructPath(ctx, config.ManifestPath))
config.ModuleConfig.ProfileClassListing = android.OptionalPathForPath(constructPath(ctx, config.ProfileClassListing))
+ config.ModuleConfig.EnforceUsesLibrariesStatusFile = constructPath(ctx, config.EnforceUsesLibrariesStatusFile)
config.ModuleConfig.ClassLoaderContexts = fromJsonClassLoaderContext(ctx, config.ClassLoaderContexts)
config.ModuleConfig.DexPreoptImages = constructPaths(ctx, config.DexPreoptImages)
config.ModuleConfig.DexPreoptImageLocations = config.DexPreoptImageLocations
@@ -292,6 +305,42 @@
return config.ModuleConfig, nil
}
+// WriteSlimModuleConfigForMake serializes a subset of ModuleConfig into a per-module
+// dexpreopt.config JSON file. It is a way to pass dexpreopt information about Soong modules to
+// Make, which is needed when a Make module has a <uses-library> dependency on a Soong module.
+func WriteSlimModuleConfigForMake(ctx android.ModuleContext, config *ModuleConfig, path android.WritablePath) {
+ if path == nil {
+ return
+ }
+
+ // JSON representation of the slim module dexpreopt.config.
+ type slimModuleJSONConfig struct {
+ Name string
+ DexLocation string
+ BuildPath string
+ EnforceUsesLibraries bool
+ ProvidesUsesLibrary string
+ ClassLoaderContexts jsonClassLoaderContextMap
+ }
+
+ jsonConfig := &slimModuleJSONConfig{
+ Name: config.Name,
+ DexLocation: config.DexLocation,
+ BuildPath: config.BuildPath.String(),
+ EnforceUsesLibraries: config.EnforceUsesLibraries,
+ ProvidesUsesLibrary: config.ProvidesUsesLibrary,
+ ClassLoaderContexts: toJsonClassLoaderContext(config.ClassLoaderContexts),
+ }
+
+ data, err := json.MarshalIndent(jsonConfig, "", " ")
+ if err != nil {
+ ctx.ModuleErrorf("failed to JSON marshal module dexpreopt.config: %v", err)
+ return
+ }
+
+ android.WriteFileRule(ctx, path, string(data))
+}
+
// dex2oatModuleName returns the name of the module to use for the dex2oat host
// tool. It should be a binary module with public visibility that is compiled
// and installed for host.
@@ -305,9 +354,23 @@
}
}
-var dex2oatDepTag = struct {
+type dex2oatDependencyTag struct {
blueprint.BaseDependencyTag
-}{}
+}
+
+func (d dex2oatDependencyTag) ExcludeFromVisibilityEnforcement() {
+}
+
+func (d dex2oatDependencyTag) ExcludeFromApexContents() {
+}
+
+// Dex2oatDepTag represents the dependency onto the dex2oatd module. It is added to any module that
+// needs dexpreopting and so it makes no sense for it to be checked for visibility or included in
+// the apex.
+var Dex2oatDepTag = dex2oatDependencyTag{}
+
+var _ android.ExcludeFromVisibilityEnforcementTag = Dex2oatDepTag
+var _ android.ExcludeFromApexContentsTag = Dex2oatDepTag
// RegisterToolDeps adds the necessary dependencies to binary modules for tools
// that are required later when Get(Cached)GlobalSoongConfig is called. It
@@ -316,7 +379,7 @@
func RegisterToolDeps(ctx android.BottomUpMutatorContext) {
dex2oatBin := dex2oatModuleName(ctx.Config())
v := ctx.Config().BuildOSTarget.Variations()
- ctx.AddFarVariationDependencies(v, dex2oatDepTag, dex2oatBin)
+ ctx.AddFarVariationDependencies(v, Dex2oatDepTag, dex2oatBin)
}
func dex2oatPathFromDep(ctx android.ModuleContext) android.Path {
@@ -332,7 +395,7 @@
// prebuilt explicitly here instead.
var dex2oatModule android.Module
ctx.WalkDeps(func(child, parent android.Module) bool {
- if parent == ctx.Module() && ctx.OtherModuleDependencyTag(child) == dex2oatDepTag {
+ if parent == ctx.Module() && ctx.OtherModuleDependencyTag(child) == Dex2oatDepTag {
// Found the source module, or prebuilt module that has replaced the source.
dex2oatModule = child
if p, ok := child.(android.PrebuiltInterface); ok && p.Prebuilt() != nil {
@@ -365,13 +428,6 @@
// createGlobalSoongConfig creates a GlobalSoongConfig from the current context.
// Should not be used in dexpreopt_gen.
func createGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig {
- if ctx.Config().TestProductVariables != nil {
- // If we're called in a test there'll be a confusing error from the path
- // functions below that gets reported without a stack trace, so let's panic
- // properly with a more helpful message.
- panic("This should not be called from tests. Please call GlobalSoongConfigForTests somewhere in the test setup.")
- }
-
return &GlobalSoongConfig{
Profman: ctx.Config().HostToolPath(ctx, "profman"),
Dex2oat: dex2oatPathFromDep(ctx),
@@ -385,14 +441,13 @@
// The main reason for this Once cache for GlobalSoongConfig is to make the
// dex2oat path available to singletons. In ordinary modules we get it through a
-// dex2oatDepTag dependency, but in singletons there's no simple way to do the
+// Dex2oatDepTag dependency, but in singletons there's no simple way to do the
// same thing and ensure the right variant is selected, hence this cache to make
// the resolved path available to singletons. This means we depend on there
-// being at least one ordinary module with a dex2oatDepTag dependency.
+// being at least one ordinary module with a Dex2oatDepTag dependency.
//
// TODO(b/147613152): Implement a way to deal with dependencies from singletons,
-// and then possibly remove this cache altogether (but the use in
-// GlobalSoongConfigForTests also needs to be rethought).
+// and then possibly remove this cache altogether.
var globalSoongConfigOnceKey = android.NewOnceKey("DexpreoptGlobalSoongConfig")
// GetGlobalSoongConfig creates a GlobalSoongConfig the first time it's called,
@@ -545,7 +600,6 @@
EmptyDirectory: "empty_dir",
CpuVariant: nil,
InstructionSetFeatures: nil,
- DirtyImageObjects: android.OptionalPath{},
BootImageProfiles: nil,
BootFlags: "",
Dex2oatImageXmx: "",
@@ -553,18 +607,14 @@
}
}
-func GlobalSoongConfigForTests(config android.Config) *GlobalSoongConfig {
- // Install the test GlobalSoongConfig in the Once cache so that later calls to
- // Get(Cached)GlobalSoongConfig returns it without trying to create a real one.
- return config.Once(globalSoongConfigOnceKey, func() interface{} {
- return &GlobalSoongConfig{
- Profman: android.PathForTesting("profman"),
- Dex2oat: android.PathForTesting("dex2oat"),
- Aapt: android.PathForTesting("aapt"),
- SoongZip: android.PathForTesting("soong_zip"),
- Zip2zip: android.PathForTesting("zip2zip"),
- ManifestCheck: android.PathForTesting("manifest_check"),
- ConstructContext: android.PathForTesting("construct_context"),
- }
- }).(*GlobalSoongConfig)
+func globalSoongConfigForTests() *GlobalSoongConfig {
+ return &GlobalSoongConfig{
+ Profman: android.PathForTesting("profman"),
+ Dex2oat: android.PathForTesting("dex2oat"),
+ Aapt: android.PathForTesting("aapt"),
+ SoongZip: android.PathForTesting("soong_zip"),
+ Zip2zip: android.PathForTesting("zip2zip"),
+ ManifestCheck: android.PathForTesting("manifest_check"),
+ ConstructContext: android.PathForTesting("construct_context"),
+ }
}
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index b0a684e..dc17c0a 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -259,41 +259,53 @@
Implicits(clcHost).
Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clcTarget, ":") + "]")
- } else if module.EnforceUsesLibraries {
+ } else {
+ // There are three categories of Java modules handled here:
+ //
+ // - Modules that have passed verify_uses_libraries check. They are AOT-compiled and
+ // expected to be loaded on device without CLC mismatch errors.
+ //
+ // - Modules that have failed the check in relaxed mode, so it didn't cause a build error.
+ // They are dexpreopted with "verify" filter and not AOT-compiled.
+ // TODO(b/132357300): ensure that CLC mismatch errors are ignored with "verify" filter.
+ //
+ // - Modules that didn't run the check. They are AOT-compiled, but it's unknown if they
+ // will have CLC mismatch errors on device (the check is disabled by default).
+ //
+ // TODO(b/132357300): enable the check by default and eliminate the last category, so that
+ // no time/space is wasted on AOT-compiling modules that will fail CLC check on device.
+
+ var manifestOrApk android.Path
+ if module.ManifestPath.Valid() {
+ // Ok, there is an XML manifest.
+ manifestOrApk = module.ManifestPath.Path()
+ } else if filepath.Ext(base) == ".apk" {
+ // Ok, there is is an APK with the manifest inside.
+ manifestOrApk = module.DexPath
+ }
+
// Generate command that saves target SDK version in a shell variable.
- if module.ManifestPath != nil {
+ if manifestOrApk == nil {
+ // There is neither an XML manifest nor APK => nowhere to extract targetSdkVersion from.
+ // Set the latest ("any") version: then construct_context will not add any compatibility
+ // libraries (if this is incorrect, there will be a CLC mismatch and dexopt on device).
+ rule.Command().Textf(`target_sdk_version=%d`, AnySdkVersion)
+ } else {
rule.Command().Text(`target_sdk_version="$(`).
Tool(globalSoong.ManifestCheck).
Flag("--extract-target-sdk-version").
- Input(module.ManifestPath).
- Text(`)"`)
- } else {
- // No manifest to extract targetSdkVersion from, hope that DexJar is an APK
- rule.Command().Text(`target_sdk_version="$(`).
- Tool(globalSoong.Aapt).
- Flag("dump badging").
- Input(module.DexPath).
- Text(`| grep "targetSdkVersion" | sed -n "s/targetSdkVersion:'\(.*\)'/\1/p"`).
+ Input(manifestOrApk).
+ FlagWithInput("--aapt ", globalSoong.Aapt).
Text(`)"`)
}
// Generate command that saves host and target class loader context in shell variables.
clc, paths := ComputeClassLoaderContext(module.ClassLoaderContexts)
- cmd := rule.Command().
+ rule.Command().
Text(`eval "$(`).Tool(globalSoong.ConstructContext).
Text(` --target-sdk-version ${target_sdk_version}`).
- Text(clc).Implicits(paths)
- cmd.Text(`)"`)
-
- } else {
- // Other libraries or APKs for which the exact <uses-library> list is unknown.
- // Pass special class loader context to skip the classpath and collision check.
- // This will get removed once LOCAL_USES_LIBRARIES is enforced.
- // Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default
- // to the &.
- rule.Command().
- Text(`class_loader_context_arg=--class-loader-context=\&`).
- Text(`stored_class_loader_context_arg=""`)
+ Text(clc).Implicits(paths).
+ Text(`)"`)
}
// Devices that do not have a product partition use a symlink from /product to /system/product.
@@ -366,7 +378,16 @@
} else {
compilerFilter = "quicken"
}
- cmd.FlagWithArg("--compiler-filter=", compilerFilter)
+ if module.EnforceUsesLibraries {
+ // If the verify_uses_libraries check failed (in this case status file contains a
+ // non-empty error message), then use "verify" compiler filter to avoid compiling any
+ // code (it would be rejected on device because of a class loader context mismatch).
+ cmd.Text("--compiler-filter=$(if test -s ").
+ Input(module.EnforceUsesLibrariesStatusFile).
+ Text(" ; then echo verify ; else echo " + compilerFilter + " ; fi)")
+ } else {
+ cmd.FlagWithArg("--compiler-filter=", compilerFilter)
+ }
}
if generateDM {
@@ -542,6 +563,12 @@
}
}
+// Returns path to a file containing the reult of verify_uses_libraries check (empty if the check
+// has succeeded, or an error message if it failed).
+func UsesLibrariesStatusFile(ctx android.ModuleContext) android.WritablePath {
+ return android.PathForModuleOut(ctx, "enforce_uses_libraries.status")
+}
+
func contains(l []string, s string) bool {
for _, e := range l {
if e == s {
diff --git a/dexpreopt/dexpreopt_gen/Android.bp b/dexpreopt/dexpreopt_gen/Android.bp
index 3f0619c..2111451 100644
--- a/dexpreopt/dexpreopt_gen/Android.bp
+++ b/dexpreopt/dexpreopt_gen/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "dexpreopt_gen",
srcs: [
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 59278fd..12df36b 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -43,6 +43,7 @@
PreoptFlags: nil,
ProfileClassListing: android.OptionalPath{},
ProfileIsTextListing: false,
+ EnforceUsesLibrariesStatusFile: android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)),
EnforceUsesLibraries: false,
ClassLoaderContexts: nil,
Archs: []android.ArchType{android.Arm},
@@ -61,7 +62,7 @@
func TestDexPreopt(t *testing.T) {
config := android.TestConfig("out", nil, "", nil)
ctx := android.BuilderContextForTesting(config)
- globalSoong := GlobalSoongConfigForTests(config)
+ globalSoong := globalSoongConfigForTests()
global := GlobalConfigForTests(ctx)
module := testSystemModuleConfig(ctx, "test")
@@ -83,7 +84,7 @@
func TestDexPreoptSystemOther(t *testing.T) {
config := android.TestConfig("out", nil, "", nil)
ctx := android.BuilderContextForTesting(config)
- globalSoong := GlobalSoongConfigForTests(config)
+ globalSoong := globalSoongConfigForTests()
global := GlobalConfigForTests(ctx)
systemModule := testSystemModuleConfig(ctx, "Stest")
systemProductModule := testSystemProductModuleConfig(ctx, "SPtest")
@@ -143,7 +144,7 @@
func TestDexPreoptProfile(t *testing.T) {
config := android.TestConfig("out", nil, "", nil)
ctx := android.BuilderContextForTesting(config)
- globalSoong := GlobalSoongConfigForTests(config)
+ globalSoong := globalSoongConfigForTests()
global := GlobalConfigForTests(ctx)
module := testSystemModuleConfig(ctx, "test")
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
index b572eb3..c0ba5ca 100644
--- a/dexpreopt/testing.go
+++ b/dexpreopt/testing.go
@@ -15,33 +15,119 @@
package dexpreopt
import (
+ "fmt"
+
"android/soong/android"
)
-type dummyToolBinary struct {
+type fakeToolBinary struct {
android.ModuleBase
}
-func (m *dummyToolBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+func (m *fakeToolBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
-func (m *dummyToolBinary) HostToolPath() android.OptionalPath {
+func (m *fakeToolBinary) HostToolPath() android.OptionalPath {
return android.OptionalPathForPath(android.PathForTesting("dex2oat"))
}
-func dummyToolBinaryFactory() android.Module {
- module := &dummyToolBinary{}
+func fakeToolBinaryFactory() android.Module {
+ module := &fakeToolBinary{}
android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
return module
}
-func RegisterToolModulesForTest(ctx *android.TestContext) {
- ctx.RegisterModuleType("dummy_tool_binary", dummyToolBinaryFactory)
+func RegisterToolModulesForTest(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("fake_tool_binary", fakeToolBinaryFactory)
}
func BpToolModulesForTest() string {
return `
- dummy_tool_binary {
+ fake_tool_binary {
name: "dex2oatd",
}
`
}
+
+func CompatLibDefinitionsForTest() string {
+ bp := ""
+
+ // For class loader context and <uses-library> tests.
+ dexpreoptModules := []string{"android.test.runner"}
+ dexpreoptModules = append(dexpreoptModules, CompatUsesLibs...)
+ dexpreoptModules = append(dexpreoptModules, OptionalCompatUsesLibs...)
+
+ for _, extra := range dexpreoptModules {
+ bp += fmt.Sprintf(`
+ java_library {
+ name: "%s",
+ srcs: ["a.java"],
+ sdk_version: "none",
+ system_modules: "stable-core-platform-api-stubs-system-modules",
+ compile_dex: true,
+ installable: true,
+ }
+ `, extra)
+ }
+
+ return bp
+}
+
+var PrepareForTestWithDexpreoptCompatLibs = android.GroupFixturePreparers(
+ android.FixtureAddFile("defaults/dexpreopt/compat/a.java", nil),
+ android.FixtureAddTextFile("defaults/dexpreopt/compat/Android.bp", CompatLibDefinitionsForTest()),
+)
+
+var PrepareForTestWithFakeDex2oatd = android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(RegisterToolModulesForTest),
+ android.FixtureAddTextFile("defaults/dexpreopt/Android.bp", BpToolModulesForTest()),
+)
+
+// Prepares a test fixture by enabling dexpreopt, registering the fake_tool_binary module type and
+// using that to define the `dex2oatd` module.
+var PrepareForTestByEnablingDexpreopt = android.GroupFixturePreparers(
+ FixtureModifyGlobalConfig(func(*GlobalConfig) {}),
+)
+
+// FixtureModifyGlobalConfig enables dexpreopt (unless modified by the mutator) and modifies the
+// configuration.
+func FixtureModifyGlobalConfig(configModifier func(dexpreoptConfig *GlobalConfig)) android.FixturePreparer {
+ return android.FixtureModifyConfig(func(config android.Config) {
+ // Initialize the dexpreopt GlobalConfig to an empty structure. This has no effect if it has
+ // already been set.
+ pathCtx := android.PathContextForTesting(config)
+ dexpreoptConfig := GlobalConfigForTests(pathCtx)
+ SetTestGlobalConfig(config, dexpreoptConfig)
+
+ // Retrieve the existing configuration and modify it.
+ dexpreoptConfig = GetGlobalConfig(pathCtx)
+ configModifier(dexpreoptConfig)
+ })
+}
+
+// FixtureSetArtBootJars enables dexpreopt and sets the ArtApexJars property.
+func FixtureSetArtBootJars(bootJars ...string) android.FixturePreparer {
+ return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+ dexpreoptConfig.ArtApexJars = android.CreateTestConfiguredJarList(bootJars)
+ })
+}
+
+// FixtureSetBootJars enables dexpreopt and sets the BootJars property.
+func FixtureSetBootJars(bootJars ...string) android.FixturePreparer {
+ return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+ dexpreoptConfig.BootJars = android.CreateTestConfiguredJarList(bootJars)
+ })
+}
+
+// FixtureSetUpdatableBootJars sets the UpdatableBootJars property in the global config.
+func FixtureSetUpdatableBootJars(bootJars ...string) android.FixturePreparer {
+ return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+ dexpreoptConfig.UpdatableBootJars = android.CreateTestConfiguredJarList(bootJars)
+ })
+}
+
+// FixtureSetPreoptWithUpdatableBcp sets the PreoptWithUpdatableBcp property in the global config.
+func FixtureSetPreoptWithUpdatableBcp(value bool) android.FixturePreparer {
+ return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+ dexpreoptConfig.PreoptWithUpdatableBcp = value
+ })
+}
diff --git a/docs/map_files.md b/docs/map_files.md
index 9fc0d14..192530f 100644
--- a/docs/map_files.md
+++ b/docs/map_files.md
@@ -52,7 +52,8 @@
symbol visibility of the library to expose only the interface named by the map
file. Without this, APIs that you have not explicitly exposed will still be
available to users via `dlsym`. Note: All comments are ignored in this case. Any
-symbol named in any `global:` group will be visible.
+symbol named in any `global:` group will be visible in the implementation
+library. Annotations in comments only affect what is exposed by the stubs.
## Special version names
@@ -76,9 +77,13 @@
### future
Indicates that the version or symbol is first introduced in the "future" API
-level. This is an abitrarily high API level used to define APIs that have not
+level. This is an arbitrarily high API level used to define APIs that have not
yet been added to a specific release.
+Warning: APIs marked `future` will be usable in any module with `sdk: "current"`
+but **will not be included in the NDK**. `future` should generally not be used,
+but is useful when developing APIs for an unknown future release.
+
### introduced
Indicates the version in which an API was first introduced. For example,
@@ -92,13 +97,15 @@
determine which API level an API was added in. The `first_version` property of
`ndk_library` will dictate which API levels stubs are generated for. If the
module sets `first_version: "21"`, no symbols were introduced before API 21.
+**Symbol names for which no other rule applies will implicitly be introduced in
+`first_version`.**
-Codenames can (and typically should) be used when defining new APIs. This allows
-the actual number of the API level to remain vague during development of that
-release. For example, `introduced=S` can be used to define APIs added in S. Any
-code name known to the build system can be used. For a list of versions known to
-the build system, see `out/soong/api_levels.json` (if not present, run `m
-out/soong/api_levels.json` to generate it).
+Code names can (and typically should) be used when defining new APIs. This
+allows the actual number of the API level to remain vague during development of
+that release. For example, `introduced=S` can be used to define APIs added in S.
+Any code name known to the build system can be used. For a list of versions
+known to the build system, see `out/soong/api_levels.json` (if not present, run
+`m out/soong/api_levels.json` to generate it).
Architecture-specific variants of this tag exist:
@@ -123,6 +130,8 @@
than the NDK. May be used in combination with `apex` if the symbol is exposed to
both APEX and the LL-NDK.
+Historically this annotation was spelled `vndk`, but it has always meant LL-NDK.
+
### platform-only
Indicates that the version or symbol is public in the implementation library but
@@ -131,9 +140,9 @@
clear to the developer that they are up to no good.
The typical use for this tag is for exposing an API to the platform that is not
-for use by the NDK, LL-NDK, or APEX. It is preferable to keep such APIs in an
-entirely separate library to protect them from access via `dlsym`, but this is
-not always possible.
+for use by the NDK, LL-NDK, or APEX (similar to Java's `@SystemAPI`). It is
+preferable to keep such APIs in an entirely separate library to protect them
+from access via `dlsym`, but this is not always possible.
### var
diff --git a/env/Android.bp b/env/Android.bp
deleted file mode 100644
index 90c6047..0000000
--- a/env/Android.bp
+++ /dev/null
@@ -1,7 +0,0 @@
-bootstrap_go_package {
- name: "soong-env",
- pkgPath: "android/soong/env",
- srcs: [
- "env.go",
- ],
-}
diff --git a/etc/Android.bp b/etc/Android.bp
index cfd303e..cab7389 100644
--- a/etc/Android.bp
+++ b/etc/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-etc",
pkgPath: "android/soong/etc",
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 850c8f9..3204e70 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -28,6 +28,9 @@
// various `prebuilt_*` mutators.
import (
+ "fmt"
+ "strings"
+
"github.com/google/blueprint/proptools"
"android/soong/android"
@@ -45,6 +48,7 @@
func RegisterPrebuiltEtcBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
ctx.RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
+ ctx.RegisterModuleType("prebuilt_root", PrebuiltRootFactory)
ctx.RegisterModuleType("prebuilt_usr_share", PrebuiltUserShareFactory)
ctx.RegisterModuleType("prebuilt_usr_share_host", PrebuiltUserShareHostFactory)
ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
@@ -52,18 +56,12 @@
ctx.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory)
}
+var PrepareForTestWithPrebuiltEtc = android.FixtureRegisterWithContext(RegisterPrebuiltEtcBuildComponents)
+
type prebuiltEtcProperties struct {
// Source file of this prebuilt. Can reference a genrule type module with the ":module" syntax.
Src *string `android:"path,arch_variant"`
- // Optional subdirectory under which this file is installed into, cannot be specified with
- // relative_install_path, prefer relative_install_path.
- Sub_dir *string `android:"arch_variant"`
-
- // Optional subdirectory under which this file is installed into, cannot be specified with
- // sub_dir.
- Relative_install_path *string `android:"arch_variant"`
-
// Optional name for the installed file. If unspecified, name of the module is used as the file
// name.
Filename *string `android:"arch_variant"`
@@ -86,6 +84,13 @@
// the recovery variant instead.
Vendor_ramdisk_available *bool
+ // Make this module available when building for debug ramdisk.
+ // On device without a dedicated recovery partition, the module is only
+ // available after switching root into
+ // /first_stage_ramdisk. To expose the module before switching root, install
+ // the recovery variant instead.
+ Debug_ramdisk_available *bool
+
// Make this module available when building for recovery.
Recovery_available *bool
@@ -96,6 +101,16 @@
Symlinks []string `android:"arch_variant"`
}
+type prebuiltSubdirProperties struct {
+ // Optional subdirectory under which this file is installed into, cannot be specified with
+ // relative_install_path, prefer relative_install_path.
+ Sub_dir *string `android:"arch_variant"`
+
+ // Optional subdirectory under which this file is installed into, cannot be specified with
+ // sub_dir.
+ Relative_install_path *string `android:"arch_variant"`
+}
+
type PrebuiltEtcModule interface {
android.Module
@@ -113,7 +128,8 @@
type PrebuiltEtc struct {
android.ModuleBase
- properties prebuiltEtcProperties
+ properties prebuiltEtcProperties
+ subdirProperties prebuiltSubdirProperties
sourceFilePath android.Path
outputFilePath android.OutputPath
@@ -150,6 +166,18 @@
return p.inVendorRamdisk()
}
+func (p *PrebuiltEtc) inDebugRamdisk() bool {
+ return p.ModuleBase.InDebugRamdisk() || p.ModuleBase.InstallInDebugRamdisk()
+}
+
+func (p *PrebuiltEtc) onlyInDebugRamdisk() bool {
+ return p.ModuleBase.InstallInDebugRamdisk()
+}
+
+func (p *PrebuiltEtc) InstallInDebugRamdisk() bool {
+ return p.inDebugRamdisk()
+}
+
func (p *PrebuiltEtc) inRecovery() bool {
return p.ModuleBase.InRecovery() || p.ModuleBase.InstallInRecovery()
}
@@ -168,7 +196,7 @@
func (p *PrebuiltEtc) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
return !p.ModuleBase.InstallInRecovery() && !p.ModuleBase.InstallInRamdisk() &&
- !p.ModuleBase.InstallInVendorRamdisk()
+ !p.ModuleBase.InstallInVendorRamdisk() && !p.ModuleBase.InstallInDebugRamdisk()
}
func (p *PrebuiltEtc) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
@@ -179,6 +207,10 @@
return proptools.Bool(p.properties.Vendor_ramdisk_available) || p.ModuleBase.InstallInVendorRamdisk()
}
+func (p *PrebuiltEtc) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return proptools.Bool(p.properties.Debug_ramdisk_available) || p.ModuleBase.InstallInDebugRamdisk()
+}
+
func (p *PrebuiltEtc) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
return proptools.Bool(p.properties.Recovery_available) || p.ModuleBase.InstallInRecovery()
}
@@ -208,11 +240,22 @@
return p.outputFilePath
}
+var _ android.OutputFileProducer = (*PrebuiltEtc)(nil)
+
+func (p *PrebuiltEtc) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case "":
+ return android.Paths{p.outputFilePath}, nil
+ default:
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+ }
+}
+
func (p *PrebuiltEtc) SubDir() string {
- if subDir := proptools.String(p.properties.Sub_dir); subDir != "" {
+ if subDir := proptools.String(p.subdirProperties.Sub_dir); subDir != "" {
return subDir
}
- return proptools.String(p.properties.Relative_install_path)
+ return proptools.String(p.subdirProperties.Relative_install_path)
}
func (p *PrebuiltEtc) BaseDir() string {
@@ -248,8 +291,13 @@
}
p.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
+ if strings.Contains(filename, "/") {
+ ctx.PropertyErrorf("filename", "filename cannot contain separator '/'")
+ return
+ }
+
// Check that `sub_dir` and `relative_install_path` are not set at the same time.
- if p.properties.Sub_dir != nil && p.properties.Relative_install_path != nil {
+ if p.subdirProperties.Sub_dir != nil && p.subdirProperties.Relative_install_path != nil {
ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir")
}
@@ -269,11 +317,14 @@
Input: p.sourceFilePath,
})
- if p.Installable() {
- installPath := ctx.InstallFile(p.installDirPath, p.outputFilePath.Base(), p.outputFilePath)
- for _, sl := range p.properties.Symlinks {
- ctx.InstallSymlink(p.installDirPath, sl, installPath)
- }
+ if !p.Installable() {
+ p.SkipInstall()
+ }
+
+ // Call InstallFile even when uninstallable to make the module included in the package
+ installPath := ctx.InstallFile(p.installDirPath, p.outputFilePath.Base(), p.outputFilePath)
+ for _, sl := range p.properties.Symlinks {
+ ctx.InstallSymlink(p.installDirPath, sl, installPath)
}
}
@@ -285,6 +336,9 @@
if p.inVendorRamdisk() && !p.onlyInVendorRamdisk() {
nameSuffix = ".vendor_ramdisk"
}
+ if p.inDebugRamdisk() && !p.onlyInDebugRamdisk() {
+ nameSuffix = ".debug_ramdisk"
+ }
if p.inRecovery() && !p.onlyInRecovery() {
nameSuffix = ".recovery"
}
@@ -293,7 +347,7 @@
SubName: nameSuffix,
OutputFile: android.OptionalPathForPath(p.outputFilePath),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_TAGS", "optional")
entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
@@ -312,6 +366,12 @@
func InitPrebuiltEtcModule(p *PrebuiltEtc, dirBase string) {
p.installDirBase = dirBase
p.AddProperties(&p.properties)
+ p.AddProperties(&p.subdirProperties)
+}
+
+func InitPrebuiltRootModule(p *PrebuiltEtc) {
+ p.installDirBase = "."
+ p.AddProperties(&p.properties)
}
// prebuilt_etc is for a prebuilt artifact that is installed in
@@ -334,6 +394,16 @@
return module
}
+// prebuilt_root is for a prebuilt artifact that is installed in
+// <partition>/ directory. Can't have any sub directories.
+func PrebuiltRootFactory() android.Module {
+ module := &PrebuiltEtc{}
+ InitPrebuiltRootModule(module)
+ // This module is device-only
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ return module
+}
+
// prebuilt_usr_share is for a prebuilt artifact that is installed in
// <partition>/usr/share/<sub_dir> directory.
func PrebuiltUserShareFactory() android.Module {
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index 6c4c0b6..fdb5648 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -15,94 +15,29 @@
package etc
import (
- "io/ioutil"
"os"
"path/filepath"
- "reflect"
"testing"
"android/soong/android"
)
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_etc_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ os.Exit(m.Run())
}
-func testPrebuiltEtcContext(t *testing.T, bp string) (*android.TestContext, android.Config) {
- fs := map[string][]byte{
+var prepareForPrebuiltEtcTest = android.GroupFixturePreparers(
+ android.PrepareForTestWithArchMutator,
+ PrepareForTestWithPrebuiltEtc,
+ android.FixtureMergeMockFs(android.MockFS{
"foo.conf": nil,
"bar.conf": nil,
"baz.conf": nil,
- }
+ }),
+)
- config := android.TestArchConfig(buildDir, nil, bp, fs)
-
- ctx := android.NewTestArchContext(config)
- ctx.RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
- ctx.RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
- ctx.RegisterModuleType("prebuilt_usr_share", PrebuiltUserShareFactory)
- ctx.RegisterModuleType("prebuilt_usr_share_host", PrebuiltUserShareHostFactory)
- ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
- ctx.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
- ctx.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory)
- ctx.Register()
-
- return ctx, config
-}
-
-func testPrebuiltEtc(t *testing.T, bp string) (*android.TestContext, android.Config) {
- t.Helper()
-
- ctx, config := testPrebuiltEtcContext(t, bp)
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
-
- return ctx, config
-}
-
-func testPrebuiltEtcError(t *testing.T, pattern, bp string) {
- t.Helper()
-
- ctx, config := testPrebuiltEtcContext(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 TestPrebuiltEtcVariants(t *testing.T) {
- ctx, _ := testPrebuiltEtc(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_etc {
name: "foo.conf",
src: "foo.conf",
@@ -119,24 +54,24 @@
}
`)
- foo_variants := ctx.ModuleVariantsForTests("foo.conf")
+ foo_variants := result.ModuleVariantsForTests("foo.conf")
if len(foo_variants) != 1 {
t.Errorf("expected 1, got %#v", foo_variants)
}
- bar_variants := ctx.ModuleVariantsForTests("bar.conf")
+ bar_variants := result.ModuleVariantsForTests("bar.conf")
if len(bar_variants) != 2 {
t.Errorf("expected 2, got %#v", bar_variants)
}
- baz_variants := ctx.ModuleVariantsForTests("baz.conf")
+ baz_variants := result.ModuleVariantsForTests("baz.conf")
if len(baz_variants) != 1 {
t.Errorf("expected 1, got %#v", bar_variants)
}
}
func TestPrebuiltEtcOutputPath(t *testing.T) {
- ctx, _ := testPrebuiltEtc(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_etc {
name: "foo.conf",
src: "foo.conf",
@@ -144,14 +79,12 @@
}
`)
- p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
- if p.outputFilePath.Base() != "foo.installed.conf" {
- t.Errorf("expected foo.installed.conf, got %q", p.outputFilePath.Base())
- }
+ p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+ android.AssertStringEquals(t, "output file path", "foo.installed.conf", p.outputFilePath.Base())
}
func TestPrebuiltEtcGlob(t *testing.T) {
- ctx, _ := testPrebuiltEtc(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_etc {
name: "my_foo",
src: "foo.*",
@@ -163,19 +96,15 @@
}
`)
- p := ctx.ModuleForTests("my_foo", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
- if p.outputFilePath.Base() != "my_foo" {
- t.Errorf("expected my_foo, got %q", p.outputFilePath.Base())
- }
+ p := result.Module("my_foo", "android_arm64_armv8-a").(*PrebuiltEtc)
+ android.AssertStringEquals(t, "my_foo output file path", "my_foo", p.outputFilePath.Base())
- p = ctx.ModuleForTests("my_bar", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
- if p.outputFilePath.Base() != "bar.conf" {
- t.Errorf("expected bar.conf, got %q", p.outputFilePath.Base())
- }
+ p = result.Module("my_bar", "android_arm64_armv8-a").(*PrebuiltEtc)
+ android.AssertStringEquals(t, "my_bar output file path", "bar.conf", p.outputFilePath.Base())
}
func TestPrebuiltEtcAndroidMk(t *testing.T) {
- ctx, config := testPrebuiltEtc(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_etc {
name: "foo",
src: "foo.conf",
@@ -197,13 +126,11 @@
"LOCAL_TARGET_REQUIRED_MODULES": {"targetModA"},
}
- mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
- entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+ mod := result.Module("foo", "android_arm64_armv8-a").(*PrebuiltEtc)
+ entries := android.AndroidMkEntriesForTest(t, result.TestContext, mod)[0]
for k, expectedValue := range expected {
if value, ok := entries.EntryMap[k]; ok {
- if !reflect.DeepEqual(value, expectedValue) {
- t.Errorf("Incorrect %s '%s', expected '%s'", k, value, expectedValue)
- }
+ android.AssertDeepEquals(t, k, expectedValue, value)
} else {
t.Errorf("No %s defined, saw %q", k, entries.EntryMap)
}
@@ -211,7 +138,7 @@
}
func TestPrebuiltEtcRelativeInstallPathInstallDirPath(t *testing.T) {
- ctx, _ := testPrebuiltEtc(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_etc {
name: "foo.conf",
src: "foo.conf",
@@ -219,26 +146,26 @@
}
`)
- p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
- expected := buildDir + "/target/product/test_device/system/etc/bar"
- if p.installDirPath.String() != expected {
- t.Errorf("expected %q, got %q", expected, p.installDirPath.String())
- }
+ p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+ expected := "out/soong/target/product/test_device/system/etc/bar"
+ android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
}
func TestPrebuiltEtcCannotSetRelativeInstallPathAndSubDir(t *testing.T) {
- testPrebuiltEtcError(t, "relative_install_path is set. Cannot set sub_dir", `
- prebuilt_etc {
- name: "foo.conf",
- src: "foo.conf",
- sub_dir: "bar",
- relative_install_path: "bar",
- }
- `)
+ prepareForPrebuiltEtcTest.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("relative_install_path is set. Cannot set sub_dir")).
+ RunTestWithBp(t, `
+ prebuilt_etc {
+ name: "foo.conf",
+ src: "foo.conf",
+ sub_dir: "bar",
+ relative_install_path: "bar",
+ }
+ `)
}
func TestPrebuiltEtcHost(t *testing.T) {
- ctx, _ := testPrebuiltEtc(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_etc_host {
name: "foo.conf",
src: "foo.conf",
@@ -246,14 +173,38 @@
`)
buildOS := android.BuildOs.String()
- p := ctx.ModuleForTests("foo.conf", buildOS+"_common").Module().(*PrebuiltEtc)
+ p := result.Module("foo.conf", buildOS+"_common").(*PrebuiltEtc)
if !p.Host() {
t.Errorf("host bit is not set for a prebuilt_etc_host module.")
}
}
+func TestPrebuiltRootInstallDirPath(t *testing.T) {
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+ prebuilt_root {
+ name: "foo.conf",
+ src: "foo.conf",
+ filename: "foo.conf",
+ }
+ `)
+
+ p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+ expected := "out/soong/target/product/test_device/system"
+ android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+}
+
+func TestPrebuiltRootInstallDirPathValidate(t *testing.T) {
+ prepareForPrebuiltEtcTest.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("filename cannot contain separator")).RunTestWithBp(t, `
+ prebuilt_root {
+ name: "foo.conf",
+ src: "foo.conf",
+ filename: "foo/bar.conf",
+ }
+ `)
+}
+
func TestPrebuiltUserShareInstallDirPath(t *testing.T) {
- ctx, _ := testPrebuiltEtc(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_usr_share {
name: "foo.conf",
src: "foo.conf",
@@ -261,15 +212,13 @@
}
`)
- p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
- expected := buildDir + "/target/product/test_device/system/usr/share/bar"
- if p.installDirPath.String() != expected {
- t.Errorf("expected %q, got %q", expected, p.installDirPath.String())
- }
+ p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+ expected := "out/soong/target/product/test_device/system/usr/share/bar"
+ android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
}
func TestPrebuiltUserShareHostInstallDirPath(t *testing.T) {
- ctx, config := testPrebuiltEtc(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_usr_share_host {
name: "foo.conf",
src: "foo.conf",
@@ -278,30 +227,26 @@
`)
buildOS := android.BuildOs.String()
- p := ctx.ModuleForTests("foo.conf", buildOS+"_common").Module().(*PrebuiltEtc)
- expected := filepath.Join(buildDir, "host", config.PrebuiltOS(), "usr", "share", "bar")
- if p.installDirPath.String() != expected {
- t.Errorf("expected %q, got %q", expected, p.installDirPath.String())
- }
+ p := result.Module("foo.conf", buildOS+"_common").(*PrebuiltEtc)
+ expected := filepath.Join("out/soong/host", result.Config.PrebuiltOS(), "usr", "share", "bar")
+ android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
}
func TestPrebuiltFontInstallDirPath(t *testing.T) {
- ctx, _ := testPrebuiltEtc(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_font {
name: "foo.conf",
src: "foo.conf",
}
`)
- p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
- expected := buildDir + "/target/product/test_device/system/fonts"
- if p.installDirPath.String() != expected {
- t.Errorf("expected %q, got %q", expected, p.installDirPath.String())
- }
+ p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+ expected := "out/soong/target/product/test_device/system/fonts"
+ android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
}
func TestPrebuiltFirmwareDirPath(t *testing.T) {
- targetPath := buildDir + "/target/product/test_device"
+ targetPath := "out/soong/target/product/test_device"
tests := []struct {
description string
config string
@@ -327,17 +272,15 @@
}}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
- ctx, _ := testPrebuiltEtc(t, tt.config)
- p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
- if p.installDirPath.String() != tt.expectedPath {
- t.Errorf("expected %q, got %q", tt.expectedPath, p.installDirPath)
- }
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, tt.config)
+ p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+ android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPath)
})
}
}
func TestPrebuiltDSPDirPath(t *testing.T) {
- targetPath := filepath.Join(buildDir, "/target/product/test_device")
+ targetPath := "out/soong/target/product/test_device"
tests := []struct {
description string
config string
@@ -363,11 +306,9 @@
}}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
- ctx, _ := testPrebuiltEtc(t, tt.config)
- p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a").Module().(*PrebuiltEtc)
- if p.installDirPath.String() != tt.expectedPath {
- t.Errorf("expected %q, got %q", tt.expectedPath, p.installDirPath)
- }
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, tt.config)
+ p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+ android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPath)
})
}
}
diff --git a/filesystem/Android.bp b/filesystem/Android.bp
index 926df6e..3cdaa64 100644
--- a/filesystem/Android.bp
+++ b/filesystem/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-filesystem",
pkgPath: "android/soong/filesystem",
@@ -7,9 +11,14 @@
"soong-android",
],
srcs: [
+ "bootimg.go",
"filesystem.go",
+ "logical_partition.go",
+ "vbmeta.go",
+ "testing.go",
],
testSrcs: [
+ "filesystem_test.go",
],
pluginFor: ["soong_build"],
}
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
new file mode 100644
index 0000000..29a8a39
--- /dev/null
+++ b/filesystem/bootimg.go
@@ -0,0 +1,298 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package filesystem
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+)
+
+func init() {
+ android.RegisterModuleType("bootimg", bootimgFactory)
+}
+
+type bootimg struct {
+ android.ModuleBase
+
+ properties bootimgProperties
+
+ output android.OutputPath
+ installDir android.InstallPath
+}
+
+type bootimgProperties struct {
+ // Set the name of the output. Defaults to <module_name>.img.
+ Stem *string
+
+ // Path to the linux kernel prebuilt file
+ Kernel_prebuilt *string `android:"arch_variant,path"`
+
+ // Filesystem module that is used as ramdisk
+ Ramdisk_module *string
+
+ // Path to the device tree blob (DTB) prebuilt file to add to this boot image
+ Dtb_prebuilt *string `android:"arch_variant,path"`
+
+ // Header version number. Must be set to one of the version numbers that are currently
+ // supported. Refer to
+ // https://source.android.com/devices/bootloader/boot-image-header
+ Header_version *string
+
+ // Determines if this image is for the vendor_boot partition. Default is false. Refer to
+ // https://source.android.com/devices/bootloader/partitions/vendor-boot-partitions
+ Vendor_boot *bool
+
+ // Optional kernel commandline
+ Cmdline *string `android:"arch_variant"`
+
+ // File that contains bootconfig parameters. This can be set only when `vendor_boot` is true
+ // and `header_version` is greater than or equal to 4.
+ Bootconfig *string `android:"arch_variant,path"`
+
+ // When set to true, sign the image with avbtool. Default is false.
+ Use_avb *bool
+
+ // Name of the partition stored in vbmeta desc. Defaults to the name of this module.
+ Partition_name *string
+
+ // Path to the private key that avbtool will use to sign this filesystem image.
+ // TODO(jiyong): allow apex_key to be specified here
+ Avb_private_key *string `android:"path"`
+
+ // Hash and signing algorithm for avbtool. Default is SHA256_RSA4096.
+ Avb_algorithm *string
+}
+
+// bootimg is the image for the boot partition. It consists of header, kernel, ramdisk, and dtb.
+func bootimgFactory() android.Module {
+ module := &bootimg{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ return module
+}
+
+type bootimgDep struct {
+ blueprint.BaseDependencyTag
+ kind string
+}
+
+var bootimgRamdiskDep = bootimgDep{kind: "ramdisk"}
+
+func (b *bootimg) DepsMutator(ctx android.BottomUpMutatorContext) {
+ ramdisk := proptools.String(b.properties.Ramdisk_module)
+ if ramdisk != "" {
+ ctx.AddDependency(ctx.Module(), bootimgRamdiskDep, ramdisk)
+ }
+}
+
+func (b *bootimg) installFileName() string {
+ return proptools.StringDefault(b.properties.Stem, b.BaseModuleName()+".img")
+}
+
+func (b *bootimg) partitionName() string {
+ return proptools.StringDefault(b.properties.Partition_name, b.BaseModuleName())
+}
+
+func (b *bootimg) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ vendor := proptools.Bool(b.properties.Vendor_boot)
+ unsignedOutput := b.buildBootImage(ctx, vendor)
+
+ if proptools.Bool(b.properties.Use_avb) {
+ b.output = b.signImage(ctx, unsignedOutput)
+ } else {
+ b.output = unsignedOutput
+ }
+
+ b.installDir = android.PathForModuleInstall(ctx, "etc")
+ ctx.InstallFile(b.installDir, b.installFileName(), b.output)
+}
+
+func (b *bootimg) buildBootImage(ctx android.ModuleContext, vendor bool) android.OutputPath {
+ output := android.PathForModuleOut(ctx, "unsigned", b.installFileName()).OutputPath
+
+ builder := android.NewRuleBuilder(pctx, ctx)
+ cmd := builder.Command().BuiltTool("mkbootimg")
+
+ kernel := proptools.String(b.properties.Kernel_prebuilt)
+ if vendor && kernel != "" {
+ ctx.PropertyErrorf("kernel_prebuilt", "vendor_boot partition can't have kernel")
+ return output
+ }
+ if !vendor && kernel == "" {
+ ctx.PropertyErrorf("kernel_prebuilt", "boot partition must have kernel")
+ return output
+ }
+ if kernel != "" {
+ cmd.FlagWithInput("--kernel ", android.PathForModuleSrc(ctx, kernel))
+ }
+
+ dtbName := proptools.String(b.properties.Dtb_prebuilt)
+ if dtbName == "" {
+ ctx.PropertyErrorf("dtb_prebuilt", "must be set")
+ return output
+ }
+ dtb := android.PathForModuleSrc(ctx, dtbName)
+ cmd.FlagWithInput("--dtb ", dtb)
+
+ cmdline := proptools.String(b.properties.Cmdline)
+ if cmdline != "" {
+ flag := "--cmdline "
+ if vendor {
+ flag = "--vendor_cmdline "
+ }
+ cmd.FlagWithArg(flag, proptools.ShellEscapeIncludingSpaces(cmdline))
+ }
+
+ headerVersion := proptools.String(b.properties.Header_version)
+ if headerVersion == "" {
+ ctx.PropertyErrorf("header_version", "must be set")
+ return output
+ }
+ verNum, err := strconv.Atoi(headerVersion)
+ if err != nil {
+ ctx.PropertyErrorf("header_version", "%q is not a number", headerVersion)
+ return output
+ }
+ if verNum < 3 {
+ ctx.PropertyErrorf("header_version", "must be 3 or higher for vendor_boot")
+ return output
+ }
+ cmd.FlagWithArg("--header_version ", headerVersion)
+
+ ramdiskName := proptools.String(b.properties.Ramdisk_module)
+ if ramdiskName == "" {
+ ctx.PropertyErrorf("ramdisk_module", "must be set")
+ return output
+ }
+ ramdisk := ctx.GetDirectDepWithTag(ramdiskName, bootimgRamdiskDep)
+ if filesystem, ok := ramdisk.(*filesystem); ok {
+ flag := "--ramdisk "
+ if vendor {
+ flag = "--vendor_ramdisk "
+ }
+ cmd.FlagWithInput(flag, filesystem.OutputPath())
+ } else {
+ ctx.PropertyErrorf("ramdisk", "%q is not android_filesystem module", ramdisk.Name())
+ return output
+ }
+
+ bootconfig := proptools.String(b.properties.Bootconfig)
+ if bootconfig != "" {
+ if !vendor {
+ ctx.PropertyErrorf("bootconfig", "requires vendor_boot: true")
+ return output
+ }
+ if verNum < 4 {
+ ctx.PropertyErrorf("bootconfig", "requires header_version: 4 or later")
+ return output
+ }
+ cmd.FlagWithInput("--vendor_bootconfig ", android.PathForModuleSrc(ctx, bootconfig))
+ }
+
+ flag := "--output "
+ if vendor {
+ flag = "--vendor_boot "
+ }
+ cmd.FlagWithOutput(flag, output)
+
+ builder.Build("build_bootimg", fmt.Sprintf("Creating %s", b.BaseModuleName()))
+ return output
+}
+
+func (b *bootimg) signImage(ctx android.ModuleContext, unsignedImage android.OutputPath) android.OutputPath {
+ propFile, toolDeps := b.buildPropFile(ctx)
+
+ output := android.PathForModuleOut(ctx, b.installFileName()).OutputPath
+ builder := android.NewRuleBuilder(pctx, ctx)
+ builder.Command().Text("cp").Input(unsignedImage).Output(output)
+ builder.Command().BuiltTool("verity_utils").
+ Input(propFile).
+ Implicits(toolDeps).
+ Output(output)
+
+ builder.Build("sign_bootimg", fmt.Sprintf("Signing %s", b.BaseModuleName()))
+ return output
+}
+
+func (b *bootimg) buildPropFile(ctx android.ModuleContext) (propFile android.OutputPath, toolDeps android.Paths) {
+ var sb strings.Builder
+ var deps android.Paths
+ addStr := func(name string, value string) {
+ fmt.Fprintf(&sb, "%s=%s\n", name, value)
+ }
+ addPath := func(name string, path android.Path) {
+ addStr(name, path.String())
+ deps = append(deps, path)
+ }
+
+ addStr("avb_hash_enable", "true")
+ addPath("avb_avbtool", ctx.Config().HostToolPath(ctx, "avbtool"))
+ algorithm := proptools.StringDefault(b.properties.Avb_algorithm, "SHA256_RSA4096")
+ addStr("avb_algorithm", algorithm)
+ key := android.PathForModuleSrc(ctx, proptools.String(b.properties.Avb_private_key))
+ addPath("avb_key_path", key)
+ addStr("avb_add_hash_footer_args", "") // TODO(jiyong): add --rollback_index
+ partitionName := proptools.StringDefault(b.properties.Partition_name, b.Name())
+ addStr("partition_name", partitionName)
+
+ propFile = android.PathForModuleOut(ctx, "prop").OutputPath
+ android.WriteFileRule(ctx, propFile, sb.String())
+ return propFile, deps
+}
+
+var _ android.AndroidMkEntriesProvider = (*bootimg)(nil)
+
+// Implements android.AndroidMkEntriesProvider
+func (b *bootimg) AndroidMkEntries() []android.AndroidMkEntries {
+ return []android.AndroidMkEntries{android.AndroidMkEntries{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(b.output),
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", b.installDir.ToMakePath().String())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", b.installFileName())
+ },
+ },
+ }}
+}
+
+var _ Filesystem = (*bootimg)(nil)
+
+func (b *bootimg) OutputPath() android.Path {
+ return b.output
+}
+
+func (b *bootimg) SignedOutputPath() android.Path {
+ if proptools.Bool(b.properties.Use_avb) {
+ return b.OutputPath()
+ }
+ return nil
+}
+
+var _ android.OutputFileProducer = (*bootimg)(nil)
+
+// Implements android.OutputFileProducer
+func (b *bootimg) OutputFiles(tag string) (android.Paths, error) {
+ if tag == "" {
+ return []android.Path{b.output}, nil
+ }
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+}
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index c6181bc..cf98717 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -16,24 +16,70 @@
import (
"fmt"
+ "path/filepath"
+ "strings"
"android/soong/android"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
)
func init() {
- android.RegisterModuleType("android_filesystem", filesystemFactory)
+ registerBuildComponents(android.InitRegistrationContext)
+}
+
+func registerBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("android_filesystem", filesystemFactory)
}
type filesystem struct {
android.ModuleBase
android.PackagingBase
+ properties filesystemProperties
+
output android.OutputPath
installDir android.InstallPath
}
+type symlinkDefinition struct {
+ Target *string
+ Name *string
+}
+
+type filesystemProperties struct {
+ // When set to true, sign the image with avbtool. Default is false.
+ Use_avb *bool
+
+ // Path to the private key that avbtool will use to sign this filesystem image.
+ // TODO(jiyong): allow apex_key to be specified here
+ Avb_private_key *string `android:"path"`
+
+ // Hash and signing algorithm for avbtool. Default is SHA256_RSA4096.
+ Avb_algorithm *string
+
+ // Name of the partition stored in vbmeta desc. Defaults to the name of this module.
+ Partition_name *string
+
+ // Type of the filesystem. Currently, ext4, cpio, and compressed_cpio are supported. Default
+ // is ext4.
+ Type *string
+
+ // file_contexts file to make image. Currently, only ext4 is supported.
+ File_contexts *string `android:"path"`
+
+ // Base directory relative to root, to which deps are installed, e.g. "system". Default is "."
+ // (root).
+ Base_dir *string
+
+ // Directories to be created under root. e.g. /dev, /proc, etc.
+ Dirs []string
+
+ // Symbolic links to be created under root with "ln -sf <target> <name>".
+ Symlinks []symlinkDefinition
+}
+
// android_filesystem packages a set of modules and their transitive dependencies into a filesystem
// image. The filesystem images are expected to be mounted in the target device, which means the
// modules in the filesystem image are built for the target device (i.e. Android, not Linux host).
@@ -41,17 +87,45 @@
// partitions like system.img. For example, cc_library modules are placed under ./lib[64] directory.
func filesystemFactory() android.Module {
module := &filesystem{}
+ module.AddProperties(&module.properties)
android.InitPackageModule(module)
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
return module
}
-var dependencyTag = struct{ blueprint.BaseDependencyTag }{}
+var dependencyTag = struct {
+ blueprint.BaseDependencyTag
+ android.PackagingItemAlwaysDepTag
+}{}
func (f *filesystem) DepsMutator(ctx android.BottomUpMutatorContext) {
f.AddDeps(ctx, dependencyTag)
}
+type fsType int
+
+const (
+ ext4Type fsType = iota
+ compressedCpioType
+ cpioType // uncompressed
+ unknown
+)
+
+func (f *filesystem) fsType(ctx android.ModuleContext) fsType {
+ typeStr := proptools.StringDefault(f.properties.Type, "ext4")
+ switch typeStr {
+ case "ext4":
+ return ext4Type
+ case "compressed_cpio":
+ return compressedCpioType
+ case "cpio":
+ return cpioType
+ default:
+ ctx.PropertyErrorf("type", "%q not supported", typeStr)
+ return unknown
+ }
+}
+
func (f *filesystem) installFileName() string {
return f.BaseModuleName() + ".img"
}
@@ -59,39 +133,229 @@
var pctx = android.NewPackageContext("android/soong/filesystem")
func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- zipFile := android.PathForModuleOut(ctx, "temp.zip").OutputPath
- f.CopyDepsToZip(ctx, zipFile)
+ switch f.fsType(ctx) {
+ case ext4Type:
+ f.output = f.buildImageUsingBuildImage(ctx)
+ case compressedCpioType:
+ f.output = f.buildCpioImage(ctx, true)
+ case cpioType:
+ f.output = f.buildCpioImage(ctx, false)
+ default:
+ return
+ }
+
+ f.installDir = android.PathForModuleInstall(ctx, "etc")
+ ctx.InstallFile(f.installDir, f.installFileName(), f.output)
+}
+
+// root zip will contain stuffs like dirs or symlinks.
+func (f *filesystem) buildRootZip(ctx android.ModuleContext) android.OutputPath {
+ rootDir := android.PathForModuleGen(ctx, "root").OutputPath
+ builder := android.NewRuleBuilder(pctx, ctx)
+ builder.Command().Text("rm -rf").Text(rootDir.String())
+ builder.Command().Text("mkdir -p").Text(rootDir.String())
+
+ // create dirs and symlinks
+ for _, dir := range f.properties.Dirs {
+ // OutputPath.Join verifies dir
+ builder.Command().Text("mkdir -p").Text(rootDir.Join(ctx, dir).String())
+ }
+
+ for _, symlink := range f.properties.Symlinks {
+ name := strings.TrimSpace(proptools.String(symlink.Name))
+ target := strings.TrimSpace(proptools.String(symlink.Target))
+
+ if name == "" {
+ ctx.PropertyErrorf("symlinks", "Name can't be empty")
+ continue
+ }
+
+ if target == "" {
+ ctx.PropertyErrorf("symlinks", "Target can't be empty")
+ continue
+ }
+
+ // OutputPath.Join verifies name. don't need to verify target.
+ dst := rootDir.Join(ctx, name)
+
+ builder.Command().Text("mkdir -p").Text(filepath.Dir(dst.String()))
+ builder.Command().Text("ln -sf").Text(proptools.ShellEscape(target)).Text(dst.String())
+ }
+
+ zipOut := android.PathForModuleGen(ctx, "root.zip").OutputPath
+
+ builder.Command().
+ BuiltTool("soong_zip").
+ FlagWithOutput("-o ", zipOut).
+ FlagWithArg("-C ", rootDir.String()).
+ Flag("-L 0"). // no compression because this will be unzipped soon
+ FlagWithArg("-D ", rootDir.String()).
+ Flag("-d") // include empty directories
+ builder.Command().Text("rm -rf").Text(rootDir.String())
+
+ builder.Build("zip_root", fmt.Sprintf("zipping root contents for %s", ctx.ModuleName()))
+ return zipOut
+}
+
+func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) android.OutputPath {
+ depsZipFile := android.PathForModuleOut(ctx, "deps.zip").OutputPath
+ f.CopyDepsToZip(ctx, depsZipFile)
+
+ builder := android.NewRuleBuilder(pctx, ctx)
+ depsBase := proptools.StringDefault(f.properties.Base_dir, ".")
+ rebasedDepsZip := android.PathForModuleOut(ctx, "rebased_deps.zip").OutputPath
+ builder.Command().
+ BuiltTool("zip2zip").
+ FlagWithInput("-i ", depsZipFile).
+ FlagWithOutput("-o ", rebasedDepsZip).
+ Text("**/*:" + proptools.ShellEscape(depsBase)) // zip2zip verifies depsBase
rootDir := android.PathForModuleOut(ctx, "root").OutputPath
- builder := android.NewRuleBuilder(pctx, ctx)
+ rootZip := f.buildRootZip(ctx)
builder.Command().
BuiltTool("zipsync").
FlagWithArg("-d ", rootDir.String()). // zipsync wipes this. No need to clear.
- Input(zipFile)
+ Input(rootZip).
+ Input(rebasedDepsZip)
- mkuserimg := ctx.Config().HostToolPath(ctx, "mkuserimg_mke2fs")
- propFile := android.PathForModuleOut(ctx, "prop").OutputPath
- // TODO(jiyong): support more filesystem types other than ext4
- propsText := fmt.Sprintf(`mount_point=system\n`+
- `fs_type=ext4\n`+
- `use_dynamic_partition_size=true\n`+
- `ext_mkuserimg=%s\n`, mkuserimg.String())
- builder.Command().Text("echo").Flag("-e").Flag(`"` + propsText + `"`).
- Text(">").Output(propFile).
- Implicit(mkuserimg)
-
- f.output = android.PathForModuleOut(ctx, "filesystem.img").OutputPath
+ propFile, toolDeps := f.buildPropFile(ctx)
+ output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
builder.Command().BuiltTool("build_image").
Text(rootDir.String()). // input directory
Input(propFile).
- Output(f.output).
+ Implicits(toolDeps).
+ Output(output).
Text(rootDir.String()) // directory where to find fs_config_files|dirs
// rootDir is not deleted. Might be useful for quick inspection.
builder.Build("build_filesystem_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
- f.installDir = android.PathForModuleInstall(ctx, "etc")
- ctx.InstallFile(f.installDir, f.installFileName(), f.output)
+ return output
+}
+
+func (f *filesystem) buildFileContexts(ctx android.ModuleContext) android.OutputPath {
+ builder := android.NewRuleBuilder(pctx, ctx)
+ fcBin := android.PathForModuleOut(ctx, "file_contexts.bin")
+ builder.Command().BuiltTool("sefcontext_compile").
+ FlagWithOutput("-o ", fcBin).
+ Input(android.PathForModuleSrc(ctx, proptools.String(f.properties.File_contexts)))
+ builder.Build("build_filesystem_file_contexts", fmt.Sprintf("Creating filesystem file contexts for %s", f.BaseModuleName()))
+ return fcBin.OutputPath
+}
+
+func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android.OutputPath, toolDeps android.Paths) {
+ type prop struct {
+ name string
+ value string
+ }
+
+ var props []prop
+ var deps android.Paths
+ addStr := func(name string, value string) {
+ props = append(props, prop{name, value})
+ }
+ addPath := func(name string, path android.Path) {
+ props = append(props, prop{name, path.String()})
+ deps = append(deps, path)
+ }
+
+ // Type string that build_image.py accepts.
+ fsTypeStr := func(t fsType) string {
+ switch t {
+ // TODO(jiyong): add more types like f2fs, erofs, etc.
+ case ext4Type:
+ return "ext4"
+ }
+ panic(fmt.Errorf("unsupported fs type %v", t))
+ }
+
+ addStr("fs_type", fsTypeStr(f.fsType(ctx)))
+ addStr("mount_point", "/")
+ addStr("use_dynamic_partition_size", "true")
+ addPath("ext_mkuserimg", ctx.Config().HostToolPath(ctx, "mkuserimg_mke2fs"))
+ // b/177813163 deps of the host tools have to be added. Remove this.
+ for _, t := range []string{"mke2fs", "e2fsdroid", "tune2fs"} {
+ deps = append(deps, ctx.Config().HostToolPath(ctx, t))
+ }
+
+ if proptools.Bool(f.properties.Use_avb) {
+ addStr("avb_hashtree_enable", "true")
+ addPath("avb_avbtool", ctx.Config().HostToolPath(ctx, "avbtool"))
+ algorithm := proptools.StringDefault(f.properties.Avb_algorithm, "SHA256_RSA4096")
+ addStr("avb_algorithm", algorithm)
+ key := android.PathForModuleSrc(ctx, proptools.String(f.properties.Avb_private_key))
+ addPath("avb_key_path", key)
+ addStr("avb_add_hashtree_footer_args", "--do_not_generate_fec")
+ partitionName := proptools.StringDefault(f.properties.Partition_name, f.Name())
+ addStr("partition_name", partitionName)
+ }
+
+ if proptools.String(f.properties.File_contexts) != "" {
+ addPath("selinux_fc", f.buildFileContexts(ctx))
+ }
+
+ propFile = android.PathForModuleOut(ctx, "prop").OutputPath
+ builder := android.NewRuleBuilder(pctx, ctx)
+ builder.Command().Text("rm").Flag("-rf").Output(propFile)
+ for _, p := range props {
+ builder.Command().
+ Text("echo").
+ Flag(`"` + p.name + "=" + p.value + `"`).
+ Text(">>").Output(propFile)
+ }
+ builder.Build("build_filesystem_prop", fmt.Sprintf("Creating filesystem props for %s", f.BaseModuleName()))
+ return propFile, deps
+}
+
+func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) android.OutputPath {
+ if proptools.Bool(f.properties.Use_avb) {
+ ctx.PropertyErrorf("use_avb", "signing compresed cpio image using avbtool is not supported."+
+ "Consider adding this to bootimg module and signing the entire boot image.")
+ }
+
+ if proptools.String(f.properties.File_contexts) != "" {
+ ctx.PropertyErrorf("file_contexts", "file_contexts is not supported for compressed cpio image.")
+ }
+
+ depsZipFile := android.PathForModuleOut(ctx, "deps.zip").OutputPath
+ f.CopyDepsToZip(ctx, depsZipFile)
+
+ builder := android.NewRuleBuilder(pctx, ctx)
+ depsBase := proptools.StringDefault(f.properties.Base_dir, ".")
+ rebasedDepsZip := android.PathForModuleOut(ctx, "rebased_deps.zip").OutputPath
+ builder.Command().
+ BuiltTool("zip2zip").
+ FlagWithInput("-i ", depsZipFile).
+ FlagWithOutput("-o ", rebasedDepsZip).
+ Text("**/*:" + proptools.ShellEscape(depsBase)) // zip2zip verifies depsBase
+
+ rootDir := android.PathForModuleOut(ctx, "root").OutputPath
+ rootZip := f.buildRootZip(ctx)
+ builder.Command().
+ BuiltTool("zipsync").
+ FlagWithArg("-d ", rootDir.String()). // zipsync wipes this. No need to clear.
+ Input(rootZip).
+ Input(rebasedDepsZip)
+
+ output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
+ cmd := builder.Command().
+ BuiltTool("mkbootfs").
+ Text(rootDir.String()) // input directory
+ if compressed {
+ cmd.Text("|").
+ BuiltTool("lz4").
+ Flag("--favor-decSpeed"). // for faster boot
+ Flag("-12"). // maximum compression level
+ Flag("-l"). // legacy format for kernel
+ Text(">").Output(output)
+ } else {
+ cmd.Text(">").Output(output)
+ }
+
+ // rootDir is not deleted. Might be useful for quick inspection.
+ builder.Build("build_cpio_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
+
+ return output
}
var _ android.AndroidMkEntriesProvider = (*filesystem)(nil)
@@ -102,10 +366,44 @@
Class: "ETC",
OutputFile: android.OptionalPathForPath(f.output),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_PATH", f.installDir.ToMakePath().String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
},
},
}}
}
+
+var _ android.OutputFileProducer = (*filesystem)(nil)
+
+// Implements android.OutputFileProducer
+func (f *filesystem) OutputFiles(tag string) (android.Paths, error) {
+ if tag == "" {
+ return []android.Path{f.output}, nil
+ }
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+}
+
+// Filesystem is the public interface for the filesystem struct. Currently, it's only for the apex
+// package to have access to the output file.
+type Filesystem interface {
+ android.Module
+ OutputPath() android.Path
+
+ // Returns the output file that is signed by avbtool. If this module is not signed, returns
+ // nil.
+ SignedOutputPath() android.Path
+}
+
+var _ Filesystem = (*filesystem)(nil)
+
+func (f *filesystem) OutputPath() android.Path {
+ return f.output
+}
+
+func (f *filesystem) SignedOutputPath() android.Path {
+ if proptools.Bool(f.properties.Use_avb) {
+ return f.OutputPath()
+ }
+ return nil
+}
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
new file mode 100644
index 0000000..880b177
--- /dev/null
+++ b/filesystem/filesystem_test.go
@@ -0,0 +1,42 @@
+// Copyright 2021 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 filesystem
+
+import (
+ "os"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestMain(m *testing.M) {
+ os.Exit(m.Run())
+}
+
+var fixture = android.GroupFixturePreparers(
+ android.PrepareForIntegrationTestWithAndroid,
+ PrepareForTestWithFilesystemBuildComponents,
+)
+
+func TestFileSystemDeps(t *testing.T) {
+ result := fixture.RunTestWithBp(t, `
+ android_filesystem {
+ name: "myfilesystem",
+ }
+ `)
+
+ // produces "myfilesystem.img"
+ result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img")
+}
diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go
new file mode 100644
index 0000000..739e609
--- /dev/null
+++ b/filesystem/logical_partition.go
@@ -0,0 +1,243 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package filesystem
+
+import (
+ "fmt"
+ "strconv"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+)
+
+func init() {
+ android.RegisterModuleType("logical_partition", logicalPartitionFactory)
+}
+
+type logicalPartition struct {
+ android.ModuleBase
+
+ properties logicalPartitionProperties
+
+ output android.OutputPath
+ installDir android.InstallPath
+}
+
+type logicalPartitionProperties struct {
+ // Set the name of the output. Defaults to <module_name>.img.
+ Stem *string
+
+ // Total size of the logical partition. If set to "auto", total size is automatically
+ // calcaulted as minimum.
+ Size *string
+
+ // List of partitions for default group. Default group has no size limit and automatically
+ // minimized when creating an image.
+ Default_group []partitionProperties
+
+ // List of groups. A group defines a fixed sized region. It can host one or more logical
+ // partitions and their total size is limited by the size of the group they are in.
+ Groups []groupProperties
+
+ // Whether the output is a sparse image or not. Default is false.
+ Sparse *bool
+}
+
+type groupProperties struct {
+ // Name of the partition group. Can't be "default"; use default_group instead.
+ Name *string
+
+ // Size of the partition group
+ Size *string
+
+ // List of logical partitions in this group
+ Partitions []partitionProperties
+}
+
+type partitionProperties struct {
+ // Name of the partition
+ Name *string
+
+ // Filesystem that is placed on the partition
+ Filesystem *string `android:"path"`
+}
+
+// logical_partition is a partition image which has one or more logical partitions in it.
+func logicalPartitionFactory() android.Module {
+ module := &logicalPartition{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ return module
+}
+
+func (l *logicalPartition) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // do nothing
+}
+
+func (l *logicalPartition) installFileName() string {
+ return proptools.StringDefault(l.properties.Stem, l.BaseModuleName()+".img")
+}
+
+func (l *logicalPartition) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ builder := android.NewRuleBuilder(pctx, ctx)
+
+ // Sparse the filesystem images and calculate their sizes
+ sparseImages := make(map[string]android.OutputPath)
+ sparseImageSizes := make(map[string]android.OutputPath)
+
+ sparsePartitions := func(partitions []partitionProperties) {
+ for _, part := range partitions {
+ sparseImg, sizeTxt := sparseFilesystem(ctx, part, builder)
+ pName := proptools.String(part.Name)
+ sparseImages[pName] = sparseImg
+ sparseImageSizes[pName] = sizeTxt
+ }
+ }
+
+ for _, group := range l.properties.Groups {
+ sparsePartitions(group.Partitions)
+ }
+
+ sparsePartitions(l.properties.Default_group)
+
+ cmd := builder.Command().BuiltTool("lpmake")
+
+ size := proptools.String(l.properties.Size)
+ if size == "" {
+ ctx.PropertyErrorf("size", "must be set")
+ } else if _, err := strconv.Atoi(size); err != nil && size != "auto" {
+ ctx.PropertyErrorf("size", `must be a number or "auto"`)
+ }
+ cmd.FlagWithArg("--device-size=", size)
+
+ // TODO(jiyong): consider supporting A/B devices. Then we need to adjust num of slots.
+ cmd.FlagWithArg("--metadata-slots=", "2")
+ cmd.FlagWithArg("--metadata-size=", "65536")
+
+ if proptools.Bool(l.properties.Sparse) {
+ cmd.Flag("--sparse")
+ }
+
+ groupNames := make(map[string]bool)
+ partitionNames := make(map[string]bool)
+
+ addPartitionsToGroup := func(partitions []partitionProperties, gName string) {
+ for _, part := range partitions {
+ pName := proptools.String(part.Name)
+ if pName == "" {
+ ctx.PropertyErrorf("groups.partitions.name", "must be set")
+ }
+ if _, ok := partitionNames[pName]; ok {
+ ctx.PropertyErrorf("groups.partitions.name", "already exists")
+ } else {
+ partitionNames[pName] = true
+ }
+ // Get size of the partition by reading the -size.txt file
+ pSize := fmt.Sprintf("$(cat %s)", sparseImageSizes[pName])
+ cmd.FlagWithArg("--partition=", fmt.Sprintf("%s:readonly:%s:%s", pName, pSize, gName))
+ cmd.FlagWithInput("--image="+pName+"=", sparseImages[pName])
+ }
+ }
+
+ addPartitionsToGroup(l.properties.Default_group, "default")
+
+ for _, group := range l.properties.Groups {
+ gName := proptools.String(group.Name)
+ if gName == "" {
+ ctx.PropertyErrorf("groups.name", "must be set")
+ } else if gName == "default" {
+ ctx.PropertyErrorf("groups.name", `can't use "default" as a group name. Use default_group instead`)
+ }
+ if _, ok := groupNames[gName]; ok {
+ ctx.PropertyErrorf("group.name", "already exists")
+ } else {
+ groupNames[gName] = true
+ }
+ gSize := proptools.String(group.Size)
+ if gSize == "" {
+ ctx.PropertyErrorf("groups.size", "must be set")
+ }
+ if _, err := strconv.Atoi(gSize); err != nil {
+ ctx.PropertyErrorf("groups.size", "must be a number")
+ }
+ cmd.FlagWithArg("--group=", gName+":"+gSize)
+
+ addPartitionsToGroup(group.Partitions, gName)
+ }
+
+ l.output = android.PathForModuleOut(ctx, l.installFileName()).OutputPath
+ cmd.FlagWithOutput("--output=", l.output)
+
+ builder.Build("build_logical_partition", fmt.Sprintf("Creating %s", l.BaseModuleName()))
+
+ l.installDir = android.PathForModuleInstall(ctx, "etc")
+ ctx.InstallFile(l.installDir, l.installFileName(), l.output)
+}
+
+// Add a rule that converts the filesystem for the given partition to the given rule builder. The
+// path to the sparse file and the text file having the size of the partition are returned.
+func sparseFilesystem(ctx android.ModuleContext, p partitionProperties, builder *android.RuleBuilder) (sparseImg android.OutputPath, sizeTxt android.OutputPath) {
+ img := android.PathForModuleSrc(ctx, proptools.String(p.Filesystem))
+ name := proptools.String(p.Name)
+ sparseImg = android.PathForModuleOut(ctx, name+".img").OutputPath
+
+ builder.Temporary(sparseImg)
+ builder.Command().BuiltTool("img2simg").Input(img).Output(sparseImg)
+
+ sizeTxt = android.PathForModuleOut(ctx, name+"-size.txt").OutputPath
+ builder.Temporary(sizeTxt)
+ builder.Command().BuiltTool("sparse_img").Flag("--get_partition_size").Input(sparseImg).
+ Text("| ").Text("tr").FlagWithArg("-d ", "'\n'").
+ Text("> ").Output(sizeTxt)
+
+ return sparseImg, sizeTxt
+}
+
+var _ android.AndroidMkEntriesProvider = (*logicalPartition)(nil)
+
+// Implements android.AndroidMkEntriesProvider
+func (l *logicalPartition) AndroidMkEntries() []android.AndroidMkEntries {
+ return []android.AndroidMkEntries{android.AndroidMkEntries{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(l.output),
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", l.installDir.ToMakePath().String())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.installFileName())
+ },
+ },
+ }}
+}
+
+var _ Filesystem = (*logicalPartition)(nil)
+
+func (l *logicalPartition) OutputPath() android.Path {
+ return l.output
+}
+
+func (l *logicalPartition) SignedOutputPath() android.Path {
+ return nil // logical partition is not signed by itself
+}
+
+var _ android.OutputFileProducer = (*logicalPartition)(nil)
+
+// Implements android.OutputFileProducer
+func (l *logicalPartition) OutputFiles(tag string) (android.Paths, error) {
+ if tag == "" {
+ return []android.Path{l.output}, nil
+ }
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+}
diff --git a/cmd/soong_env/Android.bp b/filesystem/testing.go
similarity index 71%
copy from cmd/soong_env/Android.bp
copy to filesystem/testing.go
index 4db0da3..631f1b1 100644
--- a/cmd/soong_env/Android.bp
+++ b/filesystem/testing.go
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2021 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.
@@ -12,13 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-bootstrap_go_binary {
- name: "soong_env",
- deps: [
- "soong-env",
- ],
- srcs: [
- "soong_env.go",
- ],
- default: true,
-}
+package filesystem
+
+import "android/soong/android"
+
+var PrepareForTestWithFilesystemBuildComponents = android.FixtureRegisterWithContext(registerBuildComponents)
diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go
new file mode 100644
index 0000000..3f16c0d
--- /dev/null
+++ b/filesystem/vbmeta.go
@@ -0,0 +1,275 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package filesystem
+
+import (
+ "fmt"
+ "strconv"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+)
+
+func init() {
+ android.RegisterModuleType("vbmeta", vbmetaFactory)
+}
+
+type vbmeta struct {
+ android.ModuleBase
+
+ properties vbmetaProperties
+
+ output android.OutputPath
+ installDir android.InstallPath
+}
+
+type vbmetaProperties struct {
+ // Name of the partition stored in vbmeta desc. Defaults to the name of this module.
+ Partition_name *string
+
+ // Set the name of the output. Defaults to <module_name>.img.
+ Stem *string
+
+ // Path to the private key that avbtool will use to sign this vbmeta image.
+ Private_key *string `android:"path"`
+
+ // Algorithm that avbtool will use to sign this vbmeta image. Default is SHA256_RSA4096.
+ Algorithm *string
+
+ // File whose content will provide the rollback index. If unspecified, the rollback index
+ // is from PLATFORM_SECURITY_PATCH
+ Rollback_index_file *string `android:"path"`
+
+ // Rollback index location of this vbmeta image. Must be 0, 1, 2, etc. Default is 0.
+ Rollback_index_location *int64
+
+ // List of filesystem modules that this vbmeta has descriptors for. The filesystem modules
+ // have to be signed (use_avb: true).
+ Partitions []string
+
+ // List of chained partitions that this vbmeta deletages the verification.
+ Chained_partitions []chainedPartitionProperties
+}
+
+type chainedPartitionProperties struct {
+ // Name of the chained partition
+ Name *string
+
+ // Rollback index location of the chained partition. Must be 0, 1, 2, etc. Default is the
+ // index of this partition in the list + 1.
+ Rollback_index_location *int64
+
+ // Path to the public key that the chained partition is signed with. If this is specified,
+ // private_key is ignored.
+ Public_key *string `android:"path"`
+
+ // Path to the private key that the chained partition is signed with. If this is specified,
+ // and public_key is not specified, a public key is extracted from this private key and
+ // the extracted public key is embedded in the vbmeta image.
+ Private_key *string `android:"path"`
+}
+
+// vbmeta is the partition image that has the verification information for other partitions.
+func vbmetaFactory() android.Module {
+ module := &vbmeta{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ return module
+}
+
+type vbmetaDep struct {
+ blueprint.BaseDependencyTag
+ kind string
+}
+
+var vbmetaPartitionDep = vbmetaDep{kind: "partition"}
+
+func (v *vbmeta) DepsMutator(ctx android.BottomUpMutatorContext) {
+ ctx.AddDependency(ctx.Module(), vbmetaPartitionDep, v.properties.Partitions...)
+}
+
+func (v *vbmeta) installFileName() string {
+ return proptools.StringDefault(v.properties.Stem, v.BaseModuleName()+".img")
+}
+
+func (v *vbmeta) partitionName() string {
+ return proptools.StringDefault(v.properties.Partition_name, v.BaseModuleName())
+}
+
+// See external/avb/libavb/avb_slot_verify.c#VBMETA_MAX_SIZE
+const vbmetaMaxSize = 64 * 1024
+
+func (v *vbmeta) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ extractedPublicKeys := v.extractPublicKeys(ctx)
+
+ v.output = android.PathForModuleOut(ctx, v.installFileName()).OutputPath
+
+ builder := android.NewRuleBuilder(pctx, ctx)
+ cmd := builder.Command().BuiltTool("avbtool").Text("make_vbmeta_image")
+
+ key := android.PathForModuleSrc(ctx, proptools.String(v.properties.Private_key))
+ cmd.FlagWithInput("--key ", key)
+
+ algorithm := proptools.StringDefault(v.properties.Algorithm, "SHA256_RSA4096")
+ cmd.FlagWithArg("--algorithm ", algorithm)
+
+ cmd.FlagWithArg("--rollback_index ", v.rollbackIndexCommand(ctx))
+ ril := proptools.IntDefault(v.properties.Rollback_index_location, 0)
+ if ril < 0 {
+ ctx.PropertyErrorf("rollback_index_location", "must be 0, 1, 2, ...")
+ return
+ }
+ cmd.FlagWithArg("--rollback_index_location ", strconv.Itoa(ril))
+
+ for _, p := range ctx.GetDirectDepsWithTag(vbmetaPartitionDep) {
+ f, ok := p.(Filesystem)
+ if !ok {
+ ctx.PropertyErrorf("partitions", "%q(type: %s) is not supported",
+ p.Name(), ctx.OtherModuleType(p))
+ continue
+ }
+ signedImage := f.SignedOutputPath()
+ if signedImage == nil {
+ ctx.PropertyErrorf("partitions", "%q(type: %s) is not signed. Use `use_avb: true`",
+ p.Name(), ctx.OtherModuleType(p))
+ continue
+ }
+ cmd.FlagWithInput("--include_descriptors_from_image ", signedImage)
+ }
+
+ for i, cp := range v.properties.Chained_partitions {
+ name := proptools.String(cp.Name)
+ if name == "" {
+ ctx.PropertyErrorf("chained_partitions", "name must be specified")
+ continue
+ }
+
+ ril := proptools.IntDefault(cp.Rollback_index_location, i+1)
+ if ril < 0 {
+ ctx.PropertyErrorf("chained_partitions", "must be 0, 1, 2, ...")
+ continue
+ }
+
+ var publicKey android.Path
+ if cp.Public_key != nil {
+ publicKey = android.PathForModuleSrc(ctx, proptools.String(cp.Public_key))
+ } else {
+ publicKey = extractedPublicKeys[name]
+ }
+ cmd.FlagWithArg("--chain_partition ", fmt.Sprintf("%s:%d:%s", name, ril, publicKey.String()))
+ cmd.Implicit(publicKey)
+ }
+
+ cmd.FlagWithOutput("--output ", v.output)
+
+ // libavb expects to be able to read the maximum vbmeta size, so we must provide a partition
+ // which matches this or the read will fail.
+ builder.Command().Text("truncate").
+ FlagWithArg("-s ", strconv.Itoa(vbmetaMaxSize)).
+ Output(v.output)
+
+ builder.Build("vbmeta", fmt.Sprintf("vbmeta %s", ctx.ModuleName()))
+
+ v.installDir = android.PathForModuleInstall(ctx, "etc")
+ ctx.InstallFile(v.installDir, v.installFileName(), v.output)
+}
+
+// Returns the embedded shell command that prints the rollback index
+func (v *vbmeta) rollbackIndexCommand(ctx android.ModuleContext) string {
+ var cmd string
+ if v.properties.Rollback_index_file != nil {
+ f := android.PathForModuleSrc(ctx, proptools.String(v.properties.Rollback_index_file))
+ cmd = "cat " + f.String()
+ } else {
+ cmd = "date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s"
+ }
+ // Take the first line and remove the newline char
+ return "$(" + cmd + " | head -1 | tr -d '\n'" + ")"
+}
+
+// Extract public keys from chained_partitions.private_key. The keys are indexed with the partition
+// name.
+func (v *vbmeta) extractPublicKeys(ctx android.ModuleContext) map[string]android.OutputPath {
+ result := make(map[string]android.OutputPath)
+
+ builder := android.NewRuleBuilder(pctx, ctx)
+ for _, cp := range v.properties.Chained_partitions {
+ if cp.Private_key == nil {
+ continue
+ }
+
+ name := proptools.String(cp.Name)
+ if name == "" {
+ ctx.PropertyErrorf("chained_partitions", "name must be specified")
+ continue
+ }
+
+ if _, ok := result[name]; ok {
+ ctx.PropertyErrorf("chained_partitions", "name %q is duplicated", name)
+ continue
+ }
+
+ privateKeyFile := android.PathForModuleSrc(ctx, proptools.String(cp.Private_key))
+ publicKeyFile := android.PathForModuleOut(ctx, name+".avbpubkey").OutputPath
+
+ builder.Command().
+ BuiltTool("avbtool").
+ Text("extract_public_key").
+ FlagWithInput("--key ", privateKeyFile).
+ FlagWithOutput("--output ", publicKeyFile)
+
+ result[name] = publicKeyFile
+ }
+ builder.Build("vbmeta_extract_public_key", fmt.Sprintf("Extract public keys for %s", ctx.ModuleName()))
+ return result
+}
+
+var _ android.AndroidMkEntriesProvider = (*vbmeta)(nil)
+
+// Implements android.AndroidMkEntriesProvider
+func (v *vbmeta) AndroidMkEntries() []android.AndroidMkEntries {
+ return []android.AndroidMkEntries{android.AndroidMkEntries{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(v.output),
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", v.installDir.ToMakePath().String())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", v.installFileName())
+ },
+ },
+ }}
+}
+
+var _ Filesystem = (*vbmeta)(nil)
+
+func (v *vbmeta) OutputPath() android.Path {
+ return v.output
+}
+
+func (v *vbmeta) SignedOutputPath() android.Path {
+ return v.OutputPath() // vbmeta is always signed
+}
+
+var _ android.OutputFileProducer = (*vbmeta)(nil)
+
+// Implements android.OutputFileProducer
+func (v *vbmeta) OutputFiles(tag string) (android.Paths, error) {
+ if tag == "" {
+ return []android.Path{v.output}, nil
+ }
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+}
diff --git a/finder/Android.bp b/finder/Android.bp
index a5d7fd4..a3df6ec 100644
--- a/finder/Android.bp
+++ b/finder/Android.bp
@@ -16,6 +16,10 @@
// fast, parallel, caching implementation of `find`
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
subdirs = [
"cmd",
]
diff --git a/finder/cmd/Android.bp b/finder/cmd/Android.bp
index e066c39..32843a0 100644
--- a/finder/cmd/Android.bp
+++ b/finder/cmd/Android.bp
@@ -16,6 +16,10 @@
// fast, parallel, caching implementation of `find`
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "finder",
srcs: [
diff --git a/finder/fs/Android.bp b/finder/fs/Android.bp
index 85929ae..14bdb30 100644
--- a/finder/fs/Android.bp
+++ b/finder/fs/Android.bp
@@ -16,6 +16,21 @@
// mock filesystem
//
+package {
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ "build_soong_finder_fs_license",
+ ],
+}
+
+license {
+ name: "build_soong_finder_fs_license",
+ license_kinds: [
+ "SPDX-license-identifier-BSD",
+ ],
+ license_text: ["LICENSE"],
+}
+
bootstrap_go_package {
name: "soong-finder-fs",
pkgPath: "android/soong/finder/fs",
diff --git a/finder/fs/LICENSE b/finder/fs/LICENSE
new file mode 100644
index 0000000..e5c5baf
--- /dev/null
+++ b/finder/fs/LICENSE
@@ -0,0 +1,28 @@
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of the Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/genrule/Android.bp b/genrule/Android.bp
index 0e27d4e..8fb5c40 100644
--- a/genrule/Android.bp
+++ b/genrule/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-genrule",
pkgPath: "android/soong/genrule",
@@ -12,6 +16,7 @@
],
srcs: [
"genrule.go",
+ "locations.go",
],
testSrcs: [
"genrule_test.go",
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 8df32f2..e6a5ab9 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -37,6 +37,27 @@
RegisterGenruleBuildComponents(android.InitRegistrationContext)
}
+// Test fixture preparer that will register most genrule build components.
+//
+// Singletons and mutators should only be added here if they are needed for a majority of genrule
+// module types, otherwise they should be added under a separate preparer to allow them to be
+// selected only when needed to reduce test execution time.
+//
+// Module types do not have much of an overhead unless they are used so this should include as many
+// module types as possible. The exceptions are those module types that require mutators and/or
+// singletons in order to function in which case they should be kept together in a separate
+// preparer.
+var PrepareForTestWithGenRuleBuildComponents = android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(RegisterGenruleBuildComponents),
+)
+
+// Prepare a fixture to use all genrule module types, mutators and singletons fully.
+//
+// This should only be used by tests that want to run with as much of the build enabled as possible.
+var PrepareForIntegrationTestWithGenrule = android.GroupFixturePreparers(
+ PrepareForTestWithGenRuleBuildComponents,
+)
+
func RegisterGenruleBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("genrule_defaults", defaultsFactory)
@@ -46,6 +67,13 @@
ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.BottomUp("genrule_tool_deps", toolDepsMutator).Parallel()
})
+
+ android.DepsBp2BuildMutators(RegisterGenruleBp2BuildDeps)
+ android.RegisterBp2BuildMutator("genrule", GenruleBp2Build)
+}
+
+func RegisterGenruleBp2BuildDeps(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("genrule_tool_deps", toolDepsMutator)
}
var (
@@ -63,7 +91,6 @@
func init() {
pctx.Import("android/soong/android")
- pctx.HostBinToolVariable("sboxCmd", "sbox")
pctx.HostBinToolVariable("soongZip", "soong_zip")
pctx.HostBinToolVariable("zipSync", "zipsync")
@@ -117,14 +144,12 @@
// input files to exclude
Exclude_srcs []string `android:"path,arch_variant"`
-
- // Properties for Bazel migration purposes.
- bazel.Properties
}
type Module struct {
android.ModuleBase
android.DefaultableModuleBase
+ android.BazelModuleBase
android.ApexModuleBase
// For other packages to make their own genrules with extra
@@ -201,14 +226,19 @@
// Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
func (c *Module) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
bazelCtx := ctx.Config().BazelContext
- filePaths, ok := bazelCtx.GetAllFiles(label)
+ filePaths, ok := bazelCtx.GetOutputFiles(label, ctx.Arch().ArchType)
if ok {
var bazelOutputFiles android.Paths
+ exportIncludeDirs := map[string]bool{}
for _, bazelOutputFile := range filePaths {
bazelOutputFiles = append(bazelOutputFiles, android.PathForBazelOut(ctx, bazelOutputFile))
+ exportIncludeDirs[filepath.Dir(bazelOutputFile)] = true
}
c.outputFiles = bazelOutputFiles
c.outputDeps = bazelOutputFiles
+ for includePath, _ := range exportIncludeDirs {
+ c.exportedIncludeDirs = append(c.exportedIncludeDirs, android.PathForBazelOut(ctx, includePath))
+ }
}
return ok
}
@@ -228,18 +258,18 @@
g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx, g.subDir))
}
- locationLabels := map[string][]string{}
+ locationLabels := map[string]location{}
firstLabel := ""
- addLocationLabel := func(label string, paths []string) {
+ addLocationLabel := func(label string, loc location) {
if firstLabel == "" {
firstLabel = label
}
if _, exists := locationLabels[label]; !exists {
- locationLabels[label] = paths
+ locationLabels[label] = loc
} else {
ctx.ModuleErrorf("multiple labels for %q, %q and %q",
- label, strings.Join(locationLabels[label], " "), strings.Join(paths, " "))
+ label, locationLabels[label], loc)
}
}
@@ -277,17 +307,17 @@
// sandbox.
packagedTools = append(packagedTools, specs...)
// Assume that the first PackagingSpec of the module is the tool.
- addLocationLabel(tag.label, []string{android.SboxPathForPackagedTool(specs[0])})
+ addLocationLabel(tag.label, packagedToolLocation{specs[0]})
} else {
tools = append(tools, path.Path())
- addLocationLabel(tag.label, []string{android.SboxPathForTool(ctx, path.Path())})
+ addLocationLabel(tag.label, toolLocation{android.Paths{path.Path()}})
}
case bootstrap.GoBinaryTool:
// A GoBinaryTool provides the install path to a tool, which will be copied.
if s, err := filepath.Rel(android.PathForOutput(ctx).String(), t.InstallPath()); err == nil {
toolPath := android.PathForOutput(ctx, s)
tools = append(tools, toolPath)
- addLocationLabel(tag.label, []string{android.SboxPathForTool(ctx, toolPath)})
+ addLocationLabel(tag.label, toolLocation{android.Paths{toolPath}})
} else {
ctx.ModuleErrorf("cannot find path for %q: %v", tool, err)
return
@@ -309,7 +339,7 @@
if ctx.Config().AllowMissingDependencies() {
for _, tool := range g.properties.Tools {
if !seenTools[tool] {
- addLocationLabel(tool, []string{"***missing tool " + tool + "***"})
+ addLocationLabel(tool, errorLocation{"***missing tool " + tool + "***"})
}
}
}
@@ -322,11 +352,7 @@
for _, toolFile := range g.properties.Tool_files {
paths := android.PathsForModuleSrc(ctx, []string{toolFile})
tools = append(tools, paths...)
- var sandboxPaths []string
- for _, path := range paths {
- sandboxPaths = append(sandboxPaths, android.SboxPathForTool(ctx, path))
- }
- addLocationLabel(toolFile, sandboxPaths)
+ addLocationLabel(toolFile, toolLocation{paths})
}
var srcFiles android.Paths
@@ -344,10 +370,10 @@
// The command that uses this placeholder file will never be executed because the rule will be
// replaced with an android.Error rule reporting the missing dependencies.
ctx.AddMissingDependencies(missingDeps)
- addLocationLabel(in, []string{"***missing srcs " + in + "***"})
+ addLocationLabel(in, errorLocation{"***missing srcs " + in + "***"})
} else {
srcFiles = append(srcFiles, paths...)
- addLocationLabel(in, paths.Strings())
+ addLocationLabel(in, inputLocation{paths})
}
}
@@ -382,7 +408,7 @@
cmd := rule.Command()
for _, out := range task.out {
- addLocationLabel(out.Rel(), []string{cmd.PathForOutput(out)})
+ addLocationLabel(out.Rel(), outputLocation{out})
}
referencedDepfile := false
@@ -400,16 +426,17 @@
if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
return reportError("at least one `tools` or `tool_files` is required if $(location) is used")
}
- paths := locationLabels[firstLabel]
+ loc := locationLabels[firstLabel]
+ paths := loc.Paths(cmd)
if len(paths) == 0 {
return reportError("default label %q has no files", firstLabel)
} else if len(paths) > 1 {
return reportError("default label %q has multiple files, use $(locations %s) to reference it",
firstLabel, firstLabel)
}
- return locationLabels[firstLabel][0], nil
+ return paths[0], nil
case "in":
- return strings.Join(srcFiles.Strings(), " "), nil
+ return strings.Join(cmd.PathsForInputs(srcFiles), " "), nil
case "out":
var sandboxOuts []string
for _, out := range task.out {
@@ -427,7 +454,8 @@
default:
if strings.HasPrefix(name, "location ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
- if paths, ok := locationLabels[label]; ok {
+ if loc, ok := locationLabels[label]; ok {
+ paths := loc.Paths(cmd)
if len(paths) == 0 {
return reportError("label %q has no files", label)
} else if len(paths) > 1 {
@@ -440,7 +468,8 @@
}
} else if strings.HasPrefix(name, "locations ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "locations "))
- if paths, ok := locationLabels[label]; ok {
+ if loc, ok := locationLabels[label]; ok {
+ paths := loc.Paths(cmd)
if len(paths) == 0 {
return reportError("label %q has no files", label)
}
@@ -512,7 +541,7 @@
g.outputFiles = outputFiles.Paths()
- bazelModuleLabel := g.properties.Bazel_module.Label
+ bazelModuleLabel := g.GetBazelLabel(ctx, g)
bazelActionsUsed := false
if ctx.Config().BazelContext.BazelEnabled() && len(bazelModuleLabel) > 0 {
bazelActionsUsed = g.generateBazelBuildActions(ctx, bazelModuleLabel)
@@ -597,6 +626,7 @@
func (x noopImageInterface) CoreVariantNeeded(android.BaseModuleContext) bool { return false }
func (x noopImageInterface) RamdiskVariantNeeded(android.BaseModuleContext) bool { return false }
func (x noopImageInterface) VendorRamdiskVariantNeeded(android.BaseModuleContext) bool { return false }
+func (x noopImageInterface) DebugRamdiskVariantNeeded(android.BaseModuleContext) bool { return false }
func (x noopImageInterface) RecoveryVariantNeeded(android.BaseModuleContext) bool { return false }
func (x noopImageInterface) ExtraImageVariations(ctx android.BaseModuleContext) []string { return nil }
func (x noopImageInterface) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
@@ -693,7 +723,7 @@
outputDepfile = android.PathForModuleGen(ctx, genSubDir, "gensrcs.d")
depFixerTool := ctx.Config().HostToolPath(ctx, "dep_fixer")
fullCommand += fmt.Sprintf(" && %s -o $(depfile) %s",
- android.SboxPathForTool(ctx, depFixerTool),
+ rule.Command().PathForTool(depFixerTool),
strings.Join(commandDepFiles, " "))
extraTools = append(extraTools, depFixerTool)
}
@@ -764,6 +794,7 @@
m := NewGenRule()
android.InitAndroidModule(m)
android.InitDefaultableModule(m)
+ android.InitBazelModule(m)
return m
}
@@ -772,6 +803,100 @@
Out []string `android:"arch_variant"`
}
+type bazelGenruleAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Outs []string
+ Tools bazel.LabelListAttribute
+ Cmd string
+}
+
+type bazelGenrule struct {
+ android.BazelTargetModuleBase
+ bazelGenruleAttributes
+}
+
+func BazelGenruleFactory() android.Module {
+ module := &bazelGenrule{}
+ module.AddProperties(&module.bazelGenruleAttributes)
+ android.InitBazelTargetModule(module)
+ return module
+}
+
+func GenruleBp2Build(ctx android.TopDownMutatorContext) {
+ m, ok := ctx.Module().(*Module)
+ if !ok || !m.ConvertWithBp2build(ctx) {
+ return
+ }
+
+ if ctx.ModuleType() != "genrule" {
+ // Not a regular genrule. Could be a cc_genrule or java_genrule.
+ return
+ }
+
+ // Bazel only has the "tools" attribute.
+ tools_prop := android.BazelLabelForModuleDeps(ctx, m.properties.Tools)
+ tool_files_prop := android.BazelLabelForModuleSrc(ctx, m.properties.Tool_files)
+ tools_prop.Append(tool_files_prop)
+
+ tools := bazel.MakeLabelListAttribute(tools_prop)
+ srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Srcs))
+
+ var allReplacements bazel.LabelList
+ allReplacements.Append(tools.Value)
+ allReplacements.Append(srcs.Value)
+
+ // Replace in and out variables with $< and $@
+ var cmd string
+ if m.properties.Cmd != nil {
+ cmd = strings.Replace(*m.properties.Cmd, "$(in)", "$(SRCS)", -1)
+ cmd = strings.Replace(cmd, "$(out)", "$(OUTS)", -1)
+ cmd = strings.Replace(cmd, "$(genDir)", "$(GENDIR)", -1)
+ if len(tools.Value.Includes) > 0 {
+ cmd = strings.Replace(cmd, "$(location)", fmt.Sprintf("$(location %s)", tools.Value.Includes[0].Label), -1)
+ cmd = strings.Replace(cmd, "$(locations)", fmt.Sprintf("$(locations %s)", tools.Value.Includes[0].Label), -1)
+ }
+ for _, l := range allReplacements.Includes {
+ bpLoc := fmt.Sprintf("$(location %s)", l.Bp_text)
+ bpLocs := fmt.Sprintf("$(locations %s)", l.Bp_text)
+ bazelLoc := fmt.Sprintf("$(location %s)", l.Label)
+ bazelLocs := fmt.Sprintf("$(locations %s)", l.Label)
+ cmd = strings.Replace(cmd, bpLoc, bazelLoc, -1)
+ cmd = strings.Replace(cmd, bpLocs, bazelLocs, -1)
+ }
+ }
+
+ // The Out prop is not in an immediately accessible field
+ // in the Module struct, so use GetProperties and cast it
+ // to the known struct prop.
+ var outs []string
+ for _, propIntf := range m.GetProperties() {
+ if props, ok := propIntf.(*genRuleProperties); ok {
+ outs = props.Out
+ break
+ }
+ }
+
+ attrs := &bazelGenruleAttributes{
+ Srcs: srcs,
+ Outs: outs,
+ Cmd: cmd,
+ Tools: tools,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "genrule",
+ }
+
+ // Create the BazelTargetModule.
+ ctx.CreateBazelTargetModule(BazelGenruleFactory, m.Name(), props, attrs)
+}
+
+func (m *bazelGenrule) Name() string {
+ return m.BaseModuleName()
+}
+
+func (m *bazelGenrule) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+
var Bool = proptools.Bool
var String = proptools.String
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 2f5605e..3ce4f85 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -15,10 +15,8 @@
package genrule
import (
- "io/ioutil"
"os"
- "reflect"
- "strings"
+ "regexp"
"testing"
"android/soong/android"
@@ -26,47 +24,34 @@
"github.com/google/blueprint/proptools"
)
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "genrule_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ os.Exit(m.Run())
}
-func testContext(config android.Config) *android.TestContext {
+var prepareForGenRuleTest = android.GroupFixturePreparers(
+ android.PrepareForTestWithArchMutator,
+ android.PrepareForTestWithDefaults,
- ctx := android.NewTestArchContext(config)
- ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
- ctx.RegisterModuleType("tool", toolFactory)
+ android.PrepareForTestWithFilegroup,
+ PrepareForTestWithGenRuleBuildComponents,
+ android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("tool", toolFactory)
+ ctx.RegisterModuleType("output", outputProducerFactory)
+ }),
+ android.FixtureMergeMockFs(android.MockFS{
+ "tool": nil,
+ "tool_file1": nil,
+ "tool_file2": nil,
+ "in1": nil,
+ "in2": nil,
+ "in1.txt": nil,
+ "in2.txt": nil,
+ "in3.txt": nil,
+ }),
+)
- RegisterGenruleBuildComponents(ctx)
-
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- ctx.Register()
-
- return ctx
-}
-
-func testConfig(bp string, fs map[string][]byte) android.Config {
- bp += `
+func testGenruleBp() string {
+ return `
tool {
name: "tool",
}
@@ -105,23 +90,6 @@
name: "empty",
}
`
-
- mockFS := map[string][]byte{
- "tool": nil,
- "tool_file1": nil,
- "tool_file2": nil,
- "in1": nil,
- "in2": nil,
- "in1.txt": nil,
- "in2.txt": nil,
- "in3.txt": nil,
- }
-
- for k, v := range fs {
- mockFS[k] = v
- }
-
- return android.TestArchConfig(buildDir, nil, bp, mockFS)
}
func TestGenruleCmd(t *testing.T) {
@@ -330,6 +298,14 @@
`,
expect: "echo foo > __SBOX_SANDBOX_DIR__/out/foo && cp __SBOX_SANDBOX_DIR__/out/foo __SBOX_SANDBOX_DIR__/out/out",
},
+ {
+ name: "$",
+ prop: `
+ out: ["out"],
+ cmd: "echo $$ > $(out)",
+ `,
+ expect: "echo $ > __SBOX_SANDBOX_DIR__/out/out",
+ },
{
name: "error empty location",
@@ -466,38 +442,29 @@
bp += test.prop
bp += "}\n"
- config := testConfig(bp, nil)
- config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(test.allowMissingDependencies)
-
- ctx := testContext(config)
- ctx.SetAllowMissingDependencies(test.allowMissingDependencies)
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- if errs == nil {
- _, errs = ctx.PrepareBuildActions(config)
+ var expectedErrors []string
+ if test.err != "" {
+ expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
}
- if errs == nil && test.err != "" {
- t.Fatalf("want error %q, got no error", test.err)
- } else if errs != nil && test.err == "" {
- android.FailIfErrored(t, errs)
- } else if test.err != "" {
- if len(errs) != 1 {
- t.Errorf("want 1 error, got %d errors:", len(errs))
- for _, err := range errs {
- t.Errorf(" %s", err.Error())
- }
- t.FailNow()
- }
- if !strings.Contains(errs[0].Error(), test.err) {
- t.Fatalf("want %q, got %q", test.err, errs[0].Error())
- }
+
+ result := android.GroupFixturePreparers(
+ prepareForGenRuleTest,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Allow_missing_dependencies = proptools.BoolPtr(test.allowMissingDependencies)
+ }),
+ android.FixtureModifyContext(func(ctx *android.TestContext) {
+ ctx.SetAllowMissingDependencies(test.allowMissingDependencies)
+ }),
+ ).
+ ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
+ RunTestWithBp(t, testGenruleBp()+bp)
+
+ if expectedErrors != nil {
return
}
- gen := ctx.ModuleForTests("gen", "").Module().(*Module)
- if g, w := gen.rawCommands[0], test.expect; w != g {
- t.Errorf("want %q, got %q", w, g)
- }
+ gen := result.Module("gen", "").(*Module)
+ android.AssertStringEquals(t, "raw commands", test.expect, gen.rawCommands[0])
})
}
}
@@ -557,25 +524,15 @@
},
}
- config := testConfig(bp, nil)
- ctx := testContext(config)
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- if errs == nil {
- _, errs = ctx.PrepareBuildActions(config)
- }
- if errs != nil {
- t.Fatal(errs)
- }
+ result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
for _, test := range testcases {
t.Run(test.name, func(t *testing.T) {
- gen := ctx.ModuleForTests(test.name, "")
+ gen := result.ModuleForTests(test.name, "")
manifest := android.RuleBuilderSboxProtoForTests(t, gen.Output("genrule.sbox.textproto"))
hash := manifest.Commands[0].GetInputHash()
- if g, w := hash, test.expectedHash; g != w {
- t.Errorf("Expected has %q, got %q", w, g)
- }
+ android.AssertStringEquals(t, "hash", test.expectedHash, hash)
})
}
}
@@ -602,8 +559,14 @@
cmds: []string{
"bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
},
- deps: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h"},
- files: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h"},
+ deps: []string{
+ "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
+ "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
+ },
+ files: []string{
+ "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
+ "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
+ },
},
{
name: "shards",
@@ -617,8 +580,16 @@
"bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
"bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in3.txt > __SBOX_SANDBOX_DIR__/out/in3.h'",
},
- deps: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h", buildDir + "/.intermediates/gen/gen/gensrcs/in3.h"},
- files: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h", buildDir + "/.intermediates/gen/gen/gensrcs/in3.h"},
+ deps: []string{
+ "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
+ "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
+ "out/soong/.intermediates/gen/gen/gensrcs/in3.h",
+ },
+ files: []string{
+ "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
+ "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
+ "out/soong/.intermediates/gen/gen/gensrcs/in3.h",
+ },
},
}
@@ -630,46 +601,27 @@
bp += test.prop
bp += "}\n"
- config := testConfig(bp, nil)
- ctx := testContext(config)
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- if errs == nil {
- _, errs = ctx.PrepareBuildActions(config)
+ var expectedErrors []string
+ if test.err != "" {
+ expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
}
- if errs == nil && test.err != "" {
- t.Fatalf("want error %q, got no error", test.err)
- } else if errs != nil && test.err == "" {
- android.FailIfErrored(t, errs)
- } else if test.err != "" {
- if len(errs) != 1 {
- t.Errorf("want 1 error, got %d errors:", len(errs))
- for _, err := range errs {
- t.Errorf(" %s", err.Error())
- }
- t.FailNow()
- }
- if !strings.Contains(errs[0].Error(), test.err) {
- t.Fatalf("want %q, got %q", test.err, errs[0].Error())
- }
+
+ result := prepareForGenRuleTest.
+ ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
+ RunTestWithBp(t, testGenruleBp()+bp)
+
+ if expectedErrors != nil {
return
}
- gen := ctx.ModuleForTests("gen", "").Module().(*Module)
- if g, w := gen.rawCommands, test.cmds; !reflect.DeepEqual(w, g) {
- t.Errorf("want %q, got %q", w, g)
- }
+ gen := result.Module("gen", "").(*Module)
+ android.AssertDeepEquals(t, "cmd", test.cmds, gen.rawCommands)
- if g, w := gen.outputDeps.Strings(), test.deps; !reflect.DeepEqual(w, g) {
- t.Errorf("want deps %q, got %q", w, g)
- }
+ android.AssertPathsRelativeToTopEquals(t, "deps", test.deps, gen.outputDeps)
- if g, w := gen.outputFiles.Strings(), test.files; !reflect.DeepEqual(w, g) {
- t.Errorf("want files %q, got %q", w, g)
- }
+ android.AssertPathsRelativeToTopEquals(t, "files", test.files, gen.outputFiles)
})
}
-
}
func TestGenruleDefaults(t *testing.T) {
@@ -690,25 +642,45 @@
defaults: ["gen_defaults1", "gen_defaults2"],
}
`
- config := testConfig(bp, nil)
- ctx := testContext(config)
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- if errs == nil {
- _, errs = ctx.PrepareBuildActions(config)
- }
- if errs != nil {
- t.Fatal(errs)
- }
- gen := ctx.ModuleForTests("gen", "").Module().(*Module)
+
+ result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
+
+ gen := result.Module("gen", "").(*Module)
expectedCmd := "cp in1 __SBOX_SANDBOX_DIR__/out/out"
- if gen.rawCommands[0] != expectedCmd {
- t.Errorf("Expected cmd: %q, actual: %q", expectedCmd, gen.rawCommands[0])
- }
+ android.AssertStringEquals(t, "cmd", expectedCmd, gen.rawCommands[0])
expectedSrcs := []string{"in1"}
- if !reflect.DeepEqual(expectedSrcs, gen.properties.Srcs) {
- t.Errorf("Expected srcs: %q, actual: %q", expectedSrcs, gen.properties.Srcs)
+ android.AssertDeepEquals(t, "srcs", expectedSrcs, gen.properties.Srcs)
+}
+
+func TestGenruleAllowMissingDependencies(t *testing.T) {
+ bp := `
+ output {
+ name: "disabled",
+ enabled: false,
+ }
+
+ genrule {
+ name: "gen",
+ srcs: [
+ ":disabled",
+ ],
+ out: ["out"],
+ cmd: "cat $(in) > $(out)",
+ }
+ `
+ result := android.GroupFixturePreparers(
+ prepareForGenRuleTest,
+ android.FixtureModifyConfigAndContext(
+ func(config android.Config, ctx *android.TestContext) {
+ config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
+ ctx.SetAllowMissingDependencies(true)
+ })).RunTestWithBp(t, bp)
+
+ gen := result.ModuleForTests("gen", "").Output("out")
+ if gen.Rule != android.ErrorRule {
+ t.Errorf("Expected missing dependency error rule for gen, got %q", gen.Rule.String())
}
}
@@ -721,29 +693,20 @@
}
`
- config := testConfig(bp, nil)
- config.BazelContext = android.MockBazelContext{
- AllFiles: map[string][]string{
- "//foo/bar:bar": []string{"bazelone.txt", "bazeltwo.txt"}}}
+ result := android.GroupFixturePreparers(
+ prepareForGenRuleTest, android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToOutputFiles: map[string][]string{
+ "//foo/bar:bar": []string{"bazelone.txt", "bazeltwo.txt"}}}
+ })).RunTestWithBp(t, testGenruleBp()+bp)
- ctx := testContext(config)
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- if errs == nil {
- _, errs = ctx.PrepareBuildActions(config)
- }
- if errs != nil {
- t.Fatal(errs)
- }
- gen := ctx.ModuleForTests("foo", "").Module().(*Module)
+ gen := result.Module("foo", "").(*Module)
expectedOutputFiles := []string{"outputbase/execroot/__main__/bazelone.txt",
"outputbase/execroot/__main__/bazeltwo.txt"}
- if !reflect.DeepEqual(gen.outputFiles.Strings(), expectedOutputFiles) {
- t.Errorf("Expected output files: %q, actual: %q", expectedOutputFiles, gen.outputFiles)
- }
- if !reflect.DeepEqual(gen.outputDeps.Strings(), expectedOutputFiles) {
- t.Errorf("Expected output deps: %q, actual: %q", expectedOutputFiles, gen.outputDeps)
- }
+ android.AssertDeepEquals(t, "output files", expectedOutputFiles, gen.outputFiles.Strings())
+ android.AssertDeepEquals(t, "output deps", expectedOutputFiles, gen.outputDeps.Strings())
}
type testTool struct {
@@ -766,3 +729,24 @@
}
var _ android.HostToolProvider = (*testTool)(nil)
+
+type testOutputProducer struct {
+ android.ModuleBase
+ outputFile android.Path
+}
+
+func outputProducerFactory() android.Module {
+ module := &testOutputProducer{}
+ android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+ return module
+}
+
+func (t *testOutputProducer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
+}
+
+func (t *testOutputProducer) OutputFiles(tag string) (android.Paths, error) {
+ return android.Paths{t.outputFile}, nil
+}
+
+var _ android.OutputFileProducer = (*testOutputProducer)(nil)
diff --git a/genrule/locations.go b/genrule/locations.go
new file mode 100644
index 0000000..2978b91
--- /dev/null
+++ b/genrule/locations.go
@@ -0,0 +1,104 @@
+// Copyright 2021 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 genrule
+
+import (
+ "strings"
+
+ "android/soong/android"
+)
+
+// location is used to service $(location) and $(locations) entries in genrule commands.
+type location interface {
+ Paths(cmd *android.RuleBuilderCommand) []string
+ String() string
+}
+
+// inputLocation is a $(location) result for an entry in the srcs property.
+type inputLocation struct {
+ paths android.Paths
+}
+
+func (l inputLocation) String() string {
+ return strings.Join(l.paths.Strings(), " ")
+}
+
+func (l inputLocation) Paths(cmd *android.RuleBuilderCommand) []string {
+ return cmd.PathsForInputs(l.paths)
+}
+
+var _ location = inputLocation{}
+
+// outputLocation is a $(location) result for an entry in the out property.
+type outputLocation struct {
+ path android.WritablePath
+}
+
+func (l outputLocation) String() string {
+ return l.path.String()
+}
+
+func (l outputLocation) Paths(cmd *android.RuleBuilderCommand) []string {
+ return []string{cmd.PathForOutput(l.path)}
+}
+
+var _ location = outputLocation{}
+
+// toolLocation is a $(location) result for an entry in the tools or tool_files property.
+type toolLocation struct {
+ paths android.Paths
+}
+
+func (l toolLocation) String() string {
+ return strings.Join(l.paths.Strings(), " ")
+}
+
+func (l toolLocation) Paths(cmd *android.RuleBuilderCommand) []string {
+ return cmd.PathsForTools(l.paths)
+}
+
+var _ location = toolLocation{}
+
+// packagedToolLocation is a $(location) result for an entry in the tools or tool_files property
+// that has PackagingSpecs.
+type packagedToolLocation struct {
+ spec android.PackagingSpec
+}
+
+func (l packagedToolLocation) String() string {
+ return l.spec.FileName()
+}
+
+func (l packagedToolLocation) Paths(cmd *android.RuleBuilderCommand) []string {
+ return []string{cmd.PathForPackagedTool(l.spec)}
+}
+
+var _ location = packagedToolLocation{}
+
+// errorLocation is a placeholder for a $(location) result that returns garbage to break the command
+// when error reporting is delayed by ALLOW_MISSING_DEPENDENCIES=true.
+type errorLocation struct {
+ err string
+}
+
+func (l errorLocation) String() string {
+ return l.err
+}
+
+func (l errorLocation) Paths(cmd *android.RuleBuilderCommand) []string {
+ return []string{l.err}
+}
+
+var _ location = errorLocation{}
diff --git a/jar/Android.bp b/jar/Android.bp
index 2563474..46113d8 100644
--- a/jar/Android.bp
+++ b/jar/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-jar",
pkgPath: "android/soong/jar",
diff --git a/java/Android.bp b/java/Android.bp
index 9c28968..2a4b596 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-java",
pkgPath: "android/soong/java",
@@ -24,17 +28,22 @@
"app.go",
"app_import.go",
"app_set.go",
+ "base.go",
+ "boot_image.go",
"boot_jars.go",
"builder.go",
+ "classpath_fragment.go",
"device_host_converter.go",
"dex.go",
"dexpreopt.go",
"dexpreopt_bootjars.go",
"dexpreopt_config.go",
"droiddoc.go",
+ "droidstubs.go",
"gen.go",
"genrule.go",
"hiddenapi.go",
+ "hiddenapi_modular.go",
"hiddenapi_singleton.go",
"jacoco.go",
"java.go",
@@ -43,6 +52,7 @@
"kotlin.go",
"lint.go",
"legacy_core_platform_api_usage.go",
+ "platform_bootclasspath.go",
"platform_compat_config.go",
"plugin.go",
"prebuilt_apis.go",
@@ -53,7 +63,6 @@
"sdk_library.go",
"sdk_library_external.go",
"support_libraries.go",
- "sysprop.go",
"system_modules.go",
"testing.go",
"tradefed.go",
@@ -63,16 +72,22 @@
"app_import_test.go",
"app_set_test.go",
"app_test.go",
+ "boot_image_test.go",
"device_host_converter_test.go",
"dexpreopt_test.go",
"dexpreopt_bootjars_test.go",
+ "droiddoc_test.go",
+ "droidstubs_test.go",
"hiddenapi_singleton_test.go",
"java_test.go",
"jdeps_test.go",
"kotlin_test.go",
+ "platform_bootclasspath_test.go",
+ "platform_compat_config_test.go",
"plugin_test.go",
"rro_test.go",
"sdk_test.go",
+ "system_modules_test.go",
],
pluginFor: ["soong_build"],
}
diff --git a/java/aar.go b/java/aar.go
index e3ad252..04727e4 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -28,7 +28,6 @@
)
type AndroidLibraryDependency interface {
- Dependency
ExportPackage() android.Path
ExportedProguardFlagFiles() android.Paths
ExportedRRODirs() []rroDir
@@ -41,15 +40,14 @@
func init() {
RegisterAARBuildComponents(android.InitRegistrationContext)
-
- android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator).Parallel()
- })
}
func RegisterAARBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("android_library_import", AARImportFactory)
ctx.RegisterModuleType("android_library", AndroidLibraryFactory)
+ ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator).Parallel()
+ })
}
//
@@ -161,11 +159,11 @@
func (a *aapt) IsRROEnforced(ctx android.BaseModuleContext) bool {
// True if RRO is enforced for this module or...
return ctx.Config().EnforceRROForModule(ctx.ModuleName()) ||
- // if RRO is enforced for any of its dependents, and this module is not exempted.
- (a.aaptProperties.RROEnforcedForDependent && !ctx.Config().EnforceRROExemptedForModule(ctx.ModuleName()))
+ // if RRO is enforced for any of its dependents.
+ a.aaptProperties.RROEnforcedForDependent
}
-func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext sdkContext,
+func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext android.SdkContext,
manifestPath android.Path) (compileFlags, linkFlags []string, linkDeps android.Paths,
resDirs, overlayDirs []globbedResourceDir, rroDirs []rroDir, resZips android.Paths) {
@@ -220,7 +218,7 @@
linkDeps = append(linkDeps, assetDeps...)
// SDK version flags
- minSdkVersion, err := sdkContext.minSdkVersion().effectiveVersionString(ctx)
+ minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
if err != nil {
ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
}
@@ -268,7 +266,7 @@
CommandDeps: []string{"${config.Zip2ZipCmd}"},
})
-func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext sdkContext,
+func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkContext,
classLoaderContexts dexpreopt.ClassLoaderContextMap, extraLinkFlags ...string) {
transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assetPackages, libDeps, libFlags :=
@@ -399,7 +397,7 @@
}
// aaptLibs collects libraries from dependencies and sdk_version and converts them into paths
-func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext, classLoaderContexts dexpreopt.ClassLoaderContextMap) (
+func aaptLibs(ctx android.ModuleContext, sdkContext android.SdkContext, classLoaderContexts dexpreopt.ClassLoaderContextMap) (
transitiveStaticLibs, transitiveStaticLibManifests android.Paths, staticRRODirs []rroDir, assets, deps android.Paths, flags []string) {
var sharedLibs android.Paths
@@ -444,16 +442,14 @@
assets = append(assets, aarDep.ExportedAssets().Path())
}
- if !ctx.Config().EnforceRROExemptedForModule(ctx.ModuleName()) {
- outer:
- for _, d := range aarDep.ExportedRRODirs() {
- for _, e := range staticRRODirs {
- if d.path == e.path {
- continue outer
- }
+ outer:
+ for _, d := range aarDep.ExportedRRODirs() {
+ for _, e := range staticRRODirs {
+ if d.path == e.path {
+ continue outer
}
- staticRRODirs = append(staticRRODirs, d)
}
+ staticRRODirs = append(staticRRODirs, d)
}
}
}
@@ -502,7 +498,7 @@
func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
a.Module.deps(ctx)
- sdkDep := decodeSdkDep(ctx, sdkContext(a))
+ sdkDep := decodeSdkDep(ctx, android.SdkContext(a))
if sdkDep.hasFrameworkLibs() {
a.aapt.deps(ctx, sdkDep)
}
@@ -511,7 +507,7 @@
func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
a.aapt.isLibrary = true
a.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
- a.aapt.buildActions(ctx, sdkContext(a), a.classLoaderContexts)
+ a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts)
a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
@@ -613,6 +609,9 @@
hideApexVariantFromMake bool
aarPath android.Path
+
+ sdkVersion android.SdkSpec
+ minSdkVersion android.SdkSpec
}
var _ android.OutputFileProducer = (*AARImport)(nil)
@@ -629,23 +628,23 @@
}
}
-func (a *AARImport) sdkVersion() sdkSpec {
- return sdkSpecFrom(String(a.properties.Sdk_version))
+func (a *AARImport) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ return android.SdkSpecFrom(ctx, String(a.properties.Sdk_version))
}
-func (a *AARImport) systemModules() string {
+func (a *AARImport) SystemModules() string {
return ""
}
-func (a *AARImport) minSdkVersion() sdkSpec {
+func (a *AARImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
if a.properties.Min_sdk_version != nil {
- return sdkSpecFrom(*a.properties.Min_sdk_version)
+ return android.SdkSpecFrom(ctx, *a.properties.Min_sdk_version)
}
- return a.sdkVersion()
+ return a.SdkVersion(ctx)
}
-func (a *AARImport) targetSdkVersion() sdkSpec {
- return a.sdkVersion()
+func (a *AARImport) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ return a.SdkVersion(ctx)
}
func (a *AARImport) javaVersion() string {
@@ -704,7 +703,7 @@
func (a *AARImport) DepsMutator(ctx android.BottomUpMutatorContext) {
if !ctx.Config().AlwaysUsePrebuiltSdks() {
- sdkDep := decodeSdkDep(ctx, sdkContext(a))
+ sdkDep := decodeSdkDep(ctx, android.SdkContext(a))
if sdkDep.useModule && sdkDep.frameworkResModule != "" {
ctx.AddVariationDependencies(nil, frameworkResTag, sdkDep.frameworkResModule)
}
@@ -731,6 +730,9 @@
return
}
+ a.sdkVersion = a.SdkVersion(ctx)
+ a.minSdkVersion = a.MinSdkVersion(ctx)
+
a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
aarName := ctx.ModuleName() + ".aar"
@@ -784,7 +786,7 @@
linkDeps = append(linkDeps, a.manifest)
transitiveStaticLibs, staticLibManifests, staticRRODirs, transitiveAssets, libDeps, libFlags :=
- aaptLibs(ctx, sdkContext(a), nil)
+ aaptLibs(ctx, android.SdkContext(a), nil)
_ = staticLibManifests
_ = staticRRODirs
@@ -796,22 +798,18 @@
aapt2Link(ctx, a.exportPackage, srcJar, proguardOptionsFile, rTxt, a.extraAaptPackagesFile,
linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil)
-}
-var _ Dependency = (*AARImport)(nil)
+ ctx.SetProvider(JavaInfoProvider, JavaInfo{
+ HeaderJars: android.PathsIfNonNil(a.classpathFile),
+ ImplementationAndResourcesJars: android.PathsIfNonNil(a.classpathFile),
+ ImplementationJars: android.PathsIfNonNil(a.classpathFile),
+ })
+}
func (a *AARImport) HeaderJars() android.Paths {
return android.Paths{a.classpathFile}
}
-func (a *AARImport) ImplementationJars() android.Paths {
- return android.Paths{a.classpathFile}
-}
-
-func (a *AARImport) ResourceJars() android.Paths {
- return nil
-}
-
func (a *AARImport) ImplementationAndResourcesJars() android.Paths {
return android.Paths{a.classpathFile}
}
@@ -824,22 +822,10 @@
return nil
}
-func (a *AARImport) AidlIncludeDirs() android.Paths {
- return nil
-}
-
func (a *AARImport) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
return nil
}
-func (d *AARImport) ExportedPlugins() (android.Paths, []string, bool) {
- return nil, nil, false
-}
-
-func (a *AARImport) SrcJarArgs() ([]string, android.Paths) {
- return nil, nil
-}
-
var _ android.ApexModule = (*AARImport)(nil)
// Implements android.ApexModule
diff --git a/java/android_manifest.go b/java/android_manifest.go
index c76bb2f..331f941 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -43,7 +43,7 @@
"args", "libs")
// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml
-func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext,
+func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext android.SdkContext,
classLoaderContexts dexpreopt.ClassLoaderContextMap, isLibrary, useEmbeddedNativeLibs, usesNonSdkApis,
useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path {
@@ -51,11 +51,11 @@
if isLibrary {
args = append(args, "--library")
} else {
- minSdkVersion, err := sdkContext.minSdkVersion().effectiveVersion(ctx)
+ minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersion(ctx)
if err != nil {
ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
}
- if minSdkVersion >= 23 {
+ if minSdkVersion.FinalOrFutureInt() >= 23 {
args = append(args, fmt.Sprintf("--extract-native-libs=%v", !useEmbeddedNativeLibs))
} else if useEmbeddedNativeLibs {
ctx.ModuleErrorf("module attempted to store uncompressed native libraries, but minSdkVersion=%d doesn't support it",
@@ -87,20 +87,20 @@
args = append(args, "--logging-parent", loggingParent)
}
var deps android.Paths
- targetSdkVersion, err := sdkContext.targetSdkVersion().effectiveVersionString(ctx)
+ targetSdkVersion, err := sdkContext.TargetSdkVersion(ctx).EffectiveVersionString(ctx)
if err != nil {
ctx.ModuleErrorf("invalid targetSdkVersion: %s", err)
}
- if UseApiFingerprint(ctx) {
+ if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
deps = append(deps, ApiFingerprintPath(ctx))
}
- minSdkVersion, err := sdkContext.minSdkVersion().effectiveVersionString(ctx)
+ minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
if err != nil {
ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
}
- if UseApiFingerprint(ctx) {
+ if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
deps = append(deps, ApiFingerprintPath(ctx))
}
diff --git a/java/android_resources.go b/java/android_resources.go
index 4d420cf..6864ebb 100644
--- a/java/android_resources.go
+++ b/java/android_resources.go
@@ -22,8 +22,11 @@
)
func init() {
- android.RegisterPreSingletonType("overlay", OverlaySingletonFactory)
+ registerOverlayBuildComponents(android.InitRegistrationContext)
+}
+func registerOverlayBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterPreSingletonType("overlay", OverlaySingletonFactory)
}
var androidResourceIgnoreFilenames = []string{
diff --git a/java/androidmk.go b/java/androidmk.go
index aaad44f..4e594a2 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -41,7 +41,7 @@
Required: library.deviceProperties.Target.Hostdex.Required,
Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetBool("LOCAL_IS_HOST_MODULE", true)
entries.SetPath("LOCAL_PREBUILT_MODULE_FILE", output)
if library.dexJarFile != nil {
@@ -74,7 +74,7 @@
OutputFile: android.OptionalPathForPath(checkedModulePaths[0]),
Include: "$(BUILD_PHONY_PACKAGE)",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", checkedModulePaths.Strings()...)
},
},
@@ -88,7 +88,7 @@
OutputFile: android.OptionalPathForPath(library.outputFile),
Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
if len(library.logtagsSrcs) > 0 {
var logtags []string
for _, l := range library.logtagsSrcs {
@@ -106,7 +106,7 @@
if len(library.dexpreopter.builtInstalled) > 0 {
entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", library.dexpreopter.builtInstalled)
}
- entries.SetString("LOCAL_SDK_VERSION", library.sdkVersion().raw)
+ entries.SetString("LOCAL_SDK_VERSION", library.sdkVersion.String())
entries.SetPath("LOCAL_SOONG_CLASSES_JAR", library.implementationAndResourcesJar)
entries.SetPath("LOCAL_SOONG_HEADER_JAR", library.headerJarFile)
@@ -125,6 +125,10 @@
entries.SetString("LOCAL_MODULE_STEM", library.Stem())
entries.SetOptionalPaths("LOCAL_SOONG_LINT_REPORTS", library.linter.reports)
+
+ if library.dexpreopter.configPath != nil {
+ entries.SetPath("LOCAL_SOONG_DEXPREOPT_CONFIG", library.dexpreopter.configPath)
+ }
},
},
})
@@ -148,7 +152,7 @@
func (j *Test) AndroidMkEntries() []android.AndroidMkEntries {
entriesList := j.Library.AndroidMkEntries()
entries := &entriesList[0]
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
testSuiteComponent(entries, j.testProperties.Test_suites)
if j.testConfig != nil {
entries.SetPath("LOCAL_FULL_TEST_CONFIG", j.testConfig)
@@ -176,7 +180,7 @@
func (j *TestHelperLibrary) AndroidMkEntries() []android.AndroidMkEntries {
entriesList := j.Library.AndroidMkEntries()
entries := &entriesList[0]
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
testSuiteComponent(entries, j.testHelperLibraryProperties.Test_suites)
})
@@ -194,11 +198,14 @@
OutputFile: android.OptionalPathForPath(prebuilt.combinedClasspathFile),
Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !Bool(prebuilt.properties.Installable))
+ if prebuilt.dexJarFile != nil {
+ entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile)
+ }
entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedClasspathFile)
entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedClasspathFile)
- entries.SetString("LOCAL_SDK_VERSION", prebuilt.makeSdkVersion())
+ entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion.String())
entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem())
},
},
@@ -213,10 +220,10 @@
}
return []android.AndroidMkEntries{android.AndroidMkEntries{
Class: "JAVA_LIBRARIES",
- OutputFile: android.OptionalPathForPath(prebuilt.maybeStrippedDexJarFile),
+ OutputFile: android.OptionalPathForPath(prebuilt.dexJarFile),
Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
if prebuilt.dexJarFile != nil {
entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile)
}
@@ -240,7 +247,7 @@
OutputFile: android.OptionalPathForPath(prebuilt.classpathFile),
Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.classpathFile)
entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.classpathFile)
@@ -248,7 +255,7 @@
entries.SetPath("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", prebuilt.proguardFlags)
entries.SetPath("LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES", prebuilt.extraAaptPackagesFile)
entries.SetPath("LOCAL_FULL_MANIFEST_FILE", prebuilt.manifest)
- entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion().raw)
+ entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion.String())
},
},
}}
@@ -262,7 +269,7 @@
OutputFile: android.OptionalPathForPath(binary.outputFile),
Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetPath("LOCAL_SOONG_HEADER_JAR", binary.headerJarFile)
entries.SetPath("LOCAL_SOONG_CLASSES_JAR", binary.implementationAndResourcesJar)
if binary.dexJarFile != nil {
@@ -280,11 +287,18 @@
},
}}
} else {
+ outputFile := binary.wrapperFile
+ // Have Make installation trigger Soong installation by using Soong's install path as
+ // the output file.
+ if binary.Host() {
+ outputFile = binary.binaryFile
+ }
+
return []android.AndroidMkEntries{android.AndroidMkEntries{
Class: "EXECUTABLES",
- OutputFile: android.OptionalPathForPath(binary.wrapperFile),
+ OutputFile: android.OptionalPathForPath(outputFile),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetBool("LOCAL_STRIP_MODULE", false)
},
},
@@ -310,7 +324,7 @@
OutputFile: android.OptionalPathForPath(app.outputFile),
Include: "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
// App module names can be overridden.
entries.SetString("LOCAL_MODULE", app.installApkName)
entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", app.appProperties.PreventInstall)
@@ -425,7 +439,7 @@
func (a *AndroidTest) AndroidMkEntries() []android.AndroidMkEntries {
entriesList := a.AndroidApp.AndroidMkEntries()
entries := &entriesList[0]
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
testSuiteComponent(entries, a.testProperties.Test_suites)
if a.testConfig != nil {
entries.SetPath("LOCAL_FULL_TEST_CONFIG", a.testConfig)
@@ -441,7 +455,7 @@
func (a *AndroidTestHelperApp) AndroidMkEntries() []android.AndroidMkEntries {
entriesList := a.AndroidApp.AndroidMkEntries()
entries := &entriesList[0]
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
testSuiteComponent(entries, a.appTestHelperAppProperties.Test_suites)
})
@@ -457,7 +471,7 @@
entriesList := a.Library.AndroidMkEntries()
entries := &entriesList[0]
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
if a.aarFile != nil {
entries.SetPath("LOCAL_SOONG_AAR", a.aarFile)
}
@@ -485,7 +499,7 @@
OutputFile: android.OptionalPathForPath(jd.stubsSrcJar),
Include: "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
if BoolDefault(jd.properties.Installable, true) {
entries.SetPath("LOCAL_DROIDDOC_DOC_ZIP", jd.docZip)
}
@@ -503,7 +517,7 @@
OutputFile: android.OptionalPathForPath(ddoc.Javadoc.docZip),
Include: "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
if ddoc.Javadoc.docZip != nil {
entries.SetPath("LOCAL_DROIDDOC_DOC_ZIP", ddoc.Javadoc.docZip)
}
@@ -532,7 +546,7 @@
OutputFile: outputFile,
Include: "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
if dstubs.Javadoc.stubsSrcJar != nil {
entries.SetPath("LOCAL_DROIDDOC_STUBS_SRCJAR", dstubs.Javadoc.stubsSrcJar)
}
@@ -631,7 +645,7 @@
OutputFile: android.OptionalPathForPath(a.outputFile),
Include: "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", a.Privileged())
entries.SetString("LOCAL_CERTIFICATE", a.certificate.AndroidMkString())
entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", a.properties.Overrides...)
@@ -639,6 +653,9 @@
entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", a.dexpreopter.builtInstalled)
}
entries.AddStrings("LOCAL_INSTALLED_MODULE_STEM", a.installPath.Rel())
+ if Bool(a.properties.Export_package_resources) {
+ entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", a.outputFile)
+ }
},
},
}}
@@ -647,7 +664,7 @@
func (a *AndroidTestImport) AndroidMkEntries() []android.AndroidMkEntries {
entriesList := a.AndroidAppImport.AndroidMkEntries()
entries := &entriesList[0]
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
testSuiteComponent(entries, a.testProperties.Test_suites)
androidMkWriteTestData(a.data, entries)
})
@@ -668,7 +685,7 @@
OutputFile: android.OptionalPathForPath(r.outputFile),
Include: "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_CERTIFICATE", r.certificate.AndroidMkString())
entries.SetPath("LOCAL_MODULE_PATH", r.installDir.ToMakePath())
entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", r.properties.Overrides...)
@@ -684,7 +701,7 @@
OutputFile: android.OptionalPathForPath(apkSet.packedOutput),
Include: "$(BUILD_SYSTEM)/soong_android_app_set.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", apkSet.Privileged())
entries.SetString("LOCAL_APK_SET_INSTALL_FILE", apkSet.InstallFile())
entries.SetPath("LOCAL_APKCERTS_FILE", apkSet.apkcertsFile)
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 233e9d5..5eaa77b 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -22,7 +22,7 @@
)
func TestRequired(t *testing.T) {
- ctx, config := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -31,7 +31,7 @@
`)
mod := ctx.ModuleForTests("foo", "android_common").Module()
- entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+ entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
expected := []string{"libfoo"}
actual := entries.EntryMap["LOCAL_REQUIRED_MODULES"]
@@ -41,7 +41,7 @@
}
func TestHostdex(t *testing.T) {
- ctx, config := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -50,7 +50,7 @@
`)
mod := ctx.ModuleForTests("foo", "android_common").Module()
- entriesList := android.AndroidMkEntriesForTest(t, config, "", mod)
+ entriesList := android.AndroidMkEntriesForTest(t, ctx, mod)
if len(entriesList) != 2 {
t.Errorf("two entries are expected, but got %d", len(entriesList))
}
@@ -71,7 +71,7 @@
}
func TestHostdexRequired(t *testing.T) {
- ctx, config := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -81,7 +81,7 @@
`)
mod := ctx.ModuleForTests("foo", "android_common").Module()
- entriesList := android.AndroidMkEntriesForTest(t, config, "", mod)
+ entriesList := android.AndroidMkEntriesForTest(t, ctx, mod)
if len(entriesList) != 2 {
t.Errorf("two entries are expected, but got %d", len(entriesList))
}
@@ -102,7 +102,7 @@
}
func TestHostdexSpecificRequired(t *testing.T) {
- ctx, config := testJava(t, `
+ ctx, _ := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -116,7 +116,7 @@
`)
mod := ctx.ModuleForTests("foo", "android_common").Module()
- entriesList := android.AndroidMkEntriesForTest(t, config, "", mod)
+ entriesList := android.AndroidMkEntriesForTest(t, ctx, mod)
if len(entriesList) != 2 {
t.Errorf("two entries are expected, but got %d", len(entriesList))
}
@@ -135,7 +135,11 @@
}
func TestJavaSdkLibrary_RequireXmlPermissionFile(t *testing.T) {
- ctx, config := testJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("foo-shared_library", "foo-no_shared_library"),
+ ).RunTestWithBp(t, `
java_sdk_library {
name: "foo-shared_library",
srcs: ["a.java"],
@@ -148,7 +152,7 @@
`)
// Verify the existence of internal modules
- ctx.ModuleForTests("foo-shared_library.xml", "android_common")
+ result.ModuleForTests("foo-shared_library.xml", "android_common")
testCases := []struct {
moduleName string
@@ -158,11 +162,29 @@
{"foo-no_shared_library", nil},
}
for _, tc := range testCases {
- mod := ctx.ModuleForTests(tc.moduleName, "android_common").Module()
- entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+ mod := result.ModuleForTests(tc.moduleName, "android_common").Module()
+ entries := android.AndroidMkEntriesForTest(t, result.TestContext, mod)[0]
actual := entries.EntryMap["LOCAL_REQUIRED_MODULES"]
if !reflect.DeepEqual(tc.expected, actual) {
t.Errorf("Unexpected required modules - expected: %q, actual: %q", tc.expected, actual)
}
}
}
+
+func TestImportSoongDexJar(t *testing.T) {
+ result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
+ java_import {
+ name: "my-java-import",
+ jars: ["a.jar"],
+ prefer: true,
+ compile_dex: true,
+ }
+ `)
+
+ mod := result.Module("my-java-import", "android_common")
+ entries := android.AndroidMkEntriesForTest(t, result.TestContext, mod)[0]
+ expectedSoongDexJar := "out/soong/.intermediates/my-java-import/android_common/dex/my-java-import.jar"
+ actualSoongDexJar := entries.EntryMap["LOCAL_SOONG_DEX_JAR"]
+
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_SOONG_DEX_JAR", result.Config, []string{expectedSoongDexJar}, actualSoongDexJar)
+}
diff --git a/java/app.go b/java/app.go
index 249313c..5695022 100755
--- a/java/app.go
+++ b/java/app.go
@@ -122,8 +122,8 @@
// or an android_app_certificate module name in the form ":module".
Certificate *string
- // Name of the signing certificate lineage file.
- Lineage *string
+ // Name of the signing certificate lineage file or filegroup module.
+ Lineage *string `android:"path"`
// the package name of this app. The package name in the manifest file is used if one was not given.
Package_name *string
@@ -213,16 +213,16 @@
func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) {
a.Module.deps(ctx)
- if String(a.appProperties.Stl) == "c++_shared" && !a.sdkVersion().specified() {
+ if String(a.appProperties.Stl) == "c++_shared" && !a.SdkVersion(ctx).Specified() {
ctx.PropertyErrorf("stl", "sdk_version must be set in order to use c++_shared")
}
- sdkDep := decodeSdkDep(ctx, sdkContext(a))
+ sdkDep := decodeSdkDep(ctx, android.SdkContext(a))
if sdkDep.hasFrameworkLibs() {
a.aapt.deps(ctx, sdkDep)
}
- usesSDK := a.sdkVersion().specified() && a.sdkVersion().kind != sdkCorePlatform
+ usesSDK := a.SdkVersion(ctx).Specified() && a.SdkVersion(ctx).Kind != android.SdkCorePlatform
if usesSDK && Bool(a.appProperties.Jni_uses_sdk_apis) {
ctx.PropertyErrorf("jni_uses_sdk_apis",
@@ -279,16 +279,16 @@
func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) {
if a.Updatable() {
- if !a.sdkVersion().stable() {
- ctx.PropertyErrorf("sdk_version", "Updatable apps must use stable SDKs, found %v", a.sdkVersion())
+ if !a.SdkVersion(ctx).Stable() {
+ ctx.PropertyErrorf("sdk_version", "Updatable apps must use stable SDKs, found %v", a.SdkVersion(ctx))
}
if String(a.deviceProperties.Min_sdk_version) == "" {
ctx.PropertyErrorf("updatable", "updatable apps must set min_sdk_version.")
}
- if minSdkVersion, err := a.minSdkVersion().effectiveVersion(ctx); err == nil {
+ if minSdkVersion, err := a.MinSdkVersion(ctx).EffectiveVersion(ctx); err == nil {
a.checkJniLibsSdkVersion(ctx, minSdkVersion)
- android.CheckMinSdkVersion(a, ctx, minSdkVersion.ApiLevel(ctx))
+ android.CheckMinSdkVersion(a, ctx, minSdkVersion)
} else {
ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
}
@@ -302,9 +302,9 @@
// This check is enforced for "updatable" APKs (including APK-in-APEX).
// b/155209650: until min_sdk_version is properly supported, use sdk_version instead.
// because, sdk_version is overridden by min_sdk_version (if set as smaller)
-// and linkType is checked with dependencies so we can be sure that the whole dependency tree
+// and sdkLinkType is checked with dependencies so we can be sure that the whole dependency tree
// will meet the requirements.
-func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVersion sdkVersion) {
+func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVersion android.ApiLevel) {
// It's enough to check direct JNI deps' sdk_version because all transitive deps from JNI deps are checked in cc.checkLinkType()
ctx.VisitDirectDeps(func(m android.Module) {
if !IsJniDepTag(ctx.OtherModuleDependencyTag(m)) {
@@ -312,10 +312,10 @@
}
dep, _ := m.(*cc.Module)
// The domain of cc.sdk_version is "current" and <number>
- // We can rely on sdkSpec to convert it to <number> so that "current" is handled
- // properly regardless of sdk finalization.
- jniSdkVersion, err := sdkSpecFrom(dep.SdkVersion()).effectiveVersion(ctx)
- if err != nil || minSdkVersion < jniSdkVersion {
+ // We can rely on android.SdkSpec to convert it to <number> so that "current" is
+ // handled properly regardless of sdk finalization.
+ jniSdkVersion, err := android.SdkSpecFrom(ctx, dep.SdkVersion()).EffectiveVersion(ctx)
+ if err != nil || minSdkVersion.LessThan(jniSdkVersion) {
ctx.OtherModuleErrorf(dep, "sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)",
dep.SdkVersion(), minSdkVersion, ctx.ModuleName())
return
@@ -327,13 +327,13 @@
// Returns true if the native libraries should be stored in the APK uncompressed and the
// extractNativeLibs application flag should be set to false in the manifest.
func (a *AndroidApp) useEmbeddedNativeLibs(ctx android.ModuleContext) bool {
- minSdkVersion, err := a.minSdkVersion().effectiveVersion(ctx)
+ minSdkVersion, err := a.MinSdkVersion(ctx).EffectiveVersion(ctx)
if err != nil {
- ctx.PropertyErrorf("min_sdk_version", "invalid value %q: %s", a.minSdkVersion(), err)
+ ctx.PropertyErrorf("min_sdk_version", "invalid value %q: %s", a.MinSdkVersion(ctx), err)
}
apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
- return (minSdkVersion >= 23 && Bool(a.appProperties.Use_embedded_native_libs)) ||
+ return (minSdkVersion.FinalOrFutureInt() >= 23 && Bool(a.appProperties.Use_embedded_native_libs)) ||
!apexInfo.IsForPlatform()
}
@@ -380,7 +380,11 @@
}
func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) {
- a.aapt.usesNonSdkApis = Bool(a.Module.deviceProperties.Platform_apis)
+ usePlatformAPI := proptools.Bool(a.Module.deviceProperties.Platform_apis)
+ if ctx.Module().(android.SdkContext).SdkVersion(ctx).Kind == android.SdkModule {
+ usePlatformAPI = true
+ }
+ a.aapt.usesNonSdkApis = usePlatformAPI
// Ask manifest_fixer to add or update the application element indicating this app has no code.
a.aapt.hasNoCode = !a.hasCode(ctx)
@@ -419,7 +423,7 @@
a.aapt.splitNames = a.appProperties.Package_splits
a.aapt.LoggingParent = String(a.overridableAppProperties.Logging_parent)
- a.aapt.buildActions(ctx, sdkContext(a), a.classLoaderContexts, aaptLinkFlags...)
+ a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts, aaptLinkFlags...)
// apps manifests are handled by aapt, don't let Module see them
a.properties.Manifest = nil
@@ -455,6 +459,7 @@
func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
a.dexpreopter.installPath = a.installPath(ctx)
+ a.dexpreopter.isApp = true
if a.dexProperties.Uncompress_dex == nil {
// If the value was not force-set by the user, use reasonable default based on the module.
a.dexProperties.Uncompress_dex = proptools.BoolPtr(a.shouldUncompressDex(ctx))
@@ -468,7 +473,7 @@
a.Module.compile(ctx, a.aaptSrcJar)
}
- return a.maybeStrippedDexJarFile
+ return a.dexJarFile
}
func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext) android.WritablePath {
@@ -719,8 +724,8 @@
}
type appDepsInterface interface {
- sdkVersion() sdkSpec
- minSdkVersion() sdkSpec
+ SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+ MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
RequiresStableAPIs(ctx android.BaseModuleContext) bool
}
@@ -733,8 +738,8 @@
seenModulePaths := make(map[string]bool)
if checkNativeSdkVersion {
- checkNativeSdkVersion = app.sdkVersion().specified() &&
- app.sdkVersion().kind != sdkCorePlatform && !app.RequiresStableAPIs(ctx)
+ checkNativeSdkVersion = app.SdkVersion(ctx).Specified() &&
+ app.SdkVersion(ctx).Kind != android.SdkCorePlatform && !app.RequiresStableAPIs(ctx)
}
ctx.WalkDeps(func(module android.Module, parent android.Module) bool {
@@ -811,13 +816,28 @@
depsInfo := android.DepNameToDepInfoMap{}
a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
depName := to.Name()
+
+ // Skip dependencies that are only available to APEXes; they are developed with updatability
+ // in mind and don't need manual approval.
+ if to.(android.ApexModule).NotAvailableForPlatform() {
+ return true
+ }
+
if info, exist := depsInfo[depName]; exist {
info.From = append(info.From, from.Name())
info.IsExternal = info.IsExternal && externalDep
depsInfo[depName] = info
} else {
toMinSdkVersion := "(no version)"
- if m, ok := to.(interface{ MinSdkVersion() string }); ok {
+ if m, ok := to.(interface {
+ MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+ }); ok {
+ if v := m.MinSdkVersion(ctx); !v.ApiLevel.IsNone() {
+ toMinSdkVersion = v.ApiLevel.String()
+ }
+ } else if m, ok := to.(interface{ MinSdkVersion() string }); ok {
+ // TODO(b/175678607) eliminate the use of MinSdkVersion returning
+ // string
if v := m.MinSdkVersion(); v != "" {
toMinSdkVersion = v
}
@@ -832,7 +852,7 @@
return true
})
- a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(), depsInfo)
+ a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(ctx).String(), depsInfo)
}
func (a *AndroidApp) Updatable() bool {
@@ -905,6 +925,8 @@
&module.appProperties,
&module.overridableAppProperties)
+ module.usesLibrary.enforce = true
+
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(module)
android.InitOverridableModule(module, &module.appProperties.Overrides)
@@ -1175,6 +1197,9 @@
// with knowledge of their shared libraries.
type usesLibrary struct {
usesLibraryProperties UsesLibraryProperties
+
+ // Whether to enforce verify_uses_library check.
+ enforce bool
}
func (u *usesLibrary) addLib(lib string, optional bool) {
@@ -1211,6 +1236,15 @@
return optionalUsesLibs
}
+// Helper function to replace string in a list.
+func replaceInList(list []string, oldstr, newstr string) {
+ for i, str := range list {
+ if str == oldstr {
+ list[i] = newstr
+ }
+ }
+}
+
// Returns a map of module names of shared library dependencies to the paths
// to their dex jars on host and on device.
func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext) dexpreopt.ClassLoaderContextMap {
@@ -1221,7 +1255,16 @@
if tag, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok {
dep := ctx.OtherModuleName(m)
if lib, ok := m.(UsesLibraryDependency); ok {
- clcMap.AddContextForSdk(ctx, tag.sdkVersion, dep,
+ libName := dep
+ if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil {
+ libName = *ulib.ProvidesUsesLib()
+ // Replace module name with library name in `uses_libs`/`optional_uses_libs`
+ // in order to pass verify_uses_libraries check (which compares these
+ // properties against library names written in the manifest).
+ replaceInList(u.usesLibraryProperties.Uses_libs, dep, libName)
+ replaceInList(u.usesLibraryProperties.Optional_uses_libs, dep, libName)
+ }
+ clcMap.AddContext(ctx, tag.sdkVersion, libName,
lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts())
} else if ctx.Config().AllowMissingDependencies() {
ctx.AddMissingDependencies([]string{dep})
@@ -1241,7 +1284,7 @@
func (u *usesLibrary) enforceUsesLibraries() bool {
defaultEnforceUsesLibs := len(u.usesLibraryProperties.Uses_libs) > 0 ||
len(u.usesLibraryProperties.Optional_uses_libs) > 0
- return BoolDefault(u.usesLibraryProperties.Enforce_uses_libs, defaultEnforceUsesLibs)
+ return BoolDefault(u.usesLibraryProperties.Enforce_uses_libs, u.enforce || defaultEnforceUsesLibs)
}
// Freeze the value of `enforce_uses_libs` based on the current values of `uses_libs` and `optional_uses_libs`.
@@ -1250,16 +1293,38 @@
u.usesLibraryProperties.Enforce_uses_libs = &enforce
}
-// verifyUsesLibrariesManifest checks the <uses-library> tags in an AndroidManifest.xml against the ones specified
-// in the uses_libs and optional_uses_libs properties. It returns the path to a copy of the manifest.
-func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path) android.Path {
- outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml")
+// verifyUsesLibraries checks the <uses-library> tags in the manifest against the ones specified
+// in the `uses_libs`/`optional_uses_libs` properties. The input can be either an XML manifest, or
+// an APK with the manifest embedded in it (manifest_check will know which one it is by the file
+// extension: APKs are supposed to end with '.apk').
+func (u *usesLibrary) verifyUsesLibraries(ctx android.ModuleContext, inputFile android.Path,
+ outputFile android.WritablePath) android.Path {
+
+ statusFile := dexpreopt.UsesLibrariesStatusFile(ctx)
+
+ // Disable verify_uses_libraries check if dexpreopt is globally disabled. Without dexpreopt the
+ // check is not necessary, and although it is good to have, it is difficult to maintain on
+ // non-linux build platforms where dexpreopt is generally disabled (the check may fail due to
+ // various unrelated reasons, such as a failure to get manifest from an APK).
+ global := dexpreopt.GetGlobalConfig(ctx)
+ if global.DisablePreopt || global.OnlyPreoptBootImageAndSystemServer {
+ return inputFile
+ }
rule := android.NewRuleBuilder(pctx, ctx)
cmd := rule.Command().BuiltTool("manifest_check").
Flag("--enforce-uses-libraries").
- Input(manifest).
- FlagWithOutput("-o ", outputFile)
+ Input(inputFile).
+ FlagWithOutput("--enforce-uses-libraries-status ", statusFile).
+ FlagWithInput("--aapt ", ctx.Config().HostToolPath(ctx, "aapt"))
+
+ if outputFile != nil {
+ cmd.FlagWithOutput("-o ", outputFile)
+ }
+
+ if dexpreopt.GetGlobalConfig(ctx).RelaxUsesLibraryCheck {
+ cmd.Flag("--enforce-uses-libraries-relax")
+ }
for _, lib := range u.usesLibraryProperties.Uses_libs {
cmd.FlagWithArg("--uses-library ", lib)
@@ -1270,25 +1335,20 @@
}
rule.Build("verify_uses_libraries", "verify <uses-library>")
-
return outputFile
}
-// verifyUsesLibrariesAPK checks the <uses-library> tags in the manifest of an APK against the ones specified
-// in the uses_libs and optional_uses_libs properties. It returns the path to a copy of the APK.
+// verifyUsesLibrariesManifest checks the <uses-library> tags in an AndroidManifest.xml against
+// the build system and returns the path to a copy of the manifest.
+func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path) android.Path {
+ outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml")
+ return u.verifyUsesLibraries(ctx, manifest, outputFile)
+}
+
+// verifyUsesLibrariesAPK checks the <uses-library> tags in the manifest of an APK against the build
+// system and returns the path to a copy of the APK.
func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk android.Path) android.Path {
+ u.verifyUsesLibraries(ctx, apk, nil) // for APKs manifest_check does not write output file
outputFile := android.PathForModuleOut(ctx, "verify_uses_libraries", apk.Base())
-
- rule := android.NewRuleBuilder(pctx, ctx)
- aapt := ctx.Config().HostToolPath(ctx, "aapt")
- rule.Command().
- Textf("aapt_binary=%s", aapt.String()).Implicit(aapt).
- Textf(`uses_library_names="%s"`, strings.Join(u.usesLibraryProperties.Uses_libs, " ")).
- Textf(`optional_uses_library_names="%s"`, strings.Join(u.usesLibraryProperties.Optional_uses_libs, " ")).
- Tool(android.PathForSource(ctx, "build/make/core/verify_uses_libraries.sh")).Input(apk)
- rule.Command().Text("cp -f").Input(apk).Output(outputFile)
-
- rule.Build("verify_uses_libraries", "verify <uses-library>")
-
return outputFile
}
diff --git a/java/app_builder.go b/java/app_builder.go
index b53c15a..4a18dca 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -30,7 +30,7 @@
)
var (
- Signapk, SignapkRE = remoteexec.StaticRules(pctx, "signapk",
+ Signapk, SignapkRE = pctx.RemoteStaticRules("signapk",
blueprint.RuleParams{
Command: `rm -f $out && $reTemplate${config.JavaCmd} ${config.JavaVmFlags} -Djava.library.path=$$(dirname ${config.SignapkJniLibrary}) ` +
`-jar ${config.SignapkCmd} $flags $certificates $in $out`,
diff --git a/java/app_import.go b/java/app_import.go
index 2054785..839051e 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -67,12 +67,15 @@
// module name in the form ":module". Should be empty if presigned or default_dev_cert is set.
Certificate *string
+ // Names of extra android_app_certificate modules to sign the apk with in the form ":module".
+ Additional_certificates []string
+
// Set this flag to true if the prebuilt apk is already signed. The certificate property must not
// be set for presigned modules.
Presigned *bool
- // Name of the signing certificate lineage file.
- Lineage *string
+ // Name of the signing certificate lineage file or filegroup module.
+ Lineage *string `android:"path"`
// Sign with the default system dev certificate. Must be used judiciously. Most imported apps
// need to either specify a specific certificate or be presigned.
@@ -92,6 +95,10 @@
// Optional name for the installed app. If unspecified, it is derived from the module name.
Filename *string
+
+ // If set, create package-export.apk, which other packages can
+ // use to get PRODUCT-agnostic resource data like IDs and type definitions.
+ Export_package_resources *bool
}
func (a *AndroidAppImport) IsInstallable() bool {
@@ -142,13 +149,27 @@
}
}
+func (a *AndroidAppImport) isPrebuiltFrameworkRes() bool {
+ return a.Name() == "prebuilt_framework-res"
+}
+
func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
cert := android.SrcIsModule(String(a.properties.Certificate))
if cert != "" {
ctx.AddDependency(ctx.Module(), certificateTag, cert)
}
- a.usesLibrary.deps(ctx, true)
+ for _, cert := range a.properties.Additional_certificates {
+ cert = android.SrcIsModule(cert)
+ if cert != "" {
+ ctx.AddDependency(ctx.Module(), certificateTag, cert)
+ } else {
+ ctx.PropertyErrorf("additional_certificates",
+ `must be names of android_app_certificate modules in the form ":module"`)
+ }
+ }
+
+ a.usesLibrary.deps(ctx, !a.isPrebuiltFrameworkRes())
}
func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
@@ -236,10 +257,6 @@
srcApk := a.prebuilt.SingleSourcePath(ctx)
- if a.usesLibrary.enforceUsesLibraries() {
- srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
- }
-
// TODO: Install or embed JNI libraries
// Uncompress JNI libraries in the apk
@@ -247,7 +264,12 @@
a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed.OutputPath)
var installDir android.InstallPath
- if Bool(a.properties.Privileged) {
+
+ if a.isPrebuiltFrameworkRes() {
+ // framework-res.apk is installed as system/framework/framework-res.apk
+ installDir = android.PathForModuleInstall(ctx, "framework")
+ a.preprocessed = true
+ } else if Bool(a.properties.Privileged) {
installDir = android.PathForModuleInstall(ctx, "priv-app", a.BaseModuleName())
} else if ctx.InstallInTestcases() {
installDir = android.PathForModuleInstall(ctx, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch())
@@ -255,6 +277,7 @@
installDir = android.PathForModuleInstall(ctx, "app", a.BaseModuleName())
}
+ a.dexpreopter.isApp = true
a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk")
a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned)
a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
@@ -262,6 +285,10 @@
a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
+ if a.usesLibrary.enforceUsesLibraries() {
+ srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
+ }
+
a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
if a.dexpreopter.uncompressedDex {
dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk")
@@ -274,16 +301,21 @@
// TODO: Handle EXTERNAL
// Sign or align the package if package has not been preprocessed
- if a.preprocessed {
+
+ if a.isPrebuiltFrameworkRes() {
+ a.outputFile = srcApk
+ certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
+ if len(certificates) != 1 {
+ ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates)
+ }
+ a.certificate = certificates[0]
+ } else if a.preprocessed {
a.outputFile = srcApk
a.certificate = PresignedCertificate
} else if !Bool(a.properties.Presigned) {
// If the certificate property is empty at this point, default_dev_cert must be set to true.
// Which makes processMainCert's behavior for the empty cert string WAI.
certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
- if len(certificates) != 1 {
- ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates)
- }
a.certificate = certificates[0]
signed := android.PathForModuleOut(ctx, "signed", apkFilename)
var lineageFile android.Path
@@ -362,12 +394,12 @@
return false
}
-func (a *AndroidAppImport) sdkVersion() sdkSpec {
- return sdkSpecFrom("")
+func (a *AndroidAppImport) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ return android.SdkSpecPrivate
}
-func (a *AndroidAppImport) minSdkVersion() sdkSpec {
- return sdkSpecFrom("")
+func (a *AndroidAppImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ return android.SdkSpecPrivate
}
var _ android.ApexModule = (*AndroidAppImport)(nil)
@@ -430,6 +462,8 @@
android.InitDefaultableModule(module)
android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
+ module.usesLibrary.enforce = true
+
return module
}
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 3b55c81..147ae45 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -109,14 +109,54 @@
name: "foo",
apk: "prebuilts/apk/app.apk",
certificate: "platform",
+ additional_certificates: [":additional_certificate"],
lineage: "lineage.bin",
}
+
+ android_app_certificate {
+ name: "additional_certificate",
+ certificate: "cert/additional_cert",
+ }
`)
variant := ctx.ModuleForTests("foo", "android_common")
- // Check cert signing lineage flag.
signedApk := variant.Output("signed/foo.apk")
+ // Check certificates
+ certificatesFlag := signedApk.Args["certificates"]
+ expected := "build/make/target/product/security/platform.x509.pem " +
+ "build/make/target/product/security/platform.pk8 " +
+ "cert/additional_cert.x509.pem cert/additional_cert.pk8"
+ if expected != certificatesFlag {
+ t.Errorf("Incorrect certificates flags, expected: %q, got: %q", expected, certificatesFlag)
+ }
+ // Check cert signing lineage flag.
+ signingFlag := signedApk.Args["flags"]
+ expected = "--lineage lineage.bin"
+ if expected != signingFlag {
+ t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+ }
+}
+
+func TestAndroidAppImport_SigningLineageFilegroup(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ certificate: "platform",
+ lineage: ":lineage_bin",
+ }
+
+ filegroup {
+ name: "lineage_bin",
+ srcs: ["lineage.bin"],
+ }
+ `)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+
+ signedApk := variant.Output("signed/foo.apk")
+ // Check cert signing lineage flag.
signingFlag := signedApk.Args["flags"]
expected := "--lineage lineage.bin"
if expected != signingFlag {
@@ -182,57 +222,58 @@
name: "no preferred",
aaptPreferredConfig: nil,
aaptPrebuiltDPI: []string{},
- expected: "prebuilts/apk/app.apk",
+ expected: "verify_uses_libraries/apk/app.apk",
},
{
name: "AAPTPreferredConfig matches",
aaptPreferredConfig: proptools.StringPtr("xhdpi"),
aaptPrebuiltDPI: []string{"xxhdpi", "ldpi"},
- expected: "prebuilts/apk/app_xhdpi.apk",
+ expected: "verify_uses_libraries/apk/app_xhdpi.apk",
},
{
name: "AAPTPrebuiltDPI matches",
aaptPreferredConfig: proptools.StringPtr("mdpi"),
aaptPrebuiltDPI: []string{"xxhdpi", "xhdpi"},
- expected: "prebuilts/apk/app_xxhdpi.apk",
+ expected: "verify_uses_libraries/apk/app_xxhdpi.apk",
},
{
name: "non-first AAPTPrebuiltDPI matches",
aaptPreferredConfig: proptools.StringPtr("mdpi"),
aaptPrebuiltDPI: []string{"ldpi", "xhdpi"},
- expected: "prebuilts/apk/app_xhdpi.apk",
+ expected: "verify_uses_libraries/apk/app_xhdpi.apk",
},
{
name: "no matches",
aaptPreferredConfig: proptools.StringPtr("mdpi"),
aaptPrebuiltDPI: []string{"ldpi", "xxxhdpi"},
- expected: "prebuilts/apk/app.apk",
+ expected: "verify_uses_libraries/apk/app.apk",
},
}
jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
for _, test := range testCases {
- config := testAppConfig(nil, bp, nil)
- config.TestProductVariables.AAPTPreferredConfig = test.aaptPreferredConfig
- config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
- ctx := testContext(config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.AAPTPreferredConfig = test.aaptPreferredConfig
+ variables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
+ }),
+ ).RunTestWithBp(t, bp)
- run(t, ctx, config)
-
- variant := ctx.ModuleForTests("foo", "android_common")
+ variant := result.ModuleForTests("foo", "android_common")
jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
if len(matches) != 2 {
t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
}
- if test.expected != matches[1] {
+ if strings.HasSuffix(matches[1], test.expected) {
t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
}
}
}
func TestAndroidAppImport_Filename(t *testing.T) {
- ctx, config := testJava(t, `
+ ctx, _ := testJava(t, `
android_app_import {
name: "foo",
apk: "prebuilts/apk/app.apk",
@@ -269,8 +310,7 @@
a := variant.Module().(*AndroidAppImport)
expectedValues := []string{test.expected}
- actualValues := android.AndroidMkEntriesForTest(
- t, config, "", a)[0].EntryMap["LOCAL_INSTALLED_MODULE_STEM"]
+ actualValues := android.AndroidMkEntriesForTest(t, ctx, a)[0].EntryMap["LOCAL_INSTALLED_MODULE_STEM"]
if !reflect.DeepEqual(actualValues, expectedValues) {
t.Errorf("Incorrect LOCAL_INSTALLED_MODULE_STEM value '%s', expected '%s'",
actualValues, expectedValues)
@@ -302,7 +342,7 @@
},
}
`,
- expected: "prebuilts/apk/app_arm64.apk",
+ expected: "verify_uses_libraries/apk/app_arm64.apk",
},
{
name: "no matching arch",
@@ -321,7 +361,7 @@
},
}
`,
- expected: "prebuilts/apk/app.apk",
+ expected: "verify_uses_libraries/apk/app.apk",
},
{
name: "no matching arch without default",
@@ -359,7 +399,7 @@
if len(matches) != 2 {
t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
}
- if test.expected != matches[1] {
+ if strings.HasSuffix(matches[1], test.expected) {
t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
}
}
@@ -393,8 +433,68 @@
}
}
+func TestAndroidAppImport_frameworkRes(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_app_import {
+ name: "framework-res",
+ certificate: "platform",
+ apk: "package-res.apk",
+ prefer: true,
+ export_package_resources: true,
+ // Disable dexpreopt and verify_uses_libraries check as the app
+ // contains no Java code to be dexpreopted.
+ enforce_uses_libs: false,
+ dex_preopt: {
+ enabled: false,
+ },
+ }
+ `)
+
+ mod := ctx.ModuleForTests("prebuilt_framework-res", "android_common").Module()
+ a := mod.(*AndroidAppImport)
+
+ if !a.preprocessed {
+ t.Errorf("prebuilt framework-res is not preprocessed")
+ }
+
+ expectedInstallPath := "out/soong/target/product/test_device/system/framework/framework-res.apk"
+
+ android.AssertPathRelativeToTopEquals(t, "prebuilt framework-res install location", expectedInstallPath, a.dexpreopter.installPath)
+
+ entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
+
+ expectedPath := "."
+ // From apk property above, in the root of the source tree.
+ expectedPrebuiltModuleFile := "package-res.apk"
+ // Verify that the apk is preprocessed: The export package is the same
+ // as the prebuilt.
+ expectedSoongResourceExportPackage := expectedPrebuiltModuleFile
+
+ actualPath := entries.EntryMap["LOCAL_PATH"]
+ actualPrebuiltModuleFile := entries.EntryMap["LOCAL_PREBUILT_MODULE_FILE"]
+ actualSoongResourceExportPackage := entries.EntryMap["LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE"]
+
+ if len(actualPath) != 1 {
+ t.Errorf("LOCAL_PATH incorrect len %d", len(actualPath))
+ } else if actualPath[0] != expectedPath {
+ t.Errorf("LOCAL_PATH mismatch, actual: %s, expected: %s", actualPath[0], expectedPath)
+ }
+
+ if len(actualPrebuiltModuleFile) != 1 {
+ t.Errorf("LOCAL_PREBUILT_MODULE_FILE incorrect len %d", len(actualPrebuiltModuleFile))
+ } else if actualPrebuiltModuleFile[0] != expectedPrebuiltModuleFile {
+ t.Errorf("LOCAL_PREBUILT_MODULE_FILE mismatch, actual: %s, expected: %s", actualPrebuiltModuleFile[0], expectedPrebuiltModuleFile)
+ }
+
+ if len(actualSoongResourceExportPackage) != 1 {
+ t.Errorf("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE incorrect len %d", len(actualSoongResourceExportPackage))
+ } else if actualSoongResourceExportPackage[0] != expectedSoongResourceExportPackage {
+ t.Errorf("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE mismatch, actual: %s, expected: %s", actualSoongResourceExportPackage[0], expectedSoongResourceExportPackage)
+ }
+}
+
func TestAndroidTestImport(t *testing.T) {
- ctx, config := testJava(t, `
+ ctx, _ := testJava(t, `
android_test_import {
name: "foo",
apk: "prebuilts/apk/app.apk",
@@ -408,7 +508,7 @@
test := ctx.ModuleForTests("foo", "android_common").Module().(*AndroidTestImport)
// Check android mks.
- entries := android.AndroidMkEntriesForTest(t, config, "", test)[0]
+ entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
expected := []string{"tests"}
actual := entries.EntryMap["LOCAL_MODULE_TAGS"]
if !reflect.DeepEqual(expected, actual) {
diff --git a/java/app_set_test.go b/java/app_set_test.go
index d31900d..adaf71b 100644
--- a/java/app_set_test.go
+++ b/java/app_set_test.go
@@ -15,6 +15,7 @@
package java
import (
+ "fmt"
"reflect"
"testing"
@@ -22,7 +23,7 @@
)
func TestAndroidAppSet(t *testing.T) {
- ctx, config := testJava(t, `
+ ctx, _ := testJava(t, `
android_app_set {
name: "foo",
set: "prebuilts/apks/app.apks",
@@ -40,7 +41,7 @@
if s := params.Args["partition"]; s != "system" {
t.Errorf("wrong partition value: '%s', expected 'system'", s)
}
- mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
+ mkEntries := android.AndroidMkEntriesForTest(t, ctx, module.Module())[0]
actualInstallFile := mkEntries.EntryMap["LOCAL_APK_SET_INSTALL_FILE"]
expectedInstallFile := []string{"foo.apk"}
if !reflect.DeepEqual(actualInstallFile, expectedInstallFile) {
@@ -96,20 +97,24 @@
}
for _, test := range testCases {
- config := testAppConfig(nil, bp, nil)
- config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
- config.TestProductVariables.Platform_sdk_version = &test.sdkVersion
- config.Targets[android.Android] = test.targets
- ctx := testContext(config)
- run(t, ctx, config)
+ ctx := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
+ variables.Platform_sdk_version = &test.sdkVersion
+ }),
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.Targets[android.Android] = test.targets
+ }),
+ ).RunTestWithBp(t, bp)
+
module := ctx.ModuleForTests("foo", "android_common")
const packedSplitApks = "foo.zip"
params := module.Output(packedSplitApks)
for k, v := range test.expected {
- if actual := params.Args[k]; actual != v {
- t.Errorf("%s: bad build arg value for '%s': '%s', expected '%s'",
- test.name, k, actual, v)
- }
+ t.Run(test.name, func(t *testing.T) {
+ android.AssertStringEquals(t, fmt.Sprintf("arg value for `%s`", k), v, params.Args[k])
+ })
}
}
}
diff --git a/java/app_test.go b/java/app_test.go
index b1abe3d..a99ac62 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -26,72 +26,64 @@
"android/soong/android"
"android/soong/cc"
+ "android/soong/dexpreopt"
+ "android/soong/genrule"
)
-var (
- resourceFiles = []string{
+// testApp runs tests using the prepareForJavaTest
+//
+// See testJava for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
+func testApp(t *testing.T, bp string) *android.TestContext {
+ t.Helper()
+ result := prepareForJavaTest.RunTestWithBp(t, bp)
+ return result.TestContext
+}
+
+func TestApp(t *testing.T) {
+ resourceFiles := []string{
"res/layout/layout.xml",
"res/values/strings.xml",
"res/values-en-rUS/strings.xml",
}
- compiledResourceFiles = []string{
+ compiledResourceFiles := []string{
"aapt2/res/layout_layout.xml.flat",
"aapt2/res/values_strings.arsc.flat",
"aapt2/res/values-en-rUS_strings.arsc.flat",
}
-)
-func testAppConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
- appFS := map[string][]byte{}
- for k, v := range fs {
- appFS[k] = v
- }
-
- for _, file := range resourceFiles {
- appFS[file] = nil
- }
-
- return testConfig(env, bp, appFS)
-}
-
-func testApp(t *testing.T, bp string) *android.TestContext {
- config := testAppConfig(nil, bp, nil)
-
- ctx := testContext(config)
-
- run(t, ctx, config)
-
- return ctx
-}
-
-func TestApp(t *testing.T) {
for _, moduleType := range []string{"android_app", "android_library"} {
t.Run(moduleType, func(t *testing.T) {
- ctx := testApp(t, moduleType+` {
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ android.FixtureModifyMockFS(func(fs android.MockFS) {
+ for _, file := range resourceFiles {
+ fs[file] = nil
+ }
+ }),
+ ).RunTestWithBp(t, moduleType+` {
name: "foo",
srcs: ["a.java"],
sdk_version: "current"
}
`)
- foo := ctx.ModuleForTests("foo", "android_common")
+ foo := result.ModuleForTests("foo", "android_common")
var expectedLinkImplicits []string
manifestFixer := foo.Output("manifest_fixer/AndroidManifest.xml")
expectedLinkImplicits = append(expectedLinkImplicits, manifestFixer.Output.String())
- frameworkRes := ctx.ModuleForTests("framework-res", "android_common")
+ frameworkRes := result.ModuleForTests("framework-res", "android_common")
expectedLinkImplicits = append(expectedLinkImplicits,
frameworkRes.Output("package-res.apk").Output.String())
// Test the mapping from input files to compiled output file names
compile := foo.Output(compiledResourceFiles[0])
- if !reflect.DeepEqual(resourceFiles, compile.Inputs.Strings()) {
- t.Errorf("expected aapt2 compile inputs expected:\n %#v\n got:\n %#v",
- resourceFiles, compile.Inputs.Strings())
- }
+ android.AssertDeepEquals(t, "aapt2 compile inputs", resourceFiles, compile.Inputs.Strings())
compiledResourceOutputs := compile.Outputs.Strings()
sort.Strings(compiledResourceOutputs)
@@ -102,11 +94,8 @@
expectedLinkImplicits = append(expectedLinkImplicits, list.Output.String())
// Check that the link rule uses
- res := ctx.ModuleForTests("foo", "android_common").Output("package-res.apk")
- if !reflect.DeepEqual(expectedLinkImplicits, res.Implicits.Strings()) {
- t.Errorf("expected aapt2 link implicits expected:\n %#v\n got:\n %#v",
- expectedLinkImplicits, res.Implicits.Strings())
- }
+ res := result.ModuleForTests("foo", "android_common").Output("package-res.apk")
+ android.AssertDeepEquals(t, "aapt2 link implicits", expectedLinkImplicits, res.Implicits.Strings())
})
}
}
@@ -123,9 +112,9 @@
foo := ctx.ModuleForTests("foo", "android_common")
expectedOutputs := []string{
- filepath.Join(buildDir, ".intermediates/foo/android_common/foo.apk"),
- filepath.Join(buildDir, ".intermediates/foo/android_common/foo_v4.apk"),
- filepath.Join(buildDir, ".intermediates/foo/android_common/foo_v7_hdpi.apk"),
+ "out/soong/.intermediates/foo/android_common/foo.apk",
+ "out/soong/.intermediates/foo/android_common/foo_v4.apk",
+ "out/soong/.intermediates/foo/android_common/foo_v7_hdpi.apk",
}
for _, expectedOutput := range expectedOutputs {
foo.Output(expectedOutput)
@@ -135,9 +124,7 @@
if err != nil {
t.Fatal(err)
}
- if g, w := outputFiles.Strings(), expectedOutputs; !reflect.DeepEqual(g, w) {
- t.Errorf(`want OutputFiles("") = %q, got %q`, w, g)
- }
+ android.AssertPathsRelativeToTopEquals(t, `OutputFiles("")`, expectedOutputs, outputFiles)
}
func TestPlatformAPIs(t *testing.T) {
@@ -375,11 +362,15 @@
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
- if test.expectedError == "" {
- testJava(t, test.bp)
- } else {
- testJavaError(t, test.expectedError, test.bp)
+ errorHandler := android.FixtureExpectsNoErrors
+ if test.expectedError != "" {
+ errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError)
}
+ android.GroupFixturePreparers(
+ prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{
+ "29": {"foo"},
+ })).
+ ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, test.bp)
})
}
}
@@ -447,7 +438,7 @@
"prebuilts/ndk/current/platforms/android-29/arch-arm/usr/lib/crtend_so.o": nil,
}
- ctx, _ := testJavaWithConfig(t, testConfig(nil, bp, fs))
+ ctx, _ := testJavaWithFS(t, bp, fs)
inputs := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").Description("link").Implicits
var crtbeginFound, crtendFound bool
@@ -548,7 +539,7 @@
},
}
- fs := map[string][]byte{
+ fs := android.MockFS{
"res/res/values/strings.xml": nil,
}
@@ -562,11 +553,13 @@
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
- config := testConfig(nil, fmt.Sprintf(bp, testCase.prop), fs)
- ctx := testContext(config)
- run(t, ctx, config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithOverlayBuildComponents,
+ fs.AddToFixture(),
+ ).RunTestWithBp(t, fmt.Sprintf(bp, testCase.prop))
- module := ctx.ModuleForTests("foo", "android_common")
+ module := result.ModuleForTests("foo", "android_common")
resourceList := module.MaybeOutput("aapt2/res.list")
var resources []string
@@ -576,10 +569,7 @@
}
}
- if !reflect.DeepEqual(resources, testCase.resources) {
- t.Errorf("expected resource files %q, got %q",
- testCase.resources, resources)
- }
+ android.AssertDeepEquals(t, "resource files", testCase.resources, resources)
})
}
}
@@ -625,9 +615,9 @@
name: "foo",
// lib1 has its own asset. lib3 doesn't have any, but provides lib4's transitively.
assetPackages: []string{
- buildDir + "/.intermediates/foo/android_common/aapt2/package-res.apk",
- buildDir + "/.intermediates/lib1/android_common/assets.zip",
- buildDir + "/.intermediates/lib3/android_common/assets.zip",
+ "out/soong/.intermediates/foo/android_common/aapt2/package-res.apk",
+ "out/soong/.intermediates/lib1/android_common/assets.zip",
+ "out/soong/.intermediates/lib3/android_common/assets.zip",
},
},
{
@@ -640,8 +630,8 @@
{
name: "lib3",
assetPackages: []string{
- buildDir + "/.intermediates/lib3/android_common/aapt2/package-res.apk",
- buildDir + "/.intermediates/lib4/android_common/assets.zip",
+ "out/soong/.intermediates/lib3/android_common/aapt2/package-res.apk",
+ "out/soong/.intermediates/lib4/android_common/assets.zip",
},
},
{
@@ -662,29 +652,68 @@
} else {
aapt2link = m.Output("package-res.apk")
}
+ aapt2link = aapt2link
aapt2Flags := aapt2link.Args["flags"]
if test.assetFlag != "" {
- if !strings.Contains(aapt2Flags, test.assetFlag) {
- t.Errorf("Can't find asset flag %q in aapt2 link flags %q", test.assetFlag, aapt2Flags)
- }
+ android.AssertStringDoesContain(t, "asset flag", aapt2Flags, test.assetFlag)
} else {
- if strings.Contains(aapt2Flags, " -A ") {
- t.Errorf("aapt2 link flags %q contain unexpected asset flag", aapt2Flags)
- }
+ android.AssertStringDoesNotContain(t, "aapt2 link flags", aapt2Flags, " -A ")
}
// Check asset merge rule.
if len(test.assetPackages) > 0 {
mergeAssets := m.Output("package-res.apk")
- if !reflect.DeepEqual(test.assetPackages, mergeAssets.Inputs.Strings()) {
- t.Errorf("Unexpected mergeAssets inputs: %v, expected: %v",
- mergeAssets.Inputs.Strings(), test.assetPackages)
- }
+ android.AssertPathsRelativeToTopEquals(t, "mergeAssets inputs", test.assetPackages, mergeAssets.Inputs)
}
})
}
}
+func TestAppJavaResources(t *testing.T) {
+ bp := `
+ android_app {
+ name: "foo",
+ sdk_version: "current",
+ java_resources: ["resources/a"],
+ srcs: ["a.java"],
+ }
+
+ android_app {
+ name: "bar",
+ sdk_version: "current",
+ java_resources: ["resources/a"],
+ }
+ `
+
+ ctx := testApp(t, bp)
+
+ foo := ctx.ModuleForTests("foo", "android_common")
+ fooResources := foo.Output("res/foo.jar")
+ fooDexJar := foo.Output("dex-withres/foo.jar")
+ fooDexJarAligned := foo.Output("dex-withres-aligned/foo.jar")
+ fooApk := foo.Rule("combineApk")
+
+ if g, w := fooDexJar.Inputs.Strings(), fooResources.Output.String(); !android.InList(w, g) {
+ t.Errorf("expected resource jar %q in foo dex jar inputs %q", w, g)
+ }
+
+ if g, w := fooDexJarAligned.Input.String(), fooDexJar.Output.String(); g != w {
+ t.Errorf("expected dex jar %q in foo aligned dex jar inputs %q", w, g)
+ }
+
+ if g, w := fooApk.Inputs.Strings(), fooDexJarAligned.Output.String(); !android.InList(w, g) {
+ t.Errorf("expected aligned dex jar %q in foo apk inputs %q", w, g)
+ }
+
+ bar := ctx.ModuleForTests("bar", "android_common")
+ barResources := bar.Output("res/bar.jar")
+ barApk := bar.Rule("combineApk")
+
+ if g, w := barApk.Inputs.Strings(), barResources.Output.String(); !android.InList(w, g) {
+ t.Errorf("expected resources jar %q in bar apk inputs %q", w, g)
+ }
+}
+
func TestAndroidResources(t *testing.T) {
testCases := []struct {
name string
@@ -706,9 +735,9 @@
},
overlayFiles: map[string][]string{
"foo": {
- buildDir + "/.intermediates/lib2/android_common/package-res.apk",
- buildDir + "/.intermediates/lib/android_common/package-res.apk",
- buildDir + "/.intermediates/lib3/android_common/package-res.apk",
+ "out/soong/.intermediates/lib2/android_common/package-res.apk",
+ "out/soong/.intermediates/lib/android_common/package-res.apk",
+ "out/soong/.intermediates/lib3/android_common/package-res.apk",
"foo/res/res/values/strings.xml",
"device/vendor/blah/static_overlay/foo/res/values/strings.xml",
"device/vendor/blah/overlay/foo/res/values/strings.xml",
@@ -719,7 +748,7 @@
"device/vendor/blah/overlay/bar/res/values/strings.xml",
},
"lib": {
- buildDir + "/.intermediates/lib2/android_common/package-res.apk",
+ "out/soong/.intermediates/lib2/android_common/package-res.apk",
"lib/res/res/values/strings.xml",
"device/vendor/blah/overlay/lib/res/values/strings.xml",
},
@@ -741,9 +770,9 @@
},
overlayFiles: map[string][]string{
"foo": {
- buildDir + "/.intermediates/lib2/android_common/package-res.apk",
- buildDir + "/.intermediates/lib/android_common/package-res.apk",
- buildDir + "/.intermediates/lib3/android_common/package-res.apk",
+ "out/soong/.intermediates/lib2/android_common/package-res.apk",
+ "out/soong/.intermediates/lib/android_common/package-res.apk",
+ "out/soong/.intermediates/lib3/android_common/package-res.apk",
"foo/res/res/values/strings.xml",
"device/vendor/blah/static_overlay/foo/res/values/strings.xml",
},
@@ -752,7 +781,7 @@
"device/vendor/blah/overlay/bar/res/values/strings.xml",
},
"lib": {
- buildDir + "/.intermediates/lib2/android_common/package-res.apk",
+ "out/soong/.intermediates/lib2/android_common/package-res.apk",
"lib/res/res/values/strings.xml",
},
},
@@ -783,15 +812,15 @@
},
overlayFiles: map[string][]string{
"foo": {
- buildDir + "/.intermediates/lib2/android_common/package-res.apk",
- buildDir + "/.intermediates/lib/android_common/package-res.apk",
- buildDir + "/.intermediates/lib3/android_common/package-res.apk",
+ "out/soong/.intermediates/lib2/android_common/package-res.apk",
+ "out/soong/.intermediates/lib/android_common/package-res.apk",
+ "out/soong/.intermediates/lib3/android_common/package-res.apk",
"foo/res/res/values/strings.xml",
"device/vendor/blah/static_overlay/foo/res/values/strings.xml",
},
"bar": {"device/vendor/blah/static_overlay/bar/res/values/strings.xml"},
"lib": {
- buildDir + "/.intermediates/lib2/android_common/package-res.apk",
+ "out/soong/.intermediates/lib2/android_common/package-res.apk",
"lib/res/res/values/strings.xml",
},
},
@@ -818,7 +847,7 @@
"product/vendor/blah/overlay",
}
- fs := map[string][]byte{
+ fs := android.MockFS{
"foo/res/res/values/strings.xml": nil,
"bar/res/res/values/strings.xml": nil,
"lib/res/res/values/strings.xml": nil,
@@ -869,18 +898,21 @@
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
- config := testAppConfig(nil, bp, fs)
- config.TestProductVariables.DeviceResourceOverlays = deviceResourceOverlays
- config.TestProductVariables.ProductResourceOverlays = productResourceOverlays
- if testCase.enforceRROTargets != nil {
- config.TestProductVariables.EnforceRROTargets = testCase.enforceRROTargets
- }
- if testCase.enforceRROExcludedOverlays != nil {
- config.TestProductVariables.EnforceRROExcludedOverlays = testCase.enforceRROExcludedOverlays
- }
-
- ctx := testContext(config)
- run(t, ctx, config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithOverlayBuildComponents,
+ fs.AddToFixture(),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.DeviceResourceOverlays = deviceResourceOverlays
+ variables.ProductResourceOverlays = productResourceOverlays
+ if testCase.enforceRROTargets != nil {
+ variables.EnforceRROTargets = testCase.enforceRROTargets
+ }
+ if testCase.enforceRROExcludedOverlays != nil {
+ variables.EnforceRROExcludedOverlays = testCase.enforceRROExcludedOverlays
+ }
+ }),
+ ).RunTestWithBp(t, bp)
resourceListToFiles := func(module android.TestingModule, list []string) (files []string) {
for _, o := range list {
@@ -898,14 +930,14 @@
}
getResources := func(moduleName string) (resourceFiles, overlayFiles, rroDirs []string) {
- module := ctx.ModuleForTests(moduleName, "android_common")
+ module := result.ModuleForTests(moduleName, "android_common")
resourceList := module.MaybeOutput("aapt2/res.list")
if resourceList.Rule != nil {
- resourceFiles = resourceListToFiles(module, resourceList.Inputs.Strings())
+ resourceFiles = resourceListToFiles(module, android.PathsRelativeToTop(resourceList.Inputs))
}
overlayList := module.MaybeOutput("aapt2/overlay.list")
if overlayList.Rule != nil {
- overlayFiles = resourceListToFiles(module, overlayList.Inputs.Strings())
+ overlayFiles = resourceListToFiles(module, android.PathsRelativeToTop(overlayList.Inputs))
}
for _, d := range module.Module().(AndroidLibraryDependency).ExportedRRODirs() {
@@ -917,7 +949,7 @@
} else {
t.Fatalf("Unexpected overlayType %d", d.overlayType)
}
- rroDirs = append(rroDirs, prefix+d.path.String())
+ rroDirs = append(rroDirs, prefix+android.PathRelativeToTop(d.path))
}
return resourceFiles, overlayFiles, rroDirs
@@ -944,12 +976,8 @@
}
}
-func checkSdkVersion(t *testing.T, config android.Config, expectedSdkVersion string) {
- ctx := testContext(config)
-
- run(t, ctx, config)
-
- foo := ctx.ModuleForTests("foo", "android_common")
+func checkSdkVersion(t *testing.T, result *android.TestResult, expectedSdkVersion string) {
+ foo := result.ModuleForTests("foo", "android_common")
link := foo.Output("package-res.apk")
linkFlags := strings.Split(link.Args["flags"], " ")
min := android.IndexList("--min-sdk-version", linkFlags)
@@ -962,15 +990,9 @@
gotMinSdkVersion := linkFlags[min+1]
gotTargetSdkVersion := linkFlags[target+1]
- if gotMinSdkVersion != expectedSdkVersion {
- t.Errorf("incorrect --min-sdk-version, expected %q got %q",
- expectedSdkVersion, gotMinSdkVersion)
- }
+ android.AssertStringEquals(t, "incorrect --min-sdk-version", expectedSdkVersion, gotMinSdkVersion)
- if gotTargetSdkVersion != expectedSdkVersion {
- t.Errorf("incorrect --target-sdk-version, expected %q got %q",
- expectedSdkVersion, gotTargetSdkVersion)
- }
+ android.AssertStringEquals(t, "incorrect --target-sdk-version", expectedSdkVersion, gotTargetSdkVersion)
}
func TestAppSdkVersion(t *testing.T) {
@@ -1043,13 +1065,20 @@
%s
}`, moduleType, test.sdkVersion, platformApiProp)
- config := testAppConfig(nil, bp, nil)
- config.TestProductVariables.Platform_sdk_version = &test.platformSdkInt
- config.TestProductVariables.Platform_sdk_codename = &test.platformSdkCodename
- config.TestProductVariables.Platform_version_active_codenames = test.activeCodenames
- config.TestProductVariables.Platform_sdk_final = &test.platformSdkFinal
- checkSdkVersion(t, config, test.expectedMinSdkVersion)
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Platform_sdk_version = &test.platformSdkInt
+ variables.Platform_sdk_codename = &test.platformSdkCodename
+ variables.Platform_version_active_codenames = test.activeCodenames
+ variables.Platform_sdk_final = &test.platformSdkFinal
+ }),
+ FixtureWithPrebuiltApis(map[string][]string{
+ "14": {"foo"},
+ }),
+ ).RunTestWithBp(t, bp)
+ checkSdkVersion(t, result, test.expectedMinSdkVersion)
})
}
}
@@ -1105,13 +1134,23 @@
vendor: true,
}`, moduleType, sdkKind, test.sdkVersion)
- config := testAppConfig(nil, bp, nil)
- config.TestProductVariables.Platform_sdk_version = &test.platformSdkInt
- config.TestProductVariables.Platform_sdk_codename = &test.platformSdkCodename
- config.TestProductVariables.Platform_sdk_final = &test.platformSdkFinal
- config.TestProductVariables.DeviceCurrentApiLevelForVendorModules = &test.deviceCurrentApiLevelForVendorModules
- config.TestProductVariables.DeviceSystemSdkVersions = []string{"28", "29"}
- checkSdkVersion(t, config, test.expectedMinSdkVersion)
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Platform_sdk_version = &test.platformSdkInt
+ variables.Platform_sdk_codename = &test.platformSdkCodename
+ variables.Platform_sdk_final = &test.platformSdkFinal
+ variables.DeviceCurrentApiLevelForVendorModules = &test.deviceCurrentApiLevelForVendorModules
+ variables.DeviceSystemSdkVersions = []string{"28", "29"}
+ }),
+ FixtureWithPrebuiltApis(map[string][]string{
+ "28": {"foo"},
+ "29": {"foo"},
+ "current": {"foo"},
+ }),
+ ).RunTestWithBp(t, bp)
+
+ checkSdkVersion(t, result, test.expectedMinSdkVersion)
})
}
}
@@ -1220,13 +1259,19 @@
}
`
- config := testAppConfig(nil, bp, nil)
- config.TestProductVariables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
+ errorHandler := android.FixtureExpectsNoErrors
if enforce {
- testJavaErrorWithConfig(t, "sdk_version must have a value when the module is located at vendor or product", config)
- } else {
- testJavaWithConfig(t, config)
+ errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern("sdk_version must have a value when the module is located at vendor or product")
}
+
+ android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
+ }),
+ ).
+ ExtendWithErrorHandler(errorHandler).
+ RunTestWithBp(t, bp)
}
}
@@ -1531,29 +1576,52 @@
expectedLineage: "--lineage lineage.bin",
expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8",
},
+ {
+ name: "lineage from filegroup",
+ bp: `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ certificate: ":new_certificate",
+ lineage: ":lineage_bin",
+ sdk_version: "current",
+ }
+
+ android_app_certificate {
+ name: "new_certificate",
+ certificate: "cert/new_cert",
+ }
+
+ filegroup {
+ name: "lineage_bin",
+ srcs: ["lineage.bin"],
+ }
+ `,
+ certificateOverride: "",
+ expectedLineage: "--lineage lineage.bin",
+ expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8",
+ },
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
- config := testAppConfig(nil, test.bp, nil)
- if test.certificateOverride != "" {
- config.TestProductVariables.CertificateOverrides = []string{test.certificateOverride}
- }
- ctx := testContext(config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ if test.certificateOverride != "" {
+ variables.CertificateOverrides = []string{test.certificateOverride}
+ }
+ }),
+ ).RunTestWithBp(t, test.bp)
- run(t, ctx, config)
- foo := ctx.ModuleForTests("foo", "android_common")
+ foo := result.ModuleForTests("foo", "android_common")
signapk := foo.Output("foo.apk")
signCertificateFlags := signapk.Args["certificates"]
- if test.expectedCertificate != signCertificateFlags {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", test.expectedCertificate, signCertificateFlags)
- }
+ android.AssertStringEquals(t, "certificates flags", test.expectedCertificate, signCertificateFlags)
signFlags := signapk.Args["flags"]
- if test.expectedLineage != signFlags {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", test.expectedLineage, signFlags)
- }
+ android.AssertStringEquals(t, "signing flags", test.expectedLineage, signFlags)
})
}
}
@@ -1603,17 +1671,15 @@
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
- config := testAppConfig(nil, test.bp, nil)
- ctx := testContext(config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ ).RunTestWithBp(t, test.bp)
- run(t, ctx, config)
- foo := ctx.ModuleForTests("foo", "android_common")
+ foo := result.ModuleForTests("foo", "android_common")
signapk := foo.Output("foo.apk")
signFlags := signapk.Args["flags"]
- if test.expected != signFlags {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", test.expected, signFlags)
- }
+ android.AssertStringEquals(t, "signing flags", test.expected, signFlags)
})
}
}
@@ -1636,8 +1702,8 @@
`,
packageNameOverride: "",
expected: []string{
- buildDir + "/.intermediates/foo/android_common/foo.apk",
- buildDir + "/target/product/test_device/system/app/foo/foo.apk",
+ "out/soong/.intermediates/foo/android_common/foo.apk",
+ "out/soong/target/product/test_device/system/app/foo/foo.apk",
},
},
{
@@ -1652,27 +1718,31 @@
packageNameOverride: "foo:bar",
expected: []string{
// The package apk should be still be the original name for test dependencies.
- buildDir + "/.intermediates/foo/android_common/bar.apk",
- buildDir + "/target/product/test_device/system/app/bar/bar.apk",
+ "out/soong/.intermediates/foo/android_common/bar.apk",
+ "out/soong/target/product/test_device/system/app/bar/bar.apk",
},
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
- config := testAppConfig(nil, test.bp, nil)
- if test.packageNameOverride != "" {
- config.TestProductVariables.PackageNameOverrides = []string{test.packageNameOverride}
- }
- ctx := testContext(config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ if test.packageNameOverride != "" {
+ variables.PackageNameOverrides = []string{test.packageNameOverride}
+ }
+ }),
+ ).RunTestWithBp(t, test.bp)
- run(t, ctx, config)
- foo := ctx.ModuleForTests("foo", "android_common")
+ foo := result.ModuleForTests("foo", "android_common")
+
+ outSoongDir := result.Config.BuildDir()
outputs := foo.AllOutputs()
outputMap := make(map[string]bool)
for _, o := range outputs {
- outputMap[o] = true
+ outputMap[android.StringPathRelativeToTop(outSoongDir, o)] = true
}
for _, e := range test.expected {
if _, exist := outputMap[e]; !exist {
@@ -1697,13 +1767,15 @@
sdk_version: "current",
}
`
- config := testAppConfig(nil, bp, nil)
- config.TestProductVariables.ManifestPackageNameOverrides = []string{"foo:org.dandroid.bp"}
- ctx := testContext(config)
- run(t, ctx, config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.ManifestPackageNameOverrides = []string{"foo:org.dandroid.bp"}
+ }),
+ ).RunTestWithBp(t, bp)
- bar := ctx.ModuleForTests("bar", "android_common")
+ bar := result.ModuleForTests("bar", "android_common")
res := bar.Output("package-res.apk")
aapt2Flags := res.Args["flags"]
e := "--rename-instrumentation-target-package org.dandroid.bp"
@@ -1713,7 +1785,8 @@
}
func TestOverrideAndroidApp(t *testing.T) {
- ctx, _ := testJava(t, `
+ result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
+ t, `
android_app {
name: "foo",
srcs: ["a.java"],
@@ -1788,7 +1861,7 @@
name: "foo",
moduleName: "foo",
variantName: "android_common",
- apkPath: "/target/product/test_device/system/app/foo/foo.apk",
+ apkPath: "out/soong/target/product/test_device/system/app/foo/foo.apk",
certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
lineageFlag: "",
overrides: []string{"qux"},
@@ -1800,7 +1873,7 @@
name: "foo",
moduleName: "bar",
variantName: "android_common_bar",
- apkPath: "/target/product/test_device/system/app/bar/bar.apk",
+ apkPath: "out/soong/target/product/test_device/system/app/bar/bar.apk",
certFlag: "cert/new_cert.x509.pem cert/new_cert.pk8",
lineageFlag: "--lineage lineage.bin",
overrides: []string{"qux", "foo"},
@@ -1812,7 +1885,7 @@
name: "foo",
moduleName: "baz",
variantName: "android_common_baz",
- apkPath: "/target/product/test_device/system/app/baz/baz.apk",
+ apkPath: "out/soong/target/product/test_device/system/app/baz/baz.apk",
certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
lineageFlag: "",
overrides: []string{"qux", "foo"},
@@ -1824,7 +1897,7 @@
name: "foo",
moduleName: "baz_no_rename_resources",
variantName: "android_common_baz_no_rename_resources",
- apkPath: "/target/product/test_device/system/app/baz_no_rename_resources/baz_no_rename_resources.apk",
+ apkPath: "out/soong/target/product/test_device/system/app/baz_no_rename_resources/baz_no_rename_resources.apk",
certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
lineageFlag: "",
overrides: []string{"qux", "foo"},
@@ -1836,7 +1909,7 @@
name: "foo_no_rename_resources",
moduleName: "baz_base_no_rename_resources",
variantName: "android_common_baz_base_no_rename_resources",
- apkPath: "/target/product/test_device/system/app/baz_base_no_rename_resources/baz_base_no_rename_resources.apk",
+ apkPath: "out/soong/target/product/test_device/system/app/baz_base_no_rename_resources/baz_base_no_rename_resources.apk",
certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
lineageFlag: "",
overrides: []string{"qux", "foo_no_rename_resources"},
@@ -1848,7 +1921,7 @@
name: "foo_no_rename_resources",
moduleName: "baz_override_base_rename_resources",
variantName: "android_common_baz_override_base_rename_resources",
- apkPath: "/target/product/test_device/system/app/baz_override_base_rename_resources/baz_override_base_rename_resources.apk",
+ apkPath: "out/soong/target/product/test_device/system/app/baz_override_base_rename_resources/baz_override_base_rename_resources.apk",
certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
lineageFlag: "",
overrides: []string{"qux", "foo_no_rename_resources"},
@@ -1858,48 +1931,27 @@
},
}
for _, expected := range expectedVariants {
- variant := ctx.ModuleForTests(expected.name, expected.variantName)
+ variant := result.ModuleForTests(expected.name, expected.variantName)
// Check the final apk name
- outputs := variant.AllOutputs()
- expectedApkPath := buildDir + expected.apkPath
- found := false
- for _, o := range outputs {
- if o == expectedApkPath {
- found = true
- break
- }
- }
- if !found {
- t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs)
- }
+ variant.Output(expected.apkPath)
// Check the certificate paths
signapk := variant.Output(expected.moduleName + ".apk")
certFlag := signapk.Args["certificates"]
- if expected.certFlag != certFlag {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected.certFlag, certFlag)
- }
+ android.AssertStringEquals(t, "certificates flags", expected.certFlag, certFlag)
// Check the lineage flags
lineageFlag := signapk.Args["flags"]
- if expected.lineageFlag != lineageFlag {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected.lineageFlag, lineageFlag)
- }
+ android.AssertStringEquals(t, "signing flags", expected.lineageFlag, lineageFlag)
// Check if the overrides field values are correctly aggregated.
mod := variant.Module().(*AndroidApp)
- if !reflect.DeepEqual(expected.overrides, mod.appProperties.Overrides) {
- t.Errorf("Incorrect overrides property value, expected: %q, got: %q",
- expected.overrides, mod.appProperties.Overrides)
- }
+ android.AssertDeepEquals(t, "overrides property", expected.overrides, mod.appProperties.Overrides)
// Test Overridable property: Logging_parent
logging_parent := mod.aapt.LoggingParent
- if expected.logging_parent != logging_parent {
- t.Errorf("Incorrect overrides property value for logging parent, expected: %q, got: %q",
- expected.logging_parent, logging_parent)
- }
+ android.AssertStringEquals(t, "overrides property value for logging parent", expected.logging_parent, logging_parent)
// Check the package renaming flag, if exists.
res := variant.Output("package-res.apk")
@@ -1942,14 +1994,14 @@
// Verify baz, which depends on the overridden module foo, has the correct classpath javac arg.
javac := ctx.ModuleForTests("baz", "android_common").Rule("javac")
- fooTurbine := filepath.Join(buildDir, ".intermediates", "foo", "android_common", "turbine-combined", "foo.jar")
+ fooTurbine := "out/soong/.intermediates/foo/android_common/turbine-combined/foo.jar"
if !strings.Contains(javac.Args["classpath"], fooTurbine) {
t.Errorf("baz classpath %v does not contain %q", javac.Args["classpath"], fooTurbine)
}
// Verify qux, which depends on the overriding module bar, has the correct classpath javac arg.
javac = ctx.ModuleForTests("qux", "android_common").Rule("javac")
- barTurbine := filepath.Join(buildDir, ".intermediates", "foo", "android_common_bar", "turbine-combined", "foo.jar")
+ barTurbine := "out/soong/.intermediates/foo/android_common_bar/turbine-combined/foo.jar"
if !strings.Contains(javac.Args["classpath"], barTurbine) {
t.Errorf("qux classpath %v does not contain %q", javac.Args["classpath"], barTurbine)
}
@@ -2015,18 +2067,7 @@
variant := ctx.ModuleForTests("foo_test", expected.variantName)
// Check the final apk name
- outputs := variant.AllOutputs()
- expectedApkPath := buildDir + expected.apkPath
- found := false
- for _, o := range outputs {
- if o == expectedApkPath {
- found = true
- break
- }
- }
- if !found {
- t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs)
- }
+ variant.Output("out/soong" + expected.apkPath)
// Check if the overrides field values are correctly aggregated.
mod := variant.Module().(*AndroidTest)
@@ -2037,7 +2078,7 @@
// Check if javac classpath has the correct jar file path. This checks instrumentation_for overrides.
javac := variant.Rule("javac")
- turbine := filepath.Join(buildDir, ".intermediates", "foo", expected.targetVariant, "turbine-combined", "foo.jar")
+ turbine := filepath.Join("out", "soong", ".intermediates", "foo", expected.targetVariant, "turbine-combined", "foo.jar")
if !strings.Contains(javac.Args["classpath"], turbine) {
t.Errorf("classpath %q does not contain %q", javac.Args["classpath"], turbine)
}
@@ -2093,7 +2134,7 @@
moduleName: "bar_test",
variantName: "android_common",
expectedFlags: []string{
- "--manifest " + buildDir + "/.intermediates/bar_test/android_common/manifest_fixer/AndroidManifest.xml",
+ "--manifest out/soong/.intermediates/bar_test/android_common/manifest_fixer/AndroidManifest.xml",
"--package-name com.android.bar.test",
},
},
@@ -2101,8 +2142,7 @@
moduleName: "foo_test",
variantName: "android_common_baz_test",
expectedFlags: []string{
- "--manifest " + buildDir +
- "/.intermediates/foo_test/android_common_baz_test/manifest_fixer/AndroidManifest.xml",
+ "--manifest out/soong/.intermediates/foo_test/android_common_baz_test/manifest_fixer/AndroidManifest.xml",
"--package-name com.android.baz.test",
"--test-file-name baz_test.apk",
},
@@ -2245,17 +2285,33 @@
sdk_version: "current",
}
+ // A library that has to use "provides_uses_lib", because:
+ // - it is not an SDK library
+ // - its library name is different from its module name
+ java_library {
+ name: "non-sdk-lib",
+ provides_uses_lib: "com.non.sdk.lib",
+ installable: true,
+ srcs: ["a.java"],
+ }
+
android_app {
name: "app",
srcs: ["a.java"],
- libs: ["qux", "quuz.stubs"],
+ libs: [
+ "qux",
+ "quuz.stubs"
+ ],
static_libs: [
"static-runtime-helper",
// statically linked component libraries should not pull their SDK libraries,
// so "fred" should not be added to class loader context
"fred.stubs",
],
- uses_libs: ["foo"],
+ uses_libs: [
+ "foo",
+ "non-sdk-lib"
+ ],
sdk_version: "current",
optional_uses_libs: [
"bar",
@@ -2267,7 +2323,11 @@
name: "prebuilt",
apk: "prebuilts/apk/app.apk",
certificate: "platform",
- uses_libs: ["foo", "android.test.runner"],
+ uses_libs: [
+ "foo",
+ "non-sdk-lib",
+ "android.test.runner"
+ ],
optional_uses_libs: [
"bar",
"baz",
@@ -2275,90 +2335,151 @@
}
`
- config := testAppConfig(nil, bp, nil)
- config.TestProductVariables.MissingUsesLibraries = []string{"baz"}
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("runtime-library", "foo", "quuz", "qux", "bar", "fred"),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.MissingUsesLibraries = []string{"baz"}
+ }),
+ ).RunTestWithBp(t, bp)
- ctx := testContext(config)
-
- run(t, ctx, config)
-
- app := ctx.ModuleForTests("app", "android_common")
- prebuilt := ctx.ModuleForTests("prebuilt", "android_common")
+ app := result.ModuleForTests("app", "android_common")
+ prebuilt := result.ModuleForTests("prebuilt", "android_common")
// Test that implicit dependencies on java_sdk_library instances are passed to the manifest.
- manifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
- for _, w := range []string{"qux", "quuz", "runtime-library"} {
- if !strings.Contains(manifestFixerArgs, "--uses-library "+w) {
- t.Errorf("unexpected manifest_fixer args: wanted %q in %q", w, manifestFixerArgs)
- }
- }
+ // This should not include explicit `uses_libs`/`optional_uses_libs` entries.
+ actualManifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+ expectManifestFixerArgs := `--extract-native-libs=true ` +
+ `--uses-library qux ` +
+ `--uses-library quuz ` +
+ `--uses-library foo ` + // TODO(b/132357300): "foo" should not be passed to manifest_fixer
+ `--uses-library com.non.sdk.lib ` + // TODO(b/132357300): "com.non.sdk.lib" should not be passed to manifest_fixer
+ `--uses-library bar ` + // TODO(b/132357300): "bar" should not be passed to manifest_fixer
+ `--uses-library runtime-library`
+ android.AssertStringEquals(t, "manifest_fixer args", expectManifestFixerArgs, actualManifestFixerArgs)
- // Test that all libraries are verified
- cmd := app.Rule("verify_uses_libraries").RuleParams.Command
- if w := "--uses-library foo"; !strings.Contains(cmd, w) {
- t.Errorf("wanted %q in %q", w, cmd)
- }
+ // Test that all libraries are verified (library order matters).
+ verifyCmd := app.Rule("verify_uses_libraries").RuleParams.Command
+ verifyArgs := `--uses-library foo ` +
+ `--uses-library com.non.sdk.lib ` +
+ `--uses-library qux ` +
+ `--uses-library quuz ` +
+ `--uses-library runtime-library ` +
+ `--optional-uses-library bar ` +
+ `--optional-uses-library baz `
+ android.AssertStringDoesContain(t, "verify cmd args", verifyCmd, verifyArgs)
- if w := "--optional-uses-library bar --optional-uses-library baz"; !strings.Contains(cmd, w) {
- t.Errorf("wanted %q in %q", w, cmd)
- }
-
- cmd = prebuilt.Rule("verify_uses_libraries").RuleParams.Command
-
- if w := `uses_library_names="foo android.test.runner"`; !strings.Contains(cmd, w) {
- t.Errorf("wanted %q in %q", w, cmd)
- }
-
- if w := `optional_uses_library_names="bar baz"`; !strings.Contains(cmd, w) {
- t.Errorf("wanted %q in %q", w, cmd)
- }
+ // Test that all libraries are verified for an APK (library order matters).
+ verifyApkCmd := prebuilt.Rule("verify_uses_libraries").RuleParams.Command
+ verifyApkArgs := `--uses-library foo ` +
+ `--uses-library com.non.sdk.lib ` +
+ `--uses-library android.test.runner ` +
+ `--optional-uses-library bar ` +
+ `--optional-uses-library baz `
+ android.AssertStringDoesContain(t, "verify apk cmd args", verifyApkCmd, verifyApkArgs)
// Test that all present libraries are preopted, including implicit SDK dependencies, possibly stubs
- cmd = app.Rule("dexpreopt").RuleParams.Command
+ cmd := app.Rule("dexpreopt").RuleParams.Command
w := `--target-context-for-sdk any ` +
`PCL[/system/framework/qux.jar]#` +
`PCL[/system/framework/quuz.jar]#` +
`PCL[/system/framework/foo.jar]#` +
+ `PCL[/system/framework/non-sdk-lib.jar]#` +
`PCL[/system/framework/bar.jar]#` +
`PCL[/system/framework/runtime-library.jar]`
- if !strings.Contains(cmd, w) {
- t.Errorf("wanted %q in %q", w, cmd)
- }
+ android.AssertStringDoesContain(t, "dexpreopt app cmd args", cmd, w)
// Test conditional context for target SDK version 28.
- if w := `--target-context-for-sdk 28` +
- ` PCL[/system/framework/org.apache.http.legacy.jar] `; !strings.Contains(cmd, w) {
- t.Errorf("wanted %q in %q", w, cmd)
- }
+ android.AssertStringDoesContain(t, "dexpreopt app cmd 28", cmd,
+ `--target-context-for-sdk 28`+
+ ` PCL[/system/framework/org.apache.http.legacy.jar] `)
// Test conditional context for target SDK version 29.
- if w := `--target-context-for-sdk 29` +
- ` PCL[/system/framework/android.hidl.manager-V1.0-java.jar]` +
- `#PCL[/system/framework/android.hidl.base-V1.0-java.jar] `; !strings.Contains(cmd, w) {
- t.Errorf("wanted %q in %q", w, cmd)
- }
+ android.AssertStringDoesContain(t, "dexpreopt app cmd 29", cmd,
+ `--target-context-for-sdk 29`+
+ ` PCL[/system/framework/android.hidl.manager-V1.0-java.jar]`+
+ `#PCL[/system/framework/android.hidl.base-V1.0-java.jar] `)
// Test conditional context for target SDK version 30.
// "android.test.mock" is absent because "android.test.runner" is not used.
- if w := `--target-context-for-sdk 30` +
- ` PCL[/system/framework/android.test.base.jar] `; !strings.Contains(cmd, w) {
- t.Errorf("wanted %q in %q", w, cmd)
- }
+ android.AssertStringDoesContain(t, "dexpreopt app cmd 30", cmd,
+ `--target-context-for-sdk 30`+
+ ` PCL[/system/framework/android.test.base.jar] `)
cmd = prebuilt.Rule("dexpreopt").RuleParams.Command
- if w := `--target-context-for-sdk any` +
- ` PCL[/system/framework/foo.jar]` +
- `#PCL[/system/framework/android.test.runner.jar]` +
- `#PCL[/system/framework/bar.jar] `; !strings.Contains(cmd, w) {
- t.Errorf("wanted %q in %q", w, cmd)
- }
+ android.AssertStringDoesContain(t, "dexpreopt prebuilt cmd", cmd,
+ `--target-context-for-sdk any`+
+ ` PCL[/system/framework/foo.jar]`+
+ `#PCL[/system/framework/non-sdk-lib.jar]`+
+ `#PCL[/system/framework/android.test.runner.jar]`+
+ `#PCL[/system/framework/bar.jar] `)
// Test conditional context for target SDK version 30.
// "android.test.mock" is present because "android.test.runner" is used.
- if w := `--target-context-for-sdk 30` +
- ` PCL[/system/framework/android.test.base.jar]` +
- `#PCL[/system/framework/android.test.mock.jar] `; !strings.Contains(cmd, w) {
- t.Errorf("wanted %q in %q", w, cmd)
+ android.AssertStringDoesContain(t, "dexpreopt prebuilt cmd 30", cmd,
+ `--target-context-for-sdk 30`+
+ ` PCL[/system/framework/android.test.base.jar]`+
+ `#PCL[/system/framework/android.test.mock.jar] `)
+}
+
+func TestDexpreoptBcp(t *testing.T) {
+ bp := `
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java"],
+ api_packages: ["foo"],
+ sdk_version: "current",
+ }
+
+ java_sdk_library {
+ name: "bar",
+ srcs: ["a.java"],
+ api_packages: ["bar"],
+ permitted_packages: ["bar"],
+ sdk_version: "current",
+ }
+
+ android_app {
+ name: "app",
+ srcs: ["a.java"],
+ sdk_version: "current",
+ }
+ `
+
+ testCases := []struct {
+ name string
+ with bool
+ expect string
+ }{
+ {
+ name: "with updatable bcp",
+ with: true,
+ expect: "/system/framework/foo.jar:/system/framework/bar.jar",
+ },
+ {
+ name: "without updatable bcp",
+ with: false,
+ expect: "/system/framework/foo.jar",
+ },
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("runtime-library", "foo", "bar"),
+ dexpreopt.FixtureSetBootJars("platform:foo"),
+ dexpreopt.FixtureSetUpdatableBootJars("platform:bar"),
+ dexpreopt.FixtureSetPreoptWithUpdatableBcp(test.with),
+ ).RunTestWithBp(t, bp)
+
+ app := result.ModuleForTests("app", "android_common")
+ cmd := app.Rule("dexpreopt").RuleParams.Command
+ bcp := " -Xbootclasspath-locations:" + test.expect + " " // space at the end matters
+ android.AssertStringDoesContain(t, "dexpreopt app bcp", cmd, bcp)
+ })
}
}
@@ -2439,7 +2560,17 @@
}
func TestEmbedNotice(t *testing.T) {
- ctx, _ := testJavaWithFS(t, cc.GatherRequiredDepsForTest(android.Android)+`
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ cc.PrepareForTestWithCcDefaultModules,
+ genrule.PrepareForTestWithGenRuleBuildComponents,
+ android.MockFS{
+ "APP_NOTICE": nil,
+ "GENRULE_NOTICE": nil,
+ "LIB_NOTICE": nil,
+ "TOOL_NOTICE": nil,
+ }.AddToFixture(),
+ ).RunTestWithBp(t, `
android_app {
name: "foo",
srcs: ["a.java"],
@@ -2495,15 +2626,10 @@
srcs: ["b.java"],
notice: "TOOL_NOTICE",
}
- `, map[string][]byte{
- "APP_NOTICE": nil,
- "GENRULE_NOTICE": nil,
- "LIB_NOTICE": nil,
- "TOOL_NOTICE": nil,
- })
+ `)
// foo has NOTICE files to process, and embed_notices is true.
- foo := ctx.ModuleForTests("foo", "android_common")
+ foo := result.ModuleForTests("foo", "android_common")
// verify merge notices rule.
mergeNotices := foo.Rule("mergeNoticesRule")
noticeInputs := mergeNotices.Inputs.Strings()
@@ -2523,25 +2649,21 @@
// 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)
- }
+ e := "-A out/soong/.intermediates/foo/android_common/NOTICE"
+ android.AssertStringDoesContain(t, "expected.apkPath", aapt2Flags, e)
// bar has NOTICE files to process, but embed_notices is not set.
- bar := ctx.ModuleForTests("bar", "android_common")
+ bar := result.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)
- }
+ e = "-A out/soong/.intermediates/bar/android_common/NOTICE"
+ android.AssertStringDoesNotContain(t, "bar shouldn't have the asset dir flag for NOTICE", aapt2Flags, e)
// baz's embed_notice is true, but it doesn't have any NOTICE files.
- baz := ctx.ModuleForTests("baz", "android_common")
+ baz := result.ModuleForTests("baz", "android_common")
res = baz.Output("package-res.apk")
aapt2Flags = res.Args["flags"]
- e = "-A " + buildDir + "/.intermediates/baz/android_common/NOTICE"
+ e = "-A out/soong/.intermediates/baz/android_common/NOTICE"
if strings.Contains(aapt2Flags, e) {
t.Errorf("baz shouldn't have the asset dir flag for NOTICE: %q", e)
}
@@ -2624,28 +2746,25 @@
test := func(t *testing.T, bp string, want bool, unbundled bool) {
t.Helper()
- config := testAppConfig(nil, bp, nil)
- if unbundled {
- config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
- config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
- }
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithPrebuiltsOfCurrentApi,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ if unbundled {
+ variables.Unbundled_build = proptools.BoolPtr(true)
+ variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
+ }
+ }),
+ ).RunTestWithBp(t, bp)
- ctx := testContext(config)
-
- run(t, ctx, config)
-
- foo := ctx.ModuleForTests("foo", "android_common")
+ foo := result.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)
- }
+ android.AssertBoolEquals(t, "uncompressed in dex", want, uncompressedInDexJar)
- if aligned != want {
- t.Errorf("want aligned %v, got %v", want, aligned)
- }
+ android.AssertBoolEquals(t, "aligne", want, aligned)
}
for _, tt := range testCases {
diff --git a/java/base.go b/java/base.go
new file mode 100644
index 0000000..19c85cd
--- /dev/null
+++ b/java/base.go
@@ -0,0 +1,1789 @@
+// Copyright 2021 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 (
+ "fmt"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "github.com/google/blueprint/pathtools"
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+ "android/soong/dexpreopt"
+ "android/soong/java/config"
+)
+
+// This file contains the definition and the implementation of the base module that most
+// source-based Java module structs embed.
+
+// TODO:
+// Autogenerated files:
+// Renderscript
+// Post-jar passes:
+// Proguard
+// Rmtypedefs
+// DroidDoc
+// Findbugs
+
+// Properties that are common to most Java modules, i.e. whether it's a host or device module.
+type CommonProperties struct {
+ // list of source files used to compile the Java module. May be .java, .kt, .logtags, .proto,
+ // or .aidl files.
+ Srcs []string `android:"path,arch_variant"`
+
+ // list Kotlin of source files containing Kotlin code that should be treated as common code in
+ // a codebase that supports Kotlin multiplatform. See
+ // https://kotlinlang.org/docs/reference/multiplatform.html. May be only be .kt files.
+ Common_srcs []string `android:"path,arch_variant"`
+
+ // list of source files that should not be used to build the Java module.
+ // This is most useful in the arch/multilib variants to remove non-common files
+ Exclude_srcs []string `android:"path,arch_variant"`
+
+ // list of directories containing Java resources
+ Java_resource_dirs []string `android:"arch_variant"`
+
+ // list of directories that should be excluded from java_resource_dirs
+ Exclude_java_resource_dirs []string `android:"arch_variant"`
+
+ // list of files to use as Java resources
+ Java_resources []string `android:"path,arch_variant"`
+
+ // list of files that should be excluded from java_resources and java_resource_dirs
+ Exclude_java_resources []string `android:"path,arch_variant"`
+
+ // list of module-specific flags that will be used for javac compiles
+ Javacflags []string `android:"arch_variant"`
+
+ // list of module-specific flags that will be used for kotlinc compiles
+ Kotlincflags []string `android:"arch_variant"`
+
+ // list of java libraries that will be in the classpath
+ Libs []string `android:"arch_variant"`
+
+ // list of java libraries that will be compiled into the resulting jar
+ Static_libs []string `android:"arch_variant"`
+
+ // manifest file to be included in resulting jar
+ Manifest *string `android:"path"`
+
+ // if not blank, run jarjar using the specified rules file
+ Jarjar_rules *string `android:"path,arch_variant"`
+
+ // If not blank, set the java version passed to javac as -source and -target
+ Java_version *string
+
+ // If set to true, allow this module to be dexed and installed on devices. Has no
+ // effect on host modules, which are always considered installable.
+ Installable *bool
+
+ // If set to true, include sources used to compile the module in to the final jar
+ Include_srcs *bool
+
+ // If not empty, classes are restricted to the specified packages and their sub-packages.
+ // This restriction is checked after applying jarjar rules and including static libs.
+ Permitted_packages []string
+
+ // List of modules to use as annotation processors
+ Plugins []string
+
+ // List of modules to export to libraries that directly depend on this library as annotation
+ // processors. Note that if the plugins set generates_api: true this will disable the turbine
+ // optimization on modules that depend on this module, which will reduce parallelism and cause
+ // more recompilation.
+ Exported_plugins []string
+
+ // The number of Java source entries each Javac instance can process
+ Javac_shard_size *int64
+
+ // Add host jdk tools.jar to bootclasspath
+ Use_tools_jar *bool
+
+ Openjdk9 struct {
+ // List of source files that should only be used when passing -source 1.9 or higher
+ Srcs []string `android:"path"`
+
+ // List of javac flags that should only be used when passing -source 1.9 or higher
+ Javacflags []string
+ }
+
+ // When compiling language level 9+ .java code in packages that are part of
+ // a system module, patch_module names the module that your sources and
+ // dependencies should be patched into. The Android runtime currently
+ // doesn't implement the JEP 261 module system so this option is only
+ // supported at compile time. It should only be needed to compile tests in
+ // packages that exist in libcore and which are inconvenient to move
+ // elsewhere.
+ Patch_module *string `android:"arch_variant"`
+
+ Jacoco struct {
+ // List of classes to include for instrumentation with jacoco to collect coverage
+ // information at runtime when building with coverage enabled. If unset defaults to all
+ // classes.
+ // Supports '*' as the last character of an entry in the list as a wildcard match.
+ // If preceded by '.' it matches all classes in the package and subpackages, otherwise
+ // it matches classes in the package that have the class name as a prefix.
+ Include_filter []string
+
+ // List of classes to exclude from instrumentation with jacoco to collect coverage
+ // information at runtime when building with coverage enabled. Overrides classes selected
+ // by the include_filter property.
+ // Supports '*' as the last character of an entry in the list as a wildcard match.
+ // If preceded by '.' it matches all classes in the package and subpackages, otherwise
+ // it matches classes in the package that have the class name as a prefix.
+ Exclude_filter []string
+ }
+
+ Errorprone struct {
+ // List of javac flags that should only be used when running errorprone.
+ Javacflags []string
+
+ // List of java_plugin modules that provide extra errorprone checks.
+ Extra_check_modules []string
+ }
+
+ Proto struct {
+ // List of extra options that will be passed to the proto generator.
+ Output_params []string
+ }
+
+ Instrument bool `blueprint:"mutated"`
+
+ // List of files to include in the META-INF/services folder of the resulting jar.
+ Services []string `android:"path,arch_variant"`
+
+ // If true, package the kotlin stdlib into the jar. Defaults to true.
+ Static_kotlin_stdlib *bool `android:"arch_variant"`
+
+ // A list of java_library instances that provide additional hiddenapi annotations for the library.
+ Hiddenapi_additional_annotations []string
+}
+
+// Properties that are specific to device modules. Host module factories should not add these when
+// constructing a new module.
+type DeviceProperties struct {
+ // if not blank, set to the version of the sdk to compile against.
+ // Defaults to compiling against the current platform.
+ Sdk_version *string
+
+ // if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
+ // Defaults to sdk_version if not set.
+ Min_sdk_version *string
+
+ // if not blank, set the targetSdkVersion in the AndroidManifest.xml.
+ // Defaults to sdk_version if not set.
+ Target_sdk_version *string
+
+ // Whether to compile against the platform APIs instead of an SDK.
+ // If true, then sdk_version must be empty. The value of this field
+ // is ignored when module's type isn't android_app.
+ Platform_apis *bool
+
+ Aidl struct {
+ // Top level directories to pass to aidl tool
+ Include_dirs []string
+
+ // Directories rooted at the Android.bp file to pass to aidl tool
+ Local_include_dirs []string
+
+ // directories that should be added as include directories for any aidl sources of modules
+ // that depend on this module, as well as to aidl for this module.
+ Export_include_dirs []string
+
+ // whether to generate traces (for systrace) for this interface
+ Generate_traces *bool
+
+ // whether to generate Binder#GetTransaction name method.
+ Generate_get_transaction_name *bool
+
+ // list of flags that will be passed to the AIDL compiler
+ Flags []string
+ }
+
+ // If true, export a copy of the module as a -hostdex module for host testing.
+ Hostdex *bool
+
+ Target struct {
+ Hostdex struct {
+ // Additional required dependencies to add to -hostdex modules.
+ Required []string
+ }
+ }
+
+ // When targeting 1.9 and above, override the modules to use with --system,
+ // otherwise provides defaults libraries to add to the bootclasspath.
+ System_modules *string
+
+ // The name of the module as used in build configuration.
+ //
+ // Allows a library to separate its actual name from the name used in
+ // build configuration, e.g.ctx.Config().BootJars().
+ ConfigurationName *string `blueprint:"mutated"`
+
+ // set the name of the output
+ Stem *string
+
+ IsSDKLibrary bool `blueprint:"mutated"`
+
+ // If true, generate the signature file of APK Signing Scheme V4, along side the signed APK file.
+ // Defaults to false.
+ V4_signature *bool
+
+ // Only for libraries created by a sysprop_library module, SyspropPublicStub is the name of the
+ // public stubs library.
+ SyspropPublicStub string `blueprint:"mutated"`
+}
+
+// Functionality common to Module and Import
+//
+// It is embedded in Module so its functionality can be used by methods in Module
+// but it is currently only initialized by Import and Library.
+type embeddableInModuleAndImport struct {
+
+ // Functionality related to this being used as a component of a java_sdk_library.
+ EmbeddableSdkLibraryComponent
+}
+
+func (e *embeddableInModuleAndImport) initModuleAndImport(moduleBase *android.ModuleBase) {
+ e.initSdkLibraryComponent(moduleBase)
+}
+
+// Module/Import's DepIsInSameApex(...) delegates to this method.
+//
+// This cannot implement DepIsInSameApex(...) directly as that leads to ambiguity with
+// the one provided by ApexModuleBase.
+func (e *embeddableInModuleAndImport) depIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+ // dependencies other than the static linkage are all considered crossing APEX boundary
+ if staticLibTag == ctx.OtherModuleDependencyTag(dep) {
+ return true
+ }
+ return false
+}
+
+// Module contains the properties and members used by all java module types
+type Module struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+ android.ApexModuleBase
+ android.SdkBase
+
+ // Functionality common to Module and Import.
+ embeddableInModuleAndImport
+
+ properties CommonProperties
+ protoProperties android.ProtoProperties
+ deviceProperties DeviceProperties
+
+ // jar file containing header classes including static library dependencies, suitable for
+ // inserting into the bootclasspath/classpath of another compile
+ headerJarFile android.Path
+
+ // jar file containing implementation classes including static library dependencies but no
+ // resources
+ implementationJarFile android.Path
+
+ // jar file containing only resources including from static library dependencies
+ resourceJar android.Path
+
+ // args and dependencies to package source files into a srcjar
+ srcJarArgs []string
+ srcJarDeps android.Paths
+
+ // jar file containing implementation classes and resources including static library
+ // dependencies
+ implementationAndResourcesJar android.Path
+
+ // output file containing classes.dex and resources
+ dexJarFile android.Path
+
+ // output file containing uninstrumented classes that will be instrumented by jacoco
+ jacocoReportClassesFile android.Path
+
+ // output file of the module, which may be a classes jar or a dex jar
+ outputFile android.Path
+ extraOutputFiles android.Paths
+
+ exportAidlIncludeDirs android.Paths
+
+ logtagsSrcs android.Paths
+
+ // installed file for binary dependency
+ installFile android.Path
+
+ // list of .java files and srcjars that was passed to javac
+ compiledJavaSrcs android.Paths
+ compiledSrcJars android.Paths
+
+ // manifest file to use instead of properties.Manifest
+ overrideManifest android.OptionalPath
+
+ // map of SDK version to class loader context
+ classLoaderContexts dexpreopt.ClassLoaderContextMap
+
+ // list of plugins that this java module is exporting
+ exportedPluginJars android.Paths
+
+ // list of plugins that this java module is exporting
+ exportedPluginClasses []string
+
+ // if true, the exported plugins generate API and require disabling turbine.
+ exportedDisableTurbine bool
+
+ // list of source files, collected from srcFiles with unique java and all kt files,
+ // will be used by android.IDEInfo struct
+ expandIDEInfoCompiledSrcs []string
+
+ // expanded Jarjar_rules
+ expandJarjarRules android.Path
+
+ // list of additional targets for checkbuild
+ additionalCheckedModules android.Paths
+
+ // Extra files generated by the module type to be added as java resources.
+ extraResources android.Paths
+
+ hiddenAPI
+ dexer
+ dexpreopter
+ usesLibrary
+ linter
+
+ // list of the xref extraction files
+ kytheFiles android.Paths
+
+ // Collect the module directory for IDE info in java/jdeps.go.
+ modulePaths []string
+
+ hideApexVariantFromMake bool
+
+ sdkVersion android.SdkSpec
+ minSdkVersion android.SdkSpec
+}
+
+func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error {
+ sdkVersion := j.SdkVersion(ctx)
+ if sdkVersion.Stable() {
+ return nil
+ }
+ if sdkVersion.Kind == android.SdkCorePlatform {
+ if useLegacyCorePlatformApiByName(j.BaseModuleName()) {
+ return fmt.Errorf("non stable SDK %v - uses legacy core platform", sdkVersion)
+ } else {
+ // Treat stable core platform as stable.
+ return nil
+ }
+ } else {
+ return fmt.Errorf("non stable SDK %v", sdkVersion)
+ }
+}
+
+// checkSdkVersions enforces restrictions around SDK dependencies.
+func (j *Module) checkSdkVersions(ctx android.ModuleContext) {
+ if j.RequiresStableAPIs(ctx) {
+ if sc, ok := ctx.Module().(android.SdkContext); ok {
+ if !sc.SdkVersion(ctx).Specified() {
+ ctx.PropertyErrorf("sdk_version",
+ "sdk_version must have a value when the module is located at vendor or product(only if PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE is set).")
+ }
+ }
+ }
+
+ // Make sure this module doesn't statically link to modules with lower-ranked SDK link type.
+ // See rank() for details.
+ ctx.VisitDirectDeps(func(module android.Module) {
+ tag := ctx.OtherModuleDependencyTag(module)
+ switch module.(type) {
+ // TODO(satayev): cover other types as well, e.g. imports
+ case *Library, *AndroidLibrary:
+ switch tag {
+ case bootClasspathTag, libTag, staticLibTag, java9LibTag:
+ j.checkSdkLinkType(ctx, module.(moduleWithSdkDep), tag.(dependencyTag))
+ }
+ }
+ })
+}
+
+func (j *Module) checkPlatformAPI(ctx android.ModuleContext) {
+ if sc, ok := ctx.Module().(android.SdkContext); ok {
+ usePlatformAPI := proptools.Bool(j.deviceProperties.Platform_apis)
+ sdkVersionSpecified := sc.SdkVersion(ctx).Specified()
+ if usePlatformAPI && sdkVersionSpecified {
+ ctx.PropertyErrorf("platform_apis", "platform_apis must be false when sdk_version is not empty.")
+ } else if !usePlatformAPI && !sdkVersionSpecified {
+ ctx.PropertyErrorf("platform_apis", "platform_apis must be true when sdk_version is empty.")
+ }
+
+ }
+}
+
+func (j *Module) addHostProperties() {
+ j.AddProperties(
+ &j.properties,
+ &j.protoProperties,
+ &j.usesLibraryProperties,
+ )
+}
+
+func (j *Module) addHostAndDeviceProperties() {
+ j.addHostProperties()
+ j.AddProperties(
+ &j.deviceProperties,
+ &j.dexer.dexProperties,
+ &j.dexpreoptProperties,
+ &j.linter.properties,
+ )
+}
+
+func (j *Module) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case "":
+ return append(android.Paths{j.outputFile}, j.extraOutputFiles...), nil
+ case android.DefaultDistTag:
+ return android.Paths{j.outputFile}, nil
+ case ".jar":
+ return android.Paths{j.implementationAndResourcesJar}, nil
+ case ".proguard_map":
+ if j.dexer.proguardDictionary.Valid() {
+ return android.Paths{j.dexer.proguardDictionary.Path()}, nil
+ }
+ return nil, fmt.Errorf("%q was requested, but no output file was found.", tag)
+ default:
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+ }
+}
+
+var _ android.OutputFileProducer = (*Module)(nil)
+
+func InitJavaModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
+ initJavaModule(module, hod, false)
+}
+
+func InitJavaModuleMultiTargets(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
+ initJavaModule(module, hod, true)
+}
+
+func initJavaModule(module android.DefaultableModule, hod android.HostOrDeviceSupported, multiTargets bool) {
+ multilib := android.MultilibCommon
+ if multiTargets {
+ android.InitAndroidMultiTargetsArchModule(module, hod, multilib)
+ } else {
+ android.InitAndroidArchModule(module, hod, multilib)
+ }
+ android.InitDefaultableModule(module)
+}
+
+func (j *Module) shouldInstrument(ctx android.BaseModuleContext) bool {
+ return j.properties.Instrument &&
+ ctx.Config().IsEnvTrue("EMMA_INSTRUMENT") &&
+ ctx.DeviceConfig().JavaCoverageEnabledForPath(ctx.ModuleDir())
+}
+
+func (j *Module) shouldInstrumentStatic(ctx android.BaseModuleContext) bool {
+ return j.shouldInstrument(ctx) &&
+ (ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_STATIC") ||
+ ctx.Config().UnbundledBuild())
+}
+
+func (j *Module) shouldInstrumentInApex(ctx android.BaseModuleContext) bool {
+ // Force enable the instrumentation for java code that is built for APEXes ...
+ // except for the jacocoagent itself (because instrumenting jacocoagent using jacocoagent
+ // doesn't make sense) or framework libraries (e.g. libraries found in the InstrumentFrameworkModules list) unless EMMA_INSTRUMENT_FRAMEWORK is true.
+ apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ isJacocoAgent := ctx.ModuleName() == "jacocoagent"
+ if j.DirectlyInAnyApex() && !isJacocoAgent && !apexInfo.IsForPlatform() {
+ if !inList(ctx.ModuleName(), config.InstrumentFrameworkModules) {
+ return true
+ } else if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
+ return true
+ }
+ }
+ return false
+}
+
+func (j *Module) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ return android.SdkSpecFrom(ctx, String(j.deviceProperties.Sdk_version))
+}
+
+func (j *Module) SystemModules() string {
+ return proptools.String(j.deviceProperties.System_modules)
+}
+
+func (j *Module) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ if j.deviceProperties.Min_sdk_version != nil {
+ return android.SdkSpecFrom(ctx, *j.deviceProperties.Min_sdk_version)
+ }
+ return j.SdkVersion(ctx)
+}
+
+func (j *Module) MinSdkVersionString() string {
+ return j.minSdkVersion.Raw
+}
+
+func (j *Module) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ if j.deviceProperties.Target_sdk_version != nil {
+ return android.SdkSpecFrom(ctx, *j.deviceProperties.Target_sdk_version)
+ }
+ return j.SdkVersion(ctx)
+}
+
+func (j *Module) AvailableFor(what string) bool {
+ if what == android.AvailableToPlatform && Bool(j.deviceProperties.Hostdex) {
+ // Exception: for hostdex: true libraries, the platform variant is created
+ // even if it's not marked as available to platform. In that case, the platform
+ // variant is used only for the hostdex and not installed to the device.
+ return true
+ }
+ return j.ApexModuleBase.AvailableFor(what)
+}
+
+func (j *Module) deps(ctx android.BottomUpMutatorContext) {
+ if ctx.Device() {
+ j.linter.deps(ctx)
+
+ sdkDeps(ctx, android.SdkContext(j), j.dexer)
+
+ if j.deviceProperties.SyspropPublicStub != "" {
+ // This is a sysprop implementation library that has a corresponding sysprop public
+ // stubs library, and a dependency on it so that dependencies on the implementation can
+ // be forwarded to the public stubs library when necessary.
+ ctx.AddVariationDependencies(nil, syspropPublicStubDepTag, j.deviceProperties.SyspropPublicStub)
+ }
+ }
+
+ libDeps := ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
+ ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs...)
+
+ // Add dependency on libraries that provide additional hidden api annotations.
+ ctx.AddVariationDependencies(nil, hiddenApiAnnotationsTag, j.properties.Hiddenapi_additional_annotations...)
+
+ if ctx.DeviceConfig().VndkVersion() != "" && ctx.Config().EnforceInterPartitionJavaSdkLibrary() {
+ // Require java_sdk_library at inter-partition java dependency to ensure stable
+ // interface between partitions. If inter-partition java_library dependency is detected,
+ // raise build error because java_library doesn't have a stable interface.
+ //
+ // Inputs:
+ // PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY
+ // if true, enable enforcement
+ // PRODUCT_INTER_PARTITION_JAVA_LIBRARY_ALLOWLIST
+ // exception list of java_library names to allow inter-partition dependency
+ for idx := range j.properties.Libs {
+ if libDeps[idx] == nil {
+ continue
+ }
+
+ if javaDep, ok := libDeps[idx].(javaSdkLibraryEnforceContext); ok {
+ // java_sdk_library is always allowed at inter-partition dependency.
+ // So, skip check.
+ if _, ok := javaDep.(*SdkLibrary); ok {
+ continue
+ }
+
+ j.checkPartitionsForJavaDependency(ctx, "libs", javaDep)
+ }
+ }
+ }
+
+ // For library dependencies that are component libraries (like stubs), add the implementation
+ // as a dependency (dexpreopt needs to be against the implementation library, not stubs).
+ for _, dep := range libDeps {
+ if dep != nil {
+ if component, ok := dep.(SdkLibraryComponentDependency); ok {
+ if lib := component.OptionalSdkLibraryImplementation(); lib != nil {
+ ctx.AddVariationDependencies(nil, usesLibTag, *lib)
+ }
+ }
+ }
+ }
+
+ ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), pluginTag, j.properties.Plugins...)
+ ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), errorpronePluginTag, j.properties.Errorprone.Extra_check_modules...)
+ ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), exportedPluginTag, j.properties.Exported_plugins...)
+
+ android.ProtoDeps(ctx, &j.protoProperties)
+ if j.hasSrcExt(".proto") {
+ protoDeps(ctx, &j.protoProperties)
+ }
+
+ if j.hasSrcExt(".kt") {
+ // TODO(ccross): move this to a mutator pass that can tell if generated sources contain
+ // Kotlin files
+ ctx.AddVariationDependencies(nil, kotlinStdlibTag,
+ "kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8")
+ if len(j.properties.Plugins) > 0 {
+ ctx.AddVariationDependencies(nil, kotlinAnnotationsTag, "kotlin-annotations")
+ }
+ }
+
+ // Framework libraries need special handling in static coverage builds: they should not have
+ // static dependency on jacoco, otherwise there would be multiple conflicting definitions of
+ // the same jacoco classes coming from different bootclasspath jars.
+ if inList(ctx.ModuleName(), config.InstrumentFrameworkModules) {
+ if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
+ j.properties.Instrument = true
+ }
+ } else if j.shouldInstrumentStatic(ctx) {
+ ctx.AddVariationDependencies(nil, staticLibTag, "jacocoagent")
+ }
+}
+
+func hasSrcExt(srcs []string, ext string) bool {
+ for _, src := range srcs {
+ if filepath.Ext(src) == ext {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (j *Module) hasSrcExt(ext string) bool {
+ return hasSrcExt(j.properties.Srcs, ext)
+}
+
+func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath,
+ aidlIncludeDirs android.Paths) (string, android.Paths) {
+
+ aidlIncludes := android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Local_include_dirs)
+ aidlIncludes = append(aidlIncludes,
+ android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)...)
+ aidlIncludes = append(aidlIncludes,
+ android.PathsForSource(ctx, j.deviceProperties.Aidl.Include_dirs)...)
+
+ var flags []string
+ var deps android.Paths
+
+ flags = append(flags, j.deviceProperties.Aidl.Flags...)
+
+ if aidlPreprocess.Valid() {
+ flags = append(flags, "-p"+aidlPreprocess.String())
+ deps = append(deps, aidlPreprocess.Path())
+ } else if len(aidlIncludeDirs) > 0 {
+ flags = append(flags, android.JoinWithPrefix(aidlIncludeDirs.Strings(), "-I"))
+ }
+
+ if len(j.exportAidlIncludeDirs) > 0 {
+ flags = append(flags, android.JoinWithPrefix(j.exportAidlIncludeDirs.Strings(), "-I"))
+ }
+
+ if len(aidlIncludes) > 0 {
+ flags = append(flags, android.JoinWithPrefix(aidlIncludes.Strings(), "-I"))
+ }
+
+ flags = append(flags, "-I"+android.PathForModuleSrc(ctx).String())
+ if src := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "src"); src.Valid() {
+ flags = append(flags, "-I"+src.String())
+ }
+
+ if Bool(j.deviceProperties.Aidl.Generate_traces) {
+ flags = append(flags, "-t")
+ }
+
+ if Bool(j.deviceProperties.Aidl.Generate_get_transaction_name) {
+ flags = append(flags, "--transaction_names")
+ }
+
+ return strings.Join(flags, " "), deps
+}
+
+func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaBuilderFlags {
+
+ var flags javaBuilderFlags
+
+ // javaVersion flag.
+ flags.javaVersion = getJavaVersion(ctx, String(j.properties.Java_version), android.SdkContext(j))
+
+ if ctx.Config().RunErrorProne() {
+ if config.ErrorProneClasspath == nil && ctx.Config().TestProductVariables == nil {
+ ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?")
+ }
+
+ errorProneFlags := []string{
+ "-Xplugin:ErrorProne",
+ "${config.ErrorProneChecks}",
+ }
+ errorProneFlags = append(errorProneFlags, j.properties.Errorprone.Javacflags...)
+
+ flags.errorProneExtraJavacFlags = "${config.ErrorProneFlags} " +
+ "'" + strings.Join(errorProneFlags, " ") + "'"
+ flags.errorProneProcessorPath = classpath(android.PathsForSource(ctx, config.ErrorProneClasspath))
+ }
+
+ // classpath
+ flags.bootClasspath = append(flags.bootClasspath, deps.bootClasspath...)
+ flags.classpath = append(flags.classpath, deps.classpath...)
+ flags.java9Classpath = append(flags.java9Classpath, deps.java9Classpath...)
+ flags.processorPath = append(flags.processorPath, deps.processorPath...)
+ flags.errorProneProcessorPath = append(flags.errorProneProcessorPath, deps.errorProneProcessorPath...)
+
+ flags.processors = append(flags.processors, deps.processorClasses...)
+ flags.processors = android.FirstUniqueStrings(flags.processors)
+
+ if len(flags.bootClasspath) == 0 && ctx.Host() && !flags.javaVersion.usesJavaModules() &&
+ decodeSdkDep(ctx, android.SdkContext(j)).hasStandardLibs() {
+ // Give host-side tools a version of OpenJDK's standard libraries
+ // close to what they're targeting. As of Dec 2017, AOSP is only
+ // bundling OpenJDK 8 and 9, so nothing < 8 is available.
+ //
+ // When building with OpenJDK 8, the following should have no
+ // effect since those jars would be available by default.
+ //
+ // When building with OpenJDK 9 but targeting a version < 1.8,
+ // putting them on the bootclasspath means that:
+ // a) code can't (accidentally) refer to OpenJDK 9 specific APIs
+ // b) references to existing APIs are not reinterpreted in an
+ // OpenJDK 9-specific way, eg. calls to subclasses of
+ // java.nio.Buffer as in http://b/70862583
+ java8Home := ctx.Config().Getenv("ANDROID_JAVA8_HOME")
+ flags.bootClasspath = append(flags.bootClasspath,
+ android.PathForSource(ctx, java8Home, "jre/lib/jce.jar"),
+ android.PathForSource(ctx, java8Home, "jre/lib/rt.jar"))
+ if Bool(j.properties.Use_tools_jar) {
+ flags.bootClasspath = append(flags.bootClasspath,
+ android.PathForSource(ctx, java8Home, "lib/tools.jar"))
+ }
+ }
+
+ // systemModules
+ flags.systemModules = deps.systemModules
+
+ // aidl flags.
+ flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
+
+ return flags
+}
+
+func (j *Module) collectJavacFlags(
+ ctx android.ModuleContext, flags javaBuilderFlags, srcFiles android.Paths) javaBuilderFlags {
+ // javac flags.
+ javacFlags := j.properties.Javacflags
+
+ if ctx.Config().MinimizeJavaDebugInfo() && !ctx.Host() {
+ // For non-host binaries, override the -g flag passed globally to remove
+ // local variable debug info to reduce disk and memory usage.
+ javacFlags = append(javacFlags, "-g:source,lines")
+ }
+ javacFlags = append(javacFlags, "-Xlint:-dep-ann")
+
+ if flags.javaVersion.usesJavaModules() {
+ javacFlags = append(javacFlags, j.properties.Openjdk9.Javacflags...)
+
+ if j.properties.Patch_module != nil {
+ // Manually specify build directory in case it is not under the repo root.
+ // (javac doesn't seem to expand into symbolic links when searching for patch-module targets, so
+ // just adding a symlink under the root doesn't help.)
+ patchPaths := []string{".", ctx.Config().BuildDir()}
+
+ // b/150878007
+ //
+ // Workaround to support *Bazel-executed* JDK9 javac in Bazel's
+ // execution root for --patch-module. If this javac command line is
+ // invoked within Bazel's execution root working directory, the top
+ // level directories (e.g. libcore/, tools/, frameworks/) are all
+ // symlinks. JDK9 javac does not traverse into symlinks, which causes
+ // --patch-module to fail source file lookups when invoked in the
+ // execution root.
+ //
+ // Short of patching javac or enumerating *all* directories as possible
+ // input dirs, manually add the top level dir of the source files to be
+ // compiled.
+ topLevelDirs := map[string]bool{}
+ for _, srcFilePath := range srcFiles {
+ srcFileParts := strings.Split(srcFilePath.String(), "/")
+ // Ignore source files that are already in the top level directory
+ // as well as generated files in the out directory. The out
+ // directory may be an absolute path, which means srcFileParts[0] is the
+ // empty string, so check that as well. Note that "out" in Bazel's execution
+ // root is *not* a symlink, which doesn't cause problems for --patch-modules
+ // anyway, so it's fine to not apply this workaround for generated
+ // source files.
+ if len(srcFileParts) > 1 &&
+ srcFileParts[0] != "" &&
+ srcFileParts[0] != "out" {
+ topLevelDirs[srcFileParts[0]] = true
+ }
+ }
+ patchPaths = append(patchPaths, android.SortedStringKeys(topLevelDirs)...)
+
+ classPath := flags.classpath.FormJavaClassPath("")
+ if classPath != "" {
+ patchPaths = append(patchPaths, classPath)
+ }
+ javacFlags = append(
+ javacFlags,
+ "--patch-module="+String(j.properties.Patch_module)+"="+strings.Join(patchPaths, ":"))
+ }
+ }
+
+ if len(javacFlags) > 0 {
+ // optimization.
+ ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " "))
+ flags.javacFlags = "$javacFlags"
+ }
+
+ return flags
+}
+
+func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) {
+ j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)
+
+ deps := j.collectDeps(ctx)
+ flags := j.collectBuilderFlags(ctx, deps)
+
+ if flags.javaVersion.usesJavaModules() {
+ j.properties.Srcs = append(j.properties.Srcs, j.properties.Openjdk9.Srcs...)
+ }
+ srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
+ if hasSrcExt(srcFiles.Strings(), ".proto") {
+ flags = protoFlags(ctx, &j.properties, &j.protoProperties, flags)
+ }
+
+ kotlinCommonSrcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Common_srcs, nil)
+ if len(kotlinCommonSrcFiles.FilterOutByExt(".kt")) > 0 {
+ ctx.PropertyErrorf("common_srcs", "common_srcs must be .kt files")
+ }
+
+ srcFiles = j.genSources(ctx, srcFiles, flags)
+
+ // Collect javac flags only after computing the full set of srcFiles to
+ // ensure that the --patch-module lookup paths are complete.
+ flags = j.collectJavacFlags(ctx, flags, srcFiles)
+
+ srcJars := srcFiles.FilterByExt(".srcjar")
+ srcJars = append(srcJars, deps.srcJars...)
+ if aaptSrcJar != nil {
+ srcJars = append(srcJars, aaptSrcJar)
+ }
+
+ if j.properties.Jarjar_rules != nil {
+ j.expandJarjarRules = android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
+ }
+
+ jarName := ctx.ModuleName() + ".jar"
+
+ javaSrcFiles := srcFiles.FilterByExt(".java")
+ var uniqueSrcFiles android.Paths
+ set := make(map[string]bool)
+ for _, v := range javaSrcFiles {
+ if _, found := set[v.String()]; !found {
+ set[v.String()] = true
+ uniqueSrcFiles = append(uniqueSrcFiles, v)
+ }
+ }
+
+ // Collect .java files for AIDEGen
+ j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, uniqueSrcFiles.Strings()...)
+
+ var kotlinJars android.Paths
+
+ if srcFiles.HasExt(".kt") {
+ // user defined kotlin flags.
+ kotlincFlags := j.properties.Kotlincflags
+ CheckKotlincFlags(ctx, kotlincFlags)
+
+ // Dogfood the JVM_IR backend.
+ kotlincFlags = append(kotlincFlags, "-Xuse-ir")
+
+ // If there are kotlin files, compile them first but pass all the kotlin and java files
+ // kotlinc will use the java files to resolve types referenced by the kotlin files, but
+ // won't emit any classes for them.
+ kotlincFlags = append(kotlincFlags, "-no-stdlib")
+ if ctx.Device() {
+ kotlincFlags = append(kotlincFlags, "-no-jdk")
+ }
+ if len(kotlincFlags) > 0 {
+ // optimization.
+ ctx.Variable(pctx, "kotlincFlags", strings.Join(kotlincFlags, " "))
+ flags.kotlincFlags += "$kotlincFlags"
+ }
+
+ var kotlinSrcFiles android.Paths
+ kotlinSrcFiles = append(kotlinSrcFiles, uniqueSrcFiles...)
+ kotlinSrcFiles = append(kotlinSrcFiles, srcFiles.FilterByExt(".kt")...)
+
+ // Collect .kt files for AIDEGen
+ j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, srcFiles.FilterByExt(".kt").Strings()...)
+ j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, kotlinCommonSrcFiles.Strings()...)
+
+ flags.classpath = append(flags.classpath, deps.kotlinStdlib...)
+ flags.classpath = append(flags.classpath, deps.kotlinAnnotations...)
+
+ flags.kotlincClasspath = append(flags.kotlincClasspath, flags.bootClasspath...)
+ flags.kotlincClasspath = append(flags.kotlincClasspath, flags.classpath...)
+
+ if len(flags.processorPath) > 0 {
+ // Use kapt for annotation processing
+ kaptSrcJar := android.PathForModuleOut(ctx, "kapt", "kapt-sources.jar")
+ kaptResJar := android.PathForModuleOut(ctx, "kapt", "kapt-res.jar")
+ kotlinKapt(ctx, kaptSrcJar, kaptResJar, kotlinSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
+ srcJars = append(srcJars, kaptSrcJar)
+ kotlinJars = append(kotlinJars, kaptResJar)
+ // Disable annotation processing in javac, it's already been handled by kapt
+ flags.processorPath = nil
+ flags.processors = nil
+ }
+
+ kotlinJar := android.PathForModuleOut(ctx, "kotlin", jarName)
+ kotlinCompile(ctx, kotlinJar, kotlinSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
+ if ctx.Failed() {
+ return
+ }
+
+ // Make javac rule depend on the kotlinc rule
+ flags.classpath = append(flags.classpath, kotlinJar)
+
+ kotlinJars = append(kotlinJars, kotlinJar)
+ // Jar kotlin classes into the final jar after javac
+ if BoolDefault(j.properties.Static_kotlin_stdlib, true) {
+ kotlinJars = append(kotlinJars, deps.kotlinStdlib...)
+ }
+ }
+
+ jars := append(android.Paths(nil), kotlinJars...)
+
+ // Store the list of .java files that was passed to javac
+ j.compiledJavaSrcs = uniqueSrcFiles
+ j.compiledSrcJars = srcJars
+
+ enableSharding := false
+ var headerJarFileWithoutJarjar android.Path
+ if ctx.Device() && !ctx.Config().IsEnvFalse("TURBINE_ENABLED") && !deps.disableTurbine {
+ if j.properties.Javac_shard_size != nil && *(j.properties.Javac_shard_size) > 0 {
+ enableSharding = true
+ // Formerly, there was a check here that prevented annotation processors
+ // from being used when sharding was enabled, as some annotation processors
+ // do not function correctly in sharded environments. It was removed to
+ // allow for the use of annotation processors that do function correctly
+ // with sharding enabled. See: b/77284273.
+ }
+ headerJarFileWithoutJarjar, j.headerJarFile =
+ j.compileJavaHeader(ctx, uniqueSrcFiles, srcJars, deps, flags, jarName, kotlinJars)
+ if ctx.Failed() {
+ return
+ }
+ }
+ if len(uniqueSrcFiles) > 0 || len(srcJars) > 0 {
+ var extraJarDeps android.Paths
+ if ctx.Config().RunErrorProne() {
+ // If error-prone is enabled, add an additional rule to compile the java files into
+ // a separate set of classes (so that they don't overwrite the normal ones and require
+ // a rebuild when error-prone is turned off).
+ // TODO(ccross): Once we always compile with javac9 we may be able to conditionally
+ // enable error-prone without affecting the output class files.
+ errorprone := android.PathForModuleOut(ctx, "errorprone", jarName)
+ RunErrorProne(ctx, errorprone, uniqueSrcFiles, srcJars, flags)
+ extraJarDeps = append(extraJarDeps, errorprone)
+ }
+
+ if enableSharding {
+ flags.classpath = append(flags.classpath, headerJarFileWithoutJarjar)
+ shardSize := int(*(j.properties.Javac_shard_size))
+ var shardSrcs []android.Paths
+ if len(uniqueSrcFiles) > 0 {
+ shardSrcs = android.ShardPaths(uniqueSrcFiles, shardSize)
+ for idx, shardSrc := range shardSrcs {
+ classes := j.compileJavaClasses(ctx, jarName, idx, shardSrc,
+ nil, flags, extraJarDeps)
+ jars = append(jars, classes)
+ }
+ }
+ if len(srcJars) > 0 {
+ classes := j.compileJavaClasses(ctx, jarName, len(shardSrcs),
+ nil, srcJars, flags, extraJarDeps)
+ jars = append(jars, classes)
+ }
+ } else {
+ classes := j.compileJavaClasses(ctx, jarName, -1, uniqueSrcFiles, srcJars, flags, extraJarDeps)
+ jars = append(jars, classes)
+ }
+ if ctx.Failed() {
+ return
+ }
+ }
+
+ j.srcJarArgs, j.srcJarDeps = resourcePathsToJarArgs(srcFiles), srcFiles
+
+ var includeSrcJar android.WritablePath
+ if Bool(j.properties.Include_srcs) {
+ includeSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+".srcjar")
+ TransformResourcesToJar(ctx, includeSrcJar, j.srcJarArgs, j.srcJarDeps)
+ }
+
+ dirArgs, dirDeps := ResourceDirsToJarArgs(ctx, j.properties.Java_resource_dirs,
+ j.properties.Exclude_java_resource_dirs, j.properties.Exclude_java_resources)
+ fileArgs, fileDeps := ResourceFilesToJarArgs(ctx, j.properties.Java_resources, j.properties.Exclude_java_resources)
+ extraArgs, extraDeps := resourcePathsToJarArgs(j.extraResources), j.extraResources
+
+ var resArgs []string
+ var resDeps android.Paths
+
+ resArgs = append(resArgs, dirArgs...)
+ resDeps = append(resDeps, dirDeps...)
+
+ resArgs = append(resArgs, fileArgs...)
+ resDeps = append(resDeps, fileDeps...)
+
+ resArgs = append(resArgs, extraArgs...)
+ resDeps = append(resDeps, extraDeps...)
+
+ if len(resArgs) > 0 {
+ resourceJar := android.PathForModuleOut(ctx, "res", jarName)
+ TransformResourcesToJar(ctx, resourceJar, resArgs, resDeps)
+ j.resourceJar = resourceJar
+ if ctx.Failed() {
+ return
+ }
+ }
+
+ var resourceJars android.Paths
+ if j.resourceJar != nil {
+ resourceJars = append(resourceJars, j.resourceJar)
+ }
+ if Bool(j.properties.Include_srcs) {
+ resourceJars = append(resourceJars, includeSrcJar)
+ }
+ resourceJars = append(resourceJars, deps.staticResourceJars...)
+
+ if len(resourceJars) > 1 {
+ combinedJar := android.PathForModuleOut(ctx, "res-combined", jarName)
+ TransformJarsToJar(ctx, combinedJar, "for resources", resourceJars, android.OptionalPath{},
+ false, nil, nil)
+ j.resourceJar = combinedJar
+ } else if len(resourceJars) == 1 {
+ j.resourceJar = resourceJars[0]
+ }
+
+ if len(deps.staticJars) > 0 {
+ jars = append(jars, deps.staticJars...)
+ }
+
+ manifest := j.overrideManifest
+ if !manifest.Valid() && j.properties.Manifest != nil {
+ manifest = android.OptionalPathForPath(android.PathForModuleSrc(ctx, *j.properties.Manifest))
+ }
+
+ services := android.PathsForModuleSrc(ctx, j.properties.Services)
+ if len(services) > 0 {
+ servicesJar := android.PathForModuleOut(ctx, "services", jarName)
+ var zipargs []string
+ for _, file := range services {
+ serviceFile := file.String()
+ zipargs = append(zipargs, "-C", filepath.Dir(serviceFile), "-f", serviceFile)
+ }
+ rule := zip
+ args := map[string]string{
+ "jarArgs": "-P META-INF/services/ " + strings.Join(proptools.NinjaAndShellEscapeList(zipargs), " "),
+ }
+ if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_ZIP") {
+ rule = zipRE
+ args["implicits"] = strings.Join(services.Strings(), ",")
+ }
+ ctx.Build(pctx, android.BuildParams{
+ Rule: rule,
+ Output: servicesJar,
+ Implicits: services,
+ Args: args,
+ })
+ jars = append(jars, servicesJar)
+ }
+
+ // Combine the classes built from sources, any manifests, and any static libraries into
+ // classes.jar. If there is only one input jar this step will be skipped.
+ var outputFile android.OutputPath
+
+ if len(jars) == 1 && !manifest.Valid() {
+ // Optimization: skip the combine step as there is nothing to do
+ // TODO(ccross): this leaves any module-info.class files, but those should only come from
+ // prebuilt dependencies until we support modules in the platform build, so there shouldn't be
+ // any if len(jars) == 1.
+
+ // Transform the single path to the jar into an OutputPath as that is required by the following
+ // code.
+ if moduleOutPath, ok := jars[0].(android.ModuleOutPath); ok {
+ // The path contains an embedded OutputPath so reuse that.
+ outputFile = moduleOutPath.OutputPath
+ } else if outputPath, ok := jars[0].(android.OutputPath); ok {
+ // The path is an OutputPath so reuse it directly.
+ outputFile = outputPath
+ } else {
+ // The file is not in the out directory so create an OutputPath into which it can be copied
+ // and which the following code can use to refer to it.
+ combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: jars[0],
+ Output: combinedJar,
+ })
+ outputFile = combinedJar.OutputPath
+ }
+ } else {
+ combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
+ TransformJarsToJar(ctx, combinedJar, "for javac", jars, manifest,
+ false, nil, nil)
+ outputFile = combinedJar.OutputPath
+ }
+
+ // jarjar implementation jar if necessary
+ if j.expandJarjarRules != nil {
+ // Transform classes.jar into classes-jarjar.jar
+ jarjarFile := android.PathForModuleOut(ctx, "jarjar", jarName).OutputPath
+ TransformJarJar(ctx, jarjarFile, outputFile, j.expandJarjarRules)
+ outputFile = jarjarFile
+
+ // jarjar resource jar if necessary
+ if j.resourceJar != nil {
+ resourceJarJarFile := android.PathForModuleOut(ctx, "res-jarjar", jarName)
+ TransformJarJar(ctx, resourceJarJarFile, j.resourceJar, j.expandJarjarRules)
+ j.resourceJar = resourceJarJarFile
+ }
+
+ if ctx.Failed() {
+ return
+ }
+ }
+
+ // Check package restrictions if necessary.
+ if len(j.properties.Permitted_packages) > 0 {
+ // Check packages and copy to package-checked file.
+ pkgckFile := android.PathForModuleOut(ctx, "package-check.stamp")
+ CheckJarPackages(ctx, pkgckFile, outputFile, j.properties.Permitted_packages)
+ j.additionalCheckedModules = append(j.additionalCheckedModules, pkgckFile)
+
+ if ctx.Failed() {
+ return
+ }
+ }
+
+ j.implementationJarFile = outputFile
+ if j.headerJarFile == nil {
+ j.headerJarFile = j.implementationJarFile
+ }
+
+ if j.shouldInstrumentInApex(ctx) {
+ j.properties.Instrument = true
+ }
+
+ if j.shouldInstrument(ctx) {
+ outputFile = j.instrument(ctx, flags, outputFile, jarName)
+ }
+
+ // merge implementation jar with resources if necessary
+ implementationAndResourcesJar := outputFile
+ if j.resourceJar != nil {
+ jars := android.Paths{j.resourceJar, implementationAndResourcesJar}
+ combinedJar := android.PathForModuleOut(ctx, "withres", jarName).OutputPath
+ TransformJarsToJar(ctx, combinedJar, "for resources", jars, manifest,
+ false, nil, nil)
+ implementationAndResourcesJar = combinedJar
+ }
+
+ j.implementationAndResourcesJar = implementationAndResourcesJar
+
+ // Enable dex compilation for the APEX variants, unless it is disabled explicitly
+ apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ if j.DirectlyInAnyApex() && !apexInfo.IsForPlatform() {
+ if j.dexProperties.Compile_dex == nil {
+ j.dexProperties.Compile_dex = proptools.BoolPtr(true)
+ }
+ if j.deviceProperties.Hostdex == nil {
+ j.deviceProperties.Hostdex = proptools.BoolPtr(true)
+ }
+ }
+
+ if ctx.Device() && (Bool(j.properties.Installable) || Bool(j.dexProperties.Compile_dex)) {
+ if j.hasCode(ctx) {
+ if j.shouldInstrumentStatic(ctx) {
+ j.dexer.extraProguardFlagFiles = append(j.dexer.extraProguardFlagFiles,
+ android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags"))
+ }
+ // Dex compilation
+ var dexOutputFile android.OutputPath
+ dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), outputFile, jarName)
+ if ctx.Failed() {
+ return
+ }
+
+ // Hidden API CSV generation and dex encoding
+ dexOutputFile = j.hiddenAPIExtractAndEncode(ctx, dexOutputFile, j.implementationJarFile,
+ proptools.Bool(j.dexProperties.Uncompress_dex))
+
+ // merge dex jar with resources if necessary
+ if j.resourceJar != nil {
+ jars := android.Paths{dexOutputFile, j.resourceJar}
+ combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName).OutputPath
+ TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{},
+ false, nil, nil)
+ if *j.dexProperties.Uncompress_dex {
+ combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName).OutputPath
+ TransformZipAlign(ctx, combinedAlignedJar, combinedJar)
+ dexOutputFile = combinedAlignedJar
+ } else {
+ dexOutputFile = combinedJar
+ }
+ }
+
+ j.dexJarFile = dexOutputFile
+
+ // Dexpreopting
+ j.dexpreopt(ctx, dexOutputFile)
+
+ outputFile = dexOutputFile
+ } else {
+ // There is no code to compile into a dex jar, make sure the resources are propagated
+ // to the APK if this is an app.
+ outputFile = implementationAndResourcesJar
+ j.dexJarFile = j.resourceJar
+ }
+
+ if ctx.Failed() {
+ return
+ }
+ } else {
+ outputFile = implementationAndResourcesJar
+ }
+
+ if ctx.Device() {
+ lintSDKVersionString := func(sdkSpec android.SdkSpec) string {
+ if v := sdkSpec.ApiLevel; !v.IsPreview() {
+ return v.String()
+ } else {
+ return ctx.Config().DefaultAppTargetSdk(ctx).String()
+ }
+ }
+
+ j.linter.name = ctx.ModuleName()
+ j.linter.srcs = srcFiles
+ j.linter.srcJars = srcJars
+ j.linter.classpath = append(append(android.Paths(nil), flags.bootClasspath...), flags.classpath...)
+ j.linter.classes = j.implementationJarFile
+ j.linter.minSdkVersion = lintSDKVersionString(j.MinSdkVersion(ctx))
+ j.linter.targetSdkVersion = lintSDKVersionString(j.TargetSdkVersion(ctx))
+ j.linter.compileSdkVersion = lintSDKVersionString(j.SdkVersion(ctx))
+ j.linter.javaLanguageLevel = flags.javaVersion.String()
+ j.linter.kotlinLanguageLevel = "1.3"
+ if !apexInfo.IsForPlatform() && ctx.Config().UnbundledBuildApps() {
+ j.linter.buildModuleReportZip = true
+ }
+ j.linter.lint(ctx)
+ }
+
+ ctx.CheckbuildFile(outputFile)
+
+ ctx.SetProvider(JavaInfoProvider, JavaInfo{
+ HeaderJars: android.PathsIfNonNil(j.headerJarFile),
+ ImplementationAndResourcesJars: android.PathsIfNonNil(j.implementationAndResourcesJar),
+ ImplementationJars: android.PathsIfNonNil(j.implementationJarFile),
+ ResourceJars: android.PathsIfNonNil(j.resourceJar),
+ AidlIncludeDirs: j.exportAidlIncludeDirs,
+ SrcJarArgs: j.srcJarArgs,
+ SrcJarDeps: j.srcJarDeps,
+ ExportedPlugins: j.exportedPluginJars,
+ ExportedPluginClasses: j.exportedPluginClasses,
+ ExportedPluginDisableTurbine: j.exportedDisableTurbine,
+ JacocoReportClassesFile: j.jacocoReportClassesFile,
+ })
+
+ // Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
+ j.outputFile = outputFile.WithoutRel()
+}
+
+func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, idx int,
+ srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.WritablePath {
+
+ kzipName := pathtools.ReplaceExtension(jarName, "kzip")
+ if idx >= 0 {
+ kzipName = strings.TrimSuffix(jarName, filepath.Ext(jarName)) + strconv.Itoa(idx) + ".kzip"
+ jarName += strconv.Itoa(idx)
+ }
+
+ classes := android.PathForModuleOut(ctx, "javac", jarName).OutputPath
+ TransformJavaToClasses(ctx, classes, idx, srcFiles, srcJars, flags, extraJarDeps)
+
+ if ctx.Config().EmitXrefRules() {
+ extractionFile := android.PathForModuleOut(ctx, kzipName)
+ emitXrefRule(ctx, extractionFile, idx, srcFiles, srcJars, flags, extraJarDeps)
+ j.kytheFiles = append(j.kytheFiles, extractionFile)
+ }
+
+ return classes
+}
+
+// Check for invalid kotlinc flags. Only use this for flags explicitly passed by the user,
+// since some of these flags may be used internally.
+func CheckKotlincFlags(ctx android.ModuleContext, flags []string) {
+ for _, flag := range flags {
+ flag = strings.TrimSpace(flag)
+
+ if !strings.HasPrefix(flag, "-") {
+ ctx.PropertyErrorf("kotlincflags", "Flag `%s` must start with `-`", flag)
+ } else if strings.HasPrefix(flag, "-Xintellij-plugin-root") {
+ ctx.PropertyErrorf("kotlincflags",
+ "Bad flag: `%s`, only use internal compiler for consistency.", flag)
+ } else if inList(flag, config.KotlincIllegalFlags) {
+ ctx.PropertyErrorf("kotlincflags", "Flag `%s` already used by build system", flag)
+ } else if flag == "-include-runtime" {
+ ctx.PropertyErrorf("kotlincflags", "Bad flag: `%s`, do not include runtime.", flag)
+ } else {
+ args := strings.Split(flag, " ")
+ if args[0] == "-kotlin-home" {
+ ctx.PropertyErrorf("kotlincflags",
+ "Bad flag: `%s`, kotlin home already set to default (path to kotlinc in the repo).", flag)
+ }
+ }
+ }
+}
+
+func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars android.Paths,
+ deps deps, flags javaBuilderFlags, jarName string,
+ extraJars android.Paths) (headerJar, jarjarHeaderJar android.Path) {
+
+ var jars android.Paths
+ if len(srcFiles) > 0 || len(srcJars) > 0 {
+ // Compile java sources into turbine.jar.
+ turbineJar := android.PathForModuleOut(ctx, "turbine", jarName)
+ TransformJavaToHeaderClasses(ctx, turbineJar, srcFiles, srcJars, flags)
+ if ctx.Failed() {
+ return nil, nil
+ }
+ jars = append(jars, turbineJar)
+ }
+
+ jars = append(jars, extraJars...)
+
+ // Combine any static header libraries into classes-header.jar. If there is only
+ // one input jar this step will be skipped.
+ jars = append(jars, deps.staticHeaderJars...)
+
+ // we cannot skip the combine step for now if there is only one jar
+ // since we have to strip META-INF/TRANSITIVE dir from turbine.jar
+ combinedJar := android.PathForModuleOut(ctx, "turbine-combined", jarName)
+ TransformJarsToJar(ctx, combinedJar, "for turbine", jars, android.OptionalPath{},
+ false, nil, []string{"META-INF/TRANSITIVE"})
+ headerJar = combinedJar
+ jarjarHeaderJar = combinedJar
+
+ if j.expandJarjarRules != nil {
+ // Transform classes.jar into classes-jarjar.jar
+ jarjarFile := android.PathForModuleOut(ctx, "turbine-jarjar", jarName)
+ TransformJarJar(ctx, jarjarFile, headerJar, j.expandJarjarRules)
+ jarjarHeaderJar = jarjarFile
+ if ctx.Failed() {
+ return nil, nil
+ }
+ }
+
+ return headerJar, jarjarHeaderJar
+}
+
+func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags,
+ classesJar android.Path, jarName string) android.OutputPath {
+
+ specs := j.jacocoModuleToZipCommand(ctx)
+
+ jacocoReportClassesFile := android.PathForModuleOut(ctx, "jacoco-report-classes", jarName)
+ instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName).OutputPath
+
+ jacocoInstrumentJar(ctx, instrumentedJar, jacocoReportClassesFile, classesJar, specs)
+
+ j.jacocoReportClassesFile = jacocoReportClassesFile
+
+ return instrumentedJar
+}
+
+func (j *Module) HeaderJars() android.Paths {
+ if j.headerJarFile == nil {
+ return nil
+ }
+ return android.Paths{j.headerJarFile}
+}
+
+func (j *Module) ImplementationJars() android.Paths {
+ if j.implementationJarFile == nil {
+ return nil
+ }
+ return android.Paths{j.implementationJarFile}
+}
+
+func (j *Module) DexJarBuildPath() android.Path {
+ return j.dexJarFile
+}
+
+func (j *Module) DexJarInstallPath() android.Path {
+ return j.installFile
+}
+
+func (j *Module) ImplementationAndResourcesJars() android.Paths {
+ if j.implementationAndResourcesJar == nil {
+ return nil
+ }
+ return android.Paths{j.implementationAndResourcesJar}
+}
+
+func (j *Module) AidlIncludeDirs() android.Paths {
+ // exportAidlIncludeDirs is type android.Paths already
+ return j.exportAidlIncludeDirs
+}
+
+func (j *Module) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
+ return j.classLoaderContexts
+}
+
+// Collect information for opening IDE project files in java/jdeps.go.
+func (j *Module) IDEInfo(dpInfo *android.IdeInfo) {
+ dpInfo.Deps = append(dpInfo.Deps, j.CompilerDeps()...)
+ dpInfo.Srcs = append(dpInfo.Srcs, j.expandIDEInfoCompiledSrcs...)
+ dpInfo.SrcJars = append(dpInfo.SrcJars, j.compiledSrcJars.Strings()...)
+ dpInfo.Aidl_include_dirs = append(dpInfo.Aidl_include_dirs, j.deviceProperties.Aidl.Include_dirs...)
+ if j.expandJarjarRules != nil {
+ dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, j.expandJarjarRules.String())
+ }
+ dpInfo.Paths = append(dpInfo.Paths, j.modulePaths...)
+}
+
+func (j *Module) CompilerDeps() []string {
+ jdeps := []string{}
+ jdeps = append(jdeps, j.properties.Libs...)
+ jdeps = append(jdeps, j.properties.Static_libs...)
+ return jdeps
+}
+
+func (j *Module) hasCode(ctx android.ModuleContext) bool {
+ srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
+ return len(srcFiles) > 0 || len(ctx.GetDirectDepsWithTag(staticLibTag)) > 0
+}
+
+// Implements android.ApexModule
+func (j *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+ return j.depIsInSameApex(ctx, dep)
+}
+
+// Implements android.ApexModule
+func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+ sdkVersion android.ApiLevel) error {
+ sdkSpec := j.MinSdkVersion(ctx)
+ if !sdkSpec.Specified() {
+ return fmt.Errorf("min_sdk_version is not specified")
+ }
+ if sdkSpec.Kind == android.SdkCore {
+ return nil
+ }
+ ver, err := sdkSpec.EffectiveVersion(ctx)
+ if err != nil {
+ return err
+ }
+ if ver.GreaterThan(sdkVersion) {
+ return fmt.Errorf("newer SDK(%v)", ver)
+ }
+ return nil
+}
+
+func (j *Module) Stem() string {
+ return proptools.StringDefault(j.deviceProperties.Stem, j.Name())
+}
+
+// ConfigurationName returns the name of the module as used in build configuration.
+//
+// This is usually the same as BaseModuleName() except for the <x>.impl libraries created by
+// java_sdk_library in which case this is the BaseModuleName() without the ".impl" suffix,
+// i.e. just <x>.
+func (j *Module) ConfigurationName() string {
+ return proptools.StringDefault(j.deviceProperties.ConfigurationName, j.BaseModuleName())
+}
+
+func (j *Module) JacocoReportClassesFile() android.Path {
+ return j.jacocoReportClassesFile
+}
+
+func (j *Module) IsInstallable() bool {
+ return Bool(j.properties.Installable)
+}
+
+type sdkLinkType int
+
+const (
+ // TODO(jiyong) rename these for better readability. Make the allowed
+ // and disallowed link types explicit
+ // order is important here. See rank()
+ javaCore sdkLinkType = iota
+ javaSdk
+ javaSystem
+ javaModule
+ javaSystemServer
+ javaPlatform
+)
+
+func (lt sdkLinkType) String() string {
+ switch lt {
+ case javaCore:
+ return "core Java API"
+ case javaSdk:
+ return "Android API"
+ case javaSystem:
+ return "system API"
+ case javaModule:
+ return "module API"
+ case javaSystemServer:
+ return "system server API"
+ case javaPlatform:
+ return "private API"
+ default:
+ panic(fmt.Errorf("unrecognized linktype: %d", lt))
+ }
+}
+
+// rank determines the total order among sdkLinkType. An SDK link type of rank A can link to
+// another SDK link type of rank B only when B <= A. For example, a module linking to Android SDK
+// can't statically depend on modules that use Platform API.
+func (lt sdkLinkType) rank() int {
+ return int(lt)
+}
+
+type moduleWithSdkDep interface {
+ android.Module
+ getSdkLinkType(ctx android.BaseModuleContext, name string) (ret sdkLinkType, stubs bool)
+}
+
+func (m *Module) getSdkLinkType(ctx android.BaseModuleContext, name string) (ret sdkLinkType, stubs bool) {
+ switch name {
+ case "core.current.stubs", "legacy.core.platform.api.stubs", "stable.core.platform.api.stubs",
+ "stub-annotations", "private-stub-annotations-jar",
+ "core-lambda-stubs", "core-generated-annotation-stubs":
+ return javaCore, true
+ case "android_stubs_current":
+ return javaSdk, true
+ case "android_system_stubs_current":
+ return javaSystem, true
+ case "android_module_lib_stubs_current":
+ return javaModule, true
+ case "android_system_server_stubs_current":
+ return javaSystemServer, true
+ case "android_test_stubs_current":
+ return javaSystem, true
+ }
+
+ if stub, linkType := moduleStubLinkType(name); stub {
+ return linkType, true
+ }
+
+ ver := m.SdkVersion(ctx)
+ switch ver.Kind {
+ case android.SdkCore:
+ return javaCore, false
+ case android.SdkSystem:
+ return javaSystem, false
+ case android.SdkPublic:
+ return javaSdk, false
+ case android.SdkModule:
+ return javaModule, false
+ case android.SdkSystemServer:
+ return javaSystemServer, false
+ case android.SdkPrivate, android.SdkNone, android.SdkCorePlatform, android.SdkTest:
+ return javaPlatform, false
+ }
+
+ if !ver.Valid() {
+ panic(fmt.Errorf("sdk_version is invalid. got %q", ver.Raw))
+ }
+ return javaSdk, false
+}
+
+// checkSdkLinkType make sures the given dependency doesn't have a lower SDK link type rank than
+// this module's. See the comment on rank() for details and an example.
+func (j *Module) checkSdkLinkType(
+ ctx android.ModuleContext, dep moduleWithSdkDep, tag dependencyTag) {
+ if ctx.Host() {
+ return
+ }
+
+ myLinkType, stubs := j.getSdkLinkType(ctx, ctx.ModuleName())
+ if stubs {
+ return
+ }
+ depLinkType, _ := dep.getSdkLinkType(ctx, ctx.OtherModuleName(dep))
+
+ if myLinkType.rank() < depLinkType.rank() {
+ ctx.ModuleErrorf("compiles against %v, but dependency %q is compiling against %v. "+
+ "In order to fix this, consider adjusting sdk_version: OR platform_apis: "+
+ "property of the source or target module so that target module is built "+
+ "with the same or smaller API set when compared to the source.",
+ myLinkType, ctx.OtherModuleName(dep), depLinkType)
+ }
+}
+
+func (j *Module) collectDeps(ctx android.ModuleContext) deps {
+ var deps deps
+
+ if ctx.Device() {
+ sdkDep := decodeSdkDep(ctx, android.SdkContext(j))
+ if sdkDep.invalidVersion {
+ ctx.AddMissingDependencies(sdkDep.bootclasspath)
+ ctx.AddMissingDependencies(sdkDep.java9Classpath)
+ } else if sdkDep.useFiles {
+ // sdkDep.jar is actually equivalent to turbine header.jar.
+ deps.classpath = append(deps.classpath, sdkDep.jars...)
+ deps.aidlPreprocess = sdkDep.aidl
+ } else {
+ deps.aidlPreprocess = sdkDep.aidl
+ }
+ }
+
+ sdkLinkType, _ := j.getSdkLinkType(ctx, ctx.ModuleName())
+
+ ctx.VisitDirectDeps(func(module android.Module) {
+ otherName := ctx.OtherModuleName(module)
+ tag := ctx.OtherModuleDependencyTag(module)
+
+ if IsJniDepTag(tag) {
+ // Handled by AndroidApp.collectAppDeps
+ return
+ }
+ if tag == certificateTag {
+ // Handled by AndroidApp.collectAppDeps
+ return
+ }
+
+ if dep, ok := module.(SdkLibraryDependency); ok {
+ switch tag {
+ case libTag:
+ deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
+ case staticLibTag:
+ ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
+ }
+ } else if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
+ dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+ if sdkLinkType != javaPlatform &&
+ ctx.OtherModuleHasProvider(module, SyspropPublicStubInfoProvider) {
+ // dep is a sysprop implementation library, but this module is not linking against
+ // the platform, so it gets the sysprop public stubs library instead. Replace
+ // dep with the JavaInfo from the SyspropPublicStubInfoProvider.
+ syspropDep := ctx.OtherModuleProvider(module, SyspropPublicStubInfoProvider).(SyspropPublicStubInfo)
+ dep = syspropDep.JavaInfo
+ }
+ switch tag {
+ case bootClasspathTag:
+ deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars...)
+ case libTag, instrumentationForTag:
+ deps.classpath = append(deps.classpath, dep.HeaderJars...)
+ deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...)
+ addPlugins(&deps, dep.ExportedPlugins, dep.ExportedPluginClasses...)
+ deps.disableTurbine = deps.disableTurbine || dep.ExportedPluginDisableTurbine
+ case java9LibTag:
+ deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars...)
+ case staticLibTag:
+ deps.classpath = append(deps.classpath, dep.HeaderJars...)
+ deps.staticJars = append(deps.staticJars, dep.ImplementationJars...)
+ deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars...)
+ deps.staticResourceJars = append(deps.staticResourceJars, dep.ResourceJars...)
+ deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...)
+ addPlugins(&deps, dep.ExportedPlugins, dep.ExportedPluginClasses...)
+ // Turbine doesn't run annotation processors, so any module that uses an
+ // annotation processor that generates API is incompatible with the turbine
+ // optimization.
+ deps.disableTurbine = deps.disableTurbine || dep.ExportedPluginDisableTurbine
+ case pluginTag:
+ if plugin, ok := module.(*Plugin); ok {
+ if plugin.pluginProperties.Processor_class != nil {
+ addPlugins(&deps, dep.ImplementationAndResourcesJars, *plugin.pluginProperties.Processor_class)
+ } else {
+ addPlugins(&deps, dep.ImplementationAndResourcesJars)
+ }
+ // Turbine doesn't run annotation processors, so any module that uses an
+ // annotation processor that generates API is incompatible with the turbine
+ // optimization.
+ deps.disableTurbine = deps.disableTurbine || Bool(plugin.pluginProperties.Generates_api)
+ } else {
+ ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName)
+ }
+ case errorpronePluginTag:
+ if _, ok := module.(*Plugin); ok {
+ deps.errorProneProcessorPath = append(deps.errorProneProcessorPath, dep.ImplementationAndResourcesJars...)
+ } else {
+ ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName)
+ }
+ case exportedPluginTag:
+ if plugin, ok := module.(*Plugin); ok {
+ j.exportedPluginJars = append(j.exportedPluginJars, dep.ImplementationAndResourcesJars...)
+ if plugin.pluginProperties.Processor_class != nil {
+ j.exportedPluginClasses = append(j.exportedPluginClasses, *plugin.pluginProperties.Processor_class)
+ }
+ // Turbine doesn't run annotation processors, so any module that uses an
+ // annotation processor that generates API is incompatible with the turbine
+ // optimization.
+ j.exportedDisableTurbine = Bool(plugin.pluginProperties.Generates_api)
+ } else {
+ ctx.PropertyErrorf("exported_plugins", "%q is not a java_plugin module", otherName)
+ }
+ case kotlinStdlibTag:
+ deps.kotlinStdlib = append(deps.kotlinStdlib, dep.HeaderJars...)
+ case kotlinAnnotationsTag:
+ deps.kotlinAnnotations = dep.HeaderJars
+ case syspropPublicStubDepTag:
+ // This is a sysprop implementation library, forward the JavaInfoProvider from
+ // the corresponding sysprop public stub library as SyspropPublicStubInfoProvider.
+ ctx.SetProvider(SyspropPublicStubInfoProvider, SyspropPublicStubInfo{
+ JavaInfo: dep,
+ })
+ }
+ } else if dep, ok := module.(android.SourceFileProducer); ok {
+ switch tag {
+ case libTag:
+ checkProducesJars(ctx, dep)
+ deps.classpath = append(deps.classpath, dep.Srcs()...)
+ case staticLibTag:
+ checkProducesJars(ctx, dep)
+ deps.classpath = append(deps.classpath, dep.Srcs()...)
+ deps.staticJars = append(deps.staticJars, dep.Srcs()...)
+ deps.staticHeaderJars = append(deps.staticHeaderJars, dep.Srcs()...)
+ }
+ } else {
+ switch tag {
+ case bootClasspathTag:
+ // If a system modules dependency has been added to the bootclasspath
+ // then add its libs to the bootclasspath.
+ sm := module.(SystemModulesProvider)
+ deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars()...)
+
+ case systemModulesTag:
+ if deps.systemModules != nil {
+ panic("Found two system module dependencies")
+ }
+ sm := module.(SystemModulesProvider)
+ outputDir, outputDeps := sm.OutputDirAndDeps()
+ deps.systemModules = &systemModules{outputDir, outputDeps}
+ }
+ }
+
+ addCLCFromDep(ctx, module, j.classLoaderContexts)
+ })
+
+ return deps
+}
+
+func addPlugins(deps *deps, pluginJars android.Paths, pluginClasses ...string) {
+ deps.processorPath = append(deps.processorPath, pluginJars...)
+ deps.processorClasses = append(deps.processorClasses, pluginClasses...)
+}
+
+// TODO(b/132357300) Generalize SdkLibrarComponentDependency to non-SDK libraries and merge with
+// this interface.
+type ProvidesUsesLib interface {
+ ProvidesUsesLib() *string
+}
+
+func (j *Module) ProvidesUsesLib() *string {
+ return j.usesLibraryProperties.Provides_uses_lib
+}
diff --git a/java/boot_image.go b/java/boot_image.go
new file mode 100644
index 0000000..0c47976
--- /dev/null
+++ b/java/boot_image.go
@@ -0,0 +1,334 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "fmt"
+ "strings"
+
+ "android/soong/android"
+ "android/soong/dexpreopt"
+ "github.com/google/blueprint/proptools"
+
+ "github.com/google/blueprint"
+)
+
+func init() {
+ RegisterBootImageBuildComponents(android.InitRegistrationContext)
+
+ // TODO(b/177892522): Remove after has been replaced by bootclasspath_fragments
+ android.RegisterSdkMemberType(&bootImageMemberType{
+ SdkMemberTypeBase: android.SdkMemberTypeBase{
+ PropertyName: "boot_images",
+ SupportsSdk: true,
+ },
+ })
+
+ android.RegisterSdkMemberType(&bootImageMemberType{
+ SdkMemberTypeBase: android.SdkMemberTypeBase{
+ PropertyName: "bootclasspath_fragments",
+ SupportsSdk: true,
+ },
+ })
+}
+
+func RegisterBootImageBuildComponents(ctx android.RegistrationContext) {
+ // TODO(b/177892522): Remove after has been replaced by bootclasspath_fragment
+ ctx.RegisterModuleType("boot_image", bootImageFactory)
+ ctx.RegisterModuleType("prebuilt_boot_image", prebuiltBootImageFactory)
+
+ ctx.RegisterModuleType("bootclasspath_fragment", bootImageFactory)
+ ctx.RegisterModuleType("prebuilt_bootclasspath_fragment", prebuiltBootImageFactory)
+}
+
+type bootImageContentDependencyTag struct {
+ blueprint.BaseDependencyTag
+}
+
+// Avoid having to make boot image content visible to the boot image.
+//
+// This is a temporary workaround to make it easier to migrate to boot image modules with proper
+// dependencies.
+// TODO(b/177892522): Remove this and add needed visibility.
+func (b bootImageContentDependencyTag) ExcludeFromVisibilityEnforcement() {
+}
+
+// The tag used for the dependency between the boot image module and its contents.
+var bootImageContentDepTag = bootImageContentDependencyTag{}
+
+var _ android.ExcludeFromVisibilityEnforcementTag = bootImageContentDepTag
+
+func IsbootImageContentDepTag(tag blueprint.DependencyTag) bool {
+ return tag == bootImageContentDepTag
+}
+
+type bootImageProperties struct {
+ // The name of the image this represents.
+ //
+ // If specified then it must be one of "art" or "boot".
+ Image_name *string
+
+ // The contents of this boot image, could be either java_library, java_sdk_library, or boot_image.
+ //
+ // The order of this list matters as it is the order that is used in the bootclasspath.
+ Contents []string
+}
+
+type BootImageModule struct {
+ android.ModuleBase
+ android.ApexModuleBase
+ android.SdkBase
+ properties bootImageProperties
+}
+
+func bootImageFactory() android.Module {
+ m := &BootImageModule{}
+ m.AddProperties(&m.properties)
+ android.InitApexModule(m)
+ android.InitSdkAwareModule(m)
+ android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
+
+ // Perform some consistency checking to ensure that the configuration is correct.
+ android.AddLoadHook(m, func(ctx android.LoadHookContext) {
+ bootImageConsistencyCheck(ctx, m)
+ })
+ return m
+}
+
+func bootImageConsistencyCheck(ctx android.EarlyModuleContext, m *BootImageModule) {
+ contents := m.properties.Contents
+ if m.properties.Image_name == nil && len(contents) == 0 {
+ ctx.ModuleErrorf(`neither of the "image_name" and "contents" properties have been supplied, please supply exactly one`)
+ }
+ if m.properties.Image_name != nil && len(contents) != 0 {
+ ctx.ModuleErrorf(`both of the "image_name" and "contents" properties have been supplied, please supply exactly one`)
+ }
+ imageName := proptools.String(m.properties.Image_name)
+ if imageName == "art" {
+ // Get the configuration for the art apex jars. Do not use getImageConfig(ctx) here as this is
+ // too early in the Soong processing for that to work.
+ global := dexpreopt.GetGlobalConfig(ctx)
+ modules := global.ArtApexJars
+
+ // Make sure that the apex specified in the configuration is consistent and is one for which
+ // this boot image is available.
+ jars := []string{}
+ commonApex := ""
+ for i := 0; i < modules.Len(); i++ {
+ apex := modules.Apex(i)
+ jar := modules.Jar(i)
+ if apex == "platform" {
+ ctx.ModuleErrorf("ArtApexJars is invalid as it requests a platform variant of %q", jar)
+ continue
+ }
+ if !m.AvailableFor(apex) {
+ ctx.ModuleErrorf("incompatible with ArtApexJars which expects this to be in apex %q but this is only in apexes %q",
+ apex, m.ApexAvailable())
+ continue
+ }
+ if commonApex == "" {
+ commonApex = apex
+ } else if commonApex != apex {
+ ctx.ModuleErrorf("ArtApexJars configuration is inconsistent, expected all jars to be in the same apex but it specifies apex %q and %q",
+ commonApex, apex)
+ }
+ jars = append(jars, jar)
+ }
+
+ // Store the jars in the Contents property so that they can be used to add dependencies.
+ m.properties.Contents = jars
+ }
+}
+
+var BootImageInfoProvider = blueprint.NewProvider(BootImageInfo{})
+
+type BootImageInfo struct {
+ // The image config, internal to this module (and the dex_bootjars singleton).
+ //
+ // Will be nil if the BootImageInfo has not been provided for a specific module. That can occur
+ // when SkipDexpreoptBootJars(ctx) returns true.
+ imageConfig *bootImageConfig
+}
+
+func (i BootImageInfo) Modules() android.ConfiguredJarList {
+ return i.imageConfig.modules
+}
+
+// Get a map from ArchType to the associated boot image's contents for Android.
+//
+// Extension boot images only return their own files, not the files of the boot images they extend.
+func (i BootImageInfo) AndroidBootImageFilesByArchType() map[android.ArchType]android.OutputPaths {
+ files := map[android.ArchType]android.OutputPaths{}
+ if i.imageConfig != nil {
+ for _, variant := range i.imageConfig.variants {
+ // We also generate boot images for host (for testing), but we don't need those in the apex.
+ // TODO(b/177892522) - consider changing this to check Os.OsClass = android.Device
+ if variant.target.Os == android.Android {
+ files[variant.target.Arch.ArchType] = variant.imagesDeps
+ }
+ }
+ }
+ return files
+}
+
+func (b *BootImageModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+ tag := ctx.OtherModuleDependencyTag(dep)
+ if tag == bootImageContentDepTag {
+ // Boot image contents are automatically added to apex.
+ return true
+ }
+ if android.IsMetaDependencyTag(tag) {
+ // Cross-cutting metadata dependencies are metadata.
+ return false
+ }
+ panic(fmt.Errorf("boot_image module %q should not have a dependency on %q via tag %s", b, dep, android.PrettyPrintTag(tag)))
+}
+
+func (b *BootImageModule) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
+ return nil
+}
+
+func (b *BootImageModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+ ctx.AddDependency(ctx.Module(), bootImageContentDepTag, b.properties.Contents...)
+
+ if SkipDexpreoptBootJars(ctx) {
+ return
+ }
+
+ // Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
+ // path is retrieved from the dependency by GetGlobalSoongConfig(ctx).
+ dexpreopt.RegisterToolDeps(ctx)
+}
+
+func (b *BootImageModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // Nothing to do if skipping the dexpreopt of boot image jars.
+ if SkipDexpreoptBootJars(ctx) {
+ return
+ }
+
+ // Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
+ // GenerateSingletonBuildActions method as it cannot create it for itself.
+ dexpreopt.GetGlobalSoongConfig(ctx)
+
+ imageConfig := b.getImageConfig(ctx)
+ if imageConfig == nil {
+ return
+ }
+
+ // Construct the boot image info from the config.
+ info := BootImageInfo{imageConfig: imageConfig}
+
+ // Make it available for other modules.
+ ctx.SetProvider(BootImageInfoProvider, info)
+}
+
+func (b *BootImageModule) getImageConfig(ctx android.EarlyModuleContext) *bootImageConfig {
+ // Get a map of the image configs that are supported.
+ imageConfigs := genBootImageConfigs(ctx)
+
+ // Retrieve the config for this image.
+ imageNamePtr := b.properties.Image_name
+ if imageNamePtr == nil {
+ return nil
+ }
+
+ imageName := *imageNamePtr
+ imageConfig := imageConfigs[imageName]
+ if imageConfig == nil {
+ ctx.PropertyErrorf("image_name", "Unknown image name %q, expected one of %s", imageName, strings.Join(android.SortedStringKeys(imageConfigs), ", "))
+ return nil
+ }
+ return imageConfig
+}
+
+type bootImageMemberType struct {
+ android.SdkMemberTypeBase
+}
+
+func (b *bootImageMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
+ mctx.AddVariationDependencies(nil, dependencyTag, names...)
+}
+
+func (b *bootImageMemberType) IsInstance(module android.Module) bool {
+ _, ok := module.(*BootImageModule)
+ return ok
+}
+
+func (b *bootImageMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
+ if b.PropertyName == "boot_images" {
+ return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_boot_image")
+ } else {
+ return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_bootclasspath_fragment")
+ }
+}
+
+func (b *bootImageMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
+ return &bootImageSdkMemberProperties{}
+}
+
+type bootImageSdkMemberProperties struct {
+ android.SdkMemberPropertiesBase
+
+ Image_name *string
+}
+
+func (b *bootImageSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
+ module := variant.(*BootImageModule)
+
+ b.Image_name = module.properties.Image_name
+}
+
+func (b *bootImageSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
+ if b.Image_name != nil {
+ propertySet.AddProperty("image_name", *b.Image_name)
+ }
+}
+
+var _ android.SdkMemberType = (*bootImageMemberType)(nil)
+
+// A prebuilt version of the boot image module.
+//
+// At the moment this is basically just a boot image module that can be used as a prebuilt.
+// Eventually as more functionality is migrated into the boot image module from the singleton then
+// this will diverge.
+type prebuiltBootImageModule struct {
+ BootImageModule
+ prebuilt android.Prebuilt
+}
+
+func (module *prebuiltBootImageModule) Prebuilt() *android.Prebuilt {
+ return &module.prebuilt
+}
+
+func (module *prebuiltBootImageModule) Name() string {
+ return module.prebuilt.Name(module.ModuleBase.Name())
+}
+
+func prebuiltBootImageFactory() android.Module {
+ m := &prebuiltBootImageModule{}
+ m.AddProperties(&m.properties)
+ // This doesn't actually have any prebuilt files of its own so pass a placeholder for the srcs
+ // array.
+ android.InitPrebuiltModule(m, &[]string{"placeholder"})
+ android.InitApexModule(m)
+ android.InitSdkAwareModule(m)
+ android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
+
+ // Perform some consistency checking to ensure that the configuration is correct.
+ android.AddLoadHook(m, func(ctx android.LoadHookContext) {
+ bootImageConsistencyCheck(ctx, &m.BootImageModule)
+ })
+ return m
+}
diff --git a/java/boot_image_test.go b/java/boot_image_test.go
new file mode 100644
index 0000000..e1866de
--- /dev/null
+++ b/java/boot_image_test.go
@@ -0,0 +1,127 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "testing"
+
+ "android/soong/android"
+ "android/soong/dexpreopt"
+)
+
+// Contains some simple tests for boot_image logic, additional tests can be found in
+// apex/boot_image_test.go as the ART boot image requires modules from the ART apex.
+
+var prepareForTestWithBootImage = android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ dexpreopt.PrepareForTestByEnablingDexpreopt,
+)
+
+func TestUnknownBootImage(t *testing.T) {
+ prepareForTestWithBootImage.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ `\Qimage_name: Unknown image name "unknown", expected one of art, boot\E`)).
+ RunTestWithBp(t, `
+ boot_image {
+ name: "unknown-boot-image",
+ image_name: "unknown",
+ }
+ `)
+}
+
+func TestUnknownBootclasspathFragmentImageName(t *testing.T) {
+ prepareForTestWithBootImage.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ `\Qimage_name: Unknown image name "unknown", expected one of art, boot\E`)).
+ RunTestWithBp(t, `
+ bootclasspath_fragment {
+ name: "unknown-boot-image",
+ image_name: "unknown",
+ }
+ `)
+}
+
+func TestUnknownPrebuiltBootImage(t *testing.T) {
+ prepareForTestWithBootImage.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ `\Qimage_name: Unknown image name "unknown", expected one of art, boot\E`)).
+ RunTestWithBp(t, `
+ prebuilt_boot_image {
+ name: "unknown-boot-image",
+ image_name: "unknown",
+ }
+ `)
+}
+
+func TestBootImageInconsistentArtConfiguration_Platform(t *testing.T) {
+ android.GroupFixturePreparers(
+ prepareForTestWithBootImage,
+ dexpreopt.FixtureSetArtBootJars("platform:foo", "apex:bar"),
+ ).
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ `\QArtApexJars is invalid as it requests a platform variant of "foo"\E`)).
+ RunTestWithBp(t, `
+ boot_image {
+ name: "boot-image",
+ image_name: "art",
+ apex_available: [
+ "apex",
+ ],
+ }
+ `)
+}
+
+func TestBootImageInconsistentArtConfiguration_ApexMixture(t *testing.T) {
+ android.GroupFixturePreparers(
+ prepareForTestWithBootImage,
+ dexpreopt.FixtureSetArtBootJars("apex1:foo", "apex2:bar"),
+ ).
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ `\QArtApexJars configuration is inconsistent, expected all jars to be in the same apex but it specifies apex "apex1" and "apex2"\E`)).
+ RunTestWithBp(t, `
+ boot_image {
+ name: "boot-image",
+ image_name: "art",
+ apex_available: [
+ "apex1",
+ "apex2",
+ ],
+ }
+ `)
+}
+
+func TestBootImageWithoutImageNameOrContents(t *testing.T) {
+ prepareForTestWithBootImage.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ `\Qneither of the "image_name" and "contents" properties\E`)).
+ RunTestWithBp(t, `
+ boot_image {
+ name: "boot-image",
+ }
+ `)
+}
+
+func TestBootImageWithImageNameAndContents(t *testing.T) {
+ prepareForTestWithBootImage.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ `\Qboth of the "image_name" and "contents" properties\E`)).
+ RunTestWithBp(t, `
+ boot_image {
+ name: "boot-image",
+ image_name: "boot",
+ contents: ["other"],
+ }
+ `)
+}
diff --git a/java/boot_jars.go b/java/boot_jars.go
index 823275b..1fb3deb 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -49,14 +49,27 @@
return true
}
+// isActiveModule returns true if the given module should be considered for boot
+// jars, i.e. if it's enabled and the preferred one in case of source and
+// prebuilt alternatives.
+func isActiveModule(module android.Module) bool {
+ if !module.Enabled() {
+ return false
+ }
+ return android.IsModulePreferred(module)
+}
+
func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
config := ctx.Config()
if config.SkipBootJarsCheck() {
return
}
- // Populate a map from module name to APEX from the boot jars. If there is a problem
- // such as duplicate modules then fail and return immediately.
+ // Populate a map from module name to APEX from the boot jars. If there is a
+ // problem such as duplicate modules then fail and return immediately. Note
+ // that both module and APEX names are tracked by base names here, so we need
+ // to be careful to remove "prebuilt_" prefixes when comparing them with
+ // actual modules and APEX bundles.
moduleToApex := make(map[string]string)
if !populateMapFromConfiguredJarList(ctx, moduleToApex, config.NonUpdatableBootJars(), "BootJars") ||
!populateMapFromConfiguredJarList(ctx, moduleToApex, config.UpdatableBootJars(), "UpdatableBootJars") {
@@ -69,10 +82,14 @@
// Scan all the modules looking for the module/apex variants corresponding to the
// boot jars.
ctx.VisitAllModules(func(module android.Module) {
- name := ctx.ModuleName(module)
+ if !isActiveModule(module) {
+ return
+ }
+
+ name := android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName(module))
if apex, ok := moduleToApex[name]; ok {
apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
- if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApex(apex) {
+ if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApexByBaseName(apex) {
// The module name/apex variant should be unique in the system but double check
// just in case something has gone wrong.
if existing, ok := nameToApexVariant[name]; ok {
diff --git a/java/builder.go b/java/builder.go
index 995160d..cde8731 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -40,7 +40,7 @@
// (if the rule produces .class files) or a .srcjar file (if the rule produces .java files).
// .srcjar files are unzipped into a temporary directory when compiled with javac.
// TODO(b/143658984): goma can't handle the --system argument to javac.
- javac, javacRE = remoteexec.MultiCommandStaticRules(pctx, "javac",
+ javac, javacRE = pctx.MultiCommandRemoteStaticRules("javac",
blueprint.RuleParams{
Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" "$out" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
@@ -80,6 +80,8 @@
func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
_ = pctx.VariableFunc("kytheCuEncoding",
func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuEncoding() })
+ _ = pctx.VariableFunc("kytheCuJavaSourceMax",
+ func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuJavaSourceMax() })
_ = pctx.SourcePathVariable("kytheVnames", "build/soong/vnames.json")
// Run it with -add-opens=java.base/java.nio=ALL-UNNAMED to avoid JDK9's warning about
// "Illegal reflective access by com.google.protobuf.Utf8$UnsafeProcessor ...
@@ -93,6 +95,7 @@
`KYTHE_CORPUS=${kytheCorpus} ` +
`KYTHE_VNAMES=${kytheVnames} ` +
`KYTHE_KZIP_ENCODING=${kytheCuEncoding} ` +
+ `KYTHE_JAVA_SOURCE_BATCH_SIZE=${kytheCuJavaSourceMax} ` +
`${config.SoongJavacWrapper} ${config.JavaCmd} ` +
`--add-opens=java.base/java.nio=ALL-UNNAMED ` +
`-jar ${config.JavaKytheExtractorJar} ` +
@@ -126,7 +129,7 @@
},
"abis", "allow-prereleased", "screen-densities", "sdk-version", "stem", "apkcerts", "partition")
- turbine, turbineRE = remoteexec.StaticRules(pctx, "turbine",
+ turbine, turbineRE = pctx.RemoteStaticRules("turbine",
blueprint.RuleParams{
Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
`$reTemplate${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.TurbineJar} --output $out.tmp ` +
@@ -147,14 +150,14 @@
&remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "turbine"},
ExecStrategy: "${config.RETurbineExecStrategy}",
Inputs: []string{"${config.TurbineJar}", "${out}.rsp", "$implicits"},
- RSPFile: "${out}.rsp",
+ RSPFiles: []string{"${out}.rsp"},
OutputFiles: []string{"$out.tmp"},
OutputDirectories: []string{"$outDir"},
ToolchainInputs: []string{"${config.JavaCmd}"},
Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
}, []string{"javacFlags", "bootClasspath", "classpath", "srcJars", "outDir", "javaVersion"}, []string{"implicits"})
- jar, jarRE = remoteexec.StaticRules(pctx, "jar",
+ jar, jarRE = pctx.RemoteStaticRules("jar",
blueprint.RuleParams{
Command: `$reTemplate${config.SoongZipCmd} -jar -o $out @$out.rsp`,
CommandDeps: []string{"${config.SoongZipCmd}"},
@@ -164,12 +167,12 @@
&remoteexec.REParams{
ExecStrategy: "${config.REJarExecStrategy}",
Inputs: []string{"${config.SoongZipCmd}", "${out}.rsp"},
- RSPFile: "${out}.rsp",
+ RSPFiles: []string{"${out}.rsp"},
OutputFiles: []string{"$out"},
Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
}, []string{"jarArgs"}, nil)
- zip, zipRE = remoteexec.StaticRules(pctx, "zip",
+ zip, zipRE = pctx.RemoteStaticRules("zip",
blueprint.RuleParams{
Command: `${config.SoongZipCmd} -o $out @$out.rsp`,
CommandDeps: []string{"${config.SoongZipCmd}"},
@@ -179,7 +182,7 @@
&remoteexec.REParams{
ExecStrategy: "${config.REZipExecStrategy}",
Inputs: []string{"${config.SoongZipCmd}", "${out}.rsp", "$implicits"},
- RSPFile: "${out}.rsp",
+ RSPFiles: []string{"${out}.rsp"},
OutputFiles: []string{"$out"},
Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
}, []string{"jarArgs"}, []string{"implicits"})
@@ -193,12 +196,19 @@
jarjar = pctx.AndroidStaticRule("jarjar",
blueprint.RuleParams{
- Command: "${config.JavaCmd} ${config.JavaVmFlags}" +
+ Command: "" +
+ // Jarjar doesn't exit with an error when the rules file contains a syntax error,
+ // leading to stale or missing files later in the build. Remove the output file
+ // before running jarjar.
+ "rm -f ${out} && " +
+ "${config.JavaCmd} ${config.JavaVmFlags}" +
// b/146418363 Enable Android specific jarjar transformer to drop compat annotations
// for newly repackaged classes. Dropping @UnsupportedAppUsage on repackaged classes
// avoids adding new hiddenapis after jarjar'ing.
" -DremoveAndroidCompatAnnotations=true" +
- " -jar ${config.JarjarCmd} process $rulesFile $in $out",
+ " -jar ${config.JarjarCmd} process $rulesFile $in $out && " +
+ // Turn a missing output file into a ninja error
+ `[ -e ${out} ] || (echo "Missing output file"; exit 1)`,
CommandDeps: []string{"${config.JavaCmd}", "${config.JarjarCmd}", "$rulesFile"},
},
"rulesFile")
@@ -234,7 +244,6 @@
func init() {
pctx.Import("android/soong/android")
pctx.Import("android/soong/java/config")
- pctx.Import("android/soong/remoteexec")
}
type javaBuilderFlags struct {
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
new file mode 100644
index 0000000..d497460
--- /dev/null
+++ b/java/classpath_fragment.go
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java
+
+import (
+ "fmt"
+ "strings"
+
+ "android/soong/android"
+)
+
+// Build rules and utilities to generate individual packages/modules/SdkExtensions/proto/classpaths.proto
+// config files based on build configuration to embed into /system and /apex on a device.
+//
+// See `derive_classpath` service that reads the configs at runtime and defines *CLASSPATH variables
+// on the device.
+
+type classpathType int
+
+const (
+ // Matches definition in packages/modules/SdkExtensions/proto/classpaths.proto
+ BOOTCLASSPATH classpathType = iota
+ DEX2OATBOOTCLASSPATH
+ SYSTEMSERVERCLASSPATH
+)
+
+func (c classpathType) String() string {
+ return [...]string{"BOOTCLASSPATH", "DEX2OATBOOTCLASSPATH", "SYSTEMSERVERCLASSPATH"}[c]
+}
+
+type classpathFragmentProperties struct {
+}
+
+// classpathFragment interface is implemented by a module that contributes jars to a *CLASSPATH
+// variables at runtime.
+type classpathFragment interface {
+ android.Module
+
+ classpathFragmentBase() *ClasspathFragmentBase
+}
+
+// ClasspathFragmentBase is meant to be embedded in any module types that implement classpathFragment;
+// such modules are expected to call initClasspathFragment().
+type ClasspathFragmentBase struct {
+ properties classpathFragmentProperties
+
+ outputFilepath android.OutputPath
+ installDirPath android.InstallPath
+}
+
+func (c *ClasspathFragmentBase) classpathFragmentBase() *ClasspathFragmentBase {
+ return c
+}
+
+// Initializes ClasspathFragmentBase struct. Must be called by all modules that include ClasspathFragmentBase.
+func initClasspathFragment(c classpathFragment) {
+ base := c.classpathFragmentBase()
+ c.AddProperties(&base.properties)
+}
+
+// Matches definition of Jar in packages/modules/SdkExtensions/proto/classpaths.proto
+type classpathJar struct {
+ path string
+ classpath classpathType
+ // TODO(satayev): propagate min/max sdk versions for the jars
+ minSdkVersion int32
+ maxSdkVersion int32
+}
+
+func (c *ClasspathFragmentBase) generateAndroidBuildActions(ctx android.ModuleContext) {
+ outputFilename := ctx.ModuleName() + ".pb"
+ c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
+ c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
+
+ var jars []classpathJar
+ jars = appendClasspathJar(jars, BOOTCLASSPATH, defaultBootclasspath(ctx)...)
+ jars = appendClasspathJar(jars, DEX2OATBOOTCLASSPATH, defaultBootImageConfig(ctx).getAnyAndroidVariant().dexLocationsDeps...)
+ jars = appendClasspathJar(jars, SYSTEMSERVERCLASSPATH, systemServerClasspath(ctx)...)
+
+ generatedJson := android.PathForModuleOut(ctx, outputFilename+".json")
+ writeClasspathsJson(ctx, generatedJson, jars)
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ BuiltTool("conv_classpaths_proto").
+ Flag("encode").
+ Flag("--format=json").
+ FlagWithInput("--input=", generatedJson).
+ FlagWithOutput("--output=", c.outputFilepath)
+
+ rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String())
+}
+
+func writeClasspathsJson(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) {
+ var content strings.Builder
+ fmt.Fprintf(&content, "{\n")
+ fmt.Fprintf(&content, "\"jars\": [\n")
+ for idx, jar := range jars {
+ fmt.Fprintf(&content, "{\n")
+
+ fmt.Fprintf(&content, "\"relativePath\": \"%s\",\n", jar.path)
+ fmt.Fprintf(&content, "\"classpath\": \"%s\"\n", jar.classpath)
+
+ if idx < len(jars)-1 {
+ fmt.Fprintf(&content, "},\n")
+ } else {
+ fmt.Fprintf(&content, "}\n")
+ }
+ }
+ fmt.Fprintf(&content, "]\n")
+ fmt.Fprintf(&content, "}\n")
+ android.WriteFileRule(ctx, output, content.String())
+}
+
+func appendClasspathJar(slice []classpathJar, classpathType classpathType, paths ...string) (result []classpathJar) {
+ result = append(result, slice...)
+ for _, path := range paths {
+ result = append(result, classpathJar{
+ path: path,
+ classpath: classpathType,
+ })
+ }
+ return
+}
+
+func (c *ClasspathFragmentBase) getAndroidMkEntries() []android.AndroidMkEntries {
+ return []android.AndroidMkEntries{android.AndroidMkEntries{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(c.outputFilepath),
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", c.installDirPath.ToMakePath().String())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.outputFilepath.Base())
+ },
+ },
+ }}
+}
diff --git a/java/config/Android.bp b/java/config/Android.bp
index 1983521..194e2c6 100644
--- a/java/config/Android.bp
+++ b/java/config/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-java-config",
pkgPath: "android/soong/java/config",
diff --git a/java/config/config.go b/java/config/config.go
index 31e2b0f..30c6f91 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -149,14 +149,14 @@
pctx.HostBinToolVariable("SoongJavacWrapper", "soong_javac_wrapper")
pctx.HostBinToolVariable("DexpreoptGen", "dexpreopt_gen")
- pctx.VariableFunc("REJavaPool", remoteexec.EnvOverrideFunc("RBE_JAVA_POOL", "java16"))
- pctx.VariableFunc("REJavacExecStrategy", remoteexec.EnvOverrideFunc("RBE_JAVAC_EXEC_STRATEGY", remoteexec.RemoteLocalFallbackExecStrategy))
- pctx.VariableFunc("RED8ExecStrategy", remoteexec.EnvOverrideFunc("RBE_D8_EXEC_STRATEGY", remoteexec.RemoteLocalFallbackExecStrategy))
- pctx.VariableFunc("RER8ExecStrategy", remoteexec.EnvOverrideFunc("RBE_R8_EXEC_STRATEGY", remoteexec.RemoteLocalFallbackExecStrategy))
- pctx.VariableFunc("RETurbineExecStrategy", remoteexec.EnvOverrideFunc("RBE_TURBINE_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
- pctx.VariableFunc("RESignApkExecStrategy", remoteexec.EnvOverrideFunc("RBE_SIGNAPK_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
- pctx.VariableFunc("REJarExecStrategy", remoteexec.EnvOverrideFunc("RBE_JAR_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
- pctx.VariableFunc("REZipExecStrategy", remoteexec.EnvOverrideFunc("RBE_ZIP_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
+ pctx.StaticVariableWithEnvOverride("REJavaPool", "RBE_JAVA_POOL", "java16")
+ pctx.StaticVariableWithEnvOverride("REJavacExecStrategy", "RBE_JAVAC_EXEC_STRATEGY", remoteexec.RemoteLocalFallbackExecStrategy)
+ pctx.StaticVariableWithEnvOverride("RED8ExecStrategy", "RBE_D8_EXEC_STRATEGY", remoteexec.RemoteLocalFallbackExecStrategy)
+ pctx.StaticVariableWithEnvOverride("RER8ExecStrategy", "RBE_R8_EXEC_STRATEGY", remoteexec.RemoteLocalFallbackExecStrategy)
+ pctx.StaticVariableWithEnvOverride("RETurbineExecStrategy", "RBE_TURBINE_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
+ pctx.StaticVariableWithEnvOverride("RESignApkExecStrategy", "RBE_SIGNAPK_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
+ pctx.StaticVariableWithEnvOverride("REJarExecStrategy", "RBE_JAR_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
+ pctx.StaticVariableWithEnvOverride("REZipExecStrategy", "RBE_ZIP_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
pctx.HostJavaToolVariable("JacocoCLIJar", "jacoco-cli.jar")
diff --git a/java/config/kotlin.go b/java/config/kotlin.go
index fd8e3db..6cb61f3 100644
--- a/java/config/kotlin.go
+++ b/java/config/kotlin.go
@@ -35,11 +35,16 @@
pctx.SourcePathVariable("KotlinAnnotationJar", "external/kotlinc/lib/annotations-13.0.jar")
pctx.SourcePathVariable("KotlinStdlibJar", KotlinStdlibJar)
- // These flags silence "Illegal reflective access" warnings when running kotlinc in OpenJDK9
- pctx.StaticVariable("KotlincSuppressJDK9Warnings", strings.Join([]string{
+ // These flags silence "Illegal reflective access" warnings when running kapt in OpenJDK9+
+ pctx.StaticVariable("KaptSuppressJDK9Warnings", strings.Join([]string{
"-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
"-J--add-opens=java.base/sun.net.www.protocol.jar=ALL-UNNAMED",
}, " "))
+
+ // These flags silence "Illegal reflective access" warnings when running kotlinc in OpenJDK9+
+ pctx.StaticVariable("KotlincSuppressJDK9Warnings", strings.Join([]string{
+ "-J--add-opens=java.base/java.util=ALL-UNNAMED", // https://youtrack.jetbrains.com/issue/KT-43704
+ }, " "))
}
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index 4914d74..39fb04a 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -97,15 +97,15 @@
}
ctx.VisitDirectDepsWithTag(deviceHostConverterDepTag, func(m android.Module) {
- if dep, ok := m.(Dependency); ok {
- d.headerJars = append(d.headerJars, dep.HeaderJars()...)
- d.implementationJars = append(d.implementationJars, dep.ImplementationJars()...)
- d.implementationAndResourceJars = append(d.implementationAndResourceJars, dep.ImplementationAndResourcesJars()...)
- d.resourceJars = append(d.resourceJars, dep.ResourceJars()...)
+ if ctx.OtherModuleHasProvider(m, JavaInfoProvider) {
+ dep := ctx.OtherModuleProvider(m, JavaInfoProvider).(JavaInfo)
+ d.headerJars = append(d.headerJars, dep.HeaderJars...)
+ d.implementationJars = append(d.implementationJars, dep.ImplementationJars...)
+ d.implementationAndResourceJars = append(d.implementationAndResourceJars, dep.ImplementationAndResourcesJars...)
+ d.resourceJars = append(d.resourceJars, dep.ResourceJars...)
- srcJarArgs, srcJarDeps := dep.SrcJarArgs()
- d.srcJarArgs = append(d.srcJarArgs, srcJarArgs...)
- d.srcJarDeps = append(d.srcJarDeps, srcJarDeps...)
+ d.srcJarArgs = append(d.srcJarArgs, dep.SrcJarArgs...)
+ d.srcJarDeps = append(d.srcJarDeps, dep.SrcJarDeps...)
} else {
ctx.PropertyErrorf("libs", "module %q cannot be used as a dependency", ctx.OtherModuleName(m))
}
@@ -131,22 +131,21 @@
d.combinedHeaderJar = d.headerJars[0]
}
-}
+ ctx.SetProvider(JavaInfoProvider, JavaInfo{
+ HeaderJars: d.headerJars,
+ ImplementationAndResourcesJars: d.implementationAndResourceJars,
+ ImplementationJars: d.implementationJars,
+ ResourceJars: d.resourceJars,
+ SrcJarArgs: d.srcJarArgs,
+ SrcJarDeps: d.srcJarDeps,
+ })
-var _ Dependency = (*DeviceHostConverter)(nil)
+}
func (d *DeviceHostConverter) HeaderJars() android.Paths {
return d.headerJars
}
-func (d *DeviceHostConverter) ImplementationJars() android.Paths {
- return d.implementationJars
-}
-
-func (d *DeviceHostConverter) ResourceJars() android.Paths {
- return d.resourceJars
-}
-
func (d *DeviceHostConverter) ImplementationAndResourcesJars() android.Paths {
return d.implementationAndResourceJars
}
@@ -167,14 +166,6 @@
return nil
}
-func (d *DeviceHostConverter) ExportedPlugins() (android.Paths, []string, bool) {
- return nil, nil, false
-}
-
-func (d *DeviceHostConverter) SrcJarArgs() ([]string, android.Paths) {
- return d.srcJarArgs, d.srcJarDeps
-}
-
func (d *DeviceHostConverter) JacocoReportClassesFile() android.Path {
return nil
}
diff --git a/java/dex.go b/java/dex.go
index 055d479..7898e9d 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -15,6 +15,7 @@
package java
import (
+ "strconv"
"strings"
"github.com/google/blueprint"
@@ -83,7 +84,7 @@
return BoolDefault(d.dexProperties.Optimize.Enabled, d.dexProperties.Optimize.EnabledByDefault)
}
-var d8, d8RE = remoteexec.MultiCommandStaticRules(pctx, "d8",
+var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8",
blueprint.RuleParams{
Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
`$d8Template${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` +
@@ -111,7 +112,7 @@
},
}, []string{"outDir", "d8Flags", "zipFlags"}, nil)
-var r8, r8RE = remoteexec.MultiCommandStaticRules(pctx, "r8",
+var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8",
blueprint.RuleParams{
Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
`rm -f "$outDict" && rm -rf "${outUsageDir}" && ` +
@@ -157,7 +158,7 @@
}, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir",
"r8Flags", "zipFlags"}, []string{"implicits"})
-func (d *dexer) dexCommonFlags(ctx android.ModuleContext, minSdkVersion sdkSpec) []string {
+func (d *dexer) dexCommonFlags(ctx android.ModuleContext, minSdkVersion android.SdkSpec) []string {
flags := d.dexProperties.Dxflags
// Translate all the DX flags to D8 ones until all the build files have been migrated
// to D8 flags. See: b/69377755
@@ -174,12 +175,12 @@
"--verbose")
}
- effectiveVersion, err := minSdkVersion.effectiveVersion(ctx)
+ effectiveVersion, err := minSdkVersion.EffectiveVersion(ctx)
if err != nil {
ctx.PropertyErrorf("min_sdk_version", "%s", err)
}
- flags = append(flags, "--min-api "+effectiveVersion.asNumberString())
+ flags = append(flags, "--min-api "+strconv.Itoa(effectiveVersion.FinalOrFutureInt()))
return flags
}
@@ -204,8 +205,9 @@
// - prevent ProGuard stripping subclass in the support library that extends class added in the higher SDK version.
// See b/20667396
var proguardRaiseDeps classpath
- ctx.VisitDirectDepsWithTag(proguardRaiseTag, func(dep android.Module) {
- proguardRaiseDeps = append(proguardRaiseDeps, dep.(Dependency).HeaderJars()...)
+ ctx.VisitDirectDepsWithTag(proguardRaiseTag, func(m android.Module) {
+ dep := ctx.OtherModuleProvider(m, JavaInfoProvider).(JavaInfo)
+ proguardRaiseDeps = append(proguardRaiseDeps, dep.HeaderJars...)
})
r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
@@ -259,14 +261,17 @@
r8Flags = append(r8Flags, "--debug")
}
+ // TODO(b/180878971): missing classes should be added to the relevant builds.
+ r8Flags = append(r8Flags, "-ignorewarnings")
+
return r8Flags, r8Deps
}
-func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, minSdkVersion sdkSpec,
- classesJar android.Path, jarName string) android.ModuleOutPath {
+func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, minSdkVersion android.SdkSpec,
+ classesJar android.Path, jarName string) android.OutputPath {
// Compile classes.jar into classes.dex and then javalib.jar
- javalibJar := android.PathForModuleOut(ctx, "dex", jarName)
+ javalibJar := android.PathForModuleOut(ctx, "dex", jarName).OutputPath
outDir := android.PathForModuleOut(ctx, "dex")
zipFlags := "--ignore_missing_files"
@@ -329,7 +334,7 @@
})
}
if proptools.Bool(d.dexProperties.Uncompress_dex) {
- alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName)
+ alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName).OutputPath
TransformZipAlign(ctx, alignedJavalibJar, javalibJar)
javalibJar = alignedJavalibJar
}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index b5830c7..3571590 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -30,14 +30,21 @@
installPath android.InstallPath
uncompressedDex bool
isSDKLibrary bool
+ isApp bool
isTest bool
isPresignedPrebuilt bool
manifestFile android.Path
+ statusFile android.WritablePath
enforceUsesLibs bool
classLoaderContexts dexpreopt.ClassLoaderContextMap
builtInstalled string
+
+ // A path to a dexpreopt.config file generated by Soong for libraries that may be used as a
+ // <uses-library> by Make modules. The path is passed to Make via LOCAL_SOONG_DEXPREOPT_CONFIG
+ // variable. If the path is nil, no config is generated (which is the case for apps and tests).
+ configPath android.WritablePath
}
type DexpreoptProperties struct {
@@ -112,25 +119,58 @@
return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
}
-func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.ModuleOutPath) {
+func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.WritablePath) {
// TODO(b/148690468): The check on d.installPath is to bail out in cases where
// the dexpreopter struct hasn't been fully initialized before we're called,
// e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively
// disabled, even if installable is true.
- if d.dexpreoptDisabled(ctx) || d.installPath.Base() == "." {
+ if d.installPath.Base() == "." {
+ return
+ }
+
+ dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
+
+ providesUsesLib := ctx.ModuleName()
+ if ulib, ok := ctx.Module().(ProvidesUsesLib); ok {
+ name := ulib.ProvidesUsesLib()
+ if name != nil {
+ providesUsesLib = *name
+ }
+ }
+
+ if !d.isApp && !d.isTest {
+ // Slim dexpreopt config is serialized to dexpreopt.config files and used by
+ // dex_preopt_config_merger.py to get information about <uses-library> dependencies.
+ // Note that it might be needed even if dexpreopt is disabled for this module.
+ slimDexpreoptConfig := &dexpreopt.ModuleConfig{
+ Name: ctx.ModuleName(),
+ DexLocation: dexLocation,
+ EnforceUsesLibraries: d.enforceUsesLibs,
+ ProvidesUsesLibrary: providesUsesLib,
+ ClassLoaderContexts: d.classLoaderContexts,
+ // The rest of the fields are not needed.
+ }
+ d.configPath = android.PathForModuleOut(ctx, "dexpreopt", "dexpreopt.config")
+ dexpreopt.WriteSlimModuleConfigForMake(ctx, slimDexpreoptConfig, d.configPath)
+ }
+
+ if d.dexpreoptDisabled(ctx) {
return
}
globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
global := dexpreopt.GetGlobalConfig(ctx)
+
+ isSystemServerJar := inList(ctx.ModuleName(), global.SystemServerJars)
+
bootImage := defaultBootImageConfig(ctx)
- dexFiles := bootImage.dexPathsDeps.Paths()
- // The dex locations for all Android variants are identical.
- dexLocations := bootImage.getAnyAndroidVariant().dexLocationsDeps
if global.UseArtImage {
bootImage = artBootImageConfig(ctx)
}
+ // System server jars are an exception: they are dexpreopted without updatable bootclasspath.
+ dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp && !isSystemServerJar)
+
targets := ctx.MultiTargets()
if len(targets) == 0 {
// assume this is a java library, dexpreopt for all arches for now
@@ -139,7 +179,7 @@
targets = append(targets, target)
}
}
- if inList(ctx.ModuleName(), global.SystemServerJars) && !d.isSDKLibrary {
+ if isSystemServerJar && !d.isSDKLibrary {
// If the module is not an SDK library and it's a system server jar, only preopt the primary arch.
targets = targets[:1]
}
@@ -157,8 +197,6 @@
// The image locations for all Android variants are identical.
imageLocations := bootImage.getAnyAndroidVariant().imageLocations()
- dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
-
var profileClassListing android.OptionalPath
var profileBootListing android.OptionalPath
profileIsTextListing := false
@@ -177,12 +215,13 @@
}
}
+ // Full dexpreopt config, used to create dexpreopt build rules.
dexpreoptConfig := &dexpreopt.ModuleConfig{
Name: ctx.ModuleName(),
DexLocation: dexLocation,
BuildPath: android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath,
DexPath: dexJarFile,
- ManifestPath: d.manifestFile,
+ ManifestPath: android.OptionalPathForPath(d.manifestFile),
UncompressedDex: d.uncompressedDex,
HasApkLibraries: false,
PreoptFlags: nil,
@@ -191,15 +230,17 @@
ProfileIsTextListing: profileIsTextListing,
ProfileBootListing: profileBootListing,
- EnforceUsesLibraries: d.enforceUsesLibs,
- ClassLoaderContexts: d.classLoaderContexts,
+ EnforceUsesLibrariesStatusFile: dexpreopt.UsesLibrariesStatusFile(ctx),
+ EnforceUsesLibraries: d.enforceUsesLibs,
+ ProvidesUsesLibrary: providesUsesLib,
+ ClassLoaderContexts: d.classLoaderContexts,
Archs: archs,
DexPreoptImages: images,
DexPreoptImagesDeps: imagesDeps,
DexPreoptImageLocations: imageLocations,
- PreoptBootClassPathDexFiles: dexFiles,
+ PreoptBootClassPathDexFiles: dexFiles.Paths(),
PreoptBootClassPathDexLocations: dexLocations,
PreoptExtractedApk: false,
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 062005b..e57c3e9 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -15,6 +15,7 @@
package java
import (
+ "fmt"
"path/filepath"
"sort"
"strings"
@@ -25,6 +26,25 @@
"github.com/google/blueprint/proptools"
)
+// =================================================================================================
+// WIP - see http://b/177892522 for details
+//
+// The build support for boot images is currently being migrated away from singleton to modules so
+// the documentation may not be strictly accurate. Rather than update the documentation at every
+// step which will create a lot of churn the changes that have been made will be listed here and the
+// documentation will be updated once it is closer to the final result.
+//
+// Changes:
+// 1) dex_bootjars is now a singleton module and not a plain singleton.
+// 2) Boot images are now represented by the boot_image module type.
+// 3) The art boot image is called "art-boot-image", the framework boot image is called
+// "framework-boot-image".
+// 4) They are defined in art/build/boot/Android.bp and frameworks/base/boot/Android.bp
+// respectively.
+// 5) Each boot_image retrieves the appropriate boot image configuration from the map returned by
+// genBootImageConfigs() using the image_name specified in the boot_image module.
+// =================================================================================================
+
// This comment describes:
// 1. ART boot images in general (their types, structure, file layout, etc.)
// 2. build system support for boot images
@@ -124,7 +144,7 @@
// The primary ART boot image needs to be compiled with one dex2oat invocation that depends on DEX
// jars for the core libraries. Framework boot image extension needs to be compiled with one dex2oat
// invocation that depends on the primary ART boot image and all bootclasspath DEX jars except the
-// Core libraries.
+// core libraries as they are already part of the primary ART boot image.
//
// 2.1. Libraries that go in the boot images
// -----------------------------------------
@@ -191,6 +211,15 @@
// apps instead of the Framework boot image extension (see DEXPREOPT_USE_ART_IMAGE and UseArtImage).
//
+var artApexNames = []string{
+ "com.android.art",
+ "com.android.art.debug",
+ "com.android.art.testing",
+ "com.google.android.art",
+ "com.google.android.art.debug",
+ "com.google.android.art.testing",
+}
+
func init() {
RegisterDexpreoptBootJarsComponents(android.InitRegistrationContext)
}
@@ -339,20 +368,24 @@
return append(imageLocations, dexpreopt.PathToLocation(image.images, image.target.Arch.ArchType))
}
-func dexpreoptBootJarsFactory() android.Singleton {
- return &dexpreoptBootJars{}
+func dexpreoptBootJarsFactory() android.SingletonModule {
+ m := &dexpreoptBootJars{}
+ android.InitAndroidModule(m)
+ return m
}
func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) {
- ctx.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory)
+ ctx.RegisterSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
}
-func skipDexpreoptBootJars(ctx android.PathContext) bool {
- return dexpreopt.GetGlobalConfig(ctx).DisablePreopt
+func SkipDexpreoptBootJars(ctx android.PathContext) bool {
+ return dexpreopt.GetGlobalConfig(ctx).DisablePreoptBootImages
}
-// Singleton for generating boot image build rules.
+// Singleton module for generating boot image build rules.
type dexpreoptBootJars struct {
+ android.SingletonModuleBase
+
// Default boot image config (currently always the Framework boot image extension). It should be
// noted that JIT-Zygote builds use ART APEX image instead of the Framework boot image extension,
// but the switch is handled not here, but in the makefiles (triggered with
@@ -369,25 +402,16 @@
dexpreoptConfigForMake android.WritablePath
}
-// Accessor function for the apex package. Returns nil if dexpreopt is disabled.
-func DexpreoptedArtApexJars(ctx android.BuilderContext) map[android.ArchType]android.OutputPaths {
- if skipDexpreoptBootJars(ctx) {
- return nil
- }
- // Include dexpreopt files for the primary boot image.
- files := map[android.ArchType]android.OutputPaths{}
- for _, variant := range artBootImageConfig(ctx).variants {
- // We also generate boot images for host (for testing), but we don't need those in the apex.
- if variant.target.Os == android.Android {
- files[variant.target.Arch.ArchType] = variant.imagesDeps
- }
- }
- return files
+// Provide paths to boot images for use by modules that depend upon them.
+//
+// The build rules are created in GenerateSingletonBuildActions().
+func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // Placeholder for now.
}
// Generate build rules for boot images.
-func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) {
- if skipDexpreoptBootJars(ctx) {
+func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) {
+ if SkipDexpreoptBootJars(ctx) {
return
}
if dexpreopt.GetCachedGlobalSoongConfig(ctx) == nil {
@@ -415,55 +439,87 @@
// Create boot image for the ART apex (build artifacts are accessed via the global boot image config).
d.otherImages = append(d.otherImages, buildBootImage(ctx, artBootImageConfig(ctx)))
+ copyUpdatableBootJars(ctx)
+
dumpOatRules(ctx, d.defaultBootImage)
}
// Inspect this module to see if it contains a bootclasspath dex jar.
// Note that the same jar may occur in multiple modules.
// This logic is tested in the apex package to avoid import cycle apex <-> java.
-func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, module android.Module) (int, android.Path) {
- // Ignore any module that is not listed in the boot image configuration.
+//
+// This is similar to logic in isModuleInConfiguredList() so any changes needed here are likely to
+// be needed there too.
+//
+// TODO(b/177892522): Avoid having to perform this type of check or if necessary dedup it.
+func getBootJar(ctx android.SingletonContext, bootjars android.ConfiguredJarList,
+ module android.Module, fromWhere string) (int, android.Path, *android.ApexInfo) {
+
name := ctx.ModuleName(module)
- index := image.modules.IndexOfJar(name)
+
+ // Strip a prebuilt_ prefix so that this can access the dex jar from a prebuilt module.
+ name = android.RemoveOptionalPrebuiltPrefix(name)
+
+ // Ignore any module that is not listed in the boot image configuration.
+ index := bootjars.IndexOfJar(name)
if index == -1 {
- return -1, nil
+ return -1, nil, nil
}
- // It is an error if a module configured in the boot image does not support
- // accessing the dex jar. This is safe because every module that has the same
- // name has to have the same module type.
+ // It is an error if a module configured in the boot image does not support accessing the dex jar.
+ // This is safe because every module that has the same name has to have the same module type.
jar, hasJar := module.(interface{ DexJarBuildPath() android.Path })
if !hasJar {
- ctx.Errorf("module %q configured in boot image %q does not support accessing dex jar", module, image.name)
- return -1, nil
+ ctx.Errorf("module %q %sdoes not support accessing dex jar", module, fromWhere)
+ return -1, nil, nil
}
// It is also an error if the module is not an ApexModule.
if _, ok := module.(android.ApexModule); !ok {
- ctx.Errorf("module %q configured in boot image %q does not support being added to an apex", module, image.name)
- return -1, nil
+ ctx.Errorf("module %q %sdoes not support being added to an apex", module, fromWhere)
+ return -1, nil, nil
}
apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
// Now match the apex part of the boot image configuration.
- requiredApex := image.modules.Apex(index)
- if requiredApex == "platform" {
+ requiredApex := bootjars.Apex(index)
+ if requiredApex == "platform" || requiredApex == "system_ext" {
if len(apexInfo.InApexes) != 0 {
// A platform variant is required but this is for an apex so ignore it.
- return -1, nil
+ return -1, nil, nil
}
- } else if !android.InList(requiredApex, apexInfo.InApexes) {
+ } else if !apexInfo.InApexByBaseName(requiredApex) {
// An apex variant for a specific apex is required but this is the wrong apex.
+ return -1, nil, nil
+ }
+
+ return index, jar.DexJarBuildPath(), &apexInfo
+}
+
+// Inspect this module to see if it contains a bootclasspath dex jar from a given boot image.
+func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, module android.Module) (int, android.Path) {
+ fromImage := fmt.Sprintf("configured in boot image %q ", image.name)
+ index, jarPath, apexInfo := getBootJar(ctx, image.modules, module, fromImage)
+ if index == -1 {
return -1, nil
}
+ name := ctx.ModuleName(module)
+
// Check that this module satisfies any boot image specific constraints.
fromUpdatableApex := apexInfo.Updatable
switch image.name {
case artBootImageName:
- if len(apexInfo.InApexes) > 0 && allHavePrefix(apexInfo.InApexes, "com.android.art") {
+ inArtApex := false
+ for _, n := range artApexNames {
+ if apexInfo.InApexByBaseName(n) {
+ inArtApex = true
+ break
+ }
+ }
+ if inArtApex {
// ok: found the jar in the ART apex
} else if name == "jacocoagent" && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
// exception (skip and continue): Jacoco platform variant for a coverage build
@@ -487,43 +543,40 @@
panic("unknown boot image: " + image.name)
}
- return index, jar.DexJarBuildPath()
+ return index, jarPath
}
-func allHavePrefix(list []string, prefix string) bool {
- for _, s := range list {
- if s != prefix && !strings.HasPrefix(s, prefix+".") {
- return false
- }
- }
- return true
-}
+// Generate commands that will copy boot jars to predefined paths in the global config.
+func findAndCopyBootJars(ctx android.SingletonContext, bootjars android.ConfiguredJarList,
+ jarPathsPredefined android.WritablePaths,
+ getBootJar func(module android.Module) (int, android.Path)) []string {
-// buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
-func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootImageConfig {
- // Collect dex jar paths for the boot image modules.
// This logic is tested in the apex package to avoid import cycle apex <-> java.
- bootDexJars := make(android.Paths, image.modules.Len())
+ jarPaths := make(android.Paths, bootjars.Len())
+
ctx.VisitAllModules(func(module android.Module) {
- if i, j := getBootImageJar(ctx, image, module); i != -1 {
- if existing := bootDexJars[i]; existing != nil {
- ctx.Errorf("Multiple dex jars found for %s:%s - %s and %s",
- image.modules.Apex(i), image.modules.Jar(i), existing, j)
+ if !isActiveModule(module) {
+ return
+ }
+ if i, j := getBootJar(module); i != -1 {
+ if existing := jarPaths[i]; existing != nil {
+ ctx.Errorf("Multiple dex jars found for %s:%s - %q and %q",
+ bootjars.Apex(i), bootjars.Jar(i), existing, j)
return
}
-
- bootDexJars[i] = j
+ jarPaths[i] = j
}
})
var missingDeps []string
// Ensure all modules were converted to paths
- for i := range bootDexJars {
- if bootDexJars[i] == nil {
- m := image.modules.Jar(i)
+ for i := range jarPaths {
+ if jarPaths[i] == nil {
+ m := bootjars.Jar(i)
if ctx.Config().AllowMissingDependencies() {
missingDeps = append(missingDeps, m)
- bootDexJars[i] = android.PathForOutput(ctx, "missing/module", m, "from/apex", image.modules.Apex(i))
+ jarPaths[i] = android.PathForOutput(ctx, "missing/module", m, "from/apex",
+ bootjars.Apex(i))
} else {
ctx.Errorf("failed to find a dex jar path for module '%s'"+
", note that some jars may be filtered out by module constraints", m)
@@ -535,14 +588,24 @@
// time, before the boot images are built (these paths are used in dexpreopt rule generation for
// Java libraries and apps). Generate rules that copy bootclasspath DEX jars to the predefined
// paths.
- for i := range bootDexJars {
+ for i := range jarPaths {
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
- Input: bootDexJars[i],
- Output: image.dexPaths[i],
+ Input: jarPaths[i],
+ Output: jarPathsPredefined[i],
})
}
+ return missingDeps
+}
+
+// buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
+func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootImageConfig {
+ getBootJarFunc := func(module android.Module) (int, android.Path) {
+ return getBootImageJar(ctx, image, module)
+ }
+ missingDeps := findAndCopyBootJars(ctx, image.modules, image.dexPaths, getBootJarFunc)
+
profile := bootImageProfileRule(ctx, image, missingDeps)
bootFrameworkProfileRule(ctx, image, missingDeps)
updatableBcpPackagesRule(ctx, image, missingDeps)
@@ -569,6 +632,21 @@
return image
}
+// Generate commands that will copy updatable boot jars to predefined paths in the global config.
+func copyUpdatableBootJars(ctx android.SingletonContext) {
+ config := GetUpdatableBootConfig(ctx)
+ getBootJarFunc := func(module android.Module) (int, android.Path) {
+ index, jar, _ := getBootJar(ctx, config.modules, module, "configured in updatable boot jars ")
+ return index, jar
+ }
+ missingDeps := findAndCopyBootJars(ctx, config.modules, config.dexPaths, getBootJarFunc)
+ // Ignoring missing dependencies here. Ideally they should be added to the dexpreopt rule, but
+ // that is not possible as this rule is created after dexpreopt rules (it's in a singleton
+ // context, and they are in a module context). The true fix is to add dependencies from the
+ // dexpreopted modules on updatable boot jars and avoid this copying altogether.
+ _ = missingDeps
+}
+
// Generate boot image build rules for a specific target.
func buildBootImageVariant(ctx android.SingletonContext, image *bootImageVariant,
profile android.Path, missingDeps []string) android.WritablePaths {
@@ -622,8 +700,10 @@
cmd.FlagWithInput("--profile-file=", profile)
}
- if global.DirtyImageObjects.Valid() {
- cmd.FlagWithInput("--dirty-image-objects=", global.DirtyImageObjects.Path())
+ dirtyImageFile := "frameworks/base/config/dirty-image-objects"
+ dirtyImagePath := android.ExistentPathForSource(ctx, dirtyImageFile)
+ if dirtyImagePath.Valid() {
+ cmd.FlagWithInput("--dirty-image-objects=", dirtyImagePath.Path())
}
if image.extends != nil {
@@ -724,7 +804,7 @@
globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
global := dexpreopt.GetGlobalConfig(ctx)
- if global.DisableGenerateProfile || ctx.Config().UnbundledBuild() {
+ if global.DisableGenerateProfile {
return nil
}
profile := ctx.Config().Once(bootImageProfileRuleKey, func() interface{} {
@@ -832,6 +912,9 @@
// Collect `permitted_packages` for updatable boot jars.
var updatablePackages []string
ctx.VisitAllModules(func(module android.Module) {
+ if !isActiveModule(module) {
+ return
+ }
if j, ok := module.(PermittedPackagesForUpdatableBootJars); ok {
name := ctx.ModuleName(module)
if i := android.IndexList(name, updatableModules); i != -1 {
@@ -931,8 +1014,11 @@
image := d.defaultBootImage
if image != nil {
ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", image.profileInstalls.String())
- ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(image.dexPathsDeps.Strings(), " "))
- ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(image.getAnyAndroidVariant().dexLocationsDeps, " "))
+
+ global := dexpreopt.GetGlobalConfig(ctx)
+ dexPaths, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
+ ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(dexPaths.Strings(), " "))
+ ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(dexLocations, " "))
var imageNames []string
// TODO: the primary ART boot image should not be exposed to Make, as it is installed in a
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index 95fe5e1..73f21d1 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -16,7 +16,6 @@
import (
"path/filepath"
- "reflect"
"sort"
"testing"
@@ -36,6 +35,7 @@
name: "bar",
srcs: ["b.java"],
installable: true,
+ system_ext_specific: true,
}
dex_import {
@@ -44,26 +44,22 @@
}
`
- config := testConfig(nil, bp, nil)
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("foo"),
+ dexpreopt.FixtureSetBootJars("platform:foo", "system_ext:bar", "platform:baz"),
+ ).RunTestWithBp(t, bp)
- pathCtx := android.PathContextForTesting(config)
- dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
- dexpreoptConfig.BootJars = android.CreateTestConfiguredJarList([]string{"platform:foo", "platform:bar", "platform:baz"})
- dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
-
- ctx := testContext(config)
- RegisterDexpreoptBootJarsComponents(ctx)
- run(t, ctx, config)
-
- dexpreoptBootJars := ctx.SingletonForTests("dex_bootjars")
+ dexpreoptBootJars := result.SingletonForTests("dex_bootjars")
rule := dexpreoptBootJars.Output(ruleFile)
for i := range expectedInputs {
- expectedInputs[i] = filepath.Join(buildDir, "test_device", expectedInputs[i])
+ expectedInputs[i] = filepath.Join("out/soong/test_device", expectedInputs[i])
}
for i := range expectedOutputs {
- expectedOutputs[i] = filepath.Join(buildDir, "test_device", expectedOutputs[i])
+ expectedOutputs[i] = filepath.Join("out/soong/test_device", expectedOutputs[i])
}
inputs := rule.Implicits.Strings()
@@ -74,13 +70,9 @@
sort.Strings(outputs)
sort.Strings(expectedOutputs)
- if !reflect.DeepEqual(inputs, expectedInputs) {
- t.Errorf("want inputs %q\n got inputs %q", expectedInputs, inputs)
- }
+ android.AssertStringPathsRelativeToTopEquals(t, "inputs", result.Config, expectedInputs, inputs)
- if !reflect.DeepEqual(outputs, expectedOutputs) {
- t.Errorf("want outputs %q\n got outputs %q", expectedOutputs, outputs)
- }
+ android.AssertStringPathsRelativeToTopEquals(t, "outputs", result.Config, expectedOutputs, outputs)
}
func TestDexpreoptBootJars(t *testing.T) {
@@ -116,7 +108,7 @@
func TestDexpreoptBootZip(t *testing.T) {
ruleFile := "boot.zip"
- ctx := android.PathContextForTesting(testConfig(nil, "", nil))
+ ctx := android.PathContextForTesting(android.TestArchConfig("", nil, "", nil))
expectedInputs := []string{}
for _, target := range ctx.Config().Targets[android.Android] {
for _, ext := range []string{".art", ".oat", ".vdex"} {
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index c315124..656f5ef 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -26,7 +26,7 @@
// systemServerClasspath returns the on-device locations of the modules in the system server classpath. It is computed
// once the first time it is called for any ctx.Config(), and returns the same slice for all future calls with the same
// ctx.Config().
-func systemServerClasspath(ctx android.MakeVarsContext) []string {
+func systemServerClasspath(ctx android.PathContext) []string {
return ctx.Config().OnceStringSlice(systemServerClasspathKey, func() []string {
global := dexpreopt.GetGlobalConfig(ctx)
var systemServerClasspathLocations []string
@@ -82,10 +82,6 @@
deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
artModules := global.ArtApexJars
- // With EMMA_INSTRUMENT_FRAMEWORK=true the Core libraries depend on jacoco.
- if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
- artModules = artModules.Append("com.android.art", "jacocoagent")
- }
frameworkModules := global.BootJars.RemoveList(artModules)
artSubdir := "apex/art_boot_images/javalib"
@@ -180,6 +176,57 @@
})
}
+// Updatable boot config allows to access build/install paths of updatable boot jars without going
+// through the usual trouble of registering dependencies on those modules and extracting build paths
+// from those dependencies.
+type updatableBootConfig struct {
+ // A list of updatable boot jars.
+ modules android.ConfiguredJarList
+
+ // A list of predefined build paths to updatable boot jars. They are configured very early,
+ // before the modules for these jars are processed and the actual paths are generated, and
+ // later on a singleton adds commands to copy actual jars to the predefined paths.
+ dexPaths android.WritablePaths
+
+ // A list of dex locations (a.k.a. on-device paths) to the boot jars.
+ dexLocations []string
+}
+
+var updatableBootConfigKey = android.NewOnceKey("updatableBootConfig")
+
+// Returns updatable boot config.
+func GetUpdatableBootConfig(ctx android.PathContext) updatableBootConfig {
+ return ctx.Config().Once(updatableBootConfigKey, func() interface{} {
+ updatableBootJars := dexpreopt.GetGlobalConfig(ctx).UpdatableBootJars
+
+ dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "updatable_bootjars")
+ dexPaths := updatableBootJars.BuildPaths(ctx, dir)
+
+ dexLocations := updatableBootJars.DevicePaths(ctx.Config(), android.Android)
+
+ return updatableBootConfig{updatableBootJars, dexPaths, dexLocations}
+ }).(updatableBootConfig)
+}
+
+// Returns a list of paths and a list of locations for the boot jars used in dexpreopt (to be
+// passed in -Xbootclasspath and -Xbootclasspath-locations arguments for dex2oat).
+func bcpForDexpreopt(ctx android.PathContext, withUpdatable bool) (android.WritablePaths, []string) {
+ // Non-updatable boot jars (they are used both in the boot image and in dexpreopt).
+ bootImage := defaultBootImageConfig(ctx)
+ dexPaths := bootImage.dexPathsDeps
+ // The dex locations for all Android variants are identical.
+ dexLocations := bootImage.getAnyAndroidVariant().dexLocationsDeps
+
+ if withUpdatable {
+ // Updatable boot jars (they are used only in dexpreopt, but not in the boot image).
+ updBootConfig := GetUpdatableBootConfig(ctx)
+ dexPaths = append(dexPaths, updBootConfig.dexPaths...)
+ dexLocations = append(dexLocations, updBootConfig.dexLocations...)
+ }
+
+ return dexPaths, dexLocations
+}
+
var defaultBootclasspathKey = android.NewOnceKey("defaultBootclasspath")
var copyOf = android.CopyOf
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 5550a4c..a9e0773 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -148,7 +148,7 @@
t.Run(test.name, func(t *testing.T) {
ctx, _ := testJava(t, test.bp)
- dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeDescription("dexpreopt")
+ dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt")
enabled := dexpreopt.Rule != nil
if enabled != test.enabled {
diff --git a/java/droiddoc.go b/java/droiddoc.go
index cbca12a..01c0f16 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -23,12 +23,10 @@
"android/soong/android"
"android/soong/java/config"
- "android/soong/remoteexec"
)
func init() {
RegisterDocsBuildComponents(android.InitRegistrationContext)
- RegisterStubsBuildComponents(android.InitRegistrationContext)
}
func RegisterDocsBuildComponents(ctx android.RegistrationContext) {
@@ -41,19 +39,6 @@
ctx.RegisterModuleType("javadoc_host", JavadocHostFactory)
}
-func RegisterStubsBuildComponents(ctx android.RegistrationContext) {
- ctx.RegisterModuleType("stubs_defaults", StubsDefaultsFactory)
-
- ctx.RegisterModuleType("droidstubs", DroidstubsFactory)
- ctx.RegisterModuleType("droidstubs_host", DroidstubsHostFactory)
-
- ctx.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory)
-}
-
-var (
- srcsLibTag = dependencyTag{name: "sources from javalib"}
-)
-
type JavadocProperties struct {
// list of source files used to compile the Java module. May be .java, .logtags, .proto,
// or .aidl files.
@@ -113,10 +98,6 @@
// names of the output files used in args that will be generated
Out []string
-
- // If set, metalava is sandboxed to only read files explicitly specified on the command
- // line. Defaults to false.
- Sandbox *bool
}
type ApiToCheck struct {
@@ -177,98 +158,6 @@
Compat_config *string `android:"path"`
}
-type DroidstubsProperties struct {
- // The generated public API filename by Metalava, defaults to <module>_api.txt
- Api_filename *string
-
- // the generated removed API filename by Metalava, defaults to <module>_removed.txt
- Removed_api_filename *string
-
- // the generated removed Dex API filename by Metalava.
- Removed_dex_api_filename *string
-
- Check_api struct {
- Last_released ApiToCheck
-
- Current ApiToCheck
-
- // The java_sdk_library module generates references to modules (i.e. filegroups)
- // from which information about the latest API version can be obtained. As those
- // modules may not exist (e.g. because a previous version has not been released) it
- // sets ignore_missing_latest_api=true on the droidstubs modules it creates so
- // that droidstubs can ignore those references if the modules do not yet exist.
- //
- // If true then this will ignore module references for modules that do not exist
- // in properties that supply the previous version of the API.
- //
- // There are two sets of those:
- // * Api_file, Removed_api_file in check_api.last_released
- // * New_since in check_api.api_lint.new_since
- //
- // The first two must be set as a pair, so either they should both exist or neither
- // should exist - in which case when this property is true they are ignored. If one
- // exists and the other does not then it is an error.
- Ignore_missing_latest_api *bool `blueprint:"mutated"`
-
- Api_lint struct {
- Enabled *bool
-
- // If set, performs api_lint on any new APIs not found in the given signature file
- New_since *string `android:"path"`
-
- // If not blank, path to the baseline txt file for approved API lint violations.
- Baseline_file *string `android:"path"`
- }
- }
-
- // user can specify the version of previous released API file in order to do compatibility check.
- Previous_api *string `android:"path"`
-
- // is set to true, Metalava will allow framework SDK to contain annotations.
- Annotations_enabled *bool
-
- // a list of top-level directories containing files to merge qualifier annotations (i.e. those intended to be included in the stubs written) from.
- Merge_annotations_dirs []string
-
- // a list of top-level directories containing Java stub files to merge show/hide annotations from.
- Merge_inclusion_annotations_dirs []string
-
- // a file containing a list of classes to do nullability validation for.
- Validate_nullability_from_list *string
-
- // a file containing expected warnings produced by validation of nullability annotations.
- Check_nullability_warnings *string
-
- // if set to true, allow Metalava to generate doc_stubs source files. Defaults to false.
- Create_doc_stubs *bool
-
- // if set to true, cause Metalava to output Javadoc comments in the stubs source files. Defaults to false.
- // Has no effect if create_doc_stubs: true.
- Output_javadoc_comments *bool
-
- // if set to false then do not write out stubs. Defaults to true.
- //
- // TODO(b/146727827): Remove capability when we do not need to generate stubs and API separately.
- Generate_stubs *bool
-
- // if set to true, provides a hint to the build system that this rule uses a lot of memory,
- // whicih can be used for scheduling purposes
- High_mem *bool
-
- // is set to true, Metalava will allow framework SDK to contain API levels annotations.
- Api_levels_annotations_enabled *bool
-
- // the dirs which Metalava extracts API levels annotations from.
- Api_levels_annotations_dirs []string
-
- // the filename which Metalava extracts API levels annotations from. Defaults to android.jar.
- Api_levels_jar_filename *string
-
- // if set to true, collect the values used by the Dev tools and
- // write them in files packaged with the SDK. Defaults to false.
- Write_sdk_values *bool
-}
-
//
// Common flags passed down to build rule
//
@@ -304,25 +193,6 @@
return false
}
-func ignoreMissingModules(ctx android.BottomUpMutatorContext, apiToCheck *ApiToCheck) {
- apiFile := String(apiToCheck.Api_file)
- removedApiFile := String(apiToCheck.Removed_api_file)
-
- apiModule := android.SrcIsModule(apiFile)
- removedApiModule := android.SrcIsModule(removedApiFile)
-
- if apiModule == "" || removedApiModule == "" {
- return
- }
-
- if ctx.OtherModuleExists(apiModule) || ctx.OtherModuleExists(removedApiModule) {
- return
- }
-
- apiToCheck.Api_file = nil
- apiToCheck.Removed_api_file = nil
-}
-
// Used by xsd_config
type ApiFilePath interface {
ApiFilePath() android.Path
@@ -352,11 +222,8 @@
srcJars android.Paths
srcFiles android.Paths
sourcepaths android.Paths
- argFiles android.Paths
implicits android.Paths
- args []string
-
docZip android.WritablePath
stubsSrcJar android.WritablePath
}
@@ -394,25 +261,25 @@
var _ android.OutputFileProducer = (*Javadoc)(nil)
-func (j *Javadoc) sdkVersion() sdkSpec {
- return sdkSpecFrom(String(j.properties.Sdk_version))
+func (j *Javadoc) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ return android.SdkSpecFrom(ctx, String(j.properties.Sdk_version))
}
-func (j *Javadoc) systemModules() string {
+func (j *Javadoc) SystemModules() string {
return proptools.String(j.properties.System_modules)
}
-func (j *Javadoc) minSdkVersion() sdkSpec {
- return j.sdkVersion()
+func (j *Javadoc) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ return j.SdkVersion(ctx)
}
-func (j *Javadoc) targetSdkVersion() sdkSpec {
- return j.sdkVersion()
+func (j *Javadoc) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ return j.SdkVersion(ctx)
}
func (j *Javadoc) addDeps(ctx android.BottomUpMutatorContext) {
if ctx.Device() {
- sdkDep := decodeSdkDep(ctx, sdkContext(j))
+ sdkDep := decodeSdkDep(ctx, android.SdkContext(j))
if sdkDep.useModule {
ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
@@ -490,7 +357,7 @@
func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps {
var deps deps
- sdkDep := decodeSdkDep(ctx, sdkContext(j))
+ sdkDep := decodeSdkDep(ctx, android.SdkContext(j))
if sdkDep.invalidVersion {
ctx.AddMissingDependencies(sdkDep.bootclasspath)
ctx.AddMissingDependencies(sdkDep.java9Classpath)
@@ -507,8 +374,9 @@
switch tag {
case bootClasspathTag:
- if dep, ok := module.(Dependency); ok {
- deps.bootClasspath = append(deps.bootClasspath, dep.ImplementationJars()...)
+ if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
+ dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+ deps.bootClasspath = append(deps.bootClasspath, dep.ImplementationJars...)
} else if sm, ok := module.(SystemModulesProvider); ok {
// A system modules dependency has been added to the bootclasspath
// so add its libs to the bootclasspath.
@@ -517,23 +385,23 @@
panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
}
case libTag:
- switch dep := module.(type) {
- case SdkLibraryDependency:
- deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
- case Dependency:
- deps.classpath = append(deps.classpath, dep.HeaderJars()...)
- deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
- case android.SourceFileProducer:
+ if dep, ok := module.(SdkLibraryDependency); ok {
+ deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
+ } else if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
+ dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+ deps.classpath = append(deps.classpath, dep.HeaderJars...)
+ deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...)
+ } else if dep, ok := module.(android.SourceFileProducer); ok {
checkProducesJars(ctx, dep)
deps.classpath = append(deps.classpath, dep.Srcs()...)
- default:
+ } else {
ctx.ModuleErrorf("depends on non-java module %q", otherName)
}
case java9LibTag:
- switch dep := module.(type) {
- case Dependency:
- deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars()...)
- default:
+ if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
+ dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+ deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars...)
+ } else {
ctx.ModuleErrorf("depends on non-java module %q", otherName)
}
case systemModulesTag:
@@ -605,15 +473,20 @@
j.sourcepaths = android.PathsForModuleSrc(ctx, []string{"."})
}
- j.argFiles = android.PathsForModuleSrc(ctx, j.properties.Arg_files)
+ return deps
+}
+
+func (j *Javadoc) expandArgs(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
+ var argFiles android.Paths
argFilesMap := map[string]string{}
argFileLabels := []string{}
for _, label := range j.properties.Arg_files {
var paths = android.PathsForModuleSrc(ctx, []string{label})
if _, exists := argFilesMap[label]; !exists {
- argFilesMap[label] = strings.Join(paths.Strings(), " ")
+ argFilesMap[label] = strings.Join(cmd.PathsForInputs(paths), " ")
argFileLabels = append(argFileLabels, label)
+ argFiles = append(argFiles, paths...)
} else {
ctx.ModuleErrorf("multiple arg_files for %q, %q and %q",
label, argFilesMap[label], paths)
@@ -633,7 +506,7 @@
}
for _, flag := range flags {
- args, err := android.Expand(flag, func(name string) (string, error) {
+ expanded, err := android.Expand(flag, func(name string) (string, error) {
if strings.HasPrefix(name, "location ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
if paths, ok := argFilesMap[label]; ok {
@@ -651,10 +524,10 @@
if err != nil {
ctx.PropertyErrorf(argsPropertyName, "%s", err.Error())
}
- j.args = append(j.args, args)
+ cmd.Flag(expanded)
}
- return deps
+ cmd.Implicits(argFiles)
}
func (j *Javadoc) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -678,7 +551,7 @@
srcJarList := zipSyncCmd(ctx, rule, srcJarDir, j.srcJars)
- javaVersion := getJavaVersion(ctx, String(j.properties.Java_version), sdkContext(j))
+ javaVersion := getJavaVersion(ctx, String(j.properties.Java_version), android.SdkContext(j))
cmd := javadocSystemModulesCmd(ctx, rule, j.srcFiles, outDir, srcJarDir, srcJarList,
deps.systemModules, deps.classpath, j.sourcepaths)
@@ -688,6 +561,8 @@
Flag("-XDignore.symbol.file").
Flag("-Xdoclint:none")
+ j.expandArgs(ctx, cmd)
+
rule.Command().
BuiltTool("soong_zip").
Flag("-write_if_changed").
@@ -848,7 +723,7 @@
BuiltTool("soong_javac_wrapper").Tool(config.JavadocCmd(ctx)).
Flag(config.JavacVmFlags).
FlagWithArg("-encoding ", "UTF-8").
- FlagWithRspFileInputList("@", srcs).
+ FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "javadoc.rsp"), srcs).
FlagWithInput("@", srcJarList)
// TODO(ccross): Remove this if- statement once we finish migration for all Doclava
@@ -946,7 +821,7 @@
deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
}
- cmd.Flag(strings.Join(d.Javadoc.args, " ")).Implicits(d.Javadoc.argFiles)
+ d.expandArgs(ctx, cmd)
if d.properties.Compat_config != nil {
compatConfig := android.PathForModuleSrc(ctx, String(d.properties.Compat_config))
@@ -983,671 +858,9 @@
}
//
-// Droidstubs
-//
-type Droidstubs struct {
- Javadoc
- android.SdkBase
-
- properties DroidstubsProperties
- apiFile android.WritablePath
- apiXmlFile android.WritablePath
- lastReleasedApiXmlFile android.WritablePath
- privateApiFile android.WritablePath
- removedApiFile android.WritablePath
- removedDexApiFile android.WritablePath
- nullabilityWarningsFile android.WritablePath
-
- checkCurrentApiTimestamp android.WritablePath
- updateCurrentApiTimestamp android.WritablePath
- checkLastReleasedApiTimestamp android.WritablePath
- apiLintTimestamp android.WritablePath
- apiLintReport android.WritablePath
-
- checkNullabilityWarningsTimestamp android.WritablePath
-
- annotationsZip android.WritablePath
- apiVersionsXml android.WritablePath
-
- apiFilePath android.Path
- removedApiFilePath android.Path
-
- metadataZip android.WritablePath
- metadataDir 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{}
-
- module.AddProperties(&module.properties,
- &module.Javadoc.properties)
-
- InitDroiddocModule(module, android.HostAndDeviceSupported)
- android.InitSdkAwareModule(module)
- 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{}
-
- module.AddProperties(&module.properties,
- &module.Javadoc.properties)
-
- InitDroiddocModule(module, android.HostSupported)
- return module
-}
-
-func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) {
- switch tag {
- case "":
- return android.Paths{d.stubsSrcJar}, nil
- case ".docs.zip":
- return android.Paths{d.docZip}, nil
- case ".api.txt", android.DefaultDistTag:
- // This is the default dist path for dist properties that have no tag property.
- return android.Paths{d.apiFilePath}, nil
- case ".removed-api.txt":
- return android.Paths{d.removedApiFilePath}, nil
- case ".annotations.zip":
- return android.Paths{d.annotationsZip}, nil
- case ".api_versions.xml":
- return android.Paths{d.apiVersionsXml}, nil
- default:
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
- }
-}
-
-func (d *Droidstubs) ApiFilePath() android.Path {
- return d.apiFilePath
-}
-
-func (d *Droidstubs) RemovedApiFilePath() android.Path {
- return d.removedApiFilePath
-}
-
-func (d *Droidstubs) StubsSrcJar() android.Path {
- return d.stubsSrcJar
-}
-
-func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) {
- d.Javadoc.addDeps(ctx)
-
- // If requested clear any properties that provide information about the latest version
- // of an API and which reference non-existent modules.
- if Bool(d.properties.Check_api.Ignore_missing_latest_api) {
- ignoreMissingModules(ctx, &d.properties.Check_api.Last_released)
-
- // If the new_since references a module, e.g. :module-latest-api and the module
- // does not exist then clear it.
- newSinceSrc := d.properties.Check_api.Api_lint.New_since
- newSinceSrcModule := android.SrcIsModule(proptools.String(newSinceSrc))
- if newSinceSrcModule != "" && !ctx.OtherModuleExists(newSinceSrcModule) {
- d.properties.Check_api.Api_lint.New_since = nil
- }
- }
-
- if len(d.properties.Merge_annotations_dirs) != 0 {
- for _, mergeAnnotationsDir := range d.properties.Merge_annotations_dirs {
- ctx.AddDependency(ctx.Module(), metalavaMergeAnnotationsDirTag, mergeAnnotationsDir)
- }
- }
-
- if len(d.properties.Merge_inclusion_annotations_dirs) != 0 {
- for _, mergeInclusionAnnotationsDir := range d.properties.Merge_inclusion_annotations_dirs {
- ctx.AddDependency(ctx.Module(), metalavaMergeInclusionAnnotationsDirTag, mergeInclusionAnnotationsDir)
- }
- }
-
- if len(d.properties.Api_levels_annotations_dirs) != 0 {
- for _, apiLevelsAnnotationsDir := range d.properties.Api_levels_annotations_dirs {
- ctx.AddDependency(ctx.Module(), metalavaAPILevelsAnnotationsDirTag, apiLevelsAnnotationsDir)
- }
- }
-}
-
-func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) {
- if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
- apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
- String(d.properties.Api_filename) != "" {
- filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
- d.apiFile = android.PathForModuleOut(ctx, filename)
- cmd.FlagWithOutput("--api ", d.apiFile)
- d.apiFilePath = d.apiFile
- } else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" {
- // If check api is disabled then make the source file available for export.
- d.apiFilePath = android.PathForModuleSrc(ctx, sourceApiFile)
- }
-
- if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
- apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
- String(d.properties.Removed_api_filename) != "" {
- filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt")
- d.removedApiFile = android.PathForModuleOut(ctx, filename)
- cmd.FlagWithOutput("--removed-api ", d.removedApiFile)
- d.removedApiFilePath = d.removedApiFile
- } else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" {
- // If check api is disabled then make the source removed api file available for export.
- d.removedApiFilePath = android.PathForModuleSrc(ctx, sourceRemovedApiFile)
- }
-
- if String(d.properties.Removed_dex_api_filename) != "" {
- d.removedDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Removed_dex_api_filename))
- cmd.FlagWithOutput("--removed-dex-api ", d.removedDexApiFile)
- }
-
- if Bool(d.properties.Write_sdk_values) {
- d.metadataDir = android.PathForModuleOut(ctx, "metadata")
- cmd.FlagWithArg("--sdk-values ", d.metadataDir.String())
- }
-
- if stubsDir.Valid() {
- if Bool(d.properties.Create_doc_stubs) {
- cmd.FlagWithArg("--doc-stubs ", stubsDir.String())
- } else {
- cmd.FlagWithArg("--stubs ", stubsDir.String())
- if !Bool(d.properties.Output_javadoc_comments) {
- cmd.Flag("--exclude-documentation-from-stubs")
- }
- }
- }
-}
-
-func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
- if Bool(d.properties.Annotations_enabled) {
- cmd.Flag("--include-annotations")
-
- validatingNullability :=
- android.InList("--validate-nullability-from-merged-stubs", d.Javadoc.args) ||
- String(d.properties.Validate_nullability_from_list) != ""
-
- migratingNullability := String(d.properties.Previous_api) != ""
- if migratingNullability {
- previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api))
- cmd.FlagWithInput("--migrate-nullness ", previousApi)
- }
-
- if s := String(d.properties.Validate_nullability_from_list); s != "" {
- cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s))
- }
-
- if validatingNullability {
- d.nullabilityWarningsFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_nullability_warnings.txt")
- cmd.FlagWithOutput("--nullability-warnings-txt ", d.nullabilityWarningsFile)
- }
-
- d.annotationsZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"_annotations.zip")
- cmd.FlagWithOutput("--extract-annotations ", d.annotationsZip)
-
- if len(d.properties.Merge_annotations_dirs) != 0 {
- 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) 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 {
- 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))
- }
- })
-}
-
-func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
- if !Bool(d.properties.Api_levels_annotations_enabled) {
- return
- }
-
- d.apiVersionsXml = android.PathForModuleOut(ctx, "api-versions.xml")
-
- 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!")
- }
-
- cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml)
- cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml)
- cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String())
- cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
-
- filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
-
- ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
- if t, ok := m.(*ExportedDroiddocDir); ok {
- for _, dep := range t.deps {
- if strings.HasSuffix(dep.String(), filename) {
- cmd.Implicit(dep)
- }
- }
- cmd.FlagWithArg("--android-jar-pattern ", t.dir.String()+"/%/public/"+filename)
- } else {
- ctx.PropertyErrorf("api_levels_annotations_dirs",
- "module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
- }
- })
-}
-
-func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
- srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths, implicitsRsp android.WritablePath, sandbox bool) *android.RuleBuilderCommand {
- cmd := rule.Command()
- if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") {
- rule.Remoteable(android.RemoteRuleSupports{RBE: true})
- pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "metalava")
- execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
- labels := map[string]string{"type": "compile", "lang": "java", "compiler": "metalava"}
- if !sandbox {
- execStrategy = remoteexec.LocalExecStrategy
- labels["shallow"] = "true"
- }
- inputs := []string{android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "metalava.jar").String()}
- if v := ctx.Config().Getenv("RBE_METALAVA_INPUTS"); v != "" {
- inputs = append(inputs, strings.Split(v, ",")...)
- }
- cmd.Text((&remoteexec.REParams{
- Labels: labels,
- ExecStrategy: execStrategy,
- Inputs: inputs,
- RSPFile: implicitsRsp.String(),
- ToolchainInputs: []string{config.JavaCmd(ctx).String()},
- Platform: map[string]string{remoteexec.PoolKey: pool},
- }).NoVarTemplate(ctx.Config()))
- }
-
- cmd.BuiltTool("metalava").
- Flag(config.JavacVmFlags).
- Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED").
- FlagWithArg("-encoding ", "UTF-8").
- FlagWithArg("-source ", javaVersion.String()).
- FlagWithRspFileInputList("@", srcs).
- FlagWithInput("@", srcJarList)
-
- if javaHome := ctx.Config().Getenv("ANDROID_JAVA_HOME"); javaHome != "" {
- cmd.Implicit(android.PathForSource(ctx, javaHome))
- }
-
- if sandbox {
- cmd.FlagWithOutput("--strict-input-files ", android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"violations.txt"))
- } else {
- cmd.FlagWithOutput("--strict-input-files:warn ", android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"violations.txt"))
- }
-
- if implicitsRsp != nil {
- cmd.FlagWithArg("--strict-input-files-exempt ", "@"+implicitsRsp.String())
- }
-
- if len(bootclasspath) > 0 {
- cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":")
- }
-
- if len(classpath) > 0 {
- cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
- }
-
- if len(sourcepaths) > 0 {
- cmd.FlagWithList("-sourcepath ", sourcepaths.Strings(), ":")
- } else {
- cmd.FlagWithArg("-sourcepath ", `""`)
- }
-
- cmd.Flag("--no-banner").
- Flag("--color").
- Flag("--quiet").
- Flag("--format=v2").
- FlagWithArg("--repeat-errors-max ", "10").
- FlagWithArg("--hide ", "UnresolvedImport")
-
- return cmd
-}
-
-func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- deps := d.Javadoc.collectDeps(ctx)
-
- javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), sdkContext(d))
-
- // Create rule for metalava
-
- srcJarDir := android.PathForModuleOut(ctx, "srcjars")
-
- rule := android.NewRuleBuilder(pctx, ctx)
-
- if BoolDefault(d.properties.High_mem, false) {
- // This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel.
- rule.HighMem()
- }
-
- generateStubs := BoolDefault(d.properties.Generate_stubs, true)
- var stubsDir android.OptionalPath
- if generateStubs {
- d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
- stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "stubsDir"))
- rule.Command().Text("rm -rf").Text(stubsDir.String())
- rule.Command().Text("mkdir -p").Text(stubsDir.String())
- }
-
- srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
-
- implicitsRsp := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"implicits.rsp")
-
- cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
- deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths, implicitsRsp,
- Bool(d.Javadoc.properties.Sandbox))
- cmd.Implicits(d.Javadoc.implicits)
-
- d.stubsFlags(ctx, cmd, stubsDir)
-
- d.annotationsFlags(ctx, cmd)
- d.inclusionAnnotationsFlags(ctx, cmd)
- d.apiLevelsAnnotationsFlags(ctx, cmd)
-
- if android.InList("--generate-documentation", d.Javadoc.args) {
- // Currently Metalava have the ability to invoke Javadoc in a seperate process.
- // Pass "-nodocs" to suppress the Javadoc invocation when Metalava receives
- // "--generate-documentation" arg. This is not needed when Metalava removes this feature.
- d.Javadoc.args = append(d.Javadoc.args, "-nodocs")
- }
-
- cmd.Flag(strings.Join(d.Javadoc.args, " ")).Implicits(d.Javadoc.argFiles)
- for _, o := range d.Javadoc.properties.Out {
- cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
- }
-
- // Add options for the other optional tasks: API-lint and check-released.
- // We generate separate timestamp files for them.
-
- doApiLint := false
- doCheckReleased := false
-
- // Add API lint options.
-
- if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) {
- doApiLint = true
-
- newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
- if newSince.Valid() {
- cmd.FlagWithInput("--api-lint ", newSince.Path())
- } else {
- cmd.Flag("--api-lint")
- }
- d.apiLintReport = android.PathForModuleOut(ctx, "api_lint_report.txt")
- cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO: Change to ":api-lint"
-
- // TODO(b/154317059): Clean up this whitelist by baselining and/or checking in last-released.
- if d.Name() != "android.car-system-stubs-docs" &&
- d.Name() != "android.car-stubs-docs" {
- cmd.Flag("--lints-as-errors")
- cmd.Flag("--warnings-as-errors") // Most lints are actually warnings.
- }
-
- baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file)
- updatedBaselineOutput := android.PathForModuleOut(ctx, "api_lint_baseline.txt")
- d.apiLintTimestamp = android.PathForModuleOut(ctx, "api_lint.timestamp")
-
- // Note this string includes a special shell quote $' ... ', which decodes the "\n"s.
- // However, because $' ... ' doesn't expand environmental variables, we can't just embed
- // $PWD, so we have to terminate $'...', use "$PWD", then start $' ... ' again,
- // which is why we have '"$PWD"$' in it.
- //
- // TODO: metalava also has a slightly different message hardcoded. Should we unify this
- // message and metalava's one?
- msg := `$'` + // Enclose with $' ... '
- `************************************************************\n` +
- `Your API changes are triggering API Lint warnings or errors.\n` +
- `To make these errors go away, fix the code according to the\n` +
- `error and/or warning messages above.\n` +
- `\n` +
- `If it is not possible to do so, there are workarounds:\n` +
- `\n` +
- `1. You can suppress the errors with @SuppressLint("<id>")\n`
-
- if baselineFile.Valid() {
- cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path())
- cmd.FlagWithOutput("--update-baseline:api-lint ", updatedBaselineOutput)
-
- msg += fmt.Sprintf(``+
- `2. You can update the baseline by executing the following\n`+
- ` command:\n`+
- ` cp \\\n`+
- ` "'"$PWD"$'/%s" \\\n`+
- ` "'"$PWD"$'/%s"\n`+
- ` To submit the revised baseline.txt to the main Android\n`+
- ` repository, you will need approval.\n`, updatedBaselineOutput, baselineFile.Path())
- } else {
- msg += fmt.Sprintf(``+
- `2. You can add a baseline file of existing lint failures\n`+
- ` to the build rule of %s.\n`, d.Name())
- }
- // Note the message ends with a ' (single quote), to close the $' ... ' .
- msg += `************************************************************\n'`
-
- cmd.FlagWithArg("--error-message:api-lint ", msg)
- }
-
- // Add "check released" options. (Detect incompatible API changes from the last public release)
-
- if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") {
- doCheckReleased = true
-
- if len(d.Javadoc.properties.Out) > 0 {
- ctx.PropertyErrorf("out", "out property may not be combined with check_api")
- }
-
- apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
- removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
- baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
- updatedBaselineOutput := android.PathForModuleOut(ctx, "last_released_baseline.txt")
-
- d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "check_last_released_api.timestamp")
-
- cmd.FlagWithInput("--check-compatibility:api:released ", apiFile)
- cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile)
-
- if baselineFile.Valid() {
- cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path())
- cmd.FlagWithOutput("--update-baseline:compatibility:released ", updatedBaselineOutput)
- }
-
- // Note this string includes quote ($' ... '), which decodes the "\n"s.
- 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'`
-
- cmd.FlagWithArg("--error-message:compatibility:released ", msg)
- }
-
- impRule := android.NewRuleBuilder(pctx, ctx)
- impCmd := impRule.Command()
- // An action that copies the ninja generated rsp file to a new location. This allows us to
- // add a large number of inputs to a file without exceeding bash command length limits (which
- // would happen if we use the WriteFile rule). The cp is needed because RuleBuilder sets the
- // rsp file to be ${output}.rsp.
- impCmd.Text("cp").FlagWithRspFileInputList("", cmd.GetImplicits()).Output(implicitsRsp)
- impRule.Build("implicitsGen", "implicits generation")
- cmd.Implicit(implicitsRsp)
-
- if generateStubs {
- rule.Command().
- BuiltTool("soong_zip").
- Flag("-write_if_changed").
- Flag("-jar").
- FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
- FlagWithArg("-C ", stubsDir.String()).
- FlagWithArg("-D ", stubsDir.String())
- }
-
- if Bool(d.properties.Write_sdk_values) {
- d.metadataZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-metadata.zip")
- rule.Command().
- BuiltTool("soong_zip").
- Flag("-write_if_changed").
- Flag("-d").
- FlagWithOutput("-o ", d.metadataZip).
- FlagWithArg("-C ", d.metadataDir.String()).
- FlagWithArg("-D ", d.metadataDir.String())
- }
-
- // TODO: We don't really need two separate API files, but this is a reminiscence of how
- // we used to run metalava separately for API lint and the "last_released" check. Unify them.
- if doApiLint {
- rule.Command().Text("touch").Output(d.apiLintTimestamp)
- }
- if doCheckReleased {
- rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp)
- }
-
- rule.Restat()
-
- zipSyncCleanupCmd(rule, srcJarDir)
-
- rule.Build("metalava", "metalava merged")
-
- if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
-
- if len(d.Javadoc.properties.Out) > 0 {
- ctx.PropertyErrorf("out", "out property may not be combined with check_api")
- }
-
- apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
- removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
- baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
-
- if baselineFile.Valid() {
- ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
- }
-
- d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp")
-
- rule := android.NewRuleBuilder(pctx, ctx)
-
- // Diff command line.
- // -F matches the closest "opening" line, such as "package android {"
- // and " public class Intent {".
- diff := `diff -u -F '{ *$'`
-
- rule.Command().Text("( true")
- rule.Command().
- Text(diff).
- Input(apiFile).Input(d.apiFile)
-
- rule.Command().
- Text(diff).
- 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 (and remove @SystemApi/@TestApi/etc)\n`+
- ` to the new methods, etc. shown in the above diff.\n\n`+
- ` 2. You can update current.txt and/or removed.txt by executing the following command:\n`+
- ` m %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("metalavaCurrentApiCheck", "check current API")
-
- d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "update_current_api.timestamp")
-
- // update API rule
- rule = android.NewRuleBuilder(pctx, ctx)
-
- 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("metalavaCurrentApiUpdate", "update current API")
- }
-
- if String(d.properties.Check_nullability_warnings) != "" {
- if d.nullabilityWarningsFile == nil {
- ctx.PropertyErrorf("check_nullability_warnings",
- "Cannot specify check_nullability_warnings unless validating nullability")
- }
-
- 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`+
- `above. You have two options:\n`+
- ` 1. Resolve the differences by editing the nullability annotations.\n`+
- ` 2. Update the file of expected warnings by running:\n`+
- ` cp %s %s\n`+
- ` and submitting the updated file as part of your change.`,
- d.nullabilityWarningsFile, checkNullabilityWarnings)
-
- rule := android.NewRuleBuilder(pctx, ctx)
-
- 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("nullabilityWarningsCheck", "nullability warnings check")
- }
-}
-
-//
// Exported Droiddoc Directory
//
var droiddocTemplateTag = dependencyTag{name: "droiddoc-template"}
-var metalavaMergeAnnotationsDirTag = dependencyTag{name: "metalava-merge-annotations-dir"}
-var metalavaMergeInclusionAnnotationsDirTag = dependencyTag{name: "metalava-merge-inclusion-annotations-dir"}
-var metalavaAPILevelsAnnotationsDirTag = dependencyTag{name: "metalava-api-levels-annotations-dir"}
type ExportedDroiddocDirProperties struct {
// path to the directory containing Droiddoc related files.
@@ -1700,30 +913,20 @@
return module
}
-func StubsDefaultsFactory() android.Module {
- module := &DocDefaults{}
-
- module.AddProperties(
- &JavadocProperties{},
- &DroidstubsProperties{},
- )
-
- android.InitDefaultsModule(module)
-
- 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())
+ cmd := rule.Command()
+ cmd.Text("rm -rf").Text(cmd.PathForOutput(srcJarDir))
+ cmd = rule.Command()
+ cmd.Text("mkdir -p").Text(cmd.PathForOutput(srcJarDir))
srcJarList := srcJarDir.Join(ctx, "list")
rule.Temporary(srcJarList)
- rule.Command().BuiltTool("zipsync").
- FlagWithArg("-d ", srcJarDir.String()).
+ cmd = rule.Command()
+ cmd.BuiltTool("zipsync").
+ FlagWithArg("-d ", cmd.PathForOutput(srcJarDir)).
FlagWithOutput("-l ", srcJarList).
FlagWithArg("-f ", `"*.java"`).
Inputs(srcJars)
@@ -1734,94 +937,3 @@
func zipSyncCleanupCmd(rule *android.RuleBuilder, srcJarDir android.ModuleOutPath) {
rule.Command().Text("rm -rf").Text(srcJarDir.String())
}
-
-var _ android.PrebuiltInterface = (*PrebuiltStubsSources)(nil)
-
-type PrebuiltStubsSourcesProperties struct {
- Srcs []string `android:"path"`
-}
-
-type PrebuiltStubsSources struct {
- android.ModuleBase
- android.DefaultableModuleBase
- prebuilt android.Prebuilt
- android.SdkBase
-
- properties PrebuiltStubsSourcesProperties
-
- stubsSrcJar android.ModuleOutPath
-}
-
-func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) {
- switch tag {
- case "":
- return android.Paths{p.stubsSrcJar}, nil
- default:
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
- }
-}
-
-func (d *PrebuiltStubsSources) StubsSrcJar() android.Path {
- return d.stubsSrcJar
-}
-
-func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
-
- if len(p.properties.Srcs) != 1 {
- ctx.PropertyErrorf("srcs", "must only specify one directory path, contains %d paths", len(p.properties.Srcs))
- return
- }
-
- localSrcDir := p.properties.Srcs[0]
- // Although PathForModuleSrc can return nil if either the path doesn't exist or
- // the path components are invalid it won't in this case because no components
- // are specified and the module directory must exist in order to get this far.
- srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, localSrcDir)
-
- // Glob the contents of the directory just in case the directory does not exist.
- srcGlob := localSrcDir + "/**/*"
- srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob})
-
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- BuiltTool("soong_zip").
- Flag("-write_if_changed").
- Flag("-jar").
- FlagWithOutput("-o ", p.stubsSrcJar).
- FlagWithArg("-C ", srcDir.String()).
- FlagWithRspFileInputList("-r ", srcPaths)
-
- rule.Restat()
-
- rule.Build("zip src", "Create srcjar from prebuilt source")
-}
-
-func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt {
- return &p.prebuilt
-}
-
-func (p *PrebuiltStubsSources) Name() string {
- return p.prebuilt.Name(p.ModuleBase.Name())
-}
-
-// prebuilt_stubs_sources imports a set of java source files as if they were
-// generated by droidstubs.
-//
-// By default, a prebuilt_stubs_sources has a single variant that expects a
-// set of `.java` files generated by droidstubs.
-//
-// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one
-// for host modules.
-//
-// Intended only for use by sdk snapshots.
-func PrebuiltStubsSourcesFactory() android.Module {
- module := &PrebuiltStubsSources{}
-
- module.AddProperties(&module.properties)
-
- android.InitPrebuiltModule(module, &module.properties.Srcs)
- android.InitSdkAwareModule(module)
- InitDroiddocModule(module, android.HostAndDeviceSupported)
- return module
-}
diff --git a/java/droiddoc_test.go b/java/droiddoc_test.go
new file mode 100644
index 0000000..8d1f591
--- /dev/null
+++ b/java/droiddoc_test.go
@@ -0,0 +1,147 @@
+// Copyright 2021 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 (
+ "reflect"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestDroiddoc(t *testing.T) {
+ ctx, _ := testJavaWithFS(t, `
+ droiddoc_exported_dir {
+ name: "droiddoc-templates-sdk",
+ path: ".",
+ }
+ filegroup {
+ name: "bar-doc-aidl-srcs",
+ srcs: ["bar-doc/IBar.aidl"],
+ path: "bar-doc",
+ }
+ droidstubs {
+ name: "bar-stubs",
+ srcs: [
+ "bar-doc/a.java",
+ ],
+ exclude_srcs: [
+ "bar-doc/b.java"
+ ],
+ api_levels_annotations_dirs: [
+ "droiddoc-templates-sdk",
+ ],
+ api_levels_annotations_enabled: true,
+ }
+ droiddoc {
+ name: "bar-doc",
+ srcs: [
+ ":bar-stubs",
+ "bar-doc/IFoo.aidl",
+ ":bar-doc-aidl-srcs",
+ ],
+ custom_template: "droiddoc-templates-sdk",
+ hdf: [
+ "android.whichdoc offline",
+ ],
+ knowntags: [
+ "bar-doc/known_oj_tags.txt",
+ ],
+ proofread_file: "libcore-proofread.txt",
+ todo_file: "libcore-docs-todo.html",
+ flags: ["-offlinemode -title \"libcore\""],
+ }
+ `,
+ map[string][]byte{
+ "bar-doc/a.java": nil,
+ "bar-doc/b.java": nil,
+ })
+ barStubs := ctx.ModuleForTests("bar-stubs", "android_common")
+ barStubsOutputs, err := barStubs.Module().(*Droidstubs).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error %q retrieving \"bar-stubs\" output file", err)
+ }
+ if len(barStubsOutputs) != 1 {
+ t.Errorf("Expected one output from \"bar-stubs\" got %s", barStubsOutputs)
+ }
+
+ barStubsOutput := barStubsOutputs[0]
+ barDoc := ctx.ModuleForTests("bar-doc", "android_common")
+ javaDoc := barDoc.Rule("javadoc")
+ if g, w := android.PathsRelativeToTop(javaDoc.Implicits), android.PathRelativeToTop(barStubsOutput); !inList(w, g) {
+ t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
+ }
+
+ expected := "-sourcepath out/soong/.intermediates/bar-doc/android_common/srcjars "
+ if !strings.Contains(javaDoc.RuleParams.Command, expected) {
+ t.Errorf("bar-doc command does not contain flag %q, but should\n%q", expected, javaDoc.RuleParams.Command)
+ }
+
+ aidl := barDoc.Rule("aidl")
+ if g, w := android.PathsRelativeToTop(javaDoc.Implicits), android.PathRelativeToTop(aidl.Output); !inList(w, g) {
+ t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
+ }
+
+ if g, w := aidl.Implicits.Strings(), []string{"bar-doc/IBar.aidl", "bar-doc/IFoo.aidl"}; !reflect.DeepEqual(w, g) {
+ t.Errorf("aidl inputs must be %q, but was %q", w, g)
+ }
+}
+
+func TestDroiddocArgsAndFlagsCausesError(t *testing.T) {
+ testJavaError(t, "flags is set. Cannot set args", `
+ droiddoc_exported_dir {
+ name: "droiddoc-templates-sdk",
+ path: ".",
+ }
+ filegroup {
+ name: "bar-doc-aidl-srcs",
+ srcs: ["bar-doc/IBar.aidl"],
+ path: "bar-doc",
+ }
+ droidstubs {
+ name: "bar-stubs",
+ srcs: [
+ "bar-doc/a.java",
+ ],
+ exclude_srcs: [
+ "bar-doc/b.java"
+ ],
+ api_levels_annotations_dirs: [
+ "droiddoc-templates-sdk",
+ ],
+ api_levels_annotations_enabled: true,
+ }
+ droiddoc {
+ name: "bar-doc",
+ srcs: [
+ ":bar-stubs",
+ "bar-doc/IFoo.aidl",
+ ":bar-doc-aidl-srcs",
+ ],
+ custom_template: "droiddoc-templates-sdk",
+ hdf: [
+ "android.whichdoc offline",
+ ],
+ knowntags: [
+ "bar-doc/known_oj_tags.txt",
+ ],
+ proofread_file: "libcore-proofread.txt",
+ todo_file: "libcore-docs-todo.html",
+ flags: ["-offlinemode -title \"libcore\""],
+ args: "-offlinemode -title \"libcore\"",
+ }
+ `)
+}
diff --git a/java/droidstubs.go b/java/droidstubs.go
new file mode 100644
index 0000000..a9e2749
--- /dev/null
+++ b/java/droidstubs.go
@@ -0,0 +1,845 @@
+// Copyright 2021 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 (
+ "fmt"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+ "android/soong/java/config"
+ "android/soong/remoteexec"
+)
+
+func init() {
+ RegisterStubsBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterStubsBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("stubs_defaults", StubsDefaultsFactory)
+
+ ctx.RegisterModuleType("droidstubs", DroidstubsFactory)
+ ctx.RegisterModuleType("droidstubs_host", DroidstubsHostFactory)
+
+ ctx.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory)
+}
+
+//
+// Droidstubs
+//
+type Droidstubs struct {
+ Javadoc
+ android.SdkBase
+
+ properties DroidstubsProperties
+ apiFile android.WritablePath
+ apiXmlFile android.WritablePath
+ lastReleasedApiXmlFile android.WritablePath
+ privateApiFile android.WritablePath
+ removedApiFile android.WritablePath
+ removedDexApiFile android.WritablePath
+ nullabilityWarningsFile android.WritablePath
+
+ checkCurrentApiTimestamp android.WritablePath
+ updateCurrentApiTimestamp android.WritablePath
+ checkLastReleasedApiTimestamp android.WritablePath
+ apiLintTimestamp android.WritablePath
+ apiLintReport android.WritablePath
+
+ checkNullabilityWarningsTimestamp android.WritablePath
+
+ annotationsZip android.WritablePath
+ apiVersionsXml android.WritablePath
+
+ apiFilePath android.Path
+ removedApiFilePath android.Path
+
+ metadataZip android.WritablePath
+ metadataDir android.WritablePath
+}
+
+type DroidstubsProperties struct {
+ // The generated public API filename by Metalava, defaults to <module>_api.txt
+ Api_filename *string
+
+ // the generated removed API filename by Metalava, defaults to <module>_removed.txt
+ Removed_api_filename *string
+
+ // the generated removed Dex API filename by Metalava.
+ Removed_dex_api_filename *string
+
+ Check_api struct {
+ Last_released ApiToCheck
+
+ Current ApiToCheck
+
+ Api_lint struct {
+ Enabled *bool
+
+ // If set, performs api_lint on any new APIs not found in the given signature file
+ New_since *string `android:"path"`
+
+ // If not blank, path to the baseline txt file for approved API lint violations.
+ Baseline_file *string `android:"path"`
+ }
+ }
+
+ // user can specify the version of previous released API file in order to do compatibility check.
+ Previous_api *string `android:"path"`
+
+ // is set to true, Metalava will allow framework SDK to contain annotations.
+ Annotations_enabled *bool
+
+ // a list of top-level directories containing files to merge qualifier annotations (i.e. those intended to be included in the stubs written) from.
+ Merge_annotations_dirs []string
+
+ // a list of top-level directories containing Java stub files to merge show/hide annotations from.
+ Merge_inclusion_annotations_dirs []string
+
+ // a file containing a list of classes to do nullability validation for.
+ Validate_nullability_from_list *string
+
+ // a file containing expected warnings produced by validation of nullability annotations.
+ Check_nullability_warnings *string
+
+ // if set to true, allow Metalava to generate doc_stubs source files. Defaults to false.
+ Create_doc_stubs *bool
+
+ // if set to true, cause Metalava to output Javadoc comments in the stubs source files. Defaults to false.
+ // Has no effect if create_doc_stubs: true.
+ Output_javadoc_comments *bool
+
+ // if set to false then do not write out stubs. Defaults to true.
+ //
+ // TODO(b/146727827): Remove capability when we do not need to generate stubs and API separately.
+ Generate_stubs *bool
+
+ // if set to true, provides a hint to the build system that this rule uses a lot of memory,
+ // whicih can be used for scheduling purposes
+ High_mem *bool
+
+ // is set to true, Metalava will allow framework SDK to contain API levels annotations.
+ Api_levels_annotations_enabled *bool
+
+ // the dirs which Metalava extracts API levels annotations from.
+ Api_levels_annotations_dirs []string
+
+ // the filename which Metalava extracts API levels annotations from. Defaults to android.jar.
+ Api_levels_jar_filename *string
+
+ // if set to true, collect the values used by the Dev tools and
+ // write them in files packaged with the SDK. Defaults to false.
+ Write_sdk_values *bool
+}
+
+// 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{}
+
+ module.AddProperties(&module.properties,
+ &module.Javadoc.properties)
+
+ InitDroiddocModule(module, android.HostAndDeviceSupported)
+ android.InitSdkAwareModule(module)
+ 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{}
+
+ module.AddProperties(&module.properties,
+ &module.Javadoc.properties)
+
+ InitDroiddocModule(module, android.HostSupported)
+ return module
+}
+
+func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case "":
+ return android.Paths{d.stubsSrcJar}, nil
+ case ".docs.zip":
+ return android.Paths{d.docZip}, nil
+ case ".api.txt", android.DefaultDistTag:
+ // This is the default dist path for dist properties that have no tag property.
+ return android.Paths{d.apiFilePath}, nil
+ case ".removed-api.txt":
+ return android.Paths{d.removedApiFilePath}, nil
+ case ".annotations.zip":
+ return android.Paths{d.annotationsZip}, nil
+ case ".api_versions.xml":
+ return android.Paths{d.apiVersionsXml}, nil
+ default:
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+ }
+}
+
+func (d *Droidstubs) ApiFilePath() android.Path {
+ return d.apiFilePath
+}
+
+func (d *Droidstubs) RemovedApiFilePath() android.Path {
+ return d.removedApiFilePath
+}
+
+func (d *Droidstubs) StubsSrcJar() android.Path {
+ return d.stubsSrcJar
+}
+
+var metalavaMergeAnnotationsDirTag = dependencyTag{name: "metalava-merge-annotations-dir"}
+var metalavaMergeInclusionAnnotationsDirTag = dependencyTag{name: "metalava-merge-inclusion-annotations-dir"}
+var metalavaAPILevelsAnnotationsDirTag = dependencyTag{name: "metalava-api-levels-annotations-dir"}
+
+func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) {
+ d.Javadoc.addDeps(ctx)
+
+ if len(d.properties.Merge_annotations_dirs) != 0 {
+ for _, mergeAnnotationsDir := range d.properties.Merge_annotations_dirs {
+ ctx.AddDependency(ctx.Module(), metalavaMergeAnnotationsDirTag, mergeAnnotationsDir)
+ }
+ }
+
+ if len(d.properties.Merge_inclusion_annotations_dirs) != 0 {
+ for _, mergeInclusionAnnotationsDir := range d.properties.Merge_inclusion_annotations_dirs {
+ ctx.AddDependency(ctx.Module(), metalavaMergeInclusionAnnotationsDirTag, mergeInclusionAnnotationsDir)
+ }
+ }
+
+ if len(d.properties.Api_levels_annotations_dirs) != 0 {
+ for _, apiLevelsAnnotationsDir := range d.properties.Api_levels_annotations_dirs {
+ ctx.AddDependency(ctx.Module(), metalavaAPILevelsAnnotationsDirTag, apiLevelsAnnotationsDir)
+ }
+ }
+}
+
+func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) {
+ if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
+ apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
+ String(d.properties.Api_filename) != "" {
+ filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
+ d.apiFile = android.PathForModuleOut(ctx, "metalava", filename)
+ cmd.FlagWithOutput("--api ", d.apiFile)
+ d.apiFilePath = d.apiFile
+ } else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" {
+ // If check api is disabled then make the source file available for export.
+ d.apiFilePath = android.PathForModuleSrc(ctx, sourceApiFile)
+ }
+
+ if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
+ apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
+ String(d.properties.Removed_api_filename) != "" {
+ filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt")
+ d.removedApiFile = android.PathForModuleOut(ctx, "metalava", filename)
+ cmd.FlagWithOutput("--removed-api ", d.removedApiFile)
+ d.removedApiFilePath = d.removedApiFile
+ } else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" {
+ // If check api is disabled then make the source removed api file available for export.
+ d.removedApiFilePath = android.PathForModuleSrc(ctx, sourceRemovedApiFile)
+ }
+
+ if String(d.properties.Removed_dex_api_filename) != "" {
+ d.removedDexApiFile = android.PathForModuleOut(ctx, "metalava", String(d.properties.Removed_dex_api_filename))
+ cmd.FlagWithOutput("--removed-dex-api ", d.removedDexApiFile)
+ }
+
+ if Bool(d.properties.Write_sdk_values) {
+ d.metadataDir = android.PathForModuleOut(ctx, "metalava", "metadata")
+ cmd.FlagWithArg("--sdk-values ", d.metadataDir.String())
+ }
+
+ if stubsDir.Valid() {
+ if Bool(d.properties.Create_doc_stubs) {
+ cmd.FlagWithArg("--doc-stubs ", stubsDir.String())
+ } else {
+ cmd.FlagWithArg("--stubs ", stubsDir.String())
+ if !Bool(d.properties.Output_javadoc_comments) {
+ cmd.Flag("--exclude-documentation-from-stubs")
+ }
+ }
+ }
+}
+
+func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
+ if Bool(d.properties.Annotations_enabled) {
+ cmd.Flag("--include-annotations")
+
+ validatingNullability :=
+ strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") ||
+ String(d.properties.Validate_nullability_from_list) != ""
+
+ migratingNullability := String(d.properties.Previous_api) != ""
+ if migratingNullability {
+ previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api))
+ cmd.FlagWithInput("--migrate-nullness ", previousApi)
+ }
+
+ if s := String(d.properties.Validate_nullability_from_list); s != "" {
+ cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s))
+ }
+
+ if validatingNullability {
+ d.nullabilityWarningsFile = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_nullability_warnings.txt")
+ cmd.FlagWithOutput("--nullability-warnings-txt ", d.nullabilityWarningsFile)
+ }
+
+ d.annotationsZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_annotations.zip")
+ cmd.FlagWithOutput("--extract-annotations ", d.annotationsZip)
+
+ if len(d.properties.Merge_annotations_dirs) != 0 {
+ 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) 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 {
+ 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))
+ }
+ })
+}
+
+func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
+ if !Bool(d.properties.Api_levels_annotations_enabled) {
+ return
+ }
+
+ d.apiVersionsXml = android.PathForModuleOut(ctx, "metalava", "api-versions.xml")
+
+ 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!")
+ }
+
+ cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml)
+ cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml)
+ cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String())
+ cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
+
+ filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
+
+ ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
+ if t, ok := m.(*ExportedDroiddocDir); ok {
+ for _, dep := range t.deps {
+ if dep.Base() == filename {
+ cmd.Implicit(dep)
+ }
+ if filename != "android.jar" && dep.Base() == "android.jar" {
+ // Metalava implicitly searches these patterns:
+ // prebuilts/tools/common/api-versions/android-%/android.jar
+ // prebuilts/sdk/%/public/android.jar
+ // Add android.jar files from the api_levels_annotations_dirs directories to try
+ // to satisfy these patterns. If Metalava can't find a match for an API level
+ // between 1 and 28 in at least one pattern it will fail.
+ cmd.Implicit(dep)
+ }
+ }
+ cmd.FlagWithArg("--android-jar-pattern ", t.dir.String()+"/%/public/"+filename)
+ } else {
+ ctx.PropertyErrorf("api_levels_annotations_dirs",
+ "module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
+ }
+ })
+}
+
+func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
+ srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths,
+ homeDir android.WritablePath) *android.RuleBuilderCommand {
+ rule.Command().Text("rm -rf").Flag(homeDir.String())
+ rule.Command().Text("mkdir -p").Flag(homeDir.String())
+
+ cmd := rule.Command()
+ cmd.FlagWithArg("ANDROID_PREFS_ROOT=", homeDir.String())
+
+ if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") {
+ rule.Remoteable(android.RemoteRuleSupports{RBE: true})
+ execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
+ labels := map[string]string{"type": "tool", "name": "metalava"}
+ // TODO: metalava pool rejects these jobs
+ pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "java16")
+ rule.Rewrapper(&remoteexec.REParams{
+ Labels: labels,
+ ExecStrategy: execStrategy,
+ ToolchainInputs: []string{config.JavaCmd(ctx).String()},
+ Platform: map[string]string{remoteexec.PoolKey: pool},
+ })
+ }
+
+ cmd.BuiltTool("metalava").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "metalava.jar")).
+ Flag(config.JavacVmFlags).
+ Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED").
+ FlagWithArg("-encoding ", "UTF-8").
+ FlagWithArg("-source ", javaVersion.String()).
+ FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "metalava.rsp"), srcs).
+ FlagWithInput("@", srcJarList)
+
+ if len(bootclasspath) > 0 {
+ cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":")
+ }
+
+ if len(classpath) > 0 {
+ cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
+ }
+
+ if len(sourcepaths) > 0 {
+ cmd.FlagWithList("-sourcepath ", sourcepaths.Strings(), ":")
+ } else {
+ cmd.FlagWithArg("-sourcepath ", `""`)
+ }
+
+ cmd.Flag("--no-banner").
+ Flag("--color").
+ Flag("--quiet").
+ Flag("--format=v2").
+ FlagWithArg("--repeat-errors-max ", "10").
+ FlagWithArg("--hide ", "UnresolvedImport")
+
+ return cmd
+}
+
+func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ deps := d.Javadoc.collectDeps(ctx)
+
+ javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), android.SdkContext(d))
+
+ // Create rule for metalava
+
+ srcJarDir := android.PathForModuleOut(ctx, "metalava", "srcjars")
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+
+ rule.Sbox(android.PathForModuleOut(ctx, "metalava"),
+ android.PathForModuleOut(ctx, "metalava.sbox.textproto")).
+ SandboxInputs()
+
+ if BoolDefault(d.properties.High_mem, false) {
+ // This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel.
+ rule.HighMem()
+ }
+
+ generateStubs := BoolDefault(d.properties.Generate_stubs, true)
+ var stubsDir android.OptionalPath
+ if generateStubs {
+ d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-"+"stubs.srcjar")
+ stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "metalava", "stubsDir"))
+ rule.Command().Text("rm -rf").Text(stubsDir.String())
+ rule.Command().Text("mkdir -p").Text(stubsDir.String())
+ }
+
+ srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
+
+ homeDir := android.PathForModuleOut(ctx, "metalava", "home")
+ cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
+ deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths, homeDir)
+ cmd.Implicits(d.Javadoc.implicits)
+
+ d.stubsFlags(ctx, cmd, stubsDir)
+
+ d.annotationsFlags(ctx, cmd)
+ d.inclusionAnnotationsFlags(ctx, cmd)
+ d.apiLevelsAnnotationsFlags(ctx, cmd)
+
+ d.expandArgs(ctx, cmd)
+
+ for _, o := range d.Javadoc.properties.Out {
+ cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
+ }
+
+ // Add options for the other optional tasks: API-lint and check-released.
+ // We generate separate timestamp files for them.
+
+ doApiLint := false
+ doCheckReleased := false
+
+ // Add API lint options.
+
+ if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) {
+ doApiLint = true
+
+ newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
+ if newSince.Valid() {
+ cmd.FlagWithInput("--api-lint ", newSince.Path())
+ } else {
+ cmd.Flag("--api-lint")
+ }
+ d.apiLintReport = android.PathForModuleOut(ctx, "metalava", "api_lint_report.txt")
+ cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO: Change to ":api-lint"
+
+ // TODO(b/154317059): Clean up this allowlist by baselining and/or checking in last-released.
+ if d.Name() != "android.car-system-stubs-docs" &&
+ d.Name() != "android.car-stubs-docs" {
+ cmd.Flag("--lints-as-errors")
+ cmd.Flag("--warnings-as-errors") // Most lints are actually warnings.
+ }
+
+ baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file)
+ updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "api_lint_baseline.txt")
+ d.apiLintTimestamp = android.PathForModuleOut(ctx, "metalava", "api_lint.timestamp")
+
+ // Note this string includes a special shell quote $' ... ', which decodes the "\n"s.
+ // However, because $' ... ' doesn't expand environmental variables, we can't just embed
+ // $PWD, so we have to terminate $'...', use "$PWD", then start $' ... ' again,
+ // which is why we have '"$PWD"$' in it.
+ //
+ // TODO: metalava also has a slightly different message hardcoded. Should we unify this
+ // message and metalava's one?
+ msg := `$'` + // Enclose with $' ... '
+ `************************************************************\n` +
+ `Your API changes are triggering API Lint warnings or errors.\n` +
+ `To make these errors go away, fix the code according to the\n` +
+ `error and/or warning messages above.\n` +
+ `\n` +
+ `If it is not possible to do so, there are workarounds:\n` +
+ `\n` +
+ `1. You can suppress the errors with @SuppressLint("<id>")\n`
+
+ if baselineFile.Valid() {
+ cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path())
+ cmd.FlagWithOutput("--update-baseline:api-lint ", updatedBaselineOutput)
+
+ msg += fmt.Sprintf(``+
+ `2. You can update the baseline by executing the following\n`+
+ ` command:\n`+
+ ` cp \\\n`+
+ ` "'"$PWD"$'/%s" \\\n`+
+ ` "'"$PWD"$'/%s"\n`+
+ ` To submit the revised baseline.txt to the main Android\n`+
+ ` repository, you will need approval.\n`, updatedBaselineOutput, baselineFile.Path())
+ } else {
+ msg += fmt.Sprintf(``+
+ `2. You can add a baseline file of existing lint failures\n`+
+ ` to the build rule of %s.\n`, d.Name())
+ }
+ // Note the message ends with a ' (single quote), to close the $' ... ' .
+ msg += `************************************************************\n'`
+
+ cmd.FlagWithArg("--error-message:api-lint ", msg)
+ }
+
+ // Add "check released" options. (Detect incompatible API changes from the last public release)
+
+ if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") {
+ doCheckReleased = true
+
+ if len(d.Javadoc.properties.Out) > 0 {
+ ctx.PropertyErrorf("out", "out property may not be combined with check_api")
+ }
+
+ apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
+ removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
+ baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
+ updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "last_released_baseline.txt")
+
+ d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_last_released_api.timestamp")
+
+ cmd.FlagWithInput("--check-compatibility:api:released ", apiFile)
+ cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile)
+
+ if baselineFile.Valid() {
+ cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path())
+ cmd.FlagWithOutput("--update-baseline:compatibility:released ", updatedBaselineOutput)
+ }
+
+ // Note this string includes quote ($' ... '), which decodes the "\n"s.
+ 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'`
+
+ cmd.FlagWithArg("--error-message:compatibility:released ", msg)
+ }
+
+ if generateStubs {
+ rule.Command().
+ BuiltTool("soong_zip").
+ Flag("-write_if_changed").
+ Flag("-jar").
+ FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
+ FlagWithArg("-C ", stubsDir.String()).
+ FlagWithArg("-D ", stubsDir.String())
+ }
+
+ if Bool(d.properties.Write_sdk_values) {
+ d.metadataZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-metadata.zip")
+ rule.Command().
+ BuiltTool("soong_zip").
+ Flag("-write_if_changed").
+ Flag("-d").
+ FlagWithOutput("-o ", d.metadataZip).
+ FlagWithArg("-C ", d.metadataDir.String()).
+ FlagWithArg("-D ", d.metadataDir.String())
+ }
+
+ // TODO: We don't really need two separate API files, but this is a reminiscence of how
+ // we used to run metalava separately for API lint and the "last_released" check. Unify them.
+ if doApiLint {
+ rule.Command().Text("touch").Output(d.apiLintTimestamp)
+ }
+ if doCheckReleased {
+ rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp)
+ }
+
+ // TODO(b/183630617): rewrapper doesn't support restat rules
+ // rule.Restat()
+
+ zipSyncCleanupCmd(rule, srcJarDir)
+
+ rule.Build("metalava", "metalava merged")
+
+ if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
+
+ if len(d.Javadoc.properties.Out) > 0 {
+ ctx.PropertyErrorf("out", "out property may not be combined with check_api")
+ }
+
+ apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
+ removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
+ baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
+
+ if baselineFile.Valid() {
+ ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
+ }
+
+ d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_current_api.timestamp")
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+
+ // Diff command line.
+ // -F matches the closest "opening" line, such as "package android {"
+ // and " public class Intent {".
+ diff := `diff -u -F '{ *$'`
+
+ rule.Command().Text("( true")
+ rule.Command().
+ Text(diff).
+ Input(apiFile).Input(d.apiFile)
+
+ rule.Command().
+ Text(diff).
+ 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 (and remove @SystemApi/@TestApi/etc)\n`+
+ ` to the new methods, etc. shown in the above diff.\n\n`+
+ ` 2. You can update current.txt and/or removed.txt by executing the following command:\n`+
+ ` m %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("metalavaCurrentApiCheck", "check current API")
+
+ d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "update_current_api.timestamp")
+
+ // update API rule
+ rule = android.NewRuleBuilder(pctx, ctx)
+
+ 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("metalavaCurrentApiUpdate", "update current API")
+ }
+
+ if String(d.properties.Check_nullability_warnings) != "" {
+ if d.nullabilityWarningsFile == nil {
+ ctx.PropertyErrorf("check_nullability_warnings",
+ "Cannot specify check_nullability_warnings unless validating nullability")
+ }
+
+ checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings))
+
+ d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, "metalava", "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`+
+ `above. You have two options:\n`+
+ ` 1. Resolve the differences by editing the nullability annotations.\n`+
+ ` 2. Update the file of expected warnings by running:\n`+
+ ` cp %s %s\n`+
+ ` and submitting the updated file as part of your change.`,
+ d.nullabilityWarningsFile, checkNullabilityWarnings)
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+
+ 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("nullabilityWarningsCheck", "nullability warnings check")
+ }
+}
+
+func StubsDefaultsFactory() android.Module {
+ module := &DocDefaults{}
+
+ module.AddProperties(
+ &JavadocProperties{},
+ &DroidstubsProperties{},
+ )
+
+ android.InitDefaultsModule(module)
+
+ return module
+}
+
+var _ android.PrebuiltInterface = (*PrebuiltStubsSources)(nil)
+
+type PrebuiltStubsSourcesProperties struct {
+ Srcs []string `android:"path"`
+}
+
+type PrebuiltStubsSources struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+ prebuilt android.Prebuilt
+ android.SdkBase
+
+ properties PrebuiltStubsSourcesProperties
+
+ stubsSrcJar android.ModuleOutPath
+}
+
+func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case "":
+ return android.Paths{p.stubsSrcJar}, nil
+ default:
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+ }
+}
+
+func (d *PrebuiltStubsSources) StubsSrcJar() android.Path {
+ return d.stubsSrcJar
+}
+
+func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
+
+ if len(p.properties.Srcs) != 1 {
+ ctx.PropertyErrorf("srcs", "must only specify one directory path, contains %d paths", len(p.properties.Srcs))
+ return
+ }
+
+ localSrcDir := p.properties.Srcs[0]
+ // Although PathForModuleSrc can return nil if either the path doesn't exist or
+ // the path components are invalid it won't in this case because no components
+ // are specified and the module directory must exist in order to get this far.
+ srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, localSrcDir)
+
+ // Glob the contents of the directory just in case the directory does not exist.
+ srcGlob := localSrcDir + "/**/*"
+ srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob})
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ BuiltTool("soong_zip").
+ Flag("-write_if_changed").
+ Flag("-jar").
+ FlagWithOutput("-o ", p.stubsSrcJar).
+ FlagWithArg("-C ", srcDir.String()).
+ FlagWithRspFileInputList("-r ", p.stubsSrcJar.ReplaceExtension(ctx, "rsp"), srcPaths)
+
+ rule.Restat()
+
+ rule.Build("zip src", "Create srcjar from prebuilt source")
+}
+
+func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt {
+ return &p.prebuilt
+}
+
+func (p *PrebuiltStubsSources) Name() string {
+ return p.prebuilt.Name(p.ModuleBase.Name())
+}
+
+// prebuilt_stubs_sources imports a set of java source files as if they were
+// generated by droidstubs.
+//
+// By default, a prebuilt_stubs_sources has a single variant that expects a
+// set of `.java` files generated by droidstubs.
+//
+// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one
+// for host modules.
+//
+// Intended only for use by sdk snapshots.
+func PrebuiltStubsSourcesFactory() android.Module {
+ module := &PrebuiltStubsSources{}
+
+ module.AddProperties(&module.properties)
+
+ android.InitPrebuiltModule(module, &module.properties.Srcs)
+ android.InitSdkAwareModule(module)
+ InitDroiddocModule(module, android.HostAndDeviceSupported)
+ return module
+}
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
new file mode 100644
index 0000000..db664c1
--- /dev/null
+++ b/java/droidstubs_test.go
@@ -0,0 +1,173 @@
+// Copyright 2021 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 (
+ "reflect"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestDroidstubs(t *testing.T) {
+ ctx, _ := testJavaWithFS(t, `
+ droiddoc_exported_dir {
+ name: "droiddoc-templates-sdk",
+ path: ".",
+ }
+
+ droidstubs {
+ name: "bar-stubs",
+ srcs: ["bar-doc/a.java"],
+ api_levels_annotations_dirs: ["droiddoc-templates-sdk"],
+ api_levels_annotations_enabled: true,
+ }
+
+ droidstubs {
+ name: "bar-stubs-other",
+ srcs: ["bar-doc/a.java"],
+ high_mem: true,
+ api_levels_annotations_dirs: ["droiddoc-templates-sdk"],
+ api_levels_annotations_enabled: true,
+ api_levels_jar_filename: "android.other.jar",
+ }
+ `,
+ map[string][]byte{
+ "bar-doc/a.java": nil,
+ })
+ testcases := []struct {
+ moduleName string
+ expectedJarFilename string
+ high_mem bool
+ }{
+ {
+ moduleName: "bar-stubs",
+ expectedJarFilename: "android.jar",
+ high_mem: false,
+ },
+ {
+ moduleName: "bar-stubs-other",
+ expectedJarFilename: "android.other.jar",
+ high_mem: true,
+ },
+ }
+ for _, c := range testcases {
+ m := ctx.ModuleForTests(c.moduleName, "android_common")
+ manifest := m.Output("metalava.sbox.textproto")
+ sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
+ expected := "--android-jar-pattern ./%/public/" + c.expectedJarFilename
+ if actual := String(sboxProto.Commands[0].Command); !strings.Contains(actual, expected) {
+ t.Errorf("For %q, expected metalava argument %q, but was not found %q", c.moduleName, expected, actual)
+ }
+
+ metalava := m.Rule("metalava")
+ rp := metalava.RuleParams
+ if actual := rp.Pool != nil && strings.Contains(rp.Pool.String(), "highmem"); actual != c.high_mem {
+ t.Errorf("Expected %q high_mem to be %v, was %v", c.moduleName, c.high_mem, actual)
+ }
+ }
+}
+
+func TestDroidstubsSandbox(t *testing.T) {
+ ctx, _ := testJavaWithFS(t, `
+ genrule {
+ name: "foo",
+ out: ["foo.txt"],
+ cmd: "touch $(out)",
+ }
+
+ droidstubs {
+ name: "bar-stubs",
+ srcs: ["bar-doc/a.java"],
+
+ args: "--reference $(location :foo)",
+ arg_files: [":foo"],
+ }
+ `,
+ map[string][]byte{
+ "bar-doc/a.java": nil,
+ })
+
+ m := ctx.ModuleForTests("bar-stubs", "android_common")
+ metalava := m.Rule("metalava")
+ if g, w := metalava.Inputs.Strings(), []string{"bar-doc/a.java"}; !reflect.DeepEqual(w, g) {
+ t.Errorf("Expected inputs %q, got %q", w, g)
+ }
+
+ manifest := android.RuleBuilderSboxProtoForTests(t, m.Output("metalava.sbox.textproto"))
+ if g, w := manifest.Commands[0].GetCommand(), "reference __SBOX_SANDBOX_DIR__/out/.intermediates/foo/gen/foo.txt"; !strings.Contains(g, w) {
+ t.Errorf("Expected command to contain %q, got %q", w, g)
+ }
+}
+
+func TestDroidstubsWithSystemModules(t *testing.T) {
+ ctx, _ := testJava(t, `
+ droidstubs {
+ name: "stubs-source-system-modules",
+ srcs: [
+ "bar-doc/a.java",
+ ],
+ sdk_version: "none",
+ system_modules: "source-system-modules",
+ }
+
+ java_library {
+ name: "source-jar",
+ srcs: [
+ "a.java",
+ ],
+ }
+
+ java_system_modules {
+ name: "source-system-modules",
+ libs: ["source-jar"],
+ }
+
+ droidstubs {
+ name: "stubs-prebuilt-system-modules",
+ srcs: [
+ "bar-doc/a.java",
+ ],
+ sdk_version: "none",
+ system_modules: "prebuilt-system-modules",
+ }
+
+ java_import {
+ name: "prebuilt-jar",
+ jars: ["a.jar"],
+ }
+
+ java_system_modules_import {
+ name: "prebuilt-system-modules",
+ libs: ["prebuilt-jar"],
+ }
+ `)
+
+ checkSystemModulesUseByDroidstubs(t, ctx, "stubs-source-system-modules", "source-jar.jar")
+
+ checkSystemModulesUseByDroidstubs(t, ctx, "stubs-prebuilt-system-modules", "prebuilt-jar.jar")
+}
+
+func checkSystemModulesUseByDroidstubs(t *testing.T, ctx *android.TestContext, moduleName string, systemJar string) {
+ metalavaRule := ctx.ModuleForTests(moduleName, "android_common").Rule("metalava")
+ var systemJars []string
+ for _, i := range metalavaRule.Implicits {
+ systemJars = append(systemJars, i.Base())
+ }
+ if len(systemJars) < 1 || systemJars[0] != systemJar {
+ t.Errorf("inputs of %q must be []string{%q}, but was %#v.", moduleName, systemJar, systemJars)
+ }
+}
diff --git a/java/gen.go b/java/gen.go
index 4f928d5..445a2d8 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -174,6 +174,12 @@
logtags() android.Paths
}
+func (j *Module) logtags() android.Paths {
+ return j.logtagsSrcs
+}
+
+var _ logtagsProducer = (*Module)(nil)
+
type logtagsSingleton struct{}
func (l *logtagsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 71f1e57..3ecb977 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -15,8 +15,6 @@
package java
import (
- "strings"
-
"github.com/google/blueprint"
"android/soong/android"
@@ -28,10 +26,64 @@
}, "outFlag", "stubAPIFlags")
type hiddenAPI struct {
- bootDexJarPath android.Path
- flagsCSVPath android.Path
- indexCSVPath android.Path
+ // The name of the module as it would be used in the boot jars configuration, e.g. without any
+ // prebuilt_ prefix (if it is a prebuilt) and without any ".impl" suffix if it is a
+ // java_sdk_library implementation library.
+ configurationName string
+
+ // True if the module containing this structure contributes to the hiddenapi information or has
+ // that information encoded within it.
+ active bool
+
+ // Identifies the active module variant which will be used as the source of hiddenapi information.
+ //
+ // A class may be compiled into a number of different module variants each of which will need the
+ // hiddenapi information encoded into it and so will be marked as active. However, only one of
+ // them must be used as a source of information by hiddenapi otherwise it will end up with
+ // duplicate entries. That module will have primary=true.
+ //
+ // Note, that modules <x>-hiddenapi that provide additional annotation information for module <x>
+ // that is on the bootclasspath are marked as primary=true as they are the primary source of that
+ // annotation information.
+ primary bool
+
+ // The path to the dex jar that is in the boot class path. If this is nil then the associated
+ // module is not a boot jar, but could be one of the <x>-hiddenapi modules that provide additional
+ // annotations for the <x> boot dex jar but which do not actually provide a boot dex jar
+ // themselves.
+ //
+ // This must be the path to the unencoded dex jar as the encoded dex jar indirectly depends on
+ // this file so using the encoded dex jar here would result in a cycle in the ninja rules.
+ bootDexJarPath android.Path
+
+ // The path to the CSV file that contains mappings from Java signature to various flags derived
+ // from annotations in the source, e.g. whether it is public or the sdk version above which it
+ // can no longer be used.
+ //
+ // It is created by the Class2NonSdkList tool which processes the .class files in the class
+ // implementation jar looking for UnsupportedAppUsage and CovariantReturnType annotations. The
+ // tool also consumes the hiddenAPISingletonPathsStruct.stubFlags file in order to perform
+ // consistency checks on the information in the annotations and to filter out bridge methods
+ // that are already part of the public API.
+ flagsCSVPath android.Path
+
+ // The path to the CSV file that contains mappings from Java signature to the value of properties
+ // specified on UnsupportedAppUsage annotations in the source.
+ //
+ // Like the flagsCSVPath file this is also created by the Class2NonSdkList in the same way.
+ // Although the two files could potentially be created in a single invocation of the
+ // Class2NonSdkList at the moment they are created using their own invocation, with the behavior
+ // being determined by the property that is used.
metadataCSVPath android.Path
+
+ // The path to the CSV file that contains mappings from Java signature to source location
+ // information.
+ //
+ // It is created by the merge_csv tool which processes the class implementation jar, extracting
+ // all the files ending in .uau (which are CSV files) and merges them together. The .uau files are
+ // created by the unsupported app usage annotation processor during compilation of the class
+ // implementation jar.
+ indexCSVPath android.Path
}
func (h *hiddenAPI) flagsCSV() android.Path {
@@ -59,60 +111,139 @@
var _ hiddenAPIIntf = (*hiddenAPI)(nil)
-func (h *hiddenAPI) hiddenAPI(ctx android.ModuleContext, name string, primary bool, dexJar android.ModuleOutPath,
- implementationJar android.Path, uncompressDex bool) android.ModuleOutPath {
- if !ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+// hiddenAPISupportingModule is the interface that is implemented by any module that supports
+// contributing to the hidden API processing.
+type hiddenAPISupportingModule interface {
+ android.Module
+ hiddenAPIIntf
+}
- // Modules whose names are of the format <x>-hiddenapi provide hiddenapi information
- // for the boot jar module <x>. Otherwise, the module provides information for itself.
- // Either way extract the name of the boot jar module.
- bootJarName := strings.TrimSuffix(name, "-hiddenapi")
+// Initialize the hiddenapi structure
+func (h *hiddenAPI) initHiddenAPI(ctx android.BaseModuleContext, configurationName string) {
+ // If hiddenapi processing is disabled treat this as inactive.
+ if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+ return
+ }
- // If this module is on the boot jars list (or providing information for a module
- // on the list) then extract the hiddenapi information from it, and if necessary
- // encode that information in the generated dex file.
- //
- // It is important that hiddenapi information is only gathered for/from modules on
- // that are actually on the boot jars list because the runtime only enforces access
- // to the hidden API for the bootclassloader. If information is gathered for modules
- // not on the list then that will cause failures in the CtsHiddenApiBlacklist...
- // tests.
- if inList(bootJarName, ctx.Config().BootJars()) {
- // Derive the greylist from classes jar.
- flagsCSV := android.PathForModuleOut(ctx, "hiddenapi", "flags.csv")
- metadataCSV := android.PathForModuleOut(ctx, "hiddenapi", "metadata.csv")
- indexCSV := android.PathForModuleOut(ctx, "hiddenapi", "index.csv")
- h.hiddenAPIGenerateCSV(ctx, flagsCSV, metadataCSV, indexCSV, implementationJar)
+ h.configurationName = configurationName
- // If this module is actually on the boot jars list and not providing
- // hiddenapi information for a module on the boot jars list then encode
- // the gathered information in the generated dex file.
- if name == bootJarName {
- hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", name+".jar")
+ // It is important that hiddenapi information is only gathered for/from modules that are actually
+ // on the boot jars list because the runtime only enforces access to the hidden API for the
+ // bootclassloader. If information is gathered for modules not on the list then that will cause
+ // failures in the CtsHiddenApiBlocklist... tests.
+ module := ctx.Module()
+ h.active = isModuleInBootClassPath(ctx, module)
+ if !h.active {
+ // The rest of the properties will be ignored if active is false.
+ return
+ }
- // More than one library with the same classes can be encoded but only one can
- // be added to the global set of flags, otherwise it will result in duplicate
- // classes which is an error. Therefore, only add the dex jar of one of them
- // to the global set of flags.
- if primary {
- h.bootDexJarPath = dexJar
+ // Determine whether this module is the primary module or not.
+ primary := true
+
+ // A prebuilt module is only primary if it is preferred and conversely a source module is only
+ // primary if it has not been replaced by a prebuilt module.
+ if pi, ok := module.(android.PrebuiltInterface); ok {
+ if p := pi.Prebuilt(); p != nil {
+ primary = p.UsePrebuilt()
+ }
+ } else {
+ // The only module that will pass a different configurationName to its module name to this
+ // method is the implementation library of a java_sdk_library. It has a configuration name of
+ // <x> the same as its parent java_sdk_library but a module name of <x>.impl. It is not the
+ // primary module, the java_sdk_library with the name of <x> is.
+ primary = configurationName == ctx.ModuleName()
+
+ // A source module that has been replaced by a prebuilt can never be the primary module.
+ if module.IsReplacedByPrebuilt() {
+ ctx.VisitDirectDepsWithTag(android.PrebuiltDepTag, func(prebuilt android.Module) {
+ if h, ok := prebuilt.(hiddenAPIIntf); ok && h.bootDexJar() != nil {
+ primary = false
+ } else {
+ ctx.ModuleErrorf(
+ "hiddenapi has determined that the source module %q should be ignored as it has been"+
+ " replaced by the prebuilt module %q but unfortunately it does not provide a"+
+ " suitable boot dex jar", ctx.ModuleName(), ctx.OtherModuleName(prebuilt))
}
- hiddenAPIEncodeDex(ctx, hiddenAPIJar, dexJar, uncompressDex)
- dexJar = hiddenAPIJar
- }
+ })
}
}
+ h.primary = primary
+}
+
+func isModuleInBootClassPath(ctx android.BaseModuleContext, module android.Module) bool {
+ // Get the configured non-updatable and updatable boot jars.
+ nonUpdatableBootJars := ctx.Config().NonUpdatableBootJars()
+ updatableBootJars := ctx.Config().UpdatableBootJars()
+ active := isModuleInConfiguredList(ctx, module, nonUpdatableBootJars) ||
+ isModuleInConfiguredList(ctx, module, updatableBootJars)
+ return active
+}
+
+// hiddenAPIExtractAndEncode is called by any module that could contribute to the hiddenapi
+// processing.
+//
+// It ignores any module that has not had initHiddenApi() called on it and which is not in the boot
+// jar list.
+//
+// Otherwise, it generates ninja rules to do the following:
+// 1. Extract information needed for hiddenapi processing from the module and output it into CSV
+// files.
+// 2. Conditionally adds the supplied dex file to the list of files used to generate the
+// hiddenAPISingletonPathsStruct.stubsFlag file.
+// 3. Conditionally creates a copy of the supplied dex file into which it has encoded the hiddenapi
+// flags and returns this instead of the supplied dex jar, otherwise simply returns the supplied
+// dex jar.
+func (h *hiddenAPI) hiddenAPIExtractAndEncode(ctx android.ModuleContext, dexJar android.OutputPath,
+ implementationJar android.Path, uncompressDex bool) android.OutputPath {
+
+ if !h.active {
+ return dexJar
+ }
+
+ h.hiddenAPIExtractInformation(ctx, dexJar, implementationJar)
+
+ hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", h.configurationName+".jar").OutputPath
+
+ // Create a copy of the dex jar which has been encoded with hiddenapi flags.
+ hiddenAPIEncodeDex(ctx, hiddenAPIJar, dexJar, uncompressDex)
+
+ // Use the encoded dex jar from here onwards.
+ dexJar = hiddenAPIJar
return dexJar
}
-func (h *hiddenAPI) hiddenAPIGenerateCSV(ctx android.ModuleContext, flagsCSV, metadataCSV, indexCSV android.WritablePath, classesJar android.Path) {
+// hiddenAPIExtractInformation generates ninja rules to extract the information from the classes
+// jar, and outputs it to the appropriate module specific CSV file.
+//
+// It also makes the dex jar available for use when generating the
+// hiddenAPISingletonPathsStruct.stubFlags.
+func (h *hiddenAPI) hiddenAPIExtractInformation(ctx android.ModuleContext, dexJar, classesJar android.Path) {
+ if !h.active {
+ return
+ }
+
+ // More than one library with the same classes may need to be encoded but only one should be
+ // used as a source of information for hidden API processing otherwise it will result in
+ // duplicate entries in the files.
+ if !h.primary {
+ return
+ }
+
+ classesJars := android.Paths{classesJar}
+ ctx.VisitDirectDepsWithTag(hiddenApiAnnotationsTag, func(dep android.Module) {
+ javaInfo := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+ classesJars = append(classesJars, javaInfo.ImplementationJars...)
+ })
+
stubFlagsCSV := hiddenAPISingletonPaths(ctx).stubFlags
+ flagsCSV := android.PathForModuleOut(ctx, "hiddenapi", "flags.csv")
ctx.Build(pctx, android.BuildParams{
Rule: hiddenAPIGenerateCSVRule,
Description: "hiddenapi flags",
- Input: classesJar,
+ Inputs: classesJars,
Output: flagsCSV,
Implicit: stubFlagsCSV,
Args: map[string]string{
@@ -122,10 +253,11 @@
})
h.flagsCSVPath = flagsCSV
+ metadataCSV := android.PathForModuleOut(ctx, "hiddenapi", "metadata.csv")
ctx.Build(pctx, android.BuildParams{
Rule: hiddenAPIGenerateCSVRule,
Description: "hiddenapi metadata",
- Input: classesJar,
+ Inputs: classesJars,
Output: metadataCSV,
Implicit: stubFlagsCSV,
Args: map[string]string{
@@ -135,13 +267,20 @@
})
h.metadataCSVPath = metadataCSV
+ indexCSV := android.PathForModuleOut(ctx, "hiddenapi", "index.csv")
rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
BuiltTool("merge_csv").
- FlagWithInput("--zip_input=", classesJar).
- FlagWithOutput("--output=", indexCSV)
+ Flag("--zip_input").
+ Flag("--key_field signature").
+ FlagWithOutput("--output=", indexCSV).
+ Inputs(classesJars)
rule.Build("merged-hiddenapi-index", "Merged Hidden API index")
h.indexCSVPath = indexCSV
+
+ // Save the unencoded dex jar so it can be used when generating the
+ // hiddenAPISingletonPathsStruct.stubFlags file.
+ h.bootDexJarPath = dexJar
}
var hiddenAPIEncodeDexRule = pctx.AndroidStaticRule("hiddenAPIEncodeDex", blueprint.RuleParams{
@@ -214,3 +353,16 @@
TransformZipAlign(ctx, output, tmpOutput)
}
}
+
+type hiddenApiAnnotationsDependencyTag struct {
+ blueprint.BaseDependencyTag
+}
+
+// Tag used to mark dependencies on java_library instances that contains Java source files whose
+// sole purpose is to provide additional hiddenapi annotations.
+var hiddenApiAnnotationsTag hiddenApiAnnotationsDependencyTag
+
+// Mark this tag so dependencies that use it are excluded from APEX contents.
+func (t hiddenApiAnnotationsDependencyTag) ExcludeFromApexContents() {}
+
+var _ android.ExcludeFromApexContentsTag = hiddenApiAnnotationsTag
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
new file mode 100644
index 0000000..7cf082b
--- /dev/null
+++ b/java/hiddenapi_modular.go
@@ -0,0 +1,219 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "android/soong/android"
+)
+
+// Contains support for processing hiddenAPI in a modular fashion.
+
+// HiddenAPIFlagFileProperties contains paths to the flag files that can be used to augment the
+// information obtained from annotations within the source code in order to create the complete set
+// of flags that should be applied to the dex implementation jars on the bootclasspath.
+//
+// Each property contains a list of paths. With the exception of the Unsupported_packages the paths
+// of each property reference a plain text file that contains a java signature per line. The flags
+// for each of those signatures will be updated in a property specific way.
+//
+// The Unsupported_packages property contains a list of paths, each of which is a plain text file
+// with one Java package per line. All members of all classes within that package (but not nested
+// packages) will be updated in a property specific way.
+type HiddenAPIFlagFileProperties struct {
+ // Marks each signature in the referenced files as being unsupported.
+ Unsupported []string `android:"path"`
+
+ // Marks each signature in the referenced files as being unsupported because it has been removed.
+ // Any conflicts with other flags are ignored.
+ Removed []string `android:"path"`
+
+ // Marks each signature in the referenced files as being supported only for targetSdkVersion <= R
+ // and low priority.
+ Max_target_r_low_priority []string `android:"path"`
+
+ // Marks each signature in the referenced files as being supported only for targetSdkVersion <= Q.
+ Max_target_q []string `android:"path"`
+
+ // Marks each signature in the referenced files as being supported only for targetSdkVersion <= P.
+ Max_target_p []string `android:"path"`
+
+ // Marks each signature in the referenced files as being supported only for targetSdkVersion <= O
+ // and low priority. Any conflicts with other flags are ignored.
+ Max_target_o_low_priority []string `android:"path"`
+
+ // Marks each signature in the referenced files as being blocked.
+ Blocked []string `android:"path"`
+
+ // Marks each signature in every package in the referenced files as being unsupported.
+ Unsupported_packages []string `android:"path"`
+}
+
+func (p *HiddenAPIFlagFileProperties) hiddenAPIFlagFileInfo(ctx android.ModuleContext) hiddenAPIFlagFileInfo {
+ info := hiddenAPIFlagFileInfo{categoryToPaths: map[*hiddenAPIFlagFileCategory]android.Paths{}}
+ for _, category := range hiddenAPIFlagFileCategories {
+ paths := android.PathsForModuleSrc(ctx, category.propertyAccessor(p))
+ info.categoryToPaths[category] = paths
+ }
+ return info
+}
+
+type hiddenAPIFlagFileCategory struct {
+ // propertyName is the name of the property for this category.
+ propertyName string
+
+ // propertyAccessor retrieves the value of the property for this category from the set of
+ // properties.
+ propertyAccessor func(properties *HiddenAPIFlagFileProperties) []string
+
+ // commandMutator adds the appropriate command line options for this category to the supplied
+ // command
+ commandMutator func(command *android.RuleBuilderCommand, path android.Path)
+}
+
+var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{
+ // See HiddenAPIFlagFileProperties.Unsupported
+ {
+ propertyName: "unsupported",
+ propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+ return properties.Unsupported
+ },
+ commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
+ command.FlagWithInput("--unsupported ", path)
+ },
+ },
+ // See HiddenAPIFlagFileProperties.Removed
+ {
+ propertyName: "removed",
+ propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+ return properties.Removed
+ },
+ commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
+ command.FlagWithInput("--unsupported ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed")
+ },
+ },
+ // See HiddenAPIFlagFileProperties.Max_target_r_low_priority
+ {
+ propertyName: "max_target_r_low_priority",
+ propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+ return properties.Max_target_r_low_priority
+ },
+ commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
+ command.FlagWithInput("--max-target-r ", path).FlagWithArg("--tag ", "lo-prio")
+ },
+ },
+ // See HiddenAPIFlagFileProperties.Max_target_q
+ {
+ propertyName: "max_target_q",
+ propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+ return properties.Max_target_q
+ },
+ commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
+ command.FlagWithInput("--max-target-q ", path)
+ },
+ },
+ // See HiddenAPIFlagFileProperties.Max_target_p
+ {
+ propertyName: "max_target_p",
+ propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+ return properties.Max_target_p
+ },
+ commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
+ command.FlagWithInput("--max-target-p ", path)
+ },
+ },
+ // See HiddenAPIFlagFileProperties.Max_target_o_low_priority
+ {
+ propertyName: "max_target_o_low_priority",
+ propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+ return properties.Max_target_o_low_priority
+ },
+ commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
+ command.FlagWithInput("--max-target-o ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio")
+ },
+ },
+ // See HiddenAPIFlagFileProperties.Blocked
+ {
+ propertyName: "blocked",
+ propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+ return properties.Blocked
+ },
+ commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
+ command.FlagWithInput("--blocked ", path)
+ },
+ },
+ // See HiddenAPIFlagFileProperties.Unsupported_packages
+ {
+ propertyName: "unsupported_packages",
+ propertyAccessor: func(properties *HiddenAPIFlagFileProperties) []string {
+ return properties.Unsupported_packages
+ },
+ commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
+ command.FlagWithInput("--unsupported ", path).Flag("--packages ")
+ },
+ },
+}
+
+// hiddenAPIFlagFileInfo contains paths resolved from HiddenAPIFlagFileProperties
+type hiddenAPIFlagFileInfo struct {
+ // categoryToPaths maps from the flag file category to the paths containing information for that
+ // category.
+ categoryToPaths map[*hiddenAPIFlagFileCategory]android.Paths
+}
+
+// ruleToGenerateHiddenApiFlags creates a rule to create the monolithic hidden API flags from the
+// flags from all the modules, the stub flags, augmented with some additional configuration files.
+//
+// baseFlagsPath is the path to the flags file containing all the information from the stubs plus
+// an entry for every single member in the dex implementation jars of the individual modules. Every
+// signature in any of the other files MUST be included in this file.
+//
+// moduleSpecificFlagsPaths are the paths to the flags files generated by each module using
+// information from the baseFlagsPath as well as from annotations within the source.
+//
+// augmentationInfo is a struct containing paths to files that augment the information provided by
+// the moduleSpecificFlagsPaths.
+// ruleToGenerateHiddenApiFlags creates a rule to create the monolithic hidden API flags from the
+// flags from all the modules, the stub flags, augmented with some additional configuration files.
+//
+// baseFlagsPath is the path to the flags file containing all the information from the stubs plus
+// an entry for every single member in the dex implementation jars of the individual modules. Every
+// signature in any of the other files MUST be included in this file.
+//
+// moduleSpecificFlagsPaths are the paths to the flags files generated by each module using
+// information from the baseFlagsPath as well as from annotations within the source.
+//
+// augmentationInfo is a struct containing paths to files that augment the information provided by
+// the moduleSpecificFlagsPaths.
+func ruleToGenerateHiddenApiFlags(ctx android.BuilderContext, outputPath android.WritablePath, baseFlagsPath android.Path, moduleSpecificFlagsPaths android.Paths, augmentationInfo hiddenAPIFlagFileInfo) {
+ tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp")
+ rule := android.NewRuleBuilder(pctx, ctx)
+ command := rule.Command().
+ BuiltTool("generate_hiddenapi_lists").
+ FlagWithInput("--csv ", baseFlagsPath).
+ Inputs(moduleSpecificFlagsPaths).
+ FlagWithOutput("--output ", tempPath)
+
+ // Add the options for the different categories of flag files.
+ for _, category := range hiddenAPIFlagFileCategories {
+ paths := augmentationInfo.categoryToPaths[category]
+ for _, path := range paths {
+ category.commandMutator(command, path)
+ }
+ }
+
+ commitChangeForRestat(rule, tempPath, outputPath)
+
+ rule.Build("hiddenAPIFlagsFile", "hiddenapi flags")
+}
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 419dc34..ed0b722 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -15,22 +15,78 @@
package java
import (
- "fmt"
-
"android/soong/android"
- "android/soong/genrule"
)
func init() {
- android.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
- android.RegisterSingletonType("hiddenapi_index", hiddenAPIIndexSingletonFactory)
- android.RegisterModuleType("hiddenapi_flags", hiddenAPIFlagsFactory)
+ RegisterHiddenApiSingletonComponents(android.InitRegistrationContext)
}
+func RegisterHiddenApiSingletonComponents(ctx android.RegistrationContext) {
+ ctx.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
+}
+
+var PrepareForTestWithHiddenApiBuildComponents = android.FixtureRegisterWithContext(RegisterHiddenApiSingletonComponents)
+
type hiddenAPISingletonPathsStruct struct {
- flags android.OutputPath
- index android.OutputPath
- metadata android.OutputPath
+ // The path to the CSV file that contains the flags that will be encoded into the dex boot jars.
+ //
+ // It is created by the generate_hiddenapi_lists.py tool that is passed the stubFlags along with
+ // a number of additional files that are used to augment the information in the stubFlags with
+ // manually curated data.
+ flags android.OutputPath
+
+ // The path to the CSV index file that contains mappings from Java signature to source location
+ // information for all Java elements annotated with the UnsupportedAppUsage annotation in the
+ // source of all the boot jars.
+ //
+ // It is created by the merge_csv tool which merges all the hiddenAPI.indexCSVPath files that have
+ // been created by the rest of the build. That includes the index files generated for
+ // <x>-hiddenapi modules.
+ index android.OutputPath
+
+ // The path to the CSV metadata file that contains mappings from Java signature to the value of
+ // properties specified on UnsupportedAppUsage annotations in the source of all the boot jars.
+ //
+ // It is created by the merge_csv tool which merges all the hiddenAPI.metadataCSVPath files that
+ // have been created by the rest of the build. That includes the metadata files generated for
+ // <x>-hiddenapi modules.
+ metadata android.OutputPath
+
+ // The path to the CSV metadata file that contains mappings from Java signature to flags obtained
+ // from the public, system and test API stubs.
+ //
+ // This is created by the hiddenapi tool which is given dex files for the public, system and test
+ // API stubs (including product specific stubs) along with dex boot jars, so does not include
+ // <x>-hiddenapi modules. For each API surface (i.e. public, system, test) it records which
+ // members in the dex boot jars match a member in the dex stub jars for that API surface and then
+ // outputs a file containing the signatures of all members in the dex boot jars along with the
+ // flags that indicate which API surface it belongs, if any.
+ //
+ // e.g. a dex member that matches a member in the public dex stubs would have flags
+ // "public-api,system-api,test-api" set (as system and test are both supersets of public). A dex
+ // member that didn't match a member in any of the dex stubs is still output it just has an empty
+ // set of flags.
+ //
+ // The notion of matching is quite complex, it is not restricted to just exact matching but also
+ // follows the Java inheritance rules. e.g. if a method is public then all overriding/implementing
+ // methods are also public. If an interface method is public and a class inherits an
+ // implementation of that method from a super class then that super class method is also public.
+ // That ensures that any method that can be called directly by an App through a public method is
+ // visible to that App.
+ //
+ // Propagating the visibility of members across the inheritance hierarchy at build time will cause
+ // problems when modularizing and unbundling as it that propagation can cross module boundaries.
+ // e.g. Say that a private framework class implements a public interface and inherits an
+ // implementation of one of its methods from a core platform ART class. In that case the ART
+ // implementation method needs to be marked as public which requires the build to have access to
+ // the framework implementation classes at build time. The work to rectify this is being tracked
+ // at http://b/178693149.
+ //
+ // This file (or at least those items marked as being in the public-api) is used by hiddenapi when
+ // creating the metadata and flags for the individual modules in order to perform consistency
+ // checks and filter out bridge methods that are part of the public API. The latter relies on the
+ // propagation of visibility across the inheritance hierarchy.
stubFlags android.OutputPath
}
@@ -41,11 +97,15 @@
// yet been created.
func hiddenAPISingletonPaths(ctx android.PathContext) hiddenAPISingletonPathsStruct {
return ctx.Config().Once(hiddenAPISingletonPathsKey, func() interface{} {
+ // Make the paths relative to the out/soong/hiddenapi directory instead of to the out/soong/
+ // directory. This ensures that if they are used as java_resources they do not end up in a
+ // hiddenapi directory in the resulting APK.
+ hiddenapiDir := android.PathForOutput(ctx, "hiddenapi")
return hiddenAPISingletonPathsStruct{
- flags: android.PathForOutput(ctx, "hiddenapi", "hiddenapi-flags.csv"),
- index: android.PathForOutput(ctx, "hiddenapi", "hiddenapi-index.csv"),
- metadata: android.PathForOutput(ctx, "hiddenapi", "hiddenapi-unsupported.csv"),
- stubFlags: android.PathForOutput(ctx, "hiddenapi", "hiddenapi-stub-flags.txt"),
+ flags: hiddenapiDir.Join(ctx, "hiddenapi-flags.csv"),
+ index: hiddenapiDir.Join(ctx, "hiddenapi-index.csv"),
+ metadata: hiddenapiDir.Join(ctx, "hiddenapi-unsupported.csv"),
+ stubFlags: hiddenapiDir.Join(ctx, "hiddenapi-stub-flags.txt"),
}
}).(hiddenAPISingletonPathsStruct)
}
@@ -55,7 +115,7 @@
}
type hiddenAPISingleton struct {
- flags, metadata android.Path
+ flags android.Path
}
// hiddenAPI singleton rules
@@ -67,27 +127,35 @@
stubFlagsRule(ctx)
+ // If there is a prebuilt hiddenapi dir, generate rules to use the
+ // files within. Generally, we build the hiddenapi files from source
+ // during the build, ensuring consistency. It's possible, in a split
+ // build (framework and vendor) scenario, for the vendor build to use
+ // prebuilt hiddenapi files from the framework build. In this scenario,
+ // the framework and vendor builds must use the same source to ensure
+ // consistency.
+
+ if ctx.Config().PrebuiltHiddenApiDir(ctx) != "" {
+ h.flags = prebuiltFlagsRule(ctx)
+ prebuiltIndexRule(ctx)
+ return
+ }
+
// 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) {
h.flags = flagsRule(ctx)
- h.metadata = metadataRule(ctx)
} else {
h.flags = emptyFlagsRule(ctx)
}
}
// Export paths to Make. INTERNAL_PLATFORM_HIDDENAPI_FLAGS is used by Make rules in art/ and cts/.
-// Both paths are used to call dist-for-goals.
func (h *hiddenAPISingleton) MakeVars(ctx android.MakeVarsContext) {
if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
return
}
ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_FLAGS", h.flags.String())
-
- if h.metadata != nil {
- ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA", h.metadata.String())
- }
}
// stubFlagsRule creates the rule to build hiddenapi-stub-flags.txt out of dex jars from stub modules and boot image
@@ -112,17 +180,6 @@
// We do not have prebuilts of the core platform api yet
corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs")
- // 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 ctx.Config().AlwaysUsePrebuiltSdks() {
- publicStubModules = append(publicStubModules, "sdk_public_current_android.test.base")
- } else {
- publicStubModules = append(publicStubModules, "android.test.base.stubs")
- }
- }
-
// Allow products to define their own stubs for custom product jars that apps can use.
publicStubModules = append(publicStubModules, ctx.Config().ProductHiddenAPIStubs()...)
systemStubModules = append(systemStubModules, ctx.Config().ProductHiddenAPIStubsSystem()...)
@@ -147,7 +204,7 @@
ctx.VisitAllModules(func(module android.Module) {
// Collect dex jar paths for the modules listed above.
- if j, ok := module.(Dependency); ok {
+ if j, ok := module.(UsesLibraryDependency); ok {
name := ctx.ModuleName(module)
for moduleList, pathList := range moduleListToPathList {
if i := android.IndexList(name, *moduleList); i != -1 {
@@ -159,14 +216,6 @@
// Collect dex jar paths for modules that had hiddenapi encode called on them.
if h, ok := module.(hiddenAPIIntf); ok {
if jar := h.bootDexJar(); jar != nil {
- // For a java lib included in an APEX, only take the one built for
- // the platform variant, and skip the variants for APEXes.
- // Otherwise, the hiddenapi tool will complain about duplicated classes
- apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
- if !apexInfo.IsForPlatform() {
- return
- }
-
bootDexJars = append(bootDexJars, jar)
}
}
@@ -212,63 +261,75 @@
rule.Build("hiddenAPIStubFlagsFile", "hiddenapi stub flags")
}
-// flagsRule creates a rule to build hiddenapi-flags.csv out of flags.csv files generated for boot image modules and
-// the unsupported API.
-func flagsRule(ctx android.SingletonContext) android.Path {
- var flagsCSV android.Paths
- var combinedRemovedApis android.Path
+// Checks to see whether the supplied module variant is in the list of boot jars.
+//
+// This is similar to logic in getBootImageJar() so any changes needed here are likely to be needed
+// there too.
+//
+// TODO(b/179354495): Avoid having to perform this type of check or if necessary dedup it.
+func isModuleInConfiguredList(ctx android.BaseModuleContext, module android.Module, configuredBootJars android.ConfiguredJarList) bool {
+ name := ctx.OtherModuleName(module)
- ctx.VisitAllModules(func(module android.Module) {
- if h, ok := module.(hiddenAPIIntf); ok {
- if csv := h.flagsCSV(); csv != nil {
- flagsCSV = append(flagsCSV, csv)
- }
- } else if g, ok := module.(*genrule.Module); ok {
- if ctx.ModuleName(module) == "combined-removed-dex" {
- if len(g.GeneratedSourceFiles()) != 1 || combinedRemovedApis != nil {
- ctx.Errorf("Expected 1 combined-removed-dex module that generates 1 output file.")
- }
- combinedRemovedApis = g.GeneratedSourceFiles()[0]
- }
- }
- })
+ // Strip a prebuilt_ prefix so that this can match a prebuilt module that has not been renamed.
+ name = android.RemoveOptionalPrebuiltPrefix(name)
- if combinedRemovedApis == nil {
- ctx.Errorf("Failed to find combined-removed-dex.")
+ // Ignore any module that is not listed in the boot image configuration.
+ index := configuredBootJars.IndexOfJar(name)
+ if index == -1 {
+ return false
}
- rule := android.NewRuleBuilder(pctx, ctx)
+ // It is an error if the module is not an ApexModule.
+ if _, ok := module.(android.ApexModule); !ok {
+ ctx.ModuleErrorf("is configured in boot jars but does not support being added to an apex")
+ return false
+ }
+ apexInfo := ctx.OtherModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+
+ // Now match the apex part of the boot image configuration.
+ requiredApex := configuredBootJars.Apex(index)
+ if requiredApex == "platform" {
+ if len(apexInfo.InApexes) != 0 {
+ // A platform variant is required but this is for an apex so ignore it.
+ return false
+ }
+ } else if !apexInfo.InApexByBaseName(requiredApex) {
+ // An apex variant for a specific apex is required but this is the wrong apex.
+ return false
+ }
+
+ return true
+}
+
+func prebuiltFlagsRule(ctx android.SingletonContext) android.Path {
outputPath := hiddenAPISingletonPaths(ctx).flags
- tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp")
+ inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-flags.csv")
- stubFlags := hiddenAPISingletonPaths(ctx).stubFlags
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Output: outputPath,
+ Input: inputPath,
+ })
- rule.Command().
- Tool(android.PathForSource(ctx, "frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py")).
- FlagWithInput("--csv ", stubFlags).
- Inputs(flagsCSV).
- FlagWithInput("--unsupported ",
- android.PathForSource(ctx, "frameworks/base/config/hiddenapi-unsupported.txt")).
- FlagWithInput("--unsupported ", combinedRemovedApis).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed").
- FlagWithInput("--max-target-q ",
- android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-q.txt")).
- FlagWithInput("--max-target-p ",
- android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-p.txt")).
- FlagWithInput("--max-target-o ", android.PathForSource(
- ctx, "frameworks/base/config/hiddenapi-max-target-o.txt")).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio").
- FlagWithInput("--blocked ",
- android.PathForSource(ctx, "frameworks/base/config/hiddenapi-force-blocked.txt")).
- FlagWithInput("--blocked ",
- android.PathForSource(ctx, "frameworks/base/config/hiddenapi-temp-blocklist.txt")).FlagWithArg("--tag ", "lo-prio").
- FlagWithInput("--unsupported ", android.PathForSource(
- ctx, "frameworks/base/config/hiddenapi-unsupported-packages.txt")).Flag("--packages ").
- FlagWithOutput("--output ", tempPath)
+ return outputPath
+}
- commitChangeForRestat(rule, tempPath, outputPath)
+func prebuiltIndexRule(ctx android.SingletonContext) {
+ outputPath := hiddenAPISingletonPaths(ctx).index
+ inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-index.csv")
- rule.Build("hiddenAPIFlagsFile", "hiddenapi flags")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Output: outputPath,
+ Input: inputPath,
+ })
+}
+// flagsRule is a placeholder that simply returns the location of the file, the generation of the
+// ninja rules is done in generateHiddenAPIBuildActions.
+func flagsRule(ctx android.SingletonContext) android.Path {
+ outputPath := hiddenAPISingletonPaths(ctx).flags
return outputPath
}
@@ -287,33 +348,6 @@
return outputPath
}
-// metadataRule creates a rule to build hiddenapi-unsupported.csv out of the metadata.csv files generated for boot image
-// modules.
-func metadataRule(ctx android.SingletonContext) android.Path {
- var metadataCSV android.Paths
-
- ctx.VisitAllModules(func(module android.Module) {
- if h, ok := module.(hiddenAPIIntf); ok {
- if csv := h.metadataCSV(); csv != nil {
- metadataCSV = append(metadataCSV, csv)
- }
- }
- })
-
- rule := android.NewRuleBuilder(pctx, ctx)
-
- outputPath := hiddenAPISingletonPaths(ctx).metadata
-
- rule.Command().
- BuiltTool("merge_csv").
- FlagWithOutput("--output=", outputPath).
- Inputs(metadataCSV)
-
- rule.Build("hiddenAPIGreylistMetadataFile", "hiddenapi greylist metadata")
-
- return outputPath
-}
-
// commitChangeForRestat adds a command to a rule that updates outputPath from tempPath if they are different. It
// also marks the rule as restat and marks the tempPath as a temporary file that should not be considered an output of
// the rule.
@@ -331,90 +365,3 @@
Text("fi").
Text(")")
}
-
-type hiddenAPIFlagsProperties struct {
- // name of the file into which the flags will be copied.
- Filename *string
-}
-
-type hiddenAPIFlags struct {
- android.ModuleBase
-
- properties hiddenAPIFlagsProperties
-
- outputFilePath android.OutputPath
-}
-
-func (h *hiddenAPIFlags) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- filename := String(h.properties.Filename)
-
- inputPath := hiddenAPISingletonPaths(ctx).flags
- h.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
-
- // This ensures that outputFilePath has the correct name for others to
- // use, as the source file may have a different name.
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cp,
- Output: h.outputFilePath,
- Input: inputPath,
- })
-}
-
-func (h *hiddenAPIFlags) OutputFiles(tag string) (android.Paths, error) {
- switch tag {
- case "":
- return android.Paths{h.outputFilePath}, nil
- default:
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
- }
-}
-
-// hiddenapi-flags provides access to the hiddenapi-flags.csv file generated during the build.
-func hiddenAPIFlagsFactory() android.Module {
- module := &hiddenAPIFlags{}
- module.AddProperties(&module.properties)
- android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
- return module
-}
-
-func hiddenAPIIndexSingletonFactory() android.Singleton {
- return &hiddenAPIIndexSingleton{}
-}
-
-type hiddenAPIIndexSingleton struct {
- index android.Path
-}
-
-func (h *hiddenAPIIndexSingleton) GenerateBuildActions(ctx android.SingletonContext) {
- // Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true
- if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
- return
- }
-
- indexes := android.Paths{}
- ctx.VisitAllModules(func(module android.Module) {
- if h, ok := module.(hiddenAPIIntf); ok {
- if h.indexCSV() != nil {
- indexes = append(indexes, h.indexCSV())
- }
- }
- })
-
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- BuiltTool("merge_csv").
- FlagWithArg("--header=", "signature,file,startline,startcol,endline,endcol,properties").
- FlagWithOutput("--output=", hiddenAPISingletonPaths(ctx).index).
- Inputs(indexes)
- rule.Build("singleton-merged-hiddenapi-index", "Singleton merged Hidden API index")
-
- h.index = hiddenAPISingletonPaths(ctx).index
-}
-
-func (h *hiddenAPIIndexSingleton) MakeVars(ctx android.MakeVarsContext) {
- if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
- return
- }
-
- ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_INDEX", h.index.String())
-}
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index 955c739..5ea9a5b 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -15,138 +15,138 @@
package java
import (
- "android/soong/android"
"fmt"
- "strings"
+ "path/filepath"
"testing"
+ "android/soong/android"
"github.com/google/blueprint/proptools"
)
-func testConfigWithBootJars(bp string, bootJars []string) android.Config {
- config := testConfig(nil, bp, nil)
- config.TestProductVariables.BootJars = android.CreateTestConfiguredJarList(bootJars)
- return config
+func fixtureSetPrebuiltHiddenApiDirProductVariable(prebuiltHiddenApiDir *string) android.FixturePreparer {
+ return android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.PrebuiltHiddenApiDir = prebuiltHiddenApiDir
+ })
}
-func testContextWithHiddenAPI(config android.Config) *android.TestContext {
- ctx := testContext(config)
- ctx.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
- return ctx
-}
-
-func testHiddenAPIWithConfig(t *testing.T, config android.Config) *android.TestContext {
- t.Helper()
-
- ctx := testContextWithHiddenAPI(config)
-
- run(t, ctx, config)
- return ctx
-}
-
-func testHiddenAPIBootJars(t *testing.T, bp string, bootJars []string) (*android.TestContext, android.Config) {
- config := testConfigWithBootJars(bp, bootJars)
-
- return testHiddenAPIWithConfig(t, config), config
-}
-
-func testHiddenAPIUnbundled(t *testing.T, unbundled bool) (*android.TestContext, android.Config) {
- config := testConfig(nil, ``, nil)
- config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(unbundled)
-
- return testHiddenAPIWithConfig(t, config), config
-}
+var hiddenApiFixtureFactory = android.GroupFixturePreparers(
+ prepareForJavaTest, PrepareForTestWithHiddenApiBuildComponents)
func TestHiddenAPISingleton(t *testing.T) {
- ctx, _ := testHiddenAPIBootJars(t, `
+ result := android.GroupFixturePreparers(
+ hiddenApiFixtureFactory,
+ FixtureConfigureBootJars("platform:foo"),
+ ).RunTestWithBp(t, `
java_library {
name: "foo",
srcs: ["a.java"],
compile_dex: true,
- }
- `, []string{":foo"})
+ }
+ `)
- hiddenAPI := ctx.SingletonForTests("hiddenapi")
+ hiddenAPI := result.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
- want := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
- if !strings.Contains(hiddenapiRule.RuleParams.Command, want) {
- t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", want, hiddenapiRule.RuleParams.Command)
- }
+ want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
+ android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
+}
+
+func TestHiddenAPISingletonWithSourceAndPrebuiltPreferredButNoDex(t *testing.T) {
+ expectedErrorMessage :=
+ "hiddenapi has determined that the source module \"foo\" should be ignored as it has been" +
+ " replaced by the prebuilt module \"prebuilt_foo\" but unfortunately it does not provide a" +
+ " suitable boot dex jar"
+
+ android.GroupFixturePreparers(
+ hiddenApiFixtureFactory,
+ FixtureConfigureBootJars("platform:foo"),
+ ).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorMessage)).
+ RunTestWithBp(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ compile_dex: true,
+ }
+
+ java_import {
+ name: "foo",
+ jars: ["a.jar"],
+ prefer: true,
+ }
+ `)
}
func TestHiddenAPISingletonWithPrebuilt(t *testing.T) {
- ctx, _ := testHiddenAPIBootJars(t, `
+ result := android.GroupFixturePreparers(
+ hiddenApiFixtureFactory,
+ FixtureConfigureBootJars("platform:foo"),
+ ).RunTestWithBp(t, `
java_import {
name: "foo",
jars: ["a.jar"],
compile_dex: true,
}
- `, []string{":foo"})
+ `)
- hiddenAPI := ctx.SingletonForTests("hiddenapi")
+ hiddenAPI := result.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
- want := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
- if !strings.Contains(hiddenapiRule.RuleParams.Command, want) {
- t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", want, hiddenapiRule.RuleParams.Command)
- }
+ want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
+ android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
}
func TestHiddenAPISingletonWithPrebuiltUseSource(t *testing.T) {
- ctx, _ := testHiddenAPIBootJars(t, `
+ result := android.GroupFixturePreparers(
+ hiddenApiFixtureFactory,
+ FixtureConfigureBootJars("platform:foo"),
+ ).RunTestWithBp(t, `
java_library {
name: "foo",
srcs: ["a.java"],
compile_dex: true,
- }
+ }
java_import {
name: "foo",
jars: ["a.jar"],
compile_dex: true,
prefer: false,
- }
- `, []string{":foo"})
+ }
+ `)
- hiddenAPI := ctx.SingletonForTests("hiddenapi")
+ hiddenAPI := result.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
- fromSourceJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
- if !strings.Contains(hiddenapiRule.RuleParams.Command, fromSourceJarArg) {
- t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", fromSourceJarArg, hiddenapiRule.RuleParams.Command)
- }
+ fromSourceJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
+ android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, fromSourceJarArg)
- prebuiltJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/dex/foo.jar"
- if strings.Contains(hiddenapiRule.RuleParams.Command, prebuiltJarArg) {
- t.Errorf("Did not expect %s in hiddenapi command, but it was present: %s", prebuiltJarArg, hiddenapiRule.RuleParams.Command)
- }
+ prebuiltJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/dex/foo.jar"
+ android.AssertStringDoesNotContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, prebuiltJarArg)
}
func TestHiddenAPISingletonWithPrebuiltOverrideSource(t *testing.T) {
- ctx, _ := testHiddenAPIBootJars(t, `
+ result := android.GroupFixturePreparers(
+ hiddenApiFixtureFactory,
+ FixtureConfigureBootJars("platform:foo"),
+ ).RunTestWithBp(t, `
java_library {
name: "foo",
srcs: ["a.java"],
compile_dex: true,
- }
+ }
java_import {
name: "foo",
jars: ["a.jar"],
compile_dex: true,
prefer: true,
- }
- `, []string{":foo"})
+ }
+ `)
- hiddenAPI := ctx.SingletonForTests("hiddenapi")
+ hiddenAPI := result.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
- prebuiltJarArg := "--boot-dex=" + buildDir + "/.intermediates/prebuilt_foo/android_common/dex/foo.jar"
- if !strings.Contains(hiddenapiRule.RuleParams.Command, prebuiltJarArg) {
- t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", prebuiltJarArg, hiddenapiRule.RuleParams.Command)
- }
+ prebuiltJarArg := "--boot-dex=out/soong/.intermediates/prebuilt_foo/android_common/dex/foo.jar"
+ android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, prebuiltJarArg)
- fromSourceJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
- if strings.Contains(hiddenapiRule.RuleParams.Command, fromSourceJarArg) {
- t.Errorf("Did not expect %s in hiddenapi command, but it was present: %s", fromSourceJarArg, hiddenapiRule.RuleParams.Command)
- }
+ fromSourceJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
+ android.AssertStringDoesNotContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, fromSourceJarArg)
}
func TestHiddenAPISingletonSdks(t *testing.T) {
@@ -157,6 +157,9 @@
systemStub string
testStub string
corePlatformStub string
+
+ // Additional test preparer
+ preparer android.FixturePreparer
}{
{
name: "testBundled",
@@ -165,6 +168,7 @@
systemStub: "android_system_stubs_current",
testStub: "android_test_stubs_current",
corePlatformStub: "legacy.core.platform.api.stubs",
+ preparer: android.GroupFixturePreparers(),
}, {
name: "testUnbundled",
unbundledBuild: true,
@@ -172,48 +176,90 @@
systemStub: "sdk_system_current_android",
testStub: "sdk_test_current_android",
corePlatformStub: "legacy.core.platform.api.stubs",
+ preparer: PrepareForTestWithPrebuiltsOfCurrentApi,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- ctx, _ := testHiddenAPIUnbundled(t, tc.unbundledBuild)
+ result := android.GroupFixturePreparers(
+ hiddenApiFixtureFactory,
+ tc.preparer,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Always_use_prebuilt_sdks = proptools.BoolPtr(tc.unbundledBuild)
+ }),
+ ).RunTest(t)
- hiddenAPI := ctx.SingletonForTests("hiddenapi")
+ hiddenAPI := result.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
wantPublicStubs := "--public-stub-classpath=" + generateSdkDexPath(tc.publicStub, tc.unbundledBuild)
- if !strings.Contains(hiddenapiRule.RuleParams.Command, wantPublicStubs) {
- t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", wantPublicStubs, hiddenapiRule.RuleParams.Command)
- }
+ android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantPublicStubs)
wantSystemStubs := "--system-stub-classpath=" + generateSdkDexPath(tc.systemStub, tc.unbundledBuild)
- if !strings.Contains(hiddenapiRule.RuleParams.Command, wantSystemStubs) {
- t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", wantSystemStubs, hiddenapiRule.RuleParams.Command)
- }
+ android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantSystemStubs)
wantTestStubs := "--test-stub-classpath=" + generateSdkDexPath(tc.testStub, tc.unbundledBuild)
- if !strings.Contains(hiddenapiRule.RuleParams.Command, wantTestStubs) {
- t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", wantTestStubs, hiddenapiRule.RuleParams.Command)
- }
+ android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantTestStubs)
- wantCorePlatformStubs := "--core-platform-stub-classpath=" + generateDexPath(tc.corePlatformStub)
- if !strings.Contains(hiddenapiRule.RuleParams.Command, wantCorePlatformStubs) {
- t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", wantCorePlatformStubs, hiddenapiRule.RuleParams.Command)
- }
+ wantCorePlatformStubs := "--core-platform-stub-classpath=" + generateDexPath(defaultJavaDir, tc.corePlatformStub)
+ android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantCorePlatformStubs)
})
}
}
func generateDexedPath(subDir, dex, module string) string {
- return fmt.Sprintf("%s/.intermediates/%s/android_common/%s/%s.jar", buildDir, subDir, dex, module)
+ return fmt.Sprintf("out/soong/.intermediates/%s/android_common/%s/%s.jar", subDir, dex, module)
}
-func generateDexPath(module string) string {
- return generateDexedPath(module, "dex", module)
+func generateDexPath(moduleDir string, module string) string {
+ return generateDexedPath(filepath.Join(moduleDir, module), "dex", module)
}
func generateSdkDexPath(module string, unbundled bool) string {
if unbundled {
return generateDexedPath("prebuilts/sdk/"+module, "dex", module)
}
- return generateDexPath(module)
+ return generateDexPath(defaultJavaDir, module)
+}
+
+func TestHiddenAPISingletonWithPrebuiltCsvFile(t *testing.T) {
+
+ // The idea behind this test is to ensure that when the build is
+ // confugured with a PrebuiltHiddenApiDir that the rules for the
+ // hiddenapi singleton copy the prebuilts to the typical output
+ // location, and then use that output location for the hiddenapi encode
+ // dex step.
+
+ // Where to find the prebuilt hiddenapi files:
+ prebuiltHiddenApiDir := "path/to/prebuilt/hiddenapi"
+
+ result := android.GroupFixturePreparers(
+ hiddenApiFixtureFactory,
+ FixtureConfigureBootJars("platform:foo"),
+ fixtureSetPrebuiltHiddenApiDirProductVariable(&prebuiltHiddenApiDir),
+ ).RunTestWithBp(t, `
+ java_import {
+ name: "foo",
+ jars: ["a.jar"],
+ compile_dex: true,
+ }
+ `)
+
+ expectedCpInput := prebuiltHiddenApiDir + "/hiddenapi-flags.csv"
+ expectedCpOutput := "out/soong/hiddenapi/hiddenapi-flags.csv"
+ expectedFlagsCsv := "out/soong/hiddenapi/hiddenapi-flags.csv"
+
+ foo := result.ModuleForTests("foo", "android_common")
+
+ hiddenAPI := result.SingletonForTests("hiddenapi")
+ cpRule := hiddenAPI.Rule("Cp")
+ actualCpInput := cpRule.BuildParams.Input
+ actualCpOutput := cpRule.BuildParams.Output
+ encodeDexRule := foo.Rule("hiddenAPIEncodeDex")
+ actualFlagsCsv := encodeDexRule.BuildParams.Args["flagsCsv"]
+
+ android.AssertPathRelativeToTopEquals(t, "hiddenapi cp rule input", expectedCpInput, actualCpInput)
+
+ android.AssertPathRelativeToTopEquals(t, "hiddenapi cp rule output", expectedCpOutput, actualCpOutput)
+
+ android.AssertStringEquals(t, "hiddenapi encode dex rule flags csv", expectedFlagsCsv, actualFlagsCsv)
}
diff --git a/java/java.go b/java/java.go
index 18dd9bd..ee4f2eb 100644
--- a/java/java.go
+++ b/java/java.go
@@ -21,11 +21,9 @@
import (
"fmt"
"path/filepath"
- "strconv"
"strings"
"github.com/google/blueprint"
- "github.com/google/blueprint/pathtools"
"github.com/google/blueprint/proptools"
"android/soong/android"
@@ -35,33 +33,12 @@
)
func init() {
- RegisterJavaBuildComponents(android.InitRegistrationContext)
+ registerJavaBuildComponents(android.InitRegistrationContext)
- // Register sdk member types.
- android.RegisterSdkMemberType(javaHeaderLibsSdkMemberType)
-
- android.RegisterSdkMemberType(&librarySdkMemberType{
- android.SdkMemberTypeBase{
- PropertyName: "java_libs",
- },
- func(j *Library) android.Path {
- implementationJars := j.ImplementationAndResourcesJars()
- if len(implementationJars) != 1 {
- panic(fmt.Errorf("there must be only one implementation jar from %q", j.Name()))
- }
-
- return implementationJars[0]
- },
- })
-
- android.RegisterSdkMemberType(&testSdkMemberType{
- SdkMemberTypeBase: android.SdkMemberTypeBase{
- PropertyName: "java_tests",
- },
- })
+ RegisterJavaSdkMemberTypes()
}
-func RegisterJavaBuildComponents(ctx android.RegistrationContext) {
+func registerJavaBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("java_defaults", DefaultsFactory)
ctx.RegisterModuleType("java_library", LibraryFactory)
@@ -87,418 +64,118 @@
ctx.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory)
}
-func (j *Module) CheckStableSdkVersion() error {
- sdkVersion := j.sdkVersion()
- if sdkVersion.stable() {
- return nil
- }
- return fmt.Errorf("non stable SDK %v", sdkVersion)
-}
+func RegisterJavaSdkMemberTypes() {
+ // Register sdk member types.
+ android.RegisterSdkMemberType(javaHeaderLibsSdkMemberType)
-func (j *Module) checkSdkVersions(ctx android.ModuleContext) {
- if j.RequiresStableAPIs(ctx) {
- if sc, ok := ctx.Module().(sdkContext); ok {
- if !sc.sdkVersion().specified() {
- ctx.PropertyErrorf("sdk_version",
- "sdk_version must have a value when the module is located at vendor or product(only if PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE is set).")
- }
+ // Export implementation classes jar as part of the sdk.
+ exportImplementationClassesJar := func(_ android.SdkMemberContext, j *Library) android.Path {
+ implementationJars := j.ImplementationAndResourcesJars()
+ if len(implementationJars) != 1 {
+ panic(fmt.Errorf("there must be only one implementation jar from %q", j.Name()))
}
+ return implementationJars[0]
}
- ctx.VisitDirectDeps(func(module android.Module) {
- tag := ctx.OtherModuleDependencyTag(module)
- switch module.(type) {
- // TODO(satayev): cover other types as well, e.g. imports
- case *Library, *AndroidLibrary:
- switch tag {
- case bootClasspathTag, libTag, staticLibTag, java9LibTag:
- checkLinkType(ctx, j, module.(linkTypeContext), tag.(dependencyTag))
- }
- }
+ // Register java implementation libraries for use only in module_exports (not sdk).
+ android.RegisterSdkMemberType(&librarySdkMemberType{
+ android.SdkMemberTypeBase{
+ PropertyName: "java_libs",
+ },
+ exportImplementationClassesJar,
+ sdkSnapshotFilePathForJar,
+ copyEverythingToSnapshot,
})
-}
-func (j *Module) checkPlatformAPI(ctx android.ModuleContext) {
- if sc, ok := ctx.Module().(sdkContext); ok {
- usePlatformAPI := proptools.Bool(j.deviceProperties.Platform_apis)
- sdkVersionSpecified := sc.sdkVersion().specified()
- if usePlatformAPI && sdkVersionSpecified {
- ctx.PropertyErrorf("platform_apis", "platform_apis must be false when sdk_version is not empty.")
- } else if !usePlatformAPI && !sdkVersionSpecified {
- ctx.PropertyErrorf("platform_apis", "platform_apis must be true when sdk_version is empty.")
- }
-
- }
-}
-
-// TODO:
-// Autogenerated files:
-// Renderscript
-// Post-jar passes:
-// Proguard
-// Rmtypedefs
-// DroidDoc
-// Findbugs
-
-type CompilerProperties struct {
- // list of source files used to compile the Java module. May be .java, .kt, .logtags, .proto,
- // or .aidl files.
- Srcs []string `android:"path,arch_variant"`
-
- // list Kotlin of source files containing Kotlin code that should be treated as common code in
- // a codebase that supports Kotlin multiplatform. See
- // https://kotlinlang.org/docs/reference/multiplatform.html. May be only be .kt files.
- Common_srcs []string `android:"path,arch_variant"`
-
- // list of source files that should not be used to build the Java module.
- // This is most useful in the arch/multilib variants to remove non-common files
- Exclude_srcs []string `android:"path,arch_variant"`
-
- // list of directories containing Java resources
- Java_resource_dirs []string `android:"arch_variant"`
-
- // list of directories that should be excluded from java_resource_dirs
- Exclude_java_resource_dirs []string `android:"arch_variant"`
-
- // list of files to use as Java resources
- Java_resources []string `android:"path,arch_variant"`
-
- // list of files that should be excluded from java_resources and java_resource_dirs
- Exclude_java_resources []string `android:"path,arch_variant"`
-
- // list of module-specific flags that will be used for javac compiles
- Javacflags []string `android:"arch_variant"`
-
- // list of module-specific flags that will be used for kotlinc compiles
- Kotlincflags []string `android:"arch_variant"`
-
- // list of of java libraries that will be in the classpath
- Libs []string `android:"arch_variant"`
-
- // list of java libraries that will be compiled into the resulting jar
- Static_libs []string `android:"arch_variant"`
-
- // manifest file to be included in resulting jar
- Manifest *string `android:"path"`
-
- // if not blank, run jarjar using the specified rules file
- Jarjar_rules *string `android:"path,arch_variant"`
-
- // If not blank, set the java version passed to javac as -source and -target
- Java_version *string
-
- // If set to true, allow this module to be dexed and installed on devices. Has no
- // effect on host modules, which are always considered installable.
- Installable *bool
-
- // If set to true, include sources used to compile the module in to the final jar
- Include_srcs *bool
-
- // If not empty, classes are restricted to the specified packages and their sub-packages.
- // This restriction is checked after applying jarjar rules and including static libs.
- Permitted_packages []string
-
- // List of modules to use as annotation processors
- Plugins []string
-
- // List of modules to export to libraries that directly depend on this library as annotation
- // processors. Note that if the plugins set generates_api: true this will disable the turbine
- // optimization on modules that depend on this module, which will reduce parallelism and cause
- // more recompilation.
- Exported_plugins []string
-
- // The number of Java source entries each Javac instance can process
- Javac_shard_size *int64
-
- // Add host jdk tools.jar to bootclasspath
- Use_tools_jar *bool
-
- Openjdk9 struct {
- // List of source files that should only be used when passing -source 1.9 or higher
- Srcs []string `android:"path"`
-
- // List of javac flags that should only be used when passing -source 1.9 or higher
- Javacflags []string
- }
-
- // When compiling language level 9+ .java code in packages that are part of
- // a system module, patch_module names the module that your sources and
- // dependencies should be patched into. The Android runtime currently
- // doesn't implement the JEP 261 module system so this option is only
- // supported at compile time. It should only be needed to compile tests in
- // packages that exist in libcore and which are inconvenient to move
- // elsewhere.
- Patch_module *string `android:"arch_variant"`
-
- Jacoco struct {
- // List of classes to include for instrumentation with jacoco to collect coverage
- // information at runtime when building with coverage enabled. If unset defaults to all
- // classes.
- // Supports '*' as the last character of an entry in the list as a wildcard match.
- // If preceded by '.' it matches all classes in the package and subpackages, otherwise
- // it matches classes in the package that have the class name as a prefix.
- Include_filter []string
-
- // List of classes to exclude from instrumentation with jacoco to collect coverage
- // information at runtime when building with coverage enabled. Overrides classes selected
- // by the include_filter property.
- // Supports '*' as the last character of an entry in the list as a wildcard match.
- // If preceded by '.' it matches all classes in the package and subpackages, otherwise
- // it matches classes in the package that have the class name as a prefix.
- Exclude_filter []string
- }
-
- Errorprone struct {
- // List of javac flags that should only be used when running errorprone.
- Javacflags []string
-
- // List of java_plugin modules that provide extra errorprone checks.
- Extra_check_modules []string
- }
-
- Proto struct {
- // List of extra options that will be passed to the proto generator.
- Output_params []string
- }
-
- Instrument bool `blueprint:"mutated"`
-
- // List of files to include in the META-INF/services folder of the resulting jar.
- Services []string `android:"path,arch_variant"`
-
- // If true, package the kotlin stdlib into the jar. Defaults to true.
- Static_kotlin_stdlib *bool `android:"arch_variant"`
-}
-
-type CompilerDeviceProperties struct {
- // if not blank, set to the version of the sdk to compile against.
- // Defaults to compiling against the current platform.
- Sdk_version *string
-
- // if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
- // Defaults to sdk_version if not set.
- Min_sdk_version *string
-
- // if not blank, set the targetSdkVersion in the AndroidManifest.xml.
- // Defaults to sdk_version if not set.
- Target_sdk_version *string
-
- // Whether to compile against the platform APIs instead of an SDK.
- // If true, then sdk_version must be empty. The value of this field
- // is ignored when module's type isn't android_app.
- Platform_apis *bool
-
- Aidl struct {
- // Top level directories to pass to aidl tool
- Include_dirs []string
-
- // Directories rooted at the Android.bp file to pass to aidl tool
- Local_include_dirs []string
-
- // directories that should be added as include directories for any aidl sources of modules
- // that depend on this module, as well as to aidl for this module.
- Export_include_dirs []string
-
- // whether to generate traces (for systrace) for this interface
- Generate_traces *bool
-
- // whether to generate Binder#GetTransaction name method.
- Generate_get_transaction_name *bool
- }
-
- // If true, export a copy of the module as a -hostdex module for host testing.
- Hostdex *bool
-
- Target struct {
- Hostdex struct {
- // Additional required dependencies to add to -hostdex modules.
- Required []string
- }
- }
-
- // When targeting 1.9 and above, override the modules to use with --system,
- // otherwise provides defaults libraries to add to the bootclasspath.
- System_modules *string
-
- // The name of the module as used in build configuration.
+ // Register java boot libraries for use in sdk.
//
- // Allows a library to separate its actual name from the name used in
- // build configuration, e.g.ctx.Config().BootJars().
- ConfigurationName *string `blueprint:"mutated"`
+ // The build has some implicit dependencies (via the boot jars configuration) on a number of
+ // modules, e.g. core-oj, apache-xml, that are part of the java boot class path and which are
+ // provided by mainline modules (e.g. art, conscrypt, runtime-i18n) but which are not otherwise
+ // used outside those mainline modules.
+ //
+ // As they are not needed outside the mainline modules adding them to the sdk/module-exports as
+ // either java_libs, or java_header_libs would end up exporting more information than was strictly
+ // necessary. The java_boot_libs property to allow those modules to be exported as part of the
+ // sdk/module_exports without exposing any unnecessary information.
+ android.RegisterSdkMemberType(&librarySdkMemberType{
+ android.SdkMemberTypeBase{
+ PropertyName: "java_boot_libs",
+ SupportsSdk: true,
+ },
+ // Temporarily export implementation classes jar for java_boot_libs as it is required for the
+ // hiddenapi processing.
+ // TODO(b/179354495): Revert once hiddenapi processing has been modularized.
+ exportImplementationClassesJar,
+ sdkSnapshotFilePathForJar,
+ onlyCopyJarToSnapshot,
+ })
- // set the name of the output
- Stem *string
+ // Register java test libraries for use only in module_exports (not sdk).
+ android.RegisterSdkMemberType(&testSdkMemberType{
+ SdkMemberTypeBase: android.SdkMemberTypeBase{
+ PropertyName: "java_tests",
+ },
+ })
- IsSDKLibrary bool `blueprint:"mutated"`
-
- // If true, generate the signature file of APK Signing Scheme V4, along side the signed APK file.
- // Defaults to false.
- V4_signature *bool
}
-// Functionality common to Module and Import
-//
-// It is embedded in Module so its functionality can be used by methods in Module
-// but it is currently only initialized by Import and Library.
-type embeddableInModuleAndImport struct {
+// JavaInfo contains information about a java module for use by modules that depend on it.
+type JavaInfo struct {
+ // HeaderJars is a list of jars that can be passed as the javac classpath in order to link
+ // against this module. If empty, ImplementationJars should be used instead.
+ HeaderJars android.Paths
- // Functionality related to this being used as a component of a java_sdk_library.
- EmbeddableSdkLibraryComponent
+ // ImplementationAndResourceJars is a list of jars that contain the implementations of classes
+ // in the module as well as any resources included in the module.
+ ImplementationAndResourcesJars android.Paths
+
+ // ImplementationJars is a list of jars that contain the implementations of classes in the
+ //module.
+ ImplementationJars android.Paths
+
+ // ResourceJars is a list of jars that contain the resources included in the module.
+ ResourceJars android.Paths
+
+ // AidlIncludeDirs is a list of directories that should be passed to the aidl tool when
+ // depending on this module.
+ AidlIncludeDirs android.Paths
+
+ // SrcJarArgs is a list of arguments to pass to soong_zip to package the sources of this
+ // module.
+ SrcJarArgs []string
+
+ // SrcJarDeps is a list of paths to depend on when packaging the sources of this module.
+ SrcJarDeps android.Paths
+
+ // ExportedPlugins is a list of paths that should be used as annotation processors for any
+ // module that depends on this module.
+ ExportedPlugins android.Paths
+
+ // ExportedPluginClasses is a list of classes that should be run as annotation processors for
+ // any module that depends on this module.
+ ExportedPluginClasses []string
+
+ // ExportedPluginDisableTurbine is true if this module's annotation processors generate APIs,
+ // requiring disbling turbine for any modules that depend on it.
+ ExportedPluginDisableTurbine bool
+
+ // JacocoReportClassesFile is the path to a jar containing uninstrumented classes that will be
+ // instrumented by jacoco.
+ JacocoReportClassesFile android.Path
}
-func (e *embeddableInModuleAndImport) initModuleAndImport(moduleBase *android.ModuleBase) {
- e.initSdkLibraryComponent(moduleBase)
+var JavaInfoProvider = blueprint.NewProvider(JavaInfo{})
+
+// SyspropPublicStubInfo contains info about the sysprop public stub library that corresponds to
+// the sysprop implementation library.
+type SyspropPublicStubInfo struct {
+ // JavaInfo is the JavaInfoProvider of the sysprop public stub library that corresponds to
+ // the sysprop implementation library.
+ JavaInfo JavaInfo
}
-// Module/Import's DepIsInSameApex(...) delegates to this method.
-//
-// This cannot implement DepIsInSameApex(...) directly as that leads to ambiguity with
-// the one provided by ApexModuleBase.
-func (e *embeddableInModuleAndImport) depIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
- // dependencies other than the static linkage are all considered crossing APEX boundary
- if staticLibTag == ctx.OtherModuleDependencyTag(dep) {
- return true
- }
- return false
-}
-
-// Module contains the properties and members used by all java module types
-type Module struct {
- android.ModuleBase
- android.DefaultableModuleBase
- android.ApexModuleBase
- android.SdkBase
-
- // Functionality common to Module and Import.
- embeddableInModuleAndImport
-
- properties CompilerProperties
- protoProperties android.ProtoProperties
- deviceProperties CompilerDeviceProperties
-
- // jar file containing header classes including static library dependencies, suitable for
- // inserting into the bootclasspath/classpath of another compile
- headerJarFile android.Path
-
- // jar file containing implementation classes including static library dependencies but no
- // resources
- implementationJarFile android.Path
-
- // jar file containing only resources including from static library dependencies
- resourceJar android.Path
-
- // args and dependencies to package source files into a srcjar
- srcJarArgs []string
- srcJarDeps android.Paths
-
- // jar file containing implementation classes and resources including static library
- // dependencies
- implementationAndResourcesJar android.Path
-
- // output file containing classes.dex and resources
- dexJarFile android.Path
-
- // output file that contains classes.dex if it should be in the output file
- maybeStrippedDexJarFile android.Path
-
- // output file containing uninstrumented classes that will be instrumented by jacoco
- jacocoReportClassesFile android.Path
-
- // output file of the module, which may be a classes jar or a dex jar
- outputFile android.Path
- extraOutputFiles android.Paths
-
- exportAidlIncludeDirs android.Paths
-
- logtagsSrcs android.Paths
-
- // installed file for binary dependency
- installFile android.Path
-
- // list of .java files and srcjars that was passed to javac
- compiledJavaSrcs android.Paths
- compiledSrcJars android.Paths
-
- // manifest file to use instead of properties.Manifest
- overrideManifest android.OptionalPath
-
- // map of SDK version to class loader context
- classLoaderContexts dexpreopt.ClassLoaderContextMap
-
- // list of plugins that this java module is exporting
- exportedPluginJars android.Paths
-
- // list of plugins that this java module is exporting
- exportedPluginClasses []string
-
- // if true, the exported plugins generate API and require disabling turbine.
- exportedDisableTurbine bool
-
- // list of source files, collected from srcFiles with unique java and all kt files,
- // will be used by android.IDEInfo struct
- expandIDEInfoCompiledSrcs []string
-
- // expanded Jarjar_rules
- expandJarjarRules android.Path
-
- // list of additional targets for checkbuild
- additionalCheckedModules android.Paths
-
- // Extra files generated by the module type to be added as java resources.
- extraResources android.Paths
-
- hiddenAPI
- dexer
- dexpreopter
- usesLibrary
- linter
-
- // list of the xref extraction files
- kytheFiles android.Paths
-
- // Collect the module directory for IDE info in java/jdeps.go.
- modulePaths []string
-
- hideApexVariantFromMake bool
-}
-
-func (j *Module) addHostProperties() {
- j.AddProperties(
- &j.properties,
- &j.protoProperties,
- &j.usesLibraryProperties,
- )
-}
-
-func (j *Module) addHostAndDeviceProperties() {
- j.addHostProperties()
- j.AddProperties(
- &j.deviceProperties,
- &j.dexer.dexProperties,
- &j.dexpreoptProperties,
- &j.linter.properties,
- )
-}
-
-func (j *Module) OutputFiles(tag string) (android.Paths, error) {
- switch tag {
- case "":
- return append(android.Paths{j.outputFile}, j.extraOutputFiles...), nil
- case android.DefaultDistTag:
- return android.Paths{j.outputFile}, nil
- case ".jar":
- return android.Paths{j.implementationAndResourcesJar}, nil
- case ".proguard_map":
- if j.dexer.proguardDictionary.Valid() {
- return android.Paths{j.dexer.proguardDictionary.Path()}, nil
- }
- return nil, fmt.Errorf("%q was requested, but no output file was found.", tag)
- default:
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
- }
-}
-
-var _ android.OutputFileProducer = (*Module)(nil)
+var SyspropPublicStubInfoProvider = blueprint.NewProvider(SyspropPublicStubInfo{})
// Methods that need to be implemented for a module that is added to apex java_libs property.
type ApexDependency interface {
@@ -513,18 +190,7 @@
ClassLoaderContexts() dexpreopt.ClassLoaderContextMap
}
-type Dependency interface {
- ApexDependency
- UsesLibraryDependency
- ImplementationJars() android.Paths
- ResourceJars() android.Paths
- AidlIncludeDirs() android.Paths
- ExportedPlugins() (android.Paths, []string, bool)
- SrcJarArgs() ([]string, android.Paths)
- BaseModuleName() string
- JacocoReportClassesFile() android.Path
-}
-
+// TODO(jungjw): Move this to kythe.go once it's created.
type xref interface {
XrefJavaFiles() android.Paths
}
@@ -533,24 +199,6 @@
return j.kytheFiles
}
-func InitJavaModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
- initJavaModule(module, hod, false)
-}
-
-func InitJavaModuleMultiTargets(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
- initJavaModule(module, hod, true)
-}
-
-func initJavaModule(module android.DefaultableModule, hod android.HostOrDeviceSupported, multiTargets bool) {
- multilib := android.MultilibCommon
- if multiTargets {
- android.InitAndroidMultiTargetsArchModule(module, hod, multilib)
- } else {
- android.InitAndroidArchModule(module, hod, multilib)
- }
- android.InitDefaultableModule(module)
-}
-
type dependencyTag struct {
blueprint.BaseDependencyTag
name string
@@ -581,29 +229,30 @@
}
var (
- dataNativeBinsTag = dependencyTag{name: "dataNativeBins"}
- staticLibTag = dependencyTag{name: "staticlib"}
- libTag = dependencyTag{name: "javalib"}
- java9LibTag = dependencyTag{name: "java9lib"}
- pluginTag = dependencyTag{name: "plugin"}
- errorpronePluginTag = dependencyTag{name: "errorprone-plugin"}
- exportedPluginTag = dependencyTag{name: "exported-plugin"}
- bootClasspathTag = dependencyTag{name: "bootclasspath"}
- systemModulesTag = dependencyTag{name: "system modules"}
- frameworkResTag = dependencyTag{name: "framework-res"}
- kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib"}
- kotlinAnnotationsTag = dependencyTag{name: "kotlin-annotations"}
- proguardRaiseTag = dependencyTag{name: "proguard-raise"}
- certificateTag = dependencyTag{name: "certificate"}
- instrumentationForTag = dependencyTag{name: "instrumentation_for"}
- extraLintCheckTag = dependencyTag{name: "extra-lint-check"}
- jniLibTag = dependencyTag{name: "jnilib"}
- jniInstallTag = installDependencyTag{name: "jni install"}
- binaryInstallTag = installDependencyTag{name: "binary install"}
- usesLibTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion)
- usesLibCompat28Tag = makeUsesLibraryDependencyTag(28)
- usesLibCompat29Tag = makeUsesLibraryDependencyTag(29)
- usesLibCompat30Tag = makeUsesLibraryDependencyTag(30)
+ dataNativeBinsTag = dependencyTag{name: "dataNativeBins"}
+ staticLibTag = dependencyTag{name: "staticlib"}
+ libTag = dependencyTag{name: "javalib"}
+ java9LibTag = dependencyTag{name: "java9lib"}
+ pluginTag = dependencyTag{name: "plugin"}
+ errorpronePluginTag = dependencyTag{name: "errorprone-plugin"}
+ exportedPluginTag = dependencyTag{name: "exported-plugin"}
+ bootClasspathTag = dependencyTag{name: "bootclasspath"}
+ systemModulesTag = dependencyTag{name: "system modules"}
+ frameworkResTag = dependencyTag{name: "framework-res"}
+ kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib"}
+ kotlinAnnotationsTag = dependencyTag{name: "kotlin-annotations"}
+ proguardRaiseTag = dependencyTag{name: "proguard-raise"}
+ certificateTag = dependencyTag{name: "certificate"}
+ instrumentationForTag = dependencyTag{name: "instrumentation_for"}
+ extraLintCheckTag = dependencyTag{name: "extra-lint-check"}
+ jniLibTag = dependencyTag{name: "jnilib"}
+ syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"}
+ jniInstallTag = installDependencyTag{name: "jni install"}
+ binaryInstallTag = installDependencyTag{name: "binary install"}
+ usesLibTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion)
+ usesLibCompat28Tag = makeUsesLibraryDependencyTag(28)
+ usesLibCompat29Tag = makeUsesLibraryDependencyTag(29)
+ usesLibCompat30Tag = makeUsesLibraryDependencyTag(30)
)
func IsLibDepTag(depTag blueprint.DependencyTag) bool {
@@ -655,71 +304,7 @@
unstrippedFile android.Path
}
-func (j *Module) shouldInstrument(ctx android.BaseModuleContext) bool {
- return j.properties.Instrument &&
- ctx.Config().IsEnvTrue("EMMA_INSTRUMENT") &&
- ctx.DeviceConfig().JavaCoverageEnabledForPath(ctx.ModuleDir())
-}
-
-func (j *Module) shouldInstrumentStatic(ctx android.BaseModuleContext) bool {
- return j.shouldInstrument(ctx) &&
- (ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_STATIC") ||
- ctx.Config().UnbundledBuild())
-}
-
-func (j *Module) shouldInstrumentInApex(ctx android.BaseModuleContext) bool {
- // Force enable the instrumentation for java code that is built for APEXes ...
- // except for the jacocoagent itself (because instrumenting jacocoagent using jacocoagent
- // doesn't make sense) or framework libraries (e.g. libraries found in the InstrumentFrameworkModules list) unless EMMA_INSTRUMENT_FRAMEWORK is true.
- apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
- isJacocoAgent := ctx.ModuleName() == "jacocoagent"
- if j.DirectlyInAnyApex() && !isJacocoAgent && !apexInfo.IsForPlatform() {
- if !inList(ctx.ModuleName(), config.InstrumentFrameworkModules) {
- return true
- } else if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
- return true
- }
- }
- return false
-}
-
-func (j *Module) sdkVersion() sdkSpec {
- return sdkSpecFrom(String(j.deviceProperties.Sdk_version))
-}
-
-func (j *Module) systemModules() string {
- return proptools.String(j.deviceProperties.System_modules)
-}
-
-func (j *Module) minSdkVersion() sdkSpec {
- if j.deviceProperties.Min_sdk_version != nil {
- return sdkSpecFrom(*j.deviceProperties.Min_sdk_version)
- }
- return j.sdkVersion()
-}
-
-func (j *Module) targetSdkVersion() sdkSpec {
- if j.deviceProperties.Target_sdk_version != nil {
- return sdkSpecFrom(*j.deviceProperties.Target_sdk_version)
- }
- return j.sdkVersion()
-}
-
-func (j *Module) MinSdkVersion() string {
- return j.minSdkVersion().version.String()
-}
-
-func (j *Module) AvailableFor(what string) bool {
- if what == android.AvailableToPlatform && Bool(j.deviceProperties.Hostdex) {
- // Exception: for hostdex: true libraries, the platform variant is created
- // even if it's not marked as available to platform. In that case, the platform
- // variant is used only for the hostdex and not installed to the device.
- return true
- }
- return j.ApexModuleBase.AvailableFor(what)
-}
-
-func sdkDeps(ctx android.BottomUpMutatorContext, sdkContext sdkContext, d dexer) {
+func sdkDeps(ctx android.BottomUpMutatorContext, sdkContext android.SdkContext, d dexer) {
sdkDep := decodeSdkDep(ctx, sdkContext)
if sdkDep.useModule {
ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
@@ -737,172 +322,6 @@
}
}
-func (j *Module) deps(ctx android.BottomUpMutatorContext) {
- if ctx.Device() {
- j.linter.deps(ctx)
-
- sdkDeps(ctx, sdkContext(j), j.dexer)
- }
-
- syspropPublicStubs := syspropPublicStubs(ctx.Config())
-
- // rewriteSyspropLibs validates if a java module can link against platform's sysprop_library,
- // and redirects dependency to public stub depending on the link type.
- rewriteSyspropLibs := func(libs []string, prop string) []string {
- // make a copy
- ret := android.CopyOf(libs)
-
- for idx, lib := range libs {
- stub, ok := syspropPublicStubs[lib]
-
- if !ok {
- continue
- }
-
- linkType, _ := j.getLinkType(ctx.ModuleName())
- // only platform modules can use internal props
- if linkType != javaPlatform {
- ret[idx] = stub
- }
- }
-
- return ret
- }
-
- libDeps := ctx.AddVariationDependencies(nil, libTag, rewriteSyspropLibs(j.properties.Libs, "libs")...)
- ctx.AddVariationDependencies(nil, staticLibTag, rewriteSyspropLibs(j.properties.Static_libs, "static_libs")...)
-
- if ctx.DeviceConfig().VndkVersion() != "" && ctx.Config().EnforceInterPartitionJavaSdkLibrary() {
- // Require java_sdk_library at inter-partition java dependency to ensure stable
- // interface between partitions. If inter-partition java_library dependency is detected,
- // raise build error because java_library doesn't have a stable interface.
- //
- // Inputs:
- // PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY
- // if true, enable enforcement
- // PRODUCT_INTER_PARTITION_JAVA_LIBRARY_ALLOWLIST
- // exception list of java_library names to allow inter-partition dependency
- for idx, lib := range j.properties.Libs {
- if libDeps[idx] == nil {
- continue
- }
-
- if _, ok := syspropPublicStubs[lib]; ok {
- continue
- }
-
- if javaDep, ok := libDeps[idx].(javaSdkLibraryEnforceContext); ok {
- // java_sdk_library is always allowed at inter-partition dependency.
- // So, skip check.
- if _, ok := javaDep.(*SdkLibrary); ok {
- continue
- }
-
- j.checkPartitionsForJavaDependency(ctx, "libs", javaDep)
- }
- }
- }
-
- // For library dependencies that are component libraries (like stubs), add the implementation
- // as a dependency (dexpreopt needs to be against the implementation library, not stubs).
- for _, dep := range libDeps {
- if dep != nil {
- if component, ok := dep.(SdkLibraryComponentDependency); ok {
- if lib := component.OptionalSdkLibraryImplementation(); lib != nil {
- ctx.AddVariationDependencies(nil, usesLibTag, *lib)
- }
- }
- }
- }
-
- ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), pluginTag, j.properties.Plugins...)
- ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), errorpronePluginTag, j.properties.Errorprone.Extra_check_modules...)
- ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), exportedPluginTag, j.properties.Exported_plugins...)
-
- android.ProtoDeps(ctx, &j.protoProperties)
- if j.hasSrcExt(".proto") {
- protoDeps(ctx, &j.protoProperties)
- }
-
- if j.hasSrcExt(".kt") {
- // TODO(ccross): move this to a mutator pass that can tell if generated sources contain
- // Kotlin files
- ctx.AddVariationDependencies(nil, kotlinStdlibTag,
- "kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8")
- if len(j.properties.Plugins) > 0 {
- ctx.AddVariationDependencies(nil, kotlinAnnotationsTag, "kotlin-annotations")
- }
- }
-
- // Framework libraries need special handling in static coverage builds: they should not have
- // static dependency on jacoco, otherwise there would be multiple conflicting definitions of
- // the same jacoco classes coming from different bootclasspath jars.
- if inList(ctx.ModuleName(), config.InstrumentFrameworkModules) {
- if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
- j.properties.Instrument = true
- }
- } else if j.shouldInstrumentStatic(ctx) {
- ctx.AddVariationDependencies(nil, staticLibTag, "jacocoagent")
- }
-}
-
-func hasSrcExt(srcs []string, ext string) bool {
- for _, src := range srcs {
- if filepath.Ext(src) == ext {
- return true
- }
- }
-
- return false
-}
-
-func (j *Module) hasSrcExt(ext string) bool {
- return hasSrcExt(j.properties.Srcs, ext)
-}
-
-func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath,
- aidlIncludeDirs android.Paths) (string, android.Paths) {
-
- aidlIncludes := android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Local_include_dirs)
- aidlIncludes = append(aidlIncludes,
- android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)...)
- aidlIncludes = append(aidlIncludes,
- android.PathsForSource(ctx, j.deviceProperties.Aidl.Include_dirs)...)
-
- var flags []string
- var deps android.Paths
-
- if aidlPreprocess.Valid() {
- flags = append(flags, "-p"+aidlPreprocess.String())
- deps = append(deps, aidlPreprocess.Path())
- } else if len(aidlIncludeDirs) > 0 {
- flags = append(flags, android.JoinWithPrefix(aidlIncludeDirs.Strings(), "-I"))
- }
-
- if len(j.exportAidlIncludeDirs) > 0 {
- flags = append(flags, android.JoinWithPrefix(j.exportAidlIncludeDirs.Strings(), "-I"))
- }
-
- if len(aidlIncludes) > 0 {
- flags = append(flags, android.JoinWithPrefix(aidlIncludes.Strings(), "-I"))
- }
-
- flags = append(flags, "-I"+android.PathForModuleSrc(ctx).String())
- if src := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "src"); src.Valid() {
- flags = append(flags, "-I"+src.String())
- }
-
- if Bool(j.deviceProperties.Aidl.Generate_traces) {
- flags = append(flags, "-t")
- }
-
- if Bool(j.deviceProperties.Aidl.Generate_get_transaction_name) {
- flags = append(flags, "--transaction_names")
- }
-
- return strings.Join(flags, " "), deps
-}
-
type deps struct {
classpath classpath
java9Classpath classpath
@@ -933,265 +352,11 @@
}
}
-type linkType int
-
-const (
- // TODO(jiyong) rename these for better readability. Make the allowed
- // and disallowed link types explicit
- javaCore linkType = iota
- javaSdk
- javaSystem
- javaModule
- javaSystemServer
- javaPlatform
-)
-
-type linkTypeContext interface {
- android.Module
- getLinkType(name string) (ret linkType, stubs bool)
-}
-
-func (m *Module) getLinkType(name string) (ret linkType, stubs bool) {
- switch name {
- case "core.current.stubs", "legacy.core.platform.api.stubs", "stable.core.platform.api.stubs",
- "stub-annotations", "private-stub-annotations-jar",
- "core-lambda-stubs", "core-generated-annotation-stubs":
- return javaCore, true
- case "android_stubs_current":
- return javaSdk, true
- case "android_system_stubs_current":
- return javaSystem, true
- case "android_module_lib_stubs_current":
- return javaModule, true
- case "android_system_server_stubs_current":
- return javaSystemServer, true
- case "android_test_stubs_current":
- return javaSystem, true
- }
-
- if stub, linkType := moduleStubLinkType(name); stub {
- return linkType, true
- }
-
- ver := m.sdkVersion()
- switch ver.kind {
- case sdkCore:
- return javaCore, false
- case sdkSystem:
- return javaSystem, false
- case sdkPublic:
- return javaSdk, false
- case sdkModule:
- return javaModule, false
- case sdkSystemServer:
- return javaSystemServer, false
- case sdkPrivate, sdkNone, sdkCorePlatform, sdkTest:
- return javaPlatform, false
- }
-
- if !ver.valid() {
- panic(fmt.Errorf("sdk_version is invalid. got %q", ver.raw))
- }
- return javaSdk, false
-}
-
-func checkLinkType(ctx android.ModuleContext, from *Module, to linkTypeContext, tag dependencyTag) {
- if ctx.Host() {
- return
- }
-
- myLinkType, stubs := from.getLinkType(ctx.ModuleName())
- if stubs {
- return
- }
- otherLinkType, _ := to.getLinkType(ctx.OtherModuleName(to))
- commonMessage := " In order to fix this, consider adjusting sdk_version: OR platform_apis: " +
- "property of the source or target module so that target module is built with the same " +
- "or smaller API set when compared to the source."
-
- switch myLinkType {
- case javaCore:
- if otherLinkType != javaCore {
- ctx.ModuleErrorf("compiles against core Java API, but dependency %q is compiling against non-core Java APIs."+commonMessage,
- ctx.OtherModuleName(to))
- }
- break
- case javaSdk:
- if otherLinkType != javaCore && otherLinkType != javaSdk {
- ctx.ModuleErrorf("compiles against Android API, but dependency %q is compiling against non-public Android API."+commonMessage,
- ctx.OtherModuleName(to))
- }
- break
- case javaSystem:
- if otherLinkType == javaPlatform || otherLinkType == javaModule || otherLinkType == javaSystemServer {
- ctx.ModuleErrorf("compiles against system API, but dependency %q is compiling against private API."+commonMessage,
- ctx.OtherModuleName(to))
- }
- break
- case javaModule:
- if otherLinkType == javaPlatform || otherLinkType == javaSystemServer {
- ctx.ModuleErrorf("compiles against module API, but dependency %q is compiling against private API."+commonMessage,
- ctx.OtherModuleName(to))
- }
- break
- case javaSystemServer:
- if otherLinkType == javaPlatform {
- ctx.ModuleErrorf("compiles against system server API, but dependency %q is compiling against private API."+commonMessage,
- ctx.OtherModuleName(to))
- }
- break
- case javaPlatform:
- // no restriction on link-type
- break
- }
-}
-
-func (j *Module) collectDeps(ctx android.ModuleContext) deps {
- var deps deps
-
- if ctx.Device() {
- sdkDep := decodeSdkDep(ctx, sdkContext(j))
- if sdkDep.invalidVersion {
- ctx.AddMissingDependencies(sdkDep.bootclasspath)
- ctx.AddMissingDependencies(sdkDep.java9Classpath)
- } else if sdkDep.useFiles {
- // sdkDep.jar is actually equivalent to turbine header.jar.
- deps.classpath = append(deps.classpath, sdkDep.jars...)
- deps.aidlPreprocess = sdkDep.aidl
- } else {
- deps.aidlPreprocess = sdkDep.aidl
- }
- }
-
- ctx.VisitDirectDeps(func(module android.Module) {
- otherName := ctx.OtherModuleName(module)
- tag := ctx.OtherModuleDependencyTag(module)
-
- if IsJniDepTag(tag) {
- // Handled by AndroidApp.collectAppDeps
- return
- }
- if tag == certificateTag {
- // Handled by AndroidApp.collectAppDeps
- return
- }
-
- switch dep := module.(type) {
- case SdkLibraryDependency:
- switch tag {
- case libTag:
- deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
- case staticLibTag:
- ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
- }
- case Dependency:
- switch tag {
- case bootClasspathTag:
- deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars()...)
- case libTag, instrumentationForTag:
- deps.classpath = append(deps.classpath, dep.HeaderJars()...)
- deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
- pluginJars, pluginClasses, disableTurbine := dep.ExportedPlugins()
- addPlugins(&deps, pluginJars, pluginClasses...)
- deps.disableTurbine = deps.disableTurbine || disableTurbine
- case java9LibTag:
- deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars()...)
- case staticLibTag:
- deps.classpath = append(deps.classpath, dep.HeaderJars()...)
- deps.staticJars = append(deps.staticJars, dep.ImplementationJars()...)
- deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars()...)
- deps.staticResourceJars = append(deps.staticResourceJars, dep.ResourceJars()...)
- deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
- pluginJars, pluginClasses, disableTurbine := dep.ExportedPlugins()
- addPlugins(&deps, pluginJars, pluginClasses...)
- // Turbine doesn't run annotation processors, so any module that uses an
- // annotation processor that generates API is incompatible with the turbine
- // optimization.
- deps.disableTurbine = deps.disableTurbine || disableTurbine
- case pluginTag:
- if plugin, ok := dep.(*Plugin); ok {
- if plugin.pluginProperties.Processor_class != nil {
- addPlugins(&deps, plugin.ImplementationAndResourcesJars(), *plugin.pluginProperties.Processor_class)
- } else {
- addPlugins(&deps, plugin.ImplementationAndResourcesJars())
- }
- // Turbine doesn't run annotation processors, so any module that uses an
- // annotation processor that generates API is incompatible with the turbine
- // optimization.
- deps.disableTurbine = deps.disableTurbine || Bool(plugin.pluginProperties.Generates_api)
- } else {
- ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName)
- }
- case errorpronePluginTag:
- if plugin, ok := dep.(*Plugin); ok {
- deps.errorProneProcessorPath = append(deps.errorProneProcessorPath, plugin.ImplementationAndResourcesJars()...)
- } else {
- ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName)
- }
- case exportedPluginTag:
- if plugin, ok := dep.(*Plugin); ok {
- j.exportedPluginJars = append(j.exportedPluginJars, plugin.ImplementationAndResourcesJars()...)
- if plugin.pluginProperties.Processor_class != nil {
- j.exportedPluginClasses = append(j.exportedPluginClasses, *plugin.pluginProperties.Processor_class)
- }
- // Turbine doesn't run annotation processors, so any module that uses an
- // annotation processor that generates API is incompatible with the turbine
- // optimization.
- j.exportedDisableTurbine = Bool(plugin.pluginProperties.Generates_api)
- } else {
- ctx.PropertyErrorf("exported_plugins", "%q is not a java_plugin module", otherName)
- }
- case kotlinStdlibTag:
- deps.kotlinStdlib = append(deps.kotlinStdlib, dep.HeaderJars()...)
- case kotlinAnnotationsTag:
- deps.kotlinAnnotations = dep.HeaderJars()
- }
-
- case android.SourceFileProducer:
- switch tag {
- case libTag:
- checkProducesJars(ctx, dep)
- deps.classpath = append(deps.classpath, dep.Srcs()...)
- case staticLibTag:
- checkProducesJars(ctx, dep)
- deps.classpath = append(deps.classpath, dep.Srcs()...)
- deps.staticJars = append(deps.staticJars, dep.Srcs()...)
- deps.staticHeaderJars = append(deps.staticHeaderJars, dep.Srcs()...)
- }
- default:
- switch tag {
- case bootClasspathTag:
- // If a system modules dependency has been added to the bootclasspath
- // then add its libs to the bootclasspath.
- sm := module.(SystemModulesProvider)
- deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars()...)
-
- case systemModulesTag:
- if deps.systemModules != nil {
- panic("Found two system module dependencies")
- }
- sm := module.(SystemModulesProvider)
- outputDir, outputDeps := sm.OutputDirAndDeps()
- deps.systemModules = &systemModules{outputDir, outputDeps}
- }
- }
-
- addCLCFromDep(ctx, module, j.classLoaderContexts)
- })
-
- return deps
-}
-
-func addPlugins(deps *deps, pluginJars android.Paths, pluginClasses ...string) {
- deps.processorPath = append(deps.processorPath, pluginJars...)
- deps.processorClasses = append(deps.processorClasses, pluginClasses...)
-}
-
-func getJavaVersion(ctx android.ModuleContext, javaVersion string, sdkContext sdkContext) javaVersion {
+func getJavaVersion(ctx android.ModuleContext, javaVersion string, sdkContext android.SdkContext) javaVersion {
if javaVersion != "" {
return normalizeJavaVersion(ctx, javaVersion)
} else if ctx.Device() {
- return sdkContext.sdkVersion().defaultJavaLanguageVersion(ctx)
+ return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx))
} else {
return JAVA_VERSION_9
}
@@ -1246,818 +411,6 @@
}
}
-func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaBuilderFlags {
-
- var flags javaBuilderFlags
-
- // javaVersion flag.
- flags.javaVersion = getJavaVersion(ctx, String(j.properties.Java_version), sdkContext(j))
-
- if ctx.Config().RunErrorProne() {
- if config.ErrorProneClasspath == nil && ctx.Config().TestProductVariables == nil {
- ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?")
- }
-
- errorProneFlags := []string{
- "-Xplugin:ErrorProne",
- "${config.ErrorProneChecks}",
- }
- errorProneFlags = append(errorProneFlags, j.properties.Errorprone.Javacflags...)
-
- flags.errorProneExtraJavacFlags = "${config.ErrorProneFlags} " +
- "'" + strings.Join(errorProneFlags, " ") + "'"
- flags.errorProneProcessorPath = classpath(android.PathsForSource(ctx, config.ErrorProneClasspath))
- }
-
- // classpath
- flags.bootClasspath = append(flags.bootClasspath, deps.bootClasspath...)
- flags.classpath = append(flags.classpath, deps.classpath...)
- flags.java9Classpath = append(flags.java9Classpath, deps.java9Classpath...)
- flags.processorPath = append(flags.processorPath, deps.processorPath...)
- flags.errorProneProcessorPath = append(flags.errorProneProcessorPath, deps.errorProneProcessorPath...)
-
- flags.processors = append(flags.processors, deps.processorClasses...)
- flags.processors = android.FirstUniqueStrings(flags.processors)
-
- if len(flags.bootClasspath) == 0 && ctx.Host() && !flags.javaVersion.usesJavaModules() &&
- decodeSdkDep(ctx, sdkContext(j)).hasStandardLibs() {
- // Give host-side tools a version of OpenJDK's standard libraries
- // close to what they're targeting. As of Dec 2017, AOSP is only
- // bundling OpenJDK 8 and 9, so nothing < 8 is available.
- //
- // When building with OpenJDK 8, the following should have no
- // effect since those jars would be available by default.
- //
- // When building with OpenJDK 9 but targeting a version < 1.8,
- // putting them on the bootclasspath means that:
- // a) code can't (accidentally) refer to OpenJDK 9 specific APIs
- // b) references to existing APIs are not reinterpreted in an
- // OpenJDK 9-specific way, eg. calls to subclasses of
- // java.nio.Buffer as in http://b/70862583
- java8Home := ctx.Config().Getenv("ANDROID_JAVA8_HOME")
- flags.bootClasspath = append(flags.bootClasspath,
- android.PathForSource(ctx, java8Home, "jre/lib/jce.jar"),
- android.PathForSource(ctx, java8Home, "jre/lib/rt.jar"))
- if Bool(j.properties.Use_tools_jar) {
- flags.bootClasspath = append(flags.bootClasspath,
- android.PathForSource(ctx, java8Home, "lib/tools.jar"))
- }
- }
-
- // systemModules
- flags.systemModules = deps.systemModules
-
- // aidl flags.
- flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
-
- return flags
-}
-
-func (j *Module) collectJavacFlags(
- ctx android.ModuleContext, flags javaBuilderFlags, srcFiles android.Paths) javaBuilderFlags {
- // javac flags.
- javacFlags := j.properties.Javacflags
-
- if ctx.Config().MinimizeJavaDebugInfo() && !ctx.Host() {
- // For non-host binaries, override the -g flag passed globally to remove
- // local variable debug info to reduce disk and memory usage.
- javacFlags = append(javacFlags, "-g:source,lines")
- }
- javacFlags = append(javacFlags, "-Xlint:-dep-ann")
-
- if flags.javaVersion.usesJavaModules() {
- javacFlags = append(javacFlags, j.properties.Openjdk9.Javacflags...)
-
- if j.properties.Patch_module != nil {
- // Manually specify build directory in case it is not under the repo root.
- // (javac doesn't seem to expand into symbolic links when searching for patch-module targets, so
- // just adding a symlink under the root doesn't help.)
- patchPaths := []string{".", ctx.Config().BuildDir()}
-
- // b/150878007
- //
- // Workaround to support *Bazel-executed* JDK9 javac in Bazel's
- // execution root for --patch-module. If this javac command line is
- // invoked within Bazel's execution root working directory, the top
- // level directories (e.g. libcore/, tools/, frameworks/) are all
- // symlinks. JDK9 javac does not traverse into symlinks, which causes
- // --patch-module to fail source file lookups when invoked in the
- // execution root.
- //
- // Short of patching javac or enumerating *all* directories as possible
- // input dirs, manually add the top level dir of the source files to be
- // compiled.
- topLevelDirs := map[string]bool{}
- for _, srcFilePath := range srcFiles {
- srcFileParts := strings.Split(srcFilePath.String(), "/")
- // Ignore source files that are already in the top level directory
- // as well as generated files in the out directory. The out
- // directory may be an absolute path, which means srcFileParts[0] is the
- // empty string, so check that as well. Note that "out" in Bazel's execution
- // root is *not* a symlink, which doesn't cause problems for --patch-modules
- // anyway, so it's fine to not apply this workaround for generated
- // source files.
- if len(srcFileParts) > 1 &&
- srcFileParts[0] != "" &&
- srcFileParts[0] != "out" {
- topLevelDirs[srcFileParts[0]] = true
- }
- }
- patchPaths = append(patchPaths, android.SortedStringKeys(topLevelDirs)...)
-
- classPath := flags.classpath.FormJavaClassPath("")
- if classPath != "" {
- patchPaths = append(patchPaths, classPath)
- }
- javacFlags = append(
- javacFlags,
- "--patch-module="+String(j.properties.Patch_module)+"="+strings.Join(patchPaths, ":"))
- }
- }
-
- if len(javacFlags) > 0 {
- // optimization.
- ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " "))
- flags.javacFlags = "$javacFlags"
- }
-
- return flags
-}
-
-func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) {
- j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)
-
- deps := j.collectDeps(ctx)
- flags := j.collectBuilderFlags(ctx, deps)
-
- if flags.javaVersion.usesJavaModules() {
- j.properties.Srcs = append(j.properties.Srcs, j.properties.Openjdk9.Srcs...)
- }
- srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
- if hasSrcExt(srcFiles.Strings(), ".proto") {
- flags = protoFlags(ctx, &j.properties, &j.protoProperties, flags)
- }
-
- kotlinCommonSrcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Common_srcs, nil)
- if len(kotlinCommonSrcFiles.FilterOutByExt(".kt")) > 0 {
- ctx.PropertyErrorf("common_srcs", "common_srcs must be .kt files")
- }
-
- srcFiles = j.genSources(ctx, srcFiles, flags)
-
- // Collect javac flags only after computing the full set of srcFiles to
- // ensure that the --patch-module lookup paths are complete.
- flags = j.collectJavacFlags(ctx, flags, srcFiles)
-
- srcJars := srcFiles.FilterByExt(".srcjar")
- srcJars = append(srcJars, deps.srcJars...)
- if aaptSrcJar != nil {
- srcJars = append(srcJars, aaptSrcJar)
- }
-
- if j.properties.Jarjar_rules != nil {
- j.expandJarjarRules = android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
- }
-
- jarName := ctx.ModuleName() + ".jar"
-
- javaSrcFiles := srcFiles.FilterByExt(".java")
- var uniqueSrcFiles android.Paths
- set := make(map[string]bool)
- for _, v := range javaSrcFiles {
- if _, found := set[v.String()]; !found {
- set[v.String()] = true
- uniqueSrcFiles = append(uniqueSrcFiles, v)
- }
- }
-
- // Collect .java files for AIDEGen
- j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, uniqueSrcFiles.Strings()...)
-
- var kotlinJars android.Paths
-
- if srcFiles.HasExt(".kt") {
- // user defined kotlin flags.
- kotlincFlags := j.properties.Kotlincflags
- CheckKotlincFlags(ctx, kotlincFlags)
-
- // Dogfood the JVM_IR backend.
- kotlincFlags = append(kotlincFlags, "-Xuse-ir")
-
- // If there are kotlin files, compile them first but pass all the kotlin and java files
- // kotlinc will use the java files to resolve types referenced by the kotlin files, but
- // won't emit any classes for them.
- kotlincFlags = append(kotlincFlags, "-no-stdlib")
- if ctx.Device() {
- kotlincFlags = append(kotlincFlags, "-no-jdk")
- }
- if len(kotlincFlags) > 0 {
- // optimization.
- ctx.Variable(pctx, "kotlincFlags", strings.Join(kotlincFlags, " "))
- flags.kotlincFlags += "$kotlincFlags"
- }
-
- var kotlinSrcFiles android.Paths
- kotlinSrcFiles = append(kotlinSrcFiles, uniqueSrcFiles...)
- kotlinSrcFiles = append(kotlinSrcFiles, srcFiles.FilterByExt(".kt")...)
-
- // Collect .kt files for AIDEGen
- j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, srcFiles.FilterByExt(".kt").Strings()...)
- j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, kotlinCommonSrcFiles.Strings()...)
-
- flags.classpath = append(flags.classpath, deps.kotlinStdlib...)
- flags.classpath = append(flags.classpath, deps.kotlinAnnotations...)
-
- flags.kotlincClasspath = append(flags.kotlincClasspath, flags.bootClasspath...)
- flags.kotlincClasspath = append(flags.kotlincClasspath, flags.classpath...)
-
- if len(flags.processorPath) > 0 {
- // Use kapt for annotation processing
- kaptSrcJar := android.PathForModuleOut(ctx, "kapt", "kapt-sources.jar")
- kaptResJar := android.PathForModuleOut(ctx, "kapt", "kapt-res.jar")
- kotlinKapt(ctx, kaptSrcJar, kaptResJar, kotlinSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
- srcJars = append(srcJars, kaptSrcJar)
- kotlinJars = append(kotlinJars, kaptResJar)
- // Disable annotation processing in javac, it's already been handled by kapt
- flags.processorPath = nil
- flags.processors = nil
- }
-
- kotlinJar := android.PathForModuleOut(ctx, "kotlin", jarName)
- kotlinCompile(ctx, kotlinJar, kotlinSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
- if ctx.Failed() {
- return
- }
-
- // Make javac rule depend on the kotlinc rule
- flags.classpath = append(flags.classpath, kotlinJar)
-
- kotlinJars = append(kotlinJars, kotlinJar)
- // Jar kotlin classes into the final jar after javac
- if BoolDefault(j.properties.Static_kotlin_stdlib, true) {
- kotlinJars = append(kotlinJars, deps.kotlinStdlib...)
- }
- }
-
- jars := append(android.Paths(nil), kotlinJars...)
-
- // Store the list of .java files that was passed to javac
- j.compiledJavaSrcs = uniqueSrcFiles
- j.compiledSrcJars = srcJars
-
- enableSharding := false
- var headerJarFileWithoutJarjar android.Path
- if ctx.Device() && !ctx.Config().IsEnvFalse("TURBINE_ENABLED") && !deps.disableTurbine {
- if j.properties.Javac_shard_size != nil && *(j.properties.Javac_shard_size) > 0 {
- enableSharding = true
- // Formerly, there was a check here that prevented annotation processors
- // from being used when sharding was enabled, as some annotation processors
- // do not function correctly in sharded environments. It was removed to
- // allow for the use of annotation processors that do function correctly
- // with sharding enabled. See: b/77284273.
- }
- headerJarFileWithoutJarjar, j.headerJarFile =
- j.compileJavaHeader(ctx, uniqueSrcFiles, srcJars, deps, flags, jarName, kotlinJars)
- if ctx.Failed() {
- return
- }
- }
- if len(uniqueSrcFiles) > 0 || len(srcJars) > 0 {
- var extraJarDeps android.Paths
- if ctx.Config().RunErrorProne() {
- // If error-prone is enabled, add an additional rule to compile the java files into
- // a separate set of classes (so that they don't overwrite the normal ones and require
- // a rebuild when error-prone is turned off).
- // TODO(ccross): Once we always compile with javac9 we may be able to conditionally
- // enable error-prone without affecting the output class files.
- errorprone := android.PathForModuleOut(ctx, "errorprone", jarName)
- RunErrorProne(ctx, errorprone, uniqueSrcFiles, srcJars, flags)
- extraJarDeps = append(extraJarDeps, errorprone)
- }
-
- if enableSharding {
- flags.classpath = append(flags.classpath, headerJarFileWithoutJarjar)
- shardSize := int(*(j.properties.Javac_shard_size))
- var shardSrcs []android.Paths
- if len(uniqueSrcFiles) > 0 {
- shardSrcs = android.ShardPaths(uniqueSrcFiles, shardSize)
- for idx, shardSrc := range shardSrcs {
- classes := j.compileJavaClasses(ctx, jarName, idx, shardSrc,
- nil, flags, extraJarDeps)
- jars = append(jars, classes)
- }
- }
- if len(srcJars) > 0 {
- classes := j.compileJavaClasses(ctx, jarName, len(shardSrcs),
- nil, srcJars, flags, extraJarDeps)
- jars = append(jars, classes)
- }
- } else {
- classes := j.compileJavaClasses(ctx, jarName, -1, uniqueSrcFiles, srcJars, flags, extraJarDeps)
- jars = append(jars, classes)
- }
- if ctx.Failed() {
- return
- }
- }
-
- j.srcJarArgs, j.srcJarDeps = resourcePathsToJarArgs(srcFiles), srcFiles
-
- var includeSrcJar android.WritablePath
- if Bool(j.properties.Include_srcs) {
- includeSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+".srcjar")
- TransformResourcesToJar(ctx, includeSrcJar, j.srcJarArgs, j.srcJarDeps)
- }
-
- dirArgs, dirDeps := ResourceDirsToJarArgs(ctx, j.properties.Java_resource_dirs,
- j.properties.Exclude_java_resource_dirs, j.properties.Exclude_java_resources)
- fileArgs, fileDeps := ResourceFilesToJarArgs(ctx, j.properties.Java_resources, j.properties.Exclude_java_resources)
- extraArgs, extraDeps := resourcePathsToJarArgs(j.extraResources), j.extraResources
-
- var resArgs []string
- var resDeps android.Paths
-
- resArgs = append(resArgs, dirArgs...)
- resDeps = append(resDeps, dirDeps...)
-
- resArgs = append(resArgs, fileArgs...)
- resDeps = append(resDeps, fileDeps...)
-
- resArgs = append(resArgs, extraArgs...)
- resDeps = append(resDeps, extraDeps...)
-
- if len(resArgs) > 0 {
- resourceJar := android.PathForModuleOut(ctx, "res", jarName)
- TransformResourcesToJar(ctx, resourceJar, resArgs, resDeps)
- j.resourceJar = resourceJar
- if ctx.Failed() {
- return
- }
- }
-
- var resourceJars android.Paths
- if j.resourceJar != nil {
- resourceJars = append(resourceJars, j.resourceJar)
- }
- if Bool(j.properties.Include_srcs) {
- resourceJars = append(resourceJars, includeSrcJar)
- }
- resourceJars = append(resourceJars, deps.staticResourceJars...)
-
- if len(resourceJars) > 1 {
- combinedJar := android.PathForModuleOut(ctx, "res-combined", jarName)
- TransformJarsToJar(ctx, combinedJar, "for resources", resourceJars, android.OptionalPath{},
- false, nil, nil)
- j.resourceJar = combinedJar
- } else if len(resourceJars) == 1 {
- j.resourceJar = resourceJars[0]
- }
-
- if len(deps.staticJars) > 0 {
- jars = append(jars, deps.staticJars...)
- }
-
- manifest := j.overrideManifest
- if !manifest.Valid() && j.properties.Manifest != nil {
- manifest = android.OptionalPathForPath(android.PathForModuleSrc(ctx, *j.properties.Manifest))
- }
-
- services := android.PathsForModuleSrc(ctx, j.properties.Services)
- if len(services) > 0 {
- servicesJar := android.PathForModuleOut(ctx, "services", jarName)
- var zipargs []string
- for _, file := range services {
- serviceFile := file.String()
- zipargs = append(zipargs, "-C", filepath.Dir(serviceFile), "-f", serviceFile)
- }
- rule := zip
- args := map[string]string{
- "jarArgs": "-P META-INF/services/ " + strings.Join(proptools.NinjaAndShellEscapeList(zipargs), " "),
- }
- if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_ZIP") {
- rule = zipRE
- args["implicits"] = strings.Join(services.Strings(), ",")
- }
- ctx.Build(pctx, android.BuildParams{
- Rule: rule,
- Output: servicesJar,
- Implicits: services,
- Args: args,
- })
- jars = append(jars, servicesJar)
- }
-
- // Combine the classes built from sources, any manifests, and any static libraries into
- // classes.jar. If there is only one input jar this step will be skipped.
- var outputFile android.ModuleOutPath
-
- if len(jars) == 1 && !manifest.Valid() {
- if moduleOutPath, ok := jars[0].(android.ModuleOutPath); ok {
- // Optimization: skip the combine step if there is nothing to do
- // TODO(ccross): this leaves any module-info.class files, but those should only come from
- // prebuilt dependencies until we support modules in the platform build, so there shouldn't be
- // any if len(jars) == 1.
- outputFile = moduleOutPath
- } else {
- combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cp,
- Input: jars[0],
- Output: combinedJar,
- })
- outputFile = combinedJar
- }
- } else {
- combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
- TransformJarsToJar(ctx, combinedJar, "for javac", jars, manifest,
- false, nil, nil)
- outputFile = combinedJar
- }
-
- // jarjar implementation jar if necessary
- if j.expandJarjarRules != nil {
- // Transform classes.jar into classes-jarjar.jar
- jarjarFile := android.PathForModuleOut(ctx, "jarjar", jarName)
- TransformJarJar(ctx, jarjarFile, outputFile, j.expandJarjarRules)
- outputFile = jarjarFile
-
- // jarjar resource jar if necessary
- if j.resourceJar != nil {
- resourceJarJarFile := android.PathForModuleOut(ctx, "res-jarjar", jarName)
- TransformJarJar(ctx, resourceJarJarFile, j.resourceJar, j.expandJarjarRules)
- j.resourceJar = resourceJarJarFile
- }
-
- if ctx.Failed() {
- return
- }
- }
-
- // Check package restrictions if necessary.
- if len(j.properties.Permitted_packages) > 0 {
- // Check packages and copy to package-checked file.
- pkgckFile := android.PathForModuleOut(ctx, "package-check.stamp")
- CheckJarPackages(ctx, pkgckFile, outputFile, j.properties.Permitted_packages)
- j.additionalCheckedModules = append(j.additionalCheckedModules, pkgckFile)
-
- if ctx.Failed() {
- return
- }
- }
-
- j.implementationJarFile = outputFile
- if j.headerJarFile == nil {
- j.headerJarFile = j.implementationJarFile
- }
-
- if j.shouldInstrumentInApex(ctx) {
- j.properties.Instrument = true
- }
-
- if j.shouldInstrument(ctx) {
- outputFile = j.instrument(ctx, flags, outputFile, jarName)
- }
-
- // merge implementation jar with resources if necessary
- implementationAndResourcesJar := outputFile
- if j.resourceJar != nil {
- jars := android.Paths{j.resourceJar, implementationAndResourcesJar}
- combinedJar := android.PathForModuleOut(ctx, "withres", jarName)
- TransformJarsToJar(ctx, combinedJar, "for resources", jars, manifest,
- false, nil, nil)
- implementationAndResourcesJar = combinedJar
- }
-
- j.implementationAndResourcesJar = implementationAndResourcesJar
-
- // Enable dex compilation for the APEX variants, unless it is disabled explicitly
- apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
- if j.DirectlyInAnyApex() && !apexInfo.IsForPlatform() {
- if j.dexProperties.Compile_dex == nil {
- j.dexProperties.Compile_dex = proptools.BoolPtr(true)
- }
- if j.deviceProperties.Hostdex == nil {
- j.deviceProperties.Hostdex = proptools.BoolPtr(true)
- }
- }
-
- if ctx.Device() && j.hasCode(ctx) &&
- (Bool(j.properties.Installable) || Bool(j.dexProperties.Compile_dex)) {
- if j.shouldInstrumentStatic(ctx) {
- j.dexer.extraProguardFlagFiles = append(j.dexer.extraProguardFlagFiles,
- android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags"))
- }
- // Dex compilation
- var dexOutputFile android.ModuleOutPath
- dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
- if ctx.Failed() {
- return
- }
-
- configurationName := j.ConfigurationName()
- primary := configurationName == ctx.ModuleName()
- // If the prebuilt is being used rather than the from source, skip this
- // module to prevent duplicated classes
- primary = primary && !j.IsReplacedByPrebuilt()
-
- // Hidden API CSV generation and dex encoding
- dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, j.implementationJarFile,
- proptools.Bool(j.dexProperties.Uncompress_dex))
-
- // merge dex jar with resources if necessary
- if j.resourceJar != nil {
- jars := android.Paths{dexOutputFile, j.resourceJar}
- combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName)
- TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{},
- false, nil, nil)
- if *j.dexProperties.Uncompress_dex {
- combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName)
- TransformZipAlign(ctx, combinedAlignedJar, combinedJar)
- dexOutputFile = combinedAlignedJar
- } else {
- dexOutputFile = combinedJar
- }
- }
-
- j.dexJarFile = dexOutputFile
-
- // Dexpreopting
- j.dexpreopt(ctx, dexOutputFile)
-
- j.maybeStrippedDexJarFile = dexOutputFile
-
- outputFile = dexOutputFile
-
- if ctx.Failed() {
- return
- }
- } else {
- outputFile = implementationAndResourcesJar
- }
-
- if ctx.Device() {
- lintSDKVersionString := func(sdkSpec sdkSpec) string {
- if v := sdkSpec.version; v.isNumbered() {
- return v.String()
- } else {
- return ctx.Config().DefaultAppTargetSdk(ctx).String()
- }
- }
-
- j.linter.name = ctx.ModuleName()
- j.linter.srcs = srcFiles
- j.linter.srcJars = srcJars
- j.linter.classpath = append(append(android.Paths(nil), flags.bootClasspath...), flags.classpath...)
- j.linter.classes = j.implementationJarFile
- j.linter.minSdkVersion = lintSDKVersionString(j.minSdkVersion())
- j.linter.targetSdkVersion = lintSDKVersionString(j.targetSdkVersion())
- j.linter.compileSdkVersion = lintSDKVersionString(j.sdkVersion())
- j.linter.javaLanguageLevel = flags.javaVersion.String()
- j.linter.kotlinLanguageLevel = "1.3"
- if !apexInfo.IsForPlatform() && ctx.Config().UnbundledBuildApps() {
- j.linter.buildModuleReportZip = true
- }
- j.linter.lint(ctx)
- }
-
- ctx.CheckbuildFile(outputFile)
-
- // Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
- j.outputFile = outputFile.WithoutRel()
-}
-
-func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, idx int,
- srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.WritablePath {
-
- kzipName := pathtools.ReplaceExtension(jarName, "kzip")
- if idx >= 0 {
- kzipName = strings.TrimSuffix(jarName, filepath.Ext(jarName)) + strconv.Itoa(idx) + ".kzip"
- jarName += strconv.Itoa(idx)
- }
-
- classes := android.PathForModuleOut(ctx, "javac", jarName)
- TransformJavaToClasses(ctx, classes, idx, srcFiles, srcJars, flags, extraJarDeps)
-
- if ctx.Config().EmitXrefRules() {
- extractionFile := android.PathForModuleOut(ctx, kzipName)
- emitXrefRule(ctx, extractionFile, idx, srcFiles, srcJars, flags, extraJarDeps)
- j.kytheFiles = append(j.kytheFiles, extractionFile)
- }
-
- return classes
-}
-
-// Check for invalid kotlinc flags. Only use this for flags explicitly passed by the user,
-// since some of these flags may be used internally.
-func CheckKotlincFlags(ctx android.ModuleContext, flags []string) {
- for _, flag := range flags {
- flag = strings.TrimSpace(flag)
-
- if !strings.HasPrefix(flag, "-") {
- ctx.PropertyErrorf("kotlincflags", "Flag `%s` must start with `-`", flag)
- } else if strings.HasPrefix(flag, "-Xintellij-plugin-root") {
- ctx.PropertyErrorf("kotlincflags",
- "Bad flag: `%s`, only use internal compiler for consistency.", flag)
- } else if inList(flag, config.KotlincIllegalFlags) {
- ctx.PropertyErrorf("kotlincflags", "Flag `%s` already used by build system", flag)
- } else if flag == "-include-runtime" {
- ctx.PropertyErrorf("kotlincflags", "Bad flag: `%s`, do not include runtime.", flag)
- } else {
- args := strings.Split(flag, " ")
- if args[0] == "-kotlin-home" {
- ctx.PropertyErrorf("kotlincflags",
- "Bad flag: `%s`, kotlin home already set to default (path to kotlinc in the repo).", flag)
- }
- }
- }
-}
-
-func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars android.Paths,
- deps deps, flags javaBuilderFlags, jarName string,
- extraJars android.Paths) (headerJar, jarjarHeaderJar android.Path) {
-
- var jars android.Paths
- if len(srcFiles) > 0 || len(srcJars) > 0 {
- // Compile java sources into turbine.jar.
- turbineJar := android.PathForModuleOut(ctx, "turbine", jarName)
- TransformJavaToHeaderClasses(ctx, turbineJar, srcFiles, srcJars, flags)
- if ctx.Failed() {
- return nil, nil
- }
- jars = append(jars, turbineJar)
- }
-
- jars = append(jars, extraJars...)
-
- // Combine any static header libraries into classes-header.jar. If there is only
- // one input jar this step will be skipped.
- jars = append(jars, deps.staticHeaderJars...)
-
- // we cannot skip the combine step for now if there is only one jar
- // since we have to strip META-INF/TRANSITIVE dir from turbine.jar
- combinedJar := android.PathForModuleOut(ctx, "turbine-combined", jarName)
- TransformJarsToJar(ctx, combinedJar, "for turbine", jars, android.OptionalPath{},
- false, nil, []string{"META-INF/TRANSITIVE"})
- headerJar = combinedJar
- jarjarHeaderJar = combinedJar
-
- if j.expandJarjarRules != nil {
- // Transform classes.jar into classes-jarjar.jar
- jarjarFile := android.PathForModuleOut(ctx, "turbine-jarjar", jarName)
- TransformJarJar(ctx, jarjarFile, headerJar, j.expandJarjarRules)
- jarjarHeaderJar = jarjarFile
- if ctx.Failed() {
- return nil, nil
- }
- }
-
- return headerJar, jarjarHeaderJar
-}
-
-func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags,
- classesJar android.Path, jarName string) android.ModuleOutPath {
-
- specs := j.jacocoModuleToZipCommand(ctx)
-
- jacocoReportClassesFile := android.PathForModuleOut(ctx, "jacoco-report-classes", jarName)
- instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName)
-
- jacocoInstrumentJar(ctx, instrumentedJar, jacocoReportClassesFile, classesJar, specs)
-
- j.jacocoReportClassesFile = jacocoReportClassesFile
-
- return instrumentedJar
-}
-
-var _ Dependency = (*Module)(nil)
-
-func (j *Module) HeaderJars() android.Paths {
- if j.headerJarFile == nil {
- return nil
- }
- return android.Paths{j.headerJarFile}
-}
-
-func (j *Module) ImplementationJars() android.Paths {
- if j.implementationJarFile == nil {
- return nil
- }
- return android.Paths{j.implementationJarFile}
-}
-
-func (j *Module) DexJarBuildPath() android.Path {
- return j.dexJarFile
-}
-
-func (j *Module) DexJarInstallPath() android.Path {
- return j.installFile
-}
-
-func (j *Module) ResourceJars() android.Paths {
- if j.resourceJar == nil {
- return nil
- }
- return android.Paths{j.resourceJar}
-}
-
-func (j *Module) ImplementationAndResourcesJars() android.Paths {
- if j.implementationAndResourcesJar == nil {
- return nil
- }
- return android.Paths{j.implementationAndResourcesJar}
-}
-
-func (j *Module) AidlIncludeDirs() android.Paths {
- // exportAidlIncludeDirs is type android.Paths already
- return j.exportAidlIncludeDirs
-}
-
-func (j *Module) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
- return j.classLoaderContexts
-}
-
-// ExportedPlugins returns the list of jars needed to run the exported plugins, the list of
-// classes for the plugins, and a boolean for whether turbine needs to be disabled due to plugins
-// that generate APIs.
-func (j *Module) ExportedPlugins() (android.Paths, []string, bool) {
- return j.exportedPluginJars, j.exportedPluginClasses, j.exportedDisableTurbine
-}
-
-func (j *Module) SrcJarArgs() ([]string, android.Paths) {
- return j.srcJarArgs, j.srcJarDeps
-}
-
-var _ logtagsProducer = (*Module)(nil)
-
-func (j *Module) logtags() android.Paths {
- return j.logtagsSrcs
-}
-
-// Collect information for opening IDE project files in java/jdeps.go.
-func (j *Module) IDEInfo(dpInfo *android.IdeInfo) {
- dpInfo.Deps = append(dpInfo.Deps, j.CompilerDeps()...)
- dpInfo.Srcs = append(dpInfo.Srcs, j.expandIDEInfoCompiledSrcs...)
- dpInfo.SrcJars = append(dpInfo.SrcJars, j.compiledSrcJars.Strings()...)
- dpInfo.Aidl_include_dirs = append(dpInfo.Aidl_include_dirs, j.deviceProperties.Aidl.Include_dirs...)
- if j.expandJarjarRules != nil {
- dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, j.expandJarjarRules.String())
- }
- dpInfo.Paths = append(dpInfo.Paths, j.modulePaths...)
-}
-
-func (j *Module) CompilerDeps() []string {
- jdeps := []string{}
- jdeps = append(jdeps, j.properties.Libs...)
- jdeps = append(jdeps, j.properties.Static_libs...)
- return jdeps
-}
-
-func (j *Module) hasCode(ctx android.ModuleContext) bool {
- srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
- return len(srcFiles) > 0 || len(ctx.GetDirectDepsWithTag(staticLibTag)) > 0
-}
-
-// Implements android.ApexModule
-func (j *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
- return j.depIsInSameApex(ctx, dep)
-}
-
-// Implements android.ApexModule
-func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
- sdkVersion android.ApiLevel) error {
- sdkSpec := j.minSdkVersion()
- if !sdkSpec.specified() {
- return fmt.Errorf("min_sdk_version is not specified")
- }
- if sdkSpec.kind == sdkCore {
- return nil
- }
- ver, err := sdkSpec.effectiveVersion(ctx)
- if err != nil {
- return err
- }
- if ver.ApiLevel(ctx).GreaterThan(sdkVersion) {
- return fmt.Errorf("newer SDK(%v)", ver)
- }
- return nil
-}
-
-func (j *Module) Stem() string {
- return proptools.StringDefault(j.deviceProperties.Stem, j.Name())
-}
-
-func (j *Module) ConfigurationName() string {
- return proptools.StringDefault(j.deviceProperties.ConfigurationName, j.BaseModuleName())
-}
-
-func (j *Module) JacocoReportClassesFile() android.Path {
- return j.jacocoReportClassesFile
-}
-
-func (j *Module) IsInstallable() bool {
- return Bool(j.properties.Installable)
-}
-
//
// Java libraries (.jar file)
//
@@ -2105,6 +458,14 @@
}
func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // Initialize the hiddenapi structure. Pass in the configuration name rather than the module name
+ // so the hidden api will encode the <x>.impl java_ library created by java_sdk_library just as it
+ // would the <x> library if <x> was configured as a boot jar.
+ j.initHiddenAPI(ctx, j.ConfigurationName())
+
+ j.sdkVersion = j.SdkVersion(ctx)
+ j.minSdkVersion = j.MinSdkVersion(ctx)
+
apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
if !apexInfo.IsForPlatform() {
j.hideApexVariantFromMake = true
@@ -2160,9 +521,22 @@
// Function to retrieve the appropriate output jar (implementation or header) from
// the library.
- jarToExportGetter func(j *Library) android.Path
+ jarToExportGetter func(ctx android.SdkMemberContext, j *Library) android.Path
+
+ // Function to compute the snapshot relative path to which the named library's
+ // jar should be copied.
+ snapshotPathGetter func(osPrefix, name string) string
+
+ // True if only the jar should be copied to the snapshot, false if the jar plus any additional
+ // files like aidl files should also be copied.
+ onlyCopyJarToSnapshot bool
}
+const (
+ onlyCopyJarToSnapshot = true
+ copyEverythingToSnapshot = false
+)
+
func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
mctx.AddVariationDependencies(nil, dependencyTag, names...)
}
@@ -2190,21 +564,32 @@
func (p *librarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
j := variant.(*Library)
- p.JarToExport = ctx.MemberType().(*librarySdkMemberType).jarToExportGetter(j)
+ p.JarToExport = ctx.MemberType().(*librarySdkMemberType).jarToExportGetter(ctx, j)
+
p.AidlIncludeDirs = j.AidlIncludeDirs()
}
func (p *librarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
builder := ctx.SnapshotBuilder()
+ memberType := ctx.MemberType().(*librarySdkMemberType)
+
exportedJar := p.JarToExport
if exportedJar != nil {
- snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(p.OsPrefix(), ctx.Name())
+ // Delegate the creation of the snapshot relative path to the member type.
+ snapshotRelativeJavaLibPath := memberType.snapshotPathGetter(p.OsPrefix(), ctx.Name())
+
+ // Copy the exported jar to the snapshot.
builder.CopyToSnapshot(exportedJar, snapshotRelativeJavaLibPath)
propertySet.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
}
+ // Do not copy anything else to the snapshot.
+ if memberType.onlyCopyJarToSnapshot {
+ return
+ }
+
aidlIncludeDirs := p.AidlIncludeDirs
if len(aidlIncludeDirs) != 0 {
sdkModuleContext := ctx.SdkModuleContext()
@@ -2225,7 +610,7 @@
PropertyName: "java_header_libs",
SupportsSdk: true,
},
- func(j *Library) android.Path {
+ func(_ android.SdkMemberContext, j *Library) android.Path {
headerJars := j.HeaderJars()
if len(headerJars) != 1 {
panic(fmt.Errorf("there must be only one header jar from %q", j.Name()))
@@ -2233,6 +618,8 @@
return headerJars[0]
},
+ sdkSnapshotFilePathForJar,
+ copyEverythingToSnapshot,
}
// java_library builds and links sources into a `.jar` file for the device, and possibly for the host as well.
@@ -2385,7 +772,16 @@
j.deps(ctx)
}
+func (j *TestHost) AddExtraResource(p android.Path) {
+ j.extraResources = append(j.extraResources, p)
+}
+
func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ if j.testProperties.Test_options.Unit_test == nil && ctx.Host() {
+ // TODO(b/): Clean temporary heuristic to avoid unexpected onboarding.
+ defaultUnitTest := !inList("tradefed", j.properties.Libs) && !inList("cts", j.testProperties.Test_suites)
+ j.testProperties.Test_options.Unit_test = proptools.BoolPtr(defaultUnitTest)
+ }
j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template,
j.testProperties.Test_suites, j.testProperties.Auto_gen_config, j.testProperties.Test_options.Unit_test)
@@ -2543,12 +939,23 @@
module.AddProperties(&module.testProperties)
module.AddProperties(&module.testHostProperties)
- module.Module.properties.Installable = proptools.BoolPtr(true)
+ InitTestHost(
+ module,
+ proptools.BoolPtr(true),
+ nil,
+ nil)
InitJavaModuleMultiTargets(module, android.HostSupported)
+
return module
}
+func InitTestHost(th *TestHost, installable *bool, testSuites []string, autoGenConfig *bool) {
+ th.properties.Installable = installable
+ th.testProperties.Auto_gen_config = autoGenConfig
+ th.testProperties.Test_suites = testSuites
+}
+
//
// Java Binaries (.jar file plus wrapper script)
//
@@ -2612,9 +1019,10 @@
}
func (j *Binary) DepsMutator(ctx android.BottomUpMutatorContext) {
- if ctx.Arch().ArchType == android.Common {
+ if ctx.Arch().ArchType == android.Common || ctx.BazelConversionMode() {
j.deps(ctx)
- } else {
+ }
+ if ctx.Arch().ArchType != android.Common || ctx.BazelConversionMode() {
// These dependencies ensure the host installation rules will install the jar file and
// the jni libraries when the wrapper is installed.
ctx.AddVariationDependencies(nil, jniInstallTag, j.binaryProperties.Jni_libs...)
@@ -2669,8 +1077,14 @@
type ImportProperties struct {
Jars []string `android:"path,arch_variant"`
+ // The version of the SDK that the source prebuilt file was built against. Defaults to the
+ // current version if not specified.
Sdk_version *string
+ // The minimum version of the SDK that this module supports. Defaults to sdk_version if not
+ // specified.
+ Min_sdk_version *string
+
Installable *bool
// List of shared java libs that this module has dependencies to
@@ -2719,30 +1133,28 @@
exportAidlIncludeDirs android.Paths
hideApexVariantFromMake bool
+
+ sdkVersion android.SdkSpec
+ minSdkVersion android.SdkSpec
}
-func (j *Import) sdkVersion() sdkSpec {
- return sdkSpecFrom(String(j.properties.Sdk_version))
+func (j *Import) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ return android.SdkSpecFrom(ctx, String(j.properties.Sdk_version))
}
-func (j *Import) makeSdkVersion() string {
- return j.sdkVersion().raw
-}
-
-func (j *Import) systemModules() string {
+func (j *Import) SystemModules() string {
return "none"
}
-func (j *Import) minSdkVersion() sdkSpec {
- return j.sdkVersion()
+func (j *Import) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ if j.properties.Min_sdk_version != nil {
+ return android.SdkSpecFrom(ctx, *j.properties.Min_sdk_version)
+ }
+ return j.SdkVersion(ctx)
}
-func (j *Import) targetSdkVersion() sdkSpec {
- return j.sdkVersion()
-}
-
-func (j *Import) MinSdkVersion() string {
- return j.minSdkVersion().version.String()
+func (j *Import) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ return j.SdkVersion(ctx)
}
func (j *Import) Prebuilt() *android.Prebuilt {
@@ -2765,15 +1177,25 @@
return nil
}
+func (j *Import) LintDepSets() LintDepSets {
+ return LintDepSets{}
+}
+
func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) {
ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
if ctx.Device() && Bool(j.dexProperties.Compile_dex) {
- sdkDeps(ctx, sdkContext(j), j.dexer)
+ sdkDeps(ctx, android.SdkContext(j), j.dexer)
}
}
func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ j.sdkVersion = j.SdkVersion(ctx)
+ j.minSdkVersion = j.MinSdkVersion(ctx)
+
+ // Initialize the hiddenapi structure.
+ j.initHiddenAPI(ctx, j.BaseModuleName())
+
if !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform() {
j.hideApexVariantFromMake = true
}
@@ -2793,26 +1215,32 @@
j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
var flags javaBuilderFlags
+ var deapexerModule android.Module
ctx.VisitDirectDeps(func(module android.Module) {
tag := ctx.OtherModuleDependencyTag(module)
- switch dep := module.(type) {
- case Dependency:
+ if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
+ dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
switch tag {
case libTag, staticLibTag:
- flags.classpath = append(flags.classpath, dep.HeaderJars()...)
+ flags.classpath = append(flags.classpath, dep.HeaderJars...)
case bootClasspathTag:
- flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars()...)
+ flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars...)
}
- case SdkLibraryDependency:
+ } else if dep, ok := module.(SdkLibraryDependency); ok {
switch tag {
case libTag:
- flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
+ flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
}
}
addCLCFromDep(ctx, module, j.classLoaderContexts)
+
+ // Save away the `deapexer` module on which this depends, if any.
+ if tag == android.DeapexerTag {
+ deapexerModule = module
+ }
})
if Bool(j.properties.Installable) {
@@ -2822,40 +1250,67 @@
j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
- if ctx.Device() && Bool(j.dexProperties.Compile_dex) {
- sdkDep := decodeSdkDep(ctx, sdkContext(j))
- if sdkDep.invalidVersion {
- ctx.AddMissingDependencies(sdkDep.bootclasspath)
- ctx.AddMissingDependencies(sdkDep.java9Classpath)
- } else if sdkDep.useFiles {
- // sdkDep.jar is actually equivalent to turbine header.jar.
- flags.classpath = append(flags.classpath, sdkDep.jars...)
+ if ctx.Device() {
+ // If this is a variant created for a prebuilt_apex then use the dex implementation jar
+ // obtained from the associated deapexer module.
+ ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ if ai.ForPrebuiltApex {
+ if deapexerModule == nil {
+ // This should never happen as a variant for a prebuilt_apex is only created if the
+ // deapxer module has been configured to export the dex implementation jar for this module.
+ ctx.ModuleErrorf("internal error: module %q does not depend on a `deapexer` module for prebuilt_apex %q",
+ j.Name(), ai.ApexVariationName)
+ }
+
+ // Get the path of the dex implementation jar from the `deapexer` module.
+ di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
+ if dexOutputPath := di.PrebuiltExportPath(j.BaseModuleName(), ".dexjar"); dexOutputPath != nil {
+ j.dexJarFile = dexOutputPath
+ j.hiddenAPIExtractInformation(ctx, dexOutputPath, outputFile)
+ } else {
+ // This should never happen as a variant for a prebuilt_apex is only created if the
+ // prebuilt_apex has been configured to export the java library dex file.
+ ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt_apex %q", deapexerModule.Name())
+ }
+ } else if Bool(j.dexProperties.Compile_dex) {
+ sdkDep := decodeSdkDep(ctx, android.SdkContext(j))
+ if sdkDep.invalidVersion {
+ ctx.AddMissingDependencies(sdkDep.bootclasspath)
+ ctx.AddMissingDependencies(sdkDep.java9Classpath)
+ } else if sdkDep.useFiles {
+ // sdkDep.jar is actually equivalent to turbine header.jar.
+ flags.classpath = append(flags.classpath, sdkDep.jars...)
+ }
+
+ // Dex compilation
+
+ j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", jarName)
+ if j.dexProperties.Uncompress_dex == nil {
+ // If the value was not force-set by the user, use reasonable default based on the module.
+ j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
+ }
+ j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
+
+ var dexOutputFile android.OutputPath
+ dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), outputFile, jarName)
+ if ctx.Failed() {
+ return
+ }
+
+ // Hidden API CSV generation and dex encoding
+ dexOutputFile = j.hiddenAPIExtractAndEncode(ctx, dexOutputFile, outputFile,
+ proptools.Bool(j.dexProperties.Uncompress_dex))
+
+ j.dexJarFile = dexOutputFile
}
-
- // Dex compilation
-
- j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", jarName)
- if j.dexProperties.Uncompress_dex == nil {
- // If the value was not force-set by the user, use reasonable default based on the module.
- j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
- }
- j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
-
- var dexOutputFile android.ModuleOutPath
- dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
- if ctx.Failed() {
- return
- }
-
- configurationName := j.BaseModuleName()
- primary := j.Prebuilt().UsePrebuilt()
-
- // Hidden API CSV generation and dex encoding
- dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, outputFile,
- proptools.Bool(j.dexProperties.Uncompress_dex))
-
- j.dexJarFile = dexOutputFile
}
+
+ ctx.SetProvider(JavaInfoProvider, JavaInfo{
+ HeaderJars: android.PathsIfNonNil(j.combinedClasspathFile),
+ ImplementationAndResourcesJars: android.PathsIfNonNil(j.combinedClasspathFile),
+ ImplementationJars: android.PathsIfNonNil(j.combinedClasspathFile),
+ AidlIncludeDirs: j.exportAidlIncludeDirs,
+ })
}
func (j *Import) OutputFiles(tag string) (android.Paths, error) {
@@ -2869,8 +1324,6 @@
var _ android.OutputFileProducer = (*Import)(nil)
-var _ Dependency = (*Import)(nil)
-
func (j *Import) HeaderJars() android.Paths {
if j.combinedClasspathFile == nil {
return nil
@@ -2878,17 +1331,6 @@
return android.Paths{j.combinedClasspathFile}
}
-func (j *Import) ImplementationJars() android.Paths {
- if j.combinedClasspathFile == nil {
- return nil
- }
- return android.Paths{j.combinedClasspathFile}
-}
-
-func (j *Import) ResourceJars() android.Paths {
- return nil
-}
-
func (j *Import) ImplementationAndResourcesJars() android.Paths {
if j.combinedClasspathFile == nil {
return nil
@@ -2904,22 +1346,10 @@
return nil
}
-func (j *Import) AidlIncludeDirs() android.Paths {
- return j.exportAidlIncludeDirs
-}
-
func (j *Import) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
return j.classLoaderContexts
}
-func (j *Import) ExportedPlugins() (android.Paths, []string, bool) {
- return nil, nil, false
-}
-
-func (j *Import) SrcJarArgs() ([]string, android.Paths) {
- return nil, nil
-}
-
var _ android.ApexModule = (*Import)(nil)
// Implements android.ApexModule
@@ -2930,7 +1360,20 @@
// Implements android.ApexModule
func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
- // Do not check for prebuilts against the min_sdk_version of enclosing APEX
+ sdkSpec := j.MinSdkVersion(ctx)
+ if !sdkSpec.Specified() {
+ return fmt.Errorf("min_sdk_version is not specified")
+ }
+ if sdkSpec.Kind == android.SdkCore {
+ return nil
+ }
+ ver, err := sdkSpec.EffectiveVersion(ctx)
+ if err != nil {
+ return err
+ }
+ if ver.GreaterThan(sdkVersion) {
+ return fmt.Errorf("newer SDK(%v)", ver)
+ }
return nil
}
@@ -3025,8 +1468,7 @@
properties DexImportProperties
- dexJarFile android.Path
- maybeStrippedDexJarFile android.Path
+ dexJarFile android.Path
dexpreopter
@@ -3113,8 +1555,6 @@
j.dexpreopt(ctx, dexOutputFile)
- j.maybeStrippedDexJarFile = dexOutputFile
-
if apexInfo.IsForPlatform() {
ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
j.Stem()+".jar", dexOutputFile)
@@ -3189,16 +1629,12 @@
// ],
// javacflags: ["-Xlint:all"],
// }
-func defaultsFactory() android.Module {
- return DefaultsFactory()
-}
-
func DefaultsFactory() android.Module {
module := &Defaults{}
module.AddProperties(
- &CompilerProperties{},
- &CompilerDeviceProperties{},
+ &CommonProperties{},
+ &DeviceProperties{},
&DexProperties{},
&DexpreoptProperties{},
&android.ProtoProperties{},
@@ -3216,6 +1652,7 @@
&android.ApexProperties{},
&RuntimeResourceOverlayProperties{},
&LintProperties{},
+ &appTestHelperAppProperties{},
)
android.InitDefaultsModule(module)
@@ -3247,16 +1684,6 @@
var String = proptools.String
var inList = android.InList
-// TODO(b/132357300) Generalize SdkLibrarComponentDependency to non-SDK libraries and merge with
-// this interface.
-type ProvidesUsesLib interface {
- ProvidesUsesLib() *string
-}
-
-func (j *Module) ProvidesUsesLib() *string {
- return j.usesLibraryProperties.Provides_uses_lib
-}
-
// Add class loader context (CLC) of a given dependency to the current CLC.
func addCLCFromDep(ctx android.ModuleContext, depModule android.Module,
clcMap dexpreopt.ClassLoaderContextMap) {
@@ -3300,7 +1727,7 @@
}
if implicitSdkLib != nil {
- clcMap.AddContextForSdk(ctx, dexpreopt.AnySdkVersion, *implicitSdkLib,
+ clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *implicitSdkLib,
dep.DexJarBuildPath(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
} else {
depName := ctx.OtherModuleName(depModule)
diff --git a/java/java_test.go b/java/java_test.go
index d1ba3db..0523458 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -16,7 +16,6 @@
import (
"fmt"
- "io/ioutil"
"os"
"path/filepath"
"reflect"
@@ -30,157 +29,102 @@
"android/soong/android"
"android/soong/cc"
"android/soong/dexpreopt"
+ "android/soong/genrule"
"android/soong/python"
)
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_java_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
+// Legacy preparer used for running tests within the java package.
+//
+// This includes everything that was needed to run any test in the java package prior to the
+// introduction of the test fixtures. Tests that are being converted to use fixtures directly
+// rather than through the testJava...() methods should avoid using this and instead use the
+// various preparers directly, using android.GroupFixturePreparers(...) to group them when
+// necessary.
+//
+// deprecated
+var prepareForJavaTest = android.GroupFixturePreparers(
+ genrule.PrepareForTestWithGenRuleBuildComponents,
+ // Get the CC build components but not default modules.
+ cc.PrepareForTestWithCcBuildComponents,
+ // Include all the default java modules.
+ PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithOverlayBuildComponents,
+ python.PrepareForTestWithPythonBuildComponents,
+ android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+ ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
+ }),
+ PrepareForTestWithDexpreopt,
+)
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ os.Exit(m.Run())
}
-func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
- bp += dexpreopt.BpToolModulesForTest()
-
- config := TestConfig(buildDir, env, bp, fs)
-
- // Set up the global Once cache used for dexpreopt.GlobalSoongConfig, so that
- // it doesn't create a real one, which would fail.
- _ = dexpreopt.GlobalSoongConfigForTests(config)
-
- return config
-}
-
-func testContext(config android.Config) *android.TestContext {
-
- ctx := android.NewTestArchContext(config)
- RegisterJavaBuildComponents(ctx)
- RegisterAppBuildComponents(ctx)
- RegisterAppImportBuildComponents(ctx)
- RegisterAppSetBuildComponents(ctx)
- RegisterAARBuildComponents(ctx)
- RegisterGenRuleBuildComponents(ctx)
- RegisterRuntimeResourceOverlayBuildComponents(ctx)
- RegisterSystemModulesBuildComponents(ctx)
- ctx.RegisterModuleType("java_plugin", PluginFactory)
- ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
- ctx.RegisterModuleType("python_binary_host", python.PythonBinaryHostFactory)
- RegisterDocsBuildComponents(ctx)
- RegisterStubsBuildComponents(ctx)
- RegisterPrebuiltApisBuildComponents(ctx)
- RegisterSdkLibraryBuildComponents(ctx)
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- ctx.PreArchMutators(android.RegisterComponentsMutator)
-
- ctx.PreDepsMutators(python.RegisterPythonPreDepsMutators)
- ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
- ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(ctx.Context, OverlaySingletonFactory))
- ctx.RegisterPreSingletonType("sdk_versions", android.SingletonFactoryAdaptor(ctx.Context, sdkPreSingletonFactory))
-
- android.RegisterPrebuiltMutators(ctx)
-
- // Register module types and mutators from cc needed for JNI testing
- cc.RegisterRequiredBuildComponentsForTest(ctx)
-
- dexpreopt.RegisterToolModulesForTest(ctx)
-
- ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator).Parallel()
- })
-
- return ctx
-}
-
-func run(t *testing.T, ctx *android.TestContext, config android.Config) {
- t.Helper()
-
- pathCtx := android.PathContextForTesting(config)
- dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
-
- ctx.Register()
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
-}
-
+// testJavaError is a legacy way of running tests of java modules that expect errors.
+//
+// See testJava for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
func testJavaError(t *testing.T, pattern string, bp string) (*android.TestContext, android.Config) {
t.Helper()
- return testJavaErrorWithConfig(t, pattern, testConfig(nil, bp, nil))
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest, dexpreopt.PrepareForTestByEnablingDexpreopt).
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
+ RunTestWithBp(t, bp)
+ return result.TestContext, result.Config
}
-func testJavaErrorWithConfig(t *testing.T, pattern string, config android.Config) (*android.TestContext, android.Config) {
+// testJavaWithFS runs tests using the prepareForJavaTest
+//
+// See testJava for an explanation as to how to stop using this deprecated method.
+//
+// deprecated
+func testJavaWithFS(t *testing.T, bp string, fs android.MockFS) (*android.TestContext, android.Config) {
t.Helper()
- ctx := testContext(config)
-
- pathCtx := android.PathContextForTesting(config)
- dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
-
- ctx.Register()
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
- if len(errs) > 0 {
- android.FailIfNoMatchingErrors(t, pattern, errs)
- return ctx, config
- }
- _, errs = ctx.PrepareBuildActions(config)
- if len(errs) > 0 {
- android.FailIfNoMatchingErrors(t, pattern, errs)
- return ctx, config
- }
-
- t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
-
- return ctx, config
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest, fs.AddToFixture()).RunTestWithBp(t, bp)
+ return result.TestContext, result.Config
}
-func testJavaWithFS(t *testing.T, bp string, fs map[string][]byte) (*android.TestContext, android.Config) {
- t.Helper()
- return testJavaWithConfig(t, testConfig(nil, bp, fs))
-}
-
+// testJava runs tests using the prepareForJavaTest
+//
+// Do not add any new usages of this, instead use the prepareForJavaTest directly as it makes it
+// much easier to customize the test behavior.
+//
+// If it is necessary to customize the behavior of an existing test that uses this then please first
+// convert the test to using prepareForJavaTest first and then in a following change add the
+// appropriate fixture preparers. Keeping the conversion change separate makes it easy to verify
+// that it did not change the test behavior unexpectedly.
+//
+// deprecated
func testJava(t *testing.T, bp string) (*android.TestContext, android.Config) {
t.Helper()
- return testJavaWithFS(t, bp, nil)
+ result := prepareForJavaTest.RunTestWithBp(t, bp)
+ return result.TestContext, result.Config
}
-func testJavaWithConfig(t *testing.T, config android.Config) (*android.TestContext, android.Config) {
- t.Helper()
- ctx := testContext(config)
- run(t, ctx, config)
-
- return ctx, config
-}
-
-func moduleToPath(name string) string {
+// defaultModuleToPath constructs a path to the turbine generate jar for a default test module that
+// is defined in PrepareForIntegrationTestWithJava
+func defaultModuleToPath(name string) string {
switch {
case name == `""`:
return name
case strings.HasSuffix(name, ".jar"):
return name
default:
- return filepath.Join(buildDir, ".intermediates", name, "android_common", "turbine-combined", name+".jar")
+ return filepath.Join("out", "soong", ".intermediates", defaultJavaDir, name, "android_common", "turbine-combined", name+".jar")
}
}
+// Test that the PrepareForTestWithJavaDefaultModules provides all the files that it uses by
+// running it in a fixture that requires all source files to exist.
+func TestPrepareForTestWithJavaDefaultModules(t *testing.T) {
+ android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.PrepareForTestDisallowNonExistentPaths,
+ ).RunTest(t)
+}
+
func TestJavaLinkType(t *testing.T) {
testJava(t, `
java_library {
@@ -297,16 +241,12 @@
}
baz := ctx.ModuleForTests("baz", "android_common").Rule("javac").Output.String()
- barTurbine := filepath.Join(buildDir, ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
- bazTurbine := filepath.Join(buildDir, ".intermediates", "baz", "android_common", "turbine-combined", "baz.jar")
+ barTurbine := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
+ bazTurbine := filepath.Join("out", "soong", ".intermediates", "baz", "android_common", "turbine-combined", "baz.jar")
- if !strings.Contains(javac.Args["classpath"], barTurbine) {
- t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], barTurbine)
- }
+ android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], barTurbine)
- if !strings.Contains(javac.Args["classpath"], bazTurbine) {
- t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], bazTurbine)
- }
+ android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], bazTurbine)
if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != baz {
t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, baz)
@@ -444,13 +384,19 @@
}
`
- config := testConfig(nil, bp, nil)
- config.TestProductVariables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
+ errorHandler := android.FixtureExpectsNoErrors
if enforce {
- testJavaErrorWithConfig(t, "sdk_version must have a value when the module is located at vendor or product", config)
- } else {
- testJavaWithConfig(t, config)
+ errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern("sdk_version must have a value when the module is located at vendor or product")
}
+
+ android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
+ }),
+ ).
+ ExtendWithErrorHandler(errorHandler).
+ RunTestWithBp(t, bp)
}
}
@@ -528,13 +474,16 @@
srcs: ["b.java"],
}
`
- config := testConfig(nil, bp, nil)
- config.TestProductVariables.MinimizeJavaDebugInfo = proptools.BoolPtr(true)
- ctx, _ := testJavaWithConfig(t, config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.MinimizeJavaDebugInfo = proptools.BoolPtr(true)
+ }),
+ ).RunTestWithBp(t, bp)
// first, check that the -g flag is added to target modules
- targetLibrary := ctx.ModuleForTests("target_library", "android_common")
+ targetLibrary := result.ModuleForTests("target_library", "android_common")
targetJavaFlags := targetLibrary.Module().VariablesForTests()["javacFlags"]
if !strings.Contains(targetJavaFlags, "-g:source,lines") {
t.Errorf("target library javac flags %v should contain "+
@@ -543,7 +492,7 @@
// check that -g is not overridden for host modules
buildOS := android.BuildOs.String()
- hostBinary := ctx.ModuleForTests("host_binary", buildOS+"_common")
+ hostBinary := result.ModuleForTests("host_binary", buildOS+"_common")
hostJavaFlags := hostBinary.Module().VariablesForTests()["javacFlags"]
if strings.Contains(hostJavaFlags, "-g:source,lines") {
t.Errorf("java_binary_host javac flags %v should not have "+
@@ -631,11 +580,9 @@
t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, bazJar.String())
}
- bazDexJar := bazModule.Module().(*Import).DexJarBuildPath().String()
- expectedDexJar := buildDir + "/.intermediates/baz/android_common/dex/baz.jar"
- if bazDexJar != expectedDexJar {
- t.Errorf("baz dex jar build path expected %q, got %q", expectedDexJar, bazDexJar)
- }
+ bazDexJar := bazModule.Module().(*Import).DexJarBuildPath()
+ expectedDexJar := "out/soong/.intermediates/baz/android_common/dex/baz.jar"
+ android.AssertPathRelativeToTopEquals(t, "baz dex jar build path", expectedDexJar, bazDexJar)
ctx.ModuleForTests("qux", "android_common").Rule("Cp")
}
@@ -664,7 +611,7 @@
}
t.Run("empty/missing directory", func(t *testing.T) {
- test(t, "empty-directory", []string{})
+ test(t, "empty-directory", nil)
})
t.Run("non-empty set of sources", func(t *testing.T) {
@@ -676,7 +623,7 @@
}
func TestJavaSdkLibraryImport(t *testing.T) {
- ctx, _ := testJava(t, `
+ result := prepareForJavaTest.RunTestWithBp(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -714,16 +661,14 @@
`)
for _, scope := range []string{"", ".system", ".test"} {
- fooModule := ctx.ModuleForTests("foo"+scope, "android_common")
+ fooModule := result.ModuleForTests("foo"+scope, "android_common")
javac := fooModule.Rule("javac")
- sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs"+scope, "android_common").Rule("combineJar").Output
- if !strings.Contains(javac.Args["classpath"], sdklibStubsJar.String()) {
- t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], sdklibStubsJar.String())
- }
+ sdklibStubsJar := result.ModuleForTests("sdklib.stubs"+scope, "android_common").Rule("combineJar").Output
+ android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], sdklibStubsJar.String())
}
- CheckModuleDependencies(t, ctx, "sdklib", "android_common", []string{
+ CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
`prebuilt_sdklib.stubs`,
`prebuilt_sdklib.stubs.source.test`,
`prebuilt_sdklib.stubs.system`,
@@ -732,7 +677,11 @@
}
func TestJavaSdkLibraryImport_WithSource(t *testing.T) {
- ctx, _ := testJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("sdklib"),
+ ).RunTestWithBp(t, `
java_sdk_library {
name: "sdklib",
srcs: ["a.java"],
@@ -751,7 +700,7 @@
}
`)
- CheckModuleDependencies(t, ctx, "sdklib", "android_common", []string{
+ CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
`dex2oatd`,
`prebuilt_sdklib`,
`sdklib.impl`,
@@ -760,7 +709,7 @@
`sdklib.xml`,
})
- CheckModuleDependencies(t, ctx, "prebuilt_sdklib", "android_common", []string{
+ CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{
`prebuilt_sdklib.stubs`,
`sdklib.impl`,
// This should be prebuilt_sdklib.stubs but is set to sdklib.stubs because the
@@ -771,7 +720,11 @@
}
func TestJavaSdkLibraryImport_Preferred(t *testing.T) {
- ctx, _ := testJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("sdklib"),
+ ).RunTestWithBp(t, `
java_sdk_library {
name: "sdklib",
srcs: ["a.java"],
@@ -791,7 +744,7 @@
}
`)
- CheckModuleDependencies(t, ctx, "sdklib", "android_common", []string{
+ CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
`dex2oatd`,
`prebuilt_sdklib`,
`sdklib.impl`,
@@ -800,7 +753,7 @@
`sdklib.xml`,
})
- CheckModuleDependencies(t, ctx, "prebuilt_sdklib", "android_common", []string{
+ CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{
`prebuilt_sdklib.stubs`,
`sdklib.impl`,
`sdklib.xml`,
@@ -831,7 +784,7 @@
allowList []string
}
- createTestConfig := func(info testConfigInfo) android.Config {
+ createPreparer := func(info testConfigInfo) android.FixturePreparer {
bpFileTemplate := `
java_library {
name: "foo",
@@ -854,98 +807,75 @@
info.libraryType,
partitionToBpOption(info.toPartition))
- config := testConfig(nil, bpFile, nil)
- configVariables := config.TestProductVariables
-
- configVariables.EnforceProductPartitionInterface = proptools.BoolPtr(info.enforceProductInterface)
- if info.enforceVendorInterface {
- configVariables.DeviceVndkVersion = proptools.StringPtr("current")
- }
- configVariables.EnforceInterPartitionJavaSdkLibrary = proptools.BoolPtr(info.enforceJavaSdkLibraryCheck)
- configVariables.InterPartitionJavaLibraryAllowList = info.allowList
-
- return config
+ return android.GroupFixturePreparers(
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("bar"),
+ android.FixtureWithRootAndroidBp(bpFile),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.EnforceProductPartitionInterface = proptools.BoolPtr(info.enforceProductInterface)
+ if info.enforceVendorInterface {
+ variables.DeviceVndkVersion = proptools.StringPtr("current")
+ }
+ variables.EnforceInterPartitionJavaSdkLibrary = proptools.BoolPtr(info.enforceJavaSdkLibraryCheck)
+ variables.InterPartitionJavaLibraryAllowList = info.allowList
+ }),
+ )
}
- isValidDependency := func(configInfo testConfigInfo) bool {
- if configInfo.enforceVendorInterface == false {
- return true
- }
-
- if configInfo.enforceJavaSdkLibraryCheck == false {
- return true
- }
-
- if inList("bar", configInfo.allowList) {
- return true
- }
-
- if configInfo.libraryType == "java_library" {
- if configInfo.fromPartition != configInfo.toPartition {
- if !configInfo.enforceProductInterface &&
- ((configInfo.fromPartition == "system" && configInfo.toPartition == "product") ||
- (configInfo.fromPartition == "product" && configInfo.toPartition == "system")) {
- return true
- }
- return false
+ runTest := func(t *testing.T, info testConfigInfo, expectedErrorPattern string) {
+ t.Run(fmt.Sprintf("%v", info), func(t *testing.T) {
+ errorHandler := android.FixtureExpectsNoErrors
+ if expectedErrorPattern != "" {
+ errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorPattern)
}
- }
-
- return true
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
+ createPreparer(info),
+ ).
+ ExtendWithErrorHandler(errorHandler).
+ RunTest(t)
+ })
}
errorMessage := "is not allowed across the partitions"
- allPartitionCombinations := func() [][2]string {
- var result [][2]string
- partitions := []string{"system", "vendor", "product"}
+ runTest(t, testConfigInfo{
+ libraryType: "java_library",
+ fromPartition: "product",
+ toPartition: "system",
+ enforceVendorInterface: true,
+ enforceProductInterface: true,
+ enforceJavaSdkLibraryCheck: false,
+ }, "")
- for _, fromPartition := range partitions {
- for _, toPartition := range partitions {
- result = append(result, [2]string{fromPartition, toPartition})
- }
- }
+ runTest(t, testConfigInfo{
+ libraryType: "java_library",
+ fromPartition: "product",
+ toPartition: "system",
+ enforceVendorInterface: true,
+ enforceProductInterface: false,
+ enforceJavaSdkLibraryCheck: true,
+ }, "")
- return result
- }
+ runTest(t, testConfigInfo{
+ libraryType: "java_library",
+ fromPartition: "product",
+ toPartition: "system",
+ enforceVendorInterface: true,
+ enforceProductInterface: true,
+ enforceJavaSdkLibraryCheck: true,
+ }, errorMessage)
- allFlagCombinations := func() [][3]bool {
- var result [][3]bool
- flagValues := [2]bool{false, true}
+ runTest(t, testConfigInfo{
+ libraryType: "java_library",
+ fromPartition: "vendor",
+ toPartition: "system",
+ enforceVendorInterface: true,
+ enforceProductInterface: true,
+ enforceJavaSdkLibraryCheck: true,
+ }, errorMessage)
- for _, vendorInterface := range flagValues {
- for _, productInterface := range flagValues {
- for _, enableEnforce := range flagValues {
- result = append(result, [3]bool{vendorInterface, productInterface, enableEnforce})
- }
- }
- }
-
- return result
- }
-
- for _, libraryType := range []string{"java_library", "java_sdk_library"} {
- for _, partitionValues := range allPartitionCombinations() {
- for _, flagValues := range allFlagCombinations() {
- testInfo := testConfigInfo{
- libraryType: libraryType,
- fromPartition: partitionValues[0],
- toPartition: partitionValues[1],
- enforceVendorInterface: flagValues[0],
- enforceProductInterface: flagValues[1],
- enforceJavaSdkLibraryCheck: flagValues[2],
- }
-
- if isValidDependency(testInfo) {
- testJavaWithConfig(t, createTestConfig(testInfo))
- } else {
- testJavaErrorWithConfig(t, errorMessage, createTestConfig(testInfo))
- }
- }
- }
- }
-
- testJavaWithConfig(t, createTestConfig(testConfigInfo{
+ runTest(t, testConfigInfo{
libraryType: "java_library",
fromPartition: "vendor",
toPartition: "system",
@@ -953,17 +883,43 @@
enforceProductInterface: true,
enforceJavaSdkLibraryCheck: true,
allowList: []string{"bar"},
- }))
+ }, "")
- testJavaErrorWithConfig(t, errorMessage, createTestConfig(testConfigInfo{
+ runTest(t, testConfigInfo{
libraryType: "java_library",
fromPartition: "vendor",
+ toPartition: "product",
+ enforceVendorInterface: true,
+ enforceProductInterface: true,
+ enforceJavaSdkLibraryCheck: true,
+ }, errorMessage)
+
+ runTest(t, testConfigInfo{
+ libraryType: "java_sdk_library",
+ fromPartition: "product",
+ toPartition: "system",
+ enforceVendorInterface: true,
+ enforceProductInterface: true,
+ enforceJavaSdkLibraryCheck: true,
+ }, "")
+
+ runTest(t, testConfigInfo{
+ libraryType: "java_sdk_library",
+ fromPartition: "vendor",
toPartition: "system",
enforceVendorInterface: true,
enforceProductInterface: true,
enforceJavaSdkLibraryCheck: true,
- allowList: []string{"foo"},
- }))
+ }, "")
+
+ runTest(t, testConfigInfo{
+ libraryType: "java_sdk_library",
+ fromPartition: "vendor",
+ toPartition: "product",
+ enforceVendorInterface: true,
+ enforceProductInterface: true,
+ enforceJavaSdkLibraryCheck: true,
+ }, "")
}
func TestDefaults(t *testing.T) {
@@ -1015,7 +971,7 @@
t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
}
- barTurbine := filepath.Join(buildDir, ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
+ barTurbine := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
if !strings.Contains(javac.Args["classpath"], barTurbine) {
t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], barTurbine)
}
@@ -1214,6 +1170,107 @@
}
}
+func TestJavaLint(t *testing.T) {
+ ctx, _ := testJavaWithFS(t, `
+ java_library {
+ name: "foo",
+ srcs: [
+ "a.java",
+ "b.java",
+ "c.java",
+ ],
+ min_sdk_version: "29",
+ sdk_version: "system_current",
+ }
+ `, map[string][]byte{
+ "lint-baseline.xml": nil,
+ })
+
+ foo := ctx.ModuleForTests("foo", "android_common")
+
+ sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
+ if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml") {
+ t.Error("did not pass --baseline flag")
+ }
+}
+
+func TestJavaLintWithoutBaseline(t *testing.T) {
+ ctx, _ := testJavaWithFS(t, `
+ java_library {
+ name: "foo",
+ srcs: [
+ "a.java",
+ "b.java",
+ "c.java",
+ ],
+ min_sdk_version: "29",
+ sdk_version: "system_current",
+ }
+ `, map[string][]byte{})
+
+ foo := ctx.ModuleForTests("foo", "android_common")
+
+ sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
+ if strings.Contains(*sboxProto.Commands[0].Command, "--baseline") {
+ t.Error("passed --baseline flag for non existent file")
+ }
+}
+
+func TestJavaLintRequiresCustomLintFileToExist(t *testing.T) {
+ android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.PrepareForTestDisallowNonExistentPaths,
+ ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern([]string{`source path "mybaseline.xml" does not exist`})).
+ RunTestWithBp(t, `
+ java_library {
+ name: "foo",
+ srcs: [
+ ],
+ min_sdk_version: "29",
+ sdk_version: "system_current",
+ lint: {
+ baseline_filename: "mybaseline.xml",
+ },
+ }
+ `)
+}
+
+func TestJavaLintUsesCorrectBpConfig(t *testing.T) {
+ ctx, _ := testJavaWithFS(t, `
+ java_library {
+ name: "foo",
+ srcs: [
+ "a.java",
+ "b.java",
+ "c.java",
+ ],
+ min_sdk_version: "29",
+ sdk_version: "system_current",
+ lint: {
+ error_checks: ["SomeCheck"],
+ baseline_filename: "mybaseline.xml",
+ },
+ }
+ `, map[string][]byte{
+ "mybaseline.xml": nil,
+ })
+
+ foo := ctx.ModuleForTests("foo", "android_common")
+
+ sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
+ if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline mybaseline.xml") {
+ t.Error("did not use the correct file for baseline")
+ }
+
+ if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check NewApi") {
+ t.Error("should check NewApi errors")
+ }
+
+ if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check SomeCheck") {
+ t.Error("should combine NewApi errors with SomeCheck errors")
+ }
+}
+
func TestGeneratedSources(t *testing.T) {
ctx, _ := testJavaWithFS(t, `
java_library {
@@ -1251,7 +1308,9 @@
}
func TestTurbine(t *testing.T) {
- ctx, _ := testJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{"14": {"foo"}})).
+ RunTestWithBp(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -1273,30 +1332,20 @@
}
`)
- fooTurbine := ctx.ModuleForTests("foo", "android_common").Rule("turbine")
- barTurbine := ctx.ModuleForTests("bar", "android_common").Rule("turbine")
- barJavac := ctx.ModuleForTests("bar", "android_common").Rule("javac")
- barTurbineCombined := ctx.ModuleForTests("bar", "android_common").Description("for turbine")
- bazJavac := ctx.ModuleForTests("baz", "android_common").Rule("javac")
+ fooTurbine := result.ModuleForTests("foo", "android_common").Rule("turbine")
+ barTurbine := result.ModuleForTests("bar", "android_common").Rule("turbine")
+ barJavac := result.ModuleForTests("bar", "android_common").Rule("javac")
+ barTurbineCombined := result.ModuleForTests("bar", "android_common").Description("for turbine")
+ bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac")
- if len(fooTurbine.Inputs) != 1 || fooTurbine.Inputs[0].String() != "a.java" {
- t.Errorf(`foo inputs %v != ["a.java"]`, fooTurbine.Inputs)
- }
+ android.AssertPathsRelativeToTopEquals(t, "foo inputs", []string{"a.java"}, fooTurbine.Inputs)
- fooHeaderJar := filepath.Join(buildDir, ".intermediates", "foo", "android_common", "turbine-combined", "foo.jar")
- if !strings.Contains(barTurbine.Args["classpath"], fooHeaderJar) {
- t.Errorf("bar turbine classpath %v does not contain %q", barTurbine.Args["classpath"], fooHeaderJar)
- }
- if !strings.Contains(barJavac.Args["classpath"], fooHeaderJar) {
- t.Errorf("bar javac classpath %v does not contain %q", barJavac.Args["classpath"], fooHeaderJar)
- }
- if len(barTurbineCombined.Inputs) != 2 || barTurbineCombined.Inputs[1].String() != fooHeaderJar {
- t.Errorf("bar turbine combineJar inputs %v does not contain %q", barTurbineCombined.Inputs, fooHeaderJar)
- }
- if !strings.Contains(bazJavac.Args["classpath"], "prebuilts/sdk/14/public/android.jar") {
- t.Errorf("baz javac classpath %v does not contain %q", bazJavac.Args["classpath"],
- "prebuilts/sdk/14/public/android.jar")
- }
+ fooHeaderJar := filepath.Join("out", "soong", ".intermediates", "foo", "android_common", "turbine-combined", "foo.jar")
+ barTurbineJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar")
+ android.AssertStringDoesContain(t, "bar turbine classpath", barTurbine.Args["classpath"], fooHeaderJar)
+ android.AssertStringDoesContain(t, "bar javac classpath", barJavac.Args["classpath"], fooHeaderJar)
+ android.AssertPathsRelativeToTopEquals(t, "bar turbine combineJar", []string{barTurbineJar, fooHeaderJar}, barTurbineCombined.Inputs)
+ android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "prebuilts/sdk/14/public/android.jar")
}
func TestSharding(t *testing.T) {
@@ -1308,7 +1357,7 @@
}
`)
- barHeaderJar := filepath.Join(buildDir, ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
+ barHeaderJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
for i := 0; i < 3; i++ {
barJavac := ctx.ModuleForTests("bar", "android_common").Description("javac" + strconv.Itoa(i))
if !strings.Contains(barJavac.Args["classpath"], barHeaderJar) {
@@ -1317,246 +1366,6 @@
}
}
-func TestDroiddoc(t *testing.T) {
- ctx, _ := testJavaWithFS(t, `
- droiddoc_exported_dir {
- name: "droiddoc-templates-sdk",
- path: ".",
- }
- filegroup {
- name: "bar-doc-aidl-srcs",
- srcs: ["bar-doc/IBar.aidl"],
- path: "bar-doc",
- }
- droidstubs {
- name: "bar-stubs",
- srcs: [
- "bar-doc/a.java",
- ],
- exclude_srcs: [
- "bar-doc/b.java"
- ],
- api_levels_annotations_dirs: [
- "droiddoc-templates-sdk",
- ],
- api_levels_annotations_enabled: true,
- }
- droiddoc {
- name: "bar-doc",
- srcs: [
- ":bar-stubs",
- "bar-doc/IFoo.aidl",
- ":bar-doc-aidl-srcs",
- ],
- custom_template: "droiddoc-templates-sdk",
- hdf: [
- "android.whichdoc offline",
- ],
- knowntags: [
- "bar-doc/known_oj_tags.txt",
- ],
- proofread_file: "libcore-proofread.txt",
- todo_file: "libcore-docs-todo.html",
- flags: ["-offlinemode -title \"libcore\""],
- }
- `,
- map[string][]byte{
- "bar-doc/a.java": nil,
- "bar-doc/b.java": nil,
- })
- barStubs := ctx.ModuleForTests("bar-stubs", "android_common")
- barStubsOutputs, err := barStubs.Module().(*Droidstubs).OutputFiles("")
- if err != nil {
- t.Errorf("Unexpected error %q retrieving \"bar-stubs\" output file", err)
- }
- if len(barStubsOutputs) != 1 {
- t.Errorf("Expected one output from \"bar-stubs\" got %s", barStubsOutputs)
- }
-
- barStubsOutput := barStubsOutputs[0]
- barDoc := ctx.ModuleForTests("bar-doc", "android_common")
- javaDoc := barDoc.Rule("javadoc")
- if g, w := javaDoc.Implicits.Strings(), barStubsOutput.String(); !inList(w, g) {
- t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
- }
-
- expected := "-sourcepath " + buildDir + "/.intermediates/bar-doc/android_common/srcjars "
- if !strings.Contains(javaDoc.RuleParams.Command, expected) {
- t.Errorf("bar-doc command does not contain flag %q, but should\n%q", expected, javaDoc.RuleParams.Command)
- }
-
- aidl := barDoc.Rule("aidl")
- if g, w := javaDoc.Implicits.Strings(), aidl.Output.String(); !inList(w, g) {
- t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
- }
-
- if g, w := aidl.Implicits.Strings(), []string{"bar-doc/IBar.aidl", "bar-doc/IFoo.aidl"}; !reflect.DeepEqual(w, g) {
- t.Errorf("aidl inputs must be %q, but was %q", w, g)
- }
-}
-
-func TestDroiddocArgsAndFlagsCausesError(t *testing.T) {
- testJavaError(t, "flags is set. Cannot set args", `
- droiddoc_exported_dir {
- name: "droiddoc-templates-sdk",
- path: ".",
- }
- filegroup {
- name: "bar-doc-aidl-srcs",
- srcs: ["bar-doc/IBar.aidl"],
- path: "bar-doc",
- }
- droidstubs {
- name: "bar-stubs",
- srcs: [
- "bar-doc/a.java",
- ],
- exclude_srcs: [
- "bar-doc/b.java"
- ],
- api_levels_annotations_dirs: [
- "droiddoc-templates-sdk",
- ],
- api_levels_annotations_enabled: true,
- }
- droiddoc {
- name: "bar-doc",
- srcs: [
- ":bar-stubs",
- "bar-doc/IFoo.aidl",
- ":bar-doc-aidl-srcs",
- ],
- custom_template: "droiddoc-templates-sdk",
- hdf: [
- "android.whichdoc offline",
- ],
- knowntags: [
- "bar-doc/known_oj_tags.txt",
- ],
- proofread_file: "libcore-proofread.txt",
- todo_file: "libcore-docs-todo.html",
- flags: ["-offlinemode -title \"libcore\""],
- args: "-offlinemode -title \"libcore\"",
- }
- `)
-}
-
-func TestDroidstubs(t *testing.T) {
- ctx, _ := testJavaWithFS(t, `
- droiddoc_exported_dir {
- name: "droiddoc-templates-sdk",
- path: ".",
- }
-
- droidstubs {
- name: "bar-stubs",
- srcs: ["bar-doc/a.java"],
- api_levels_annotations_dirs: ["droiddoc-templates-sdk"],
- api_levels_annotations_enabled: true,
- }
-
- droidstubs {
- name: "bar-stubs-other",
- srcs: ["bar-doc/a.java"],
- high_mem: true,
- api_levels_annotations_dirs: ["droiddoc-templates-sdk"],
- api_levels_annotations_enabled: true,
- api_levels_jar_filename: "android.other.jar",
- }
- `,
- map[string][]byte{
- "bar-doc/a.java": nil,
- })
- testcases := []struct {
- moduleName string
- expectedJarFilename string
- high_mem bool
- }{
- {
- moduleName: "bar-stubs",
- expectedJarFilename: "android.jar",
- high_mem: false,
- },
- {
- moduleName: "bar-stubs-other",
- expectedJarFilename: "android.other.jar",
- high_mem: true,
- },
- }
- for _, c := range testcases {
- m := ctx.ModuleForTests(c.moduleName, "android_common")
- metalava := m.Rule("metalava")
- rp := metalava.RuleParams
- expected := "--android-jar-pattern ./%/public/" + c.expectedJarFilename
- if actual := rp.Command; !strings.Contains(actual, expected) {
- t.Errorf("For %q, expected metalava argument %q, but was not found %q", c.moduleName, expected, actual)
- }
-
- if actual := rp.Pool != nil && strings.Contains(rp.Pool.String(), "highmem"); actual != c.high_mem {
- t.Errorf("Expected %q high_mem to be %v, was %v", c.moduleName, c.high_mem, actual)
- }
- }
-}
-
-func TestDroidstubsWithSystemModules(t *testing.T) {
- ctx, _ := testJava(t, `
- droidstubs {
- name: "stubs-source-system-modules",
- srcs: [
- "bar-doc/a.java",
- ],
- sdk_version: "none",
- system_modules: "source-system-modules",
- }
-
- java_library {
- name: "source-jar",
- srcs: [
- "a.java",
- ],
- }
-
- java_system_modules {
- name: "source-system-modules",
- libs: ["source-jar"],
- }
-
- droidstubs {
- name: "stubs-prebuilt-system-modules",
- srcs: [
- "bar-doc/a.java",
- ],
- sdk_version: "none",
- system_modules: "prebuilt-system-modules",
- }
-
- java_import {
- name: "prebuilt-jar",
- jars: ["a.jar"],
- }
-
- java_system_modules_import {
- name: "prebuilt-system-modules",
- libs: ["prebuilt-jar"],
- }
- `)
-
- checkSystemModulesUseByDroidstubs(t, ctx, "stubs-source-system-modules", "source-jar.jar")
-
- checkSystemModulesUseByDroidstubs(t, ctx, "stubs-prebuilt-system-modules", "prebuilt-jar.jar")
-}
-
-func checkSystemModulesUseByDroidstubs(t *testing.T, ctx *android.TestContext, moduleName string, systemJar string) {
- metalavaRule := ctx.ModuleForTests(moduleName, "android_common").Rule("metalava")
- var systemJars []string
- for _, i := range metalavaRule.Implicits {
- systemJars = append(systemJars, i.Base())
- }
- if len(systemJars) < 1 || systemJars[0] != systemJar {
- t.Errorf("inputs of %q must be []string{%q}, but was %#v.", moduleName, systemJar, systemJars)
- }
-}
-
func TestJarGenrules(t *testing.T) {
ctx, _ := testJava(t, `
java_library {
@@ -1638,7 +1447,7 @@
}
func TestJavaLibrary(t *testing.T) {
- config := testConfig(nil, "", map[string][]byte{
+ testJavaWithFS(t, "", map[string][]byte{
"libcore/Android.bp": []byte(`
java_library {
name: "core",
@@ -1650,14 +1459,12 @@
name: "core-jar",
srcs: [":core{.jar}"],
}
-`),
+ `),
})
- ctx := testContext(config)
- run(t, ctx, config)
}
func TestJavaImport(t *testing.T) {
- config := testConfig(nil, "", map[string][]byte{
+ testJavaWithFS(t, "", map[string][]byte{
"libcore/Android.bp": []byte(`
java_import {
name: "core",
@@ -1668,14 +1475,20 @@
name: "core-jar",
srcs: [":core{.jar}"],
}
-`),
+ `),
})
- ctx := testContext(config)
- run(t, ctx, config)
}
func TestJavaSdkLibrary(t *testing.T) {
- ctx, _ := testJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithPrebuiltApis(map[string][]string{
+ "28": {"foo"},
+ "29": {"foo"},
+ "30": {"bar", "barney", "baz", "betty", "foo", "fred", "quuz", "wilma"},
+ }),
+ ).RunTestWithBp(t, `
droiddoc_exported_dir {
name: "droiddoc-templates-sdk",
path: ".",
@@ -1752,68 +1565,52 @@
`)
// check the existence of the internal modules
- ctx.ModuleForTests("foo", "android_common")
- ctx.ModuleForTests(apiScopePublic.stubsLibraryModuleName("foo"), "android_common")
- ctx.ModuleForTests(apiScopeSystem.stubsLibraryModuleName("foo"), "android_common")
- ctx.ModuleForTests(apiScopeTest.stubsLibraryModuleName("foo"), "android_common")
- ctx.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo"), "android_common")
- ctx.ModuleForTests(apiScopeSystem.stubsSourceModuleName("foo"), "android_common")
- ctx.ModuleForTests(apiScopeTest.stubsSourceModuleName("foo"), "android_common")
- ctx.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
- ctx.ModuleForTests("foo.api.public.28", "")
- ctx.ModuleForTests("foo.api.system.28", "")
- ctx.ModuleForTests("foo.api.test.28", "")
+ result.ModuleForTests("foo", "android_common")
+ result.ModuleForTests(apiScopePublic.stubsLibraryModuleName("foo"), "android_common")
+ result.ModuleForTests(apiScopeSystem.stubsLibraryModuleName("foo"), "android_common")
+ result.ModuleForTests(apiScopeTest.stubsLibraryModuleName("foo"), "android_common")
+ result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo"), "android_common")
+ result.ModuleForTests(apiScopeSystem.stubsSourceModuleName("foo"), "android_common")
+ result.ModuleForTests(apiScopeTest.stubsSourceModuleName("foo"), "android_common")
+ result.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
+ result.ModuleForTests("foo.api.public.28", "")
+ result.ModuleForTests("foo.api.system.28", "")
+ result.ModuleForTests("foo.api.test.28", "")
- bazJavac := ctx.ModuleForTests("baz", "android_common").Rule("javac")
+ bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac")
// tests if baz is actually linked to the stubs lib
- if !strings.Contains(bazJavac.Args["classpath"], "foo.stubs.system.jar") {
- t.Errorf("baz javac classpath %v does not contain %q", bazJavac.Args["classpath"],
- "foo.stubs.system.jar")
- }
+ android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.system.jar")
// ... and not to the impl lib
- if strings.Contains(bazJavac.Args["classpath"], "foo.jar") {
- t.Errorf("baz javac classpath %v should not contain %q", bazJavac.Args["classpath"],
- "foo.jar")
- }
+ android.AssertStringDoesNotContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.jar")
// test if baz is not linked to the system variant of foo
- if strings.Contains(bazJavac.Args["classpath"], "foo.stubs.jar") {
- t.Errorf("baz javac classpath %v should not contain %q", bazJavac.Args["classpath"],
- "foo.stubs.jar")
- }
+ android.AssertStringDoesNotContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.jar")
- bazTestJavac := ctx.ModuleForTests("baz-test", "android_common").Rule("javac")
+ bazTestJavac := result.ModuleForTests("baz-test", "android_common").Rule("javac")
// tests if baz-test is actually linked to the test stubs lib
- if !strings.Contains(bazTestJavac.Args["classpath"], "foo.stubs.test.jar") {
- t.Errorf("baz-test javac classpath %v does not contain %q", bazTestJavac.Args["classpath"],
- "foo.stubs.test.jar")
- }
+ android.AssertStringDoesContain(t, "baz-test javac classpath", bazTestJavac.Args["classpath"], "foo.stubs.test.jar")
- baz29Javac := ctx.ModuleForTests("baz-29", "android_common").Rule("javac")
+ baz29Javac := result.ModuleForTests("baz-29", "android_common").Rule("javac")
// tests if baz-29 is actually linked to the system 29 stubs lib
- if !strings.Contains(baz29Javac.Args["classpath"], "prebuilts/sdk/29/system/foo.jar") {
- t.Errorf("baz-29 javac classpath %v does not contain %q", baz29Javac.Args["classpath"],
- "prebuilts/sdk/29/system/foo.jar")
- }
+ android.AssertStringDoesContain(t, "baz-29 javac classpath", baz29Javac.Args["classpath"], "prebuilts/sdk/29/system/foo.jar")
- bazModule30Javac := ctx.ModuleForTests("baz-module-30", "android_common").Rule("javac")
+ bazModule30Javac := result.ModuleForTests("baz-module-30", "android_common").Rule("javac")
// tests if "baz-module-30" is actually linked to the module 30 stubs lib
- if !strings.Contains(bazModule30Javac.Args["classpath"], "prebuilts/sdk/30/module-lib/foo.jar") {
- t.Errorf("baz-module-30 javac classpath %v does not contain %q", bazModule30Javac.Args["classpath"],
- "prebuilts/sdk/30/module-lib/foo.jar")
- }
+ android.AssertStringDoesContain(t, "baz-module-30 javac classpath", bazModule30Javac.Args["classpath"], "prebuilts/sdk/30/module-lib/foo.jar")
// test if baz has exported SDK lib names foo and bar to qux
- qux := ctx.ModuleForTests("qux", "android_common")
+ qux := result.ModuleForTests("qux", "android_common")
if quxLib, ok := qux.Module().(*Library); ok {
sdkLibs := quxLib.ClassLoaderContexts().UsesLibs()
- if w := []string{"foo", "bar", "fred", "quuz"}; !reflect.DeepEqual(w, sdkLibs) {
- t.Errorf("qux should export %q but exports %q", w, sdkLibs)
- }
+ android.AssertDeepEquals(t, "qux exports", []string{"foo", "bar", "fred", "quuz"}, sdkLibs)
}
}
func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) {
- ctx, _ := testJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("sdklib"),
+ ).RunTestWithBp(t, `
java_sdk_library {
name: "sdklib",
srcs: ["a.java"],
@@ -1833,20 +1630,24 @@
`)
for _, implName := range []string{"sdklib", "sdklib.impl"} {
- implJavacCp := ctx.ModuleForTests(implName, "android_common").Rule("javac").Args["classpath"]
+ implJavacCp := result.ModuleForTests(implName, "android_common").Rule("javac").Args["classpath"]
if !strings.Contains(implJavacCp, "/foo.jar") || strings.Contains(implJavacCp, "/bar.jar") {
t.Errorf("%v javac classpath %v does not contain foo and not bar", implName, implJavacCp)
}
}
stubName := apiScopePublic.stubsLibraryModuleName("sdklib")
- stubsJavacCp := ctx.ModuleForTests(stubName, "android_common").Rule("javac").Args["classpath"]
+ stubsJavacCp := result.ModuleForTests(stubName, "android_common").Rule("javac").Args["classpath"]
if strings.Contains(stubsJavacCp, "/foo.jar") || !strings.Contains(stubsJavacCp, "/bar.jar") {
t.Errorf("stubs javac classpath %v does not contain bar and not foo", stubsJavacCp)
}
}
func TestJavaSdkLibrary_DoNotAccessImplWhenItIsNotBuilt(t *testing.T) {
- ctx, _ := testJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("foo"),
+ ).RunTestWithBp(t, `
java_sdk_library {
name: "foo",
srcs: ["a.java"],
@@ -1864,14 +1665,18 @@
`)
// The bar library should depend on the stubs jar.
- barLibrary := ctx.ModuleForTests("bar", "android_common").Rule("javac")
- if expected, actual := `^-classpath .*:/[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+ barLibrary := result.ModuleForTests("bar", "android_common").Rule("javac")
+ if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
t.Errorf("expected %q, found %#q", expected, actual)
}
}
func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) {
- testJava(t, `
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("foo"),
+ ).RunTestWithBp(t, `
java_sdk_library {
name: "foo",
srcs: ["a.java"],
@@ -1889,7 +1694,13 @@
}
func TestJavaSdkLibrary_AccessOutputFiles_MissingScope(t *testing.T) {
- testJavaError(t, `"foo" does not provide api scope system`, `
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("foo"),
+ ).
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`"foo" does not provide api scope system`)).
+ RunTestWithBp(t, `
java_sdk_library {
name: "foo",
srcs: ["a.java"],
@@ -1907,7 +1718,11 @@
}
func TestJavaSdkLibrary_Deps(t *testing.T) {
- ctx, _ := testJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("sdklib"),
+ ).RunTestWithBp(t, `
java_sdk_library {
name: "sdklib",
srcs: ["a.java"],
@@ -1919,7 +1734,7 @@
}
`)
- CheckModuleDependencies(t, ctx, "sdklib", "android_common", []string{
+ CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
`dex2oatd`,
`sdklib.impl`,
`sdklib.stubs`,
@@ -1929,7 +1744,7 @@
}
func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) {
- testJava(t, `
+ prepareForJavaTest.RunTestWithBp(t, `
java_sdk_library_import {
name: "foo",
public: {
@@ -1962,63 +1777,75 @@
`
t.Run("stubs.source", func(t *testing.T) {
- testJavaError(t, `stubs.source not available for api scope public`, bp+`
- java_library {
- name: "bar",
- srcs: [":foo{.public.stubs.source}"],
- java_resources: [
- ":foo{.public.api.txt}",
- ":foo{.public.removed-api.txt}",
- ],
- }
- `)
+ prepareForJavaTest.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`stubs.source not available for api scope public`)).
+ RunTestWithBp(t, bp+`
+ java_library {
+ name: "bar",
+ srcs: [":foo{.public.stubs.source}"],
+ java_resources: [
+ ":foo{.public.api.txt}",
+ ":foo{.public.removed-api.txt}",
+ ],
+ }
+ `)
})
t.Run("api.txt", func(t *testing.T) {
- testJavaError(t, `api.txt not available for api scope public`, bp+`
- java_library {
- name: "bar",
- srcs: ["a.java"],
- java_resources: [
- ":foo{.public.api.txt}",
- ],
- }
- `)
+ prepareForJavaTest.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`api.txt not available for api scope public`)).
+ RunTestWithBp(t, bp+`
+ java_library {
+ name: "bar",
+ srcs: ["a.java"],
+ java_resources: [
+ ":foo{.public.api.txt}",
+ ],
+ }
+ `)
})
t.Run("removed-api.txt", func(t *testing.T) {
- testJavaError(t, `removed-api.txt not available for api scope public`, bp+`
- java_library {
- name: "bar",
- srcs: ["a.java"],
- java_resources: [
- ":foo{.public.removed-api.txt}",
- ],
- }
- `)
+ prepareForJavaTest.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`removed-api.txt not available for api scope public`)).
+ RunTestWithBp(t, bp+`
+ java_library {
+ name: "bar",
+ srcs: ["a.java"],
+ java_resources: [
+ ":foo{.public.removed-api.txt}",
+ ],
+ }
+ `)
})
}
func TestJavaSdkLibrary_InvalidScopes(t *testing.T) {
- testJavaError(t, `module "foo": enabled api scope "system" depends on disabled scope "public"`, `
- java_sdk_library {
- name: "foo",
- srcs: ["a.java", "b.java"],
- api_packages: ["foo"],
- // Explicitly disable public to test the check that ensures the set of enabled
- // scopes is consistent.
- public: {
- enabled: false,
- },
- system: {
- enabled: true,
- },
- }
+ prepareForJavaTest.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo": enabled api scope "system" depends on disabled scope "public"`)).
+ RunTestWithBp(t, `
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java", "b.java"],
+ api_packages: ["foo"],
+ // Explicitly disable public to test the check that ensures the set of enabled
+ // scopes is consistent.
+ public: {
+ enabled: false,
+ },
+ system: {
+ enabled: true,
+ },
+ }
`)
}
func TestJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) {
- testJava(t, `
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("foo"),
+ ).RunTestWithBp(t, `
java_sdk_library {
name: "foo",
srcs: ["a.java", "b.java"],
@@ -2032,7 +1859,11 @@
}
func TestJavaSdkLibrary_ModuleLib(t *testing.T) {
- testJava(t, `
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("foo"),
+ ).RunTestWithBp(t, `
java_sdk_library {
name: "foo",
srcs: ["a.java", "b.java"],
@@ -2048,7 +1879,11 @@
}
func TestJavaSdkLibrary_SystemServer(t *testing.T) {
- testJava(t, `
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("foo"),
+ ).RunTestWithBp(t, `
java_sdk_library {
name: "foo",
srcs: ["a.java", "b.java"],
@@ -2064,26 +1899,32 @@
}
func TestJavaSdkLibrary_MissingScope(t *testing.T) {
- testJavaError(t, `requires api scope module-lib from foo but it only has \[\] available`, `
- java_sdk_library {
- name: "foo",
- srcs: ["a.java"],
- public: {
- enabled: false,
- },
- }
+ prepareForJavaTest.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`requires api scope module-lib from foo but it only has \[\] available`)).
+ RunTestWithBp(t, `
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java"],
+ public: {
+ enabled: false,
+ },
+ }
- java_library {
- name: "baz",
- srcs: ["a.java"],
- libs: ["foo"],
- sdk_version: "module_current",
- }
+ java_library {
+ name: "baz",
+ srcs: ["a.java"],
+ libs: ["foo"],
+ sdk_version: "module_current",
+ }
`)
}
func TestJavaSdkLibrary_FallbackScope(t *testing.T) {
- testJava(t, `
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("foo"),
+ ).RunTestWithBp(t, `
java_sdk_library {
name: "foo",
srcs: ["a.java"],
@@ -2103,7 +1944,11 @@
}
func TestJavaSdkLibrary_DefaultToStubs(t *testing.T) {
- ctx, _ := testJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("foo"),
+ ).RunTestWithBp(t, `
java_sdk_library {
name: "foo",
srcs: ["a.java"],
@@ -2123,8 +1968,8 @@
}
`)
// The baz library should depend on the system stubs jar.
- bazLibrary := ctx.ModuleForTests("baz", "android_common").Rule("javac")
- if expected, actual := `^-classpath .*:/[^:]*/turbine-combined/foo\.stubs.system\.jar$`, bazLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+ bazLibrary := result.ModuleForTests("baz", "android_common").Rule("javac")
+ if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs.system\.jar$`, bazLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
t.Errorf("expected %q, found %#q", expected, actual)
}
}
@@ -2192,7 +2037,7 @@
// TODO(jungjw): Consider making this more robust by ignoring path order.
func checkPatchModuleFlag(t *testing.T, ctx *android.TestContext, moduleName string, expected string) {
- variables := ctx.ModuleForTests(moduleName, "android_common").Module().VariablesForTests()
+ variables := ctx.ModuleForTests(moduleName, "android_common").VariablesForTestsRelativeToTop()
flags := strings.Split(variables["javacFlags"], " ")
got := ""
for _, flag := range flags {
@@ -2202,7 +2047,7 @@
break
}
}
- if expected != got {
+ if expected != android.StringPathRelativeToTop(ctx.Config().BuildDir(), got) {
t.Errorf("Unexpected patch-module flag for module %q - expected %q, but got %q", moduleName, expected, got)
}
}
@@ -2272,78 +2117,14 @@
ctx, _ := testJava(t, bp)
checkPatchModuleFlag(t, ctx, "foo", "")
- expected := "java.base=.:" + buildDir
+ expected := "java.base=.:out/soong"
checkPatchModuleFlag(t, ctx, "bar", expected)
expected = "java.base=" + strings.Join([]string{
- ".", buildDir, "dir", "dir2", "nested", moduleToPath("ext"), moduleToPath("framework")}, ":")
+ ".", "out/soong", "dir", "dir2", "nested", defaultModuleToPath("ext"), defaultModuleToPath("framework")}, ":")
checkPatchModuleFlag(t, ctx, "baz", expected)
})
}
-func TestJavaSystemModules(t *testing.T) {
- ctx, _ := testJava(t, `
- java_system_modules {
- name: "system-modules",
- libs: ["system-module1", "system-module2"],
- }
- java_library {
- name: "system-module1",
- srcs: ["a.java"],
- sdk_version: "none",
- system_modules: "none",
- }
- java_library {
- name: "system-module2",
- srcs: ["b.java"],
- sdk_version: "none",
- system_modules: "none",
- }
- `)
-
- // check the existence of the module
- systemModules := ctx.ModuleForTests("system-modules", "android_common")
-
- cmd := systemModules.Rule("jarsTosystemModules")
-
- // make sure the command compiles against the supplied modules.
- for _, module := range []string{"system-module1.jar", "system-module2.jar"} {
- if !strings.Contains(cmd.Args["classpath"], module) {
- t.Errorf("system modules classpath %v does not contain %q", cmd.Args["classpath"],
- module)
- }
- }
-}
-
-func TestJavaSystemModulesImport(t *testing.T) {
- ctx, _ := testJava(t, `
- java_system_modules_import {
- name: "system-modules",
- libs: ["system-module1", "system-module2"],
- }
- java_import {
- name: "system-module1",
- jars: ["a.jar"],
- }
- java_import {
- name: "system-module2",
- jars: ["b.jar"],
- }
- `)
-
- // check the existence of the module
- systemModules := ctx.ModuleForTests("system-modules", "android_common")
-
- cmd := systemModules.Rule("jarsTosystemModules")
-
- // make sure the command compiles against the supplied modules.
- for _, module := range []string{"system-module1.jar", "system-module2.jar"} {
- if !strings.Contains(cmd.Args["classpath"], module) {
- t.Errorf("system modules classpath %v does not contain %q", cmd.Args["classpath"],
- module)
- }
- }
-}
-
func TestJavaLibraryWithSystemModules(t *testing.T) {
ctx, _ := testJava(t, `
java_library {
@@ -2424,8 +2205,24 @@
}
}
+func TestAidlFlagsArePassedToTheAidlCompiler(t *testing.T) {
+ ctx, _ := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["aidl/foo/IFoo.aidl"],
+ aidl: { flags: ["-Werror"], },
+ }
+ `)
+
+ aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+ expectedAidlFlag := "-Werror"
+ if !strings.Contains(aidlCommand, expectedAidlFlag) {
+ t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+ }
+}
+
func TestDataNativeBinaries(t *testing.T) {
- ctx, config := testJava(t, `
+ ctx, _ := testJava(t, `
java_test_host {
name: "foo",
srcs: ["a.java"],
@@ -2441,10 +2238,21 @@
buildOS := android.BuildOs.String()
test := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
- entries := android.AndroidMkEntriesForTest(t, config, "", test)[0]
- expected := []string{buildDir + "/.intermediates/bin/" + buildOS + "_x86_64_PY3/bin:bin"}
+ entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
+ expected := []string{"out/soong/.intermediates/bin/" + buildOS + "_x86_64_PY3/bin:bin"}
actual := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
- if !reflect.DeepEqual(expected, actual) {
- t.Errorf("Unexpected test data - expected: %q, actual: %q", expected, actual)
- }
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_COMPATIBILITY_SUPPORT_FILES", ctx.Config(), expected, actual)
+}
+
+func TestDefaultInstallable(t *testing.T) {
+ ctx, _ := testJava(t, `
+ java_test_host {
+ name: "foo"
+ }
+ `)
+
+ buildOS := android.BuildOs.String()
+ module := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
+ assertDeepEquals(t, "Default installable value should be true.", proptools.BoolPtr(true),
+ module.properties.Installable)
}
diff --git a/java/jdeps.go b/java/jdeps.go
index 2b5ee74..0ab2e42 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -87,8 +87,9 @@
dpInfo.Classes = append(dpInfo.Classes, data.Class)
}
- if dep, ok := module.(Dependency); ok {
- dpInfo.Installed_paths = append(dpInfo.Installed_paths, dep.ImplementationJars().Strings()...)
+ if ctx.ModuleHasProvider(module, JavaInfoProvider) {
+ dep := ctx.ModuleProvider(module, JavaInfoProvider).(JavaInfo)
+ dpInfo.Installed_paths = append(dpInfo.Installed_paths, dep.ImplementationJars.Strings()...)
}
dpInfo.Classes = android.FirstUniqueStrings(dpInfo.Classes)
dpInfo.Installed_paths = android.FirstUniqueStrings(dpInfo.Installed_paths)
diff --git a/java/kotlin.go b/java/kotlin.go
index 8067ad5..3a6fc0f 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -34,8 +34,9 @@
`${config.GenKotlinBuildFileCmd} --classpath "$classpath" --name "$name"` +
` --out_dir "$classesDir" --srcs "$out.rsp" --srcs "$srcJarDir/list"` +
` $commonSrcFilesArg --out "$kotlinBuildFile" && ` +
- `${config.KotlincCmd} ${config.JavacHeapFlags} $kotlincFlags ` +
- `-jvm-target $kotlinJvmTarget -Xbuild-file=$kotlinBuildFile -kotlin-home $emptyDir && ` +
+ `${config.KotlincCmd} ${config.KotlincSuppressJDK9Warnings} ${config.JavacHeapFlags} ` +
+ `$kotlincFlags -jvm-target $kotlinJvmTarget -Xbuild-file=$kotlinBuildFile ` +
+ `-kotlin-home $emptyDir && ` +
`${config.SoongZipCmd} -jar -o $out -C $classesDir -D $classesDir && ` +
`rm -rf "$srcJarDir"`,
CommandDeps: []string{
@@ -64,7 +65,9 @@
// Insert a second rule to write out the list of resources to a file.
commonSrcsList := android.PathForModuleOut(ctx, "kotlinc_common_srcs.list")
rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().Text("cp").FlagWithRspFileInputList("", commonSrcFiles).Output(commonSrcsList)
+ rule.Command().Text("cp").
+ FlagWithRspFileInputList("", commonSrcsList.ReplaceExtension(ctx, "rsp"), commonSrcFiles).
+ Output(commonSrcsList)
rule.Build("kotlin_common_srcs_list", "kotlin common_srcs list")
return android.OptionalPathForPath(commonSrcsList)
}
@@ -121,8 +124,8 @@
`${config.GenKotlinBuildFileCmd} --classpath "$classpath" --name "$name"` +
` --srcs "$out.rsp" --srcs "$srcJarDir/list"` +
` $commonSrcFilesArg --out "$kotlinBuildFile" && ` +
- `${config.KotlincCmd} ${config.KotlincSuppressJDK9Warnings} ${config.JavacHeapFlags} $kotlincFlags ` +
- `-Xplugin=${config.KotlinKaptJar} ` +
+ `${config.KotlincCmd} ${config.KaptSuppressJDK9Warnings} ${config.KotlincSuppressJDK9Warnings} ` +
+ `${config.JavacHeapFlags} $kotlincFlags -Xplugin=${config.KotlinKaptJar} ` +
`-P plugin:org.jetbrains.kotlin.kapt3:sources=$kaptDir/sources ` +
`-P plugin:org.jetbrains.kotlin.kapt3:classes=$kaptDir/classes ` +
`-P plugin:org.jetbrains.kotlin.kapt3:stubs=$kaptDir/stubs ` +
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index 77ef294..1c146a1 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -176,19 +176,22 @@
env := map[string]string{
"RUN_ERROR_PRONE": "true",
}
- config := testConfig(env, bp, nil)
- ctx, _ := testJavaWithConfig(t, config)
+
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureMergeEnv(env),
+ ).RunTestWithBp(t, bp)
buildOS := android.BuildOs.String()
- kapt := ctx.ModuleForTests("foo", "android_common").Rule("kapt")
+ kapt := result.ModuleForTests("foo", "android_common").Rule("kapt")
//kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
- javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
- errorprone := ctx.ModuleForTests("foo", "android_common").Description("errorprone")
+ javac := result.ModuleForTests("foo", "android_common").Description("javac")
+ errorprone := result.ModuleForTests("foo", "android_common").Description("errorprone")
- bar := ctx.ModuleForTests("bar", buildOS+"_common").Description("javac").Output.String()
- baz := ctx.ModuleForTests("baz", buildOS+"_common").Description("javac").Output.String()
- myCheck := ctx.ModuleForTests("my_check", buildOS+"_common").Description("javac").Output.String()
+ bar := result.ModuleForTests("bar", buildOS+"_common").Description("javac").Output.String()
+ baz := result.ModuleForTests("baz", buildOS+"_common").Description("javac").Output.String()
+ myCheck := result.ModuleForTests("my_check", buildOS+"_common").Description("javac").Output.String()
// Test that the errorprone plugins are not passed to kapt
expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar +
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index 021920a..5949edd 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -19,10 +19,6 @@
"android/soong/java/config"
)
-// This variable is effectively unused in pre-master branches, and is
-// included (with the same value as it has in AOSP) only to ease
-// merges between branches (see the comment in the
-// useLegacyCorePlatformApi() function):
var legacyCorePlatformApiModules = []string{
"ahat-test-dump",
"android.car",
@@ -34,29 +30,44 @@
"art_cts_jvmti_test_library",
"art-gtest-jars-MyClassNatives",
"BackupFrameworksServicesRoboTests",
+ "backuplib",
"BandwidthEnforcementTest",
"BlockedNumberProvider",
"BluetoothInstrumentationTests",
"BluetoothMidiService",
+ "CarDeveloperOptions",
+ "CarService",
+ "CarServiceTest",
"car-apps-common",
+ "car-service-test-lib",
+ "car-service-test-static-lib",
"CertInstaller",
"ConnectivityManagerTest",
"ContactsProvider",
+ "CorePerfTests",
"core-tests-support",
+ "CtsAppExitTestCases",
"CtsContentTestCases",
"CtsIkeTestCases",
+ "CtsAppExitTestCases",
"CtsLibcoreWycheproofBCTestCases",
"CtsMediaTestCases",
"CtsNetTestCases",
"CtsNetTestCasesLatestSdk",
"CtsSecurityTestCases",
+ "CtsSuspendAppsTestCases",
"CtsUsageStatsTestCases",
+ "DeadpoolService",
+ "DeadpoolServiceBtServices",
+ "DeviceInfo",
+ "DiagnosticTools",
"DisplayCutoutEmulationEmu01Overlay",
"DocumentsUIPerfTests",
"DocumentsUITests",
"DownloadProvider",
"DownloadProviderTests",
"DownloadProviderUi",
+ "ds-car-docs", // for AAOS API documentation only
"DynamicSystemInstallationService",
"EmergencyInfo-lib",
"ethernet-service",
@@ -72,7 +83,9 @@
"FrameworksNetTests",
"FrameworksServicesRoboTests",
"FrameworksServicesTests",
+ "FrameworksMockingServicesTests",
"FrameworksUtilTests",
+ "FrameworksWifiTests",
"hid",
"hidl_test_java_java",
"hwbinder",
@@ -95,6 +108,9 @@
"platform_library-docs",
"PrintSpooler",
"RollbackTest",
+ "service-blobstore",
+ "service-connectivity-pre-jarjar",
+ "service-jobscheduler",
"services",
"services.accessibility",
"services.backup",
@@ -136,10 +152,6 @@
"wifi-service",
}
-// This variable is effectively unused in pre-master branches, and is
-// included (with the same value as it has in AOSP) only to ease
-// merges between branches (see the comment in the
-// useLegacyCorePlatformApi() function):
var legacyCorePlatformApiLookup = make(map[string]struct{})
func init() {
@@ -149,12 +161,12 @@
}
func useLegacyCorePlatformApi(ctx android.EarlyModuleContext) bool {
- // In pre-master branches, we don't attempt to force usage of the stable
- // version of the core/platform API. Instead, we always use the legacy
- // version --- except in tests, where we always use stable, so that we
- // can make the test assertions the same as other branches.
- // This should be false in tests and true otherwise:
- return ctx.Config().TestProductVariables == nil
+ return useLegacyCorePlatformApiByName(ctx.ModuleName())
+}
+
+func useLegacyCorePlatformApiByName(name string) bool {
+ _, found := legacyCorePlatformApiLookup[name]
+ return found
}
func corePlatformSystemModules(ctx android.EarlyModuleContext) string {
diff --git a/java/lint.go b/java/lint.go
index cd2a904..aa308e6 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -19,7 +19,11 @@
"sort"
"strings"
+ "github.com/google/blueprint/proptools"
+
"android/soong/android"
+ "android/soong/java/config"
+ "android/soong/remoteexec"
)
type LintProperties struct {
@@ -46,28 +50,32 @@
// Modules that provide extra lint checks
Extra_check_modules []string
+
+ // Name of the file that lint uses as the baseline. Defaults to "lint-baseline.xml".
+ Baseline_filename *string
}
}
type linter struct {
- name string
- manifest android.Path
- mergedManifest android.Path
- srcs android.Paths
- srcJars android.Paths
- resources android.Paths
- classpath android.Paths
- classes android.Path
- extraLintCheckJars android.Paths
- test bool
- library bool
- minSdkVersion string
- targetSdkVersion string
- compileSdkVersion string
- javaLanguageLevel string
- kotlinLanguageLevel string
- outputs lintOutputs
- properties LintProperties
+ name string
+ manifest android.Path
+ mergedManifest android.Path
+ srcs android.Paths
+ srcJars android.Paths
+ resources android.Paths
+ classpath android.Paths
+ classes android.Path
+ extraLintCheckJars android.Paths
+ test bool
+ library bool
+ minSdkVersion string
+ targetSdkVersion string
+ compileSdkVersion string
+ javaLanguageLevel string
+ kotlinLanguageLevel string
+ outputs lintOutputs
+ properties LintProperties
+ extraMainlineLintErrors []string
reports android.Paths
@@ -167,28 +175,28 @@
extraLintCheckTag, extraCheckModules...)
}
-func (l *linter) writeLintProjectXML(ctx android.ModuleContext,
- rule *android.RuleBuilder) (projectXMLPath, configXMLPath, cacheDir, homeDir android.WritablePath, deps android.Paths) {
+// lintPaths contains the paths to lint's inputs and outputs to make it easier to pass them
+// around.
+type lintPaths struct {
+ projectXML android.WritablePath
+ configXML android.WritablePath
+ cacheDir android.WritablePath
+ homeDir android.WritablePath
+ srcjarDir android.WritablePath
+}
- var resourcesList android.WritablePath
- if len(l.resources) > 0 {
- // The list of resources may be too long to put on the command line, but
- // we can't use the rsp file because it is already being used for srcs.
- // Insert a second rule to write out the list of resources to a file.
- resourcesList = android.PathForModuleOut(ctx, "lint", "resources.list")
- resListRule := android.NewRuleBuilder(pctx, ctx)
- resListRule.Command().Text("cp").FlagWithRspFileInputList("", l.resources).Output(resourcesList)
- resListRule.Build("lint_resources_list", "lint resources list")
- deps = append(deps, l.resources...)
- }
+func lintRBEExecStrategy(ctx android.ModuleContext) string {
+ return ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
+}
- projectXMLPath = android.PathForModuleOut(ctx, "lint", "project.xml")
+func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder) lintPaths {
+ projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml")
// Lint looks for a lint.xml file next to the project.xml file, give it one.
- configXMLPath = android.PathForModuleOut(ctx, "lint", "lint.xml")
- cacheDir = android.PathForModuleOut(ctx, "lint", "cache")
- homeDir = android.PathForModuleOut(ctx, "lint", "home")
+ configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml")
+ cacheDir := android.PathForModuleOut(ctx, "lint", "cache")
+ homeDir := android.PathForModuleOut(ctx, "lint", "home")
- srcJarDir := android.PathForModuleOut(ctx, "lint-srcjars")
+ srcJarDir := android.PathForModuleOut(ctx, "lint", "srcjars")
srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars)
cmd := rule.Command().
@@ -204,36 +212,31 @@
cmd.Flag("--test")
}
if l.manifest != nil {
- deps = append(deps, l.manifest)
- cmd.FlagWithArg("--manifest ", l.manifest.String())
+ cmd.FlagWithInput("--manifest ", l.manifest)
}
if l.mergedManifest != nil {
- deps = append(deps, l.mergedManifest)
- cmd.FlagWithArg("--merged_manifest ", l.mergedManifest.String())
+ cmd.FlagWithInput("--merged_manifest ", l.mergedManifest)
}
// TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
// lint separately.
- cmd.FlagWithRspFileInputList("--srcs ", l.srcs)
- deps = append(deps, l.srcs...)
+ srcsList := android.PathForModuleOut(ctx, "lint-srcs.list")
+ cmd.FlagWithRspFileInputList("--srcs ", srcsList, l.srcs)
cmd.FlagWithInput("--generated_srcs ", srcJarList)
- deps = append(deps, l.srcJars...)
- if resourcesList != nil {
- cmd.FlagWithInput("--resources ", resourcesList)
+ if len(l.resources) > 0 {
+ resourcesList := android.PathForModuleOut(ctx, "lint-resources.list")
+ cmd.FlagWithRspFileInputList("--resources ", resourcesList, l.resources)
}
if l.classes != nil {
- deps = append(deps, l.classes)
- cmd.FlagWithArg("--classes ", l.classes.String())
+ cmd.FlagWithInput("--classes ", l.classes)
}
- cmd.FlagForEachArg("--classpath ", l.classpath.Strings())
- deps = append(deps, l.classpath...)
+ cmd.FlagForEachInput("--classpath ", l.classpath)
- cmd.FlagForEachArg("--extra_checks_jar ", l.extraLintCheckJars.Strings())
- deps = append(deps, l.extraLintCheckJars...)
+ cmd.FlagForEachInput("--extra_checks_jar ", l.extraLintCheckJars)
cmd.FlagWithArg("--root_dir ", "$PWD")
@@ -244,17 +247,24 @@
cmd.FlagWithInput("@",
android.PathForSource(ctx, "build/soong/java/lint_defaults.txt"))
+ cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors)
cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks)
cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks)
cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
- return projectXMLPath, configXMLPath, cacheDir, homeDir, deps
+ return lintPaths{
+ projectXML: projectXMLPath,
+ configXML: configXMLPath,
+ cacheDir: cacheDir,
+ homeDir: homeDir,
+ }
+
}
// generateManifest adds a command to the rule to write a simple manifest that contains the
// minSdkVersion and targetSdkVersion for modules (like java_library) that don't have a manifest.
-func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleBuilder) android.Path {
+func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleBuilder) android.WritablePath {
manifestPath := android.PathForModuleOut(ctx, "lint", "AndroidManifest.xml")
rule.Command().Text("(").
@@ -274,28 +284,51 @@
return
}
+ if l.minSdkVersion != l.compileSdkVersion {
+ l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, "NewApi")
+ }
+
extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag)
for _, extraLintCheckModule := range extraLintCheckModules {
- if dep, ok := extraLintCheckModule.(Dependency); ok {
- l.extraLintCheckJars = append(l.extraLintCheckJars, dep.ImplementationAndResourcesJars()...)
+ if ctx.OtherModuleHasProvider(extraLintCheckModule, JavaInfoProvider) {
+ dep := ctx.OtherModuleProvider(extraLintCheckModule, JavaInfoProvider).(JavaInfo)
+ l.extraLintCheckJars = append(l.extraLintCheckJars, dep.ImplementationAndResourcesJars...)
} else {
ctx.PropertyErrorf("lint.extra_check_modules",
"%s is not a java module", ctx.OtherModuleName(extraLintCheckModule))
}
}
- rule := android.NewRuleBuilder(pctx, ctx)
+ rule := android.NewRuleBuilder(pctx, ctx).
+ Sbox(android.PathForModuleOut(ctx, "lint"),
+ android.PathForModuleOut(ctx, "lint.sbox.textproto")).
+ SandboxInputs()
+
+ if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") {
+ pool := ctx.Config().GetenvWithDefault("RBE_LINT_POOL", "java16")
+ rule.Remoteable(android.RemoteRuleSupports{RBE: true})
+ rule.Rewrapper(&remoteexec.REParams{
+ Labels: map[string]string{"type": "tool", "name": "lint"},
+ ExecStrategy: lintRBEExecStrategy(ctx),
+ ToolchainInputs: []string{config.JavaCmd(ctx).String()},
+ EnvironmentVariables: []string{
+ "LANG",
+ },
+ Platform: map[string]string{remoteexec.PoolKey: pool},
+ })
+ }
if l.manifest == nil {
manifest := l.generateManifest(ctx, rule)
l.manifest = manifest
+ rule.Temporary(manifest)
}
- projectXML, lintXML, cacheDir, homeDir, deps := l.writeLintProjectXML(ctx, rule)
+ lintPaths := l.writeLintProjectXML(ctx, rule)
- html := android.PathForModuleOut(ctx, "lint-report.html")
- text := android.PathForModuleOut(ctx, "lint-report.txt")
- xml := android.PathForModuleOut(ctx, "lint-report.xml")
+ html := android.PathForModuleOut(ctx, "lint", "lint-report.html")
+ text := android.PathForModuleOut(ctx, "lint", "lint-report.txt")
+ xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml")
depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml)
@@ -305,8 +338,9 @@
}
})
- rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
- rule.Command().Text("mkdir -p").Flag(cacheDir.String()).Flag(homeDir.String())
+ rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
+ rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
+ rule.Command().Text("rm -f").Output(html).Output(text).Output(xml)
var annotationsZipPath, apiVersionsXMLPath android.Path
if ctx.Config().AlwaysUsePrebuiltSdks() {
@@ -317,17 +351,17 @@
apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx)
}
- cmd := rule.Command().
- Text("(").
- Flag("JAVA_OPTS=-Xmx3072m").
- FlagWithArg("ANDROID_SDK_HOME=", homeDir.String()).
+ cmd := rule.Command()
+
+ cmd.Flag(`JAVA_OPTS="-Xmx3072m --add-opens java.base/java.util=ALL-UNNAMED"`).
+ FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()).
FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
- FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath).
- Tool(android.PathForSource(ctx, "prebuilts/cmdline-tools/tools/bin/lint")).
- Implicit(android.PathForSource(ctx, "prebuilts/cmdline-tools/tools/lib/lint-classpath.jar")).
+ FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath)
+
+ cmd.BuiltTool("lint").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "lint.jar")).
Flag("--quiet").
- FlagWithInput("--project ", projectXML).
- FlagWithInput("--config ", lintXML).
+ FlagWithInput("--project ", lintPaths.projectXML).
+ FlagWithInput("--config ", lintPaths.configXML).
FlagWithOutput("--html ", html).
FlagWithOutput("--text ", text).
FlagWithOutput("--xml ", xml).
@@ -337,15 +371,35 @@
FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
Flag("--exitcode").
Flags(l.properties.Lint.Flags).
- Implicits(deps)
+ Implicit(annotationsZipPath).
+ Implicit(apiVersionsXMLPath)
+
+ rule.Temporary(lintPaths.projectXML)
+ rule.Temporary(lintPaths.configXML)
if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
cmd.FlagWithArg("--check ", checkOnly)
}
- cmd.Text("|| (").Text("cat").Input(text).Text("; exit 7)").Text(")")
+ if lintFilename := proptools.StringDefault(l.properties.Lint.Baseline_filename, "lint-baseline.xml"); lintFilename != "" {
+ var lintBaseline android.OptionalPath
+ if String(l.properties.Lint.Baseline_filename) != "" {
+ // if manually specified, we require the file to exist
+ lintBaseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, lintFilename))
+ } else {
+ lintBaseline = android.ExistentPathForSource(ctx, ctx.ModuleDir(), lintFilename)
+ }
+ if lintBaseline.Valid() {
+ cmd.FlagWithInput("--baseline ", lintBaseline.Path())
+ }
+ }
- rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
+ cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)")
+
+ rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
+
+ // The HTML output contains a date, remove it to make the output deterministic.
+ rule.Command().Text(`sed -i.tmp -e 's|Check performed at .*\(</nav>\)|\1|'`).Output(html)
rule.Build("lint", "lint")
@@ -419,13 +473,13 @@
}
ctx.Build(pctx, android.BuildParams{
- Rule: android.Cp,
+ Rule: android.CpIfChanged,
Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"),
Output: copiedAnnotationsZipPath(ctx),
})
ctx.Build(pctx, android.BuildParams{
- Rule: android.Cp,
+ Rule: android.CpIfChanged,
Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".api_versions.xml"),
Output: copiedAPIVersionsXmlPath(ctx),
})
@@ -516,7 +570,7 @@
rule.Command().BuiltTool("soong_zip").
FlagWithOutput("-o ", outputPath).
FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
- FlagWithRspFileInputList("-r ", paths)
+ FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths)
rule.Build(outputPath.Base(), outputPath.Base())
}
diff --git a/java/lint_defaults.txt b/java/lint_defaults.txt
index 0786b7c..2de05b0 100644
--- a/java/lint_defaults.txt
+++ b/java/lint_defaults.txt
@@ -76,3 +76,5 @@
# TODO(b/158390965): remove this when lint doesn't crash
--disable_check HardcodedDebugMode
+
+--warning_check QueryAllPackagesPermission
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
new file mode 100644
index 0000000..cb8ad68
--- /dev/null
+++ b/java/platform_bootclasspath.go
@@ -0,0 +1,384 @@
+// Copyright 2021 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 (
+ "fmt"
+
+ "android/soong/android"
+ "android/soong/dexpreopt"
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+func init() {
+ registerPlatformBootclasspathBuildComponents(android.InitRegistrationContext)
+}
+
+func registerPlatformBootclasspathBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("platform_bootclasspath", platformBootclasspathFactory)
+
+ ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("platform_bootclasspath_deps", platformBootclasspathDepsMutator)
+ })
+}
+
+type platformBootclasspathDependencyTag struct {
+ blueprint.BaseDependencyTag
+
+ name string
+}
+
+// Avoid having to make platform bootclasspath content visible to the platform bootclasspath.
+//
+// This is a temporary workaround to make it easier to migrate to platform bootclasspath with proper
+// dependencies.
+// TODO(b/177892522): Remove this and add needed visibility.
+func (t platformBootclasspathDependencyTag) ExcludeFromVisibilityEnforcement() {
+}
+
+// The tag used for the dependency between the platform bootclasspath and any configured boot jars.
+var platformBootclasspathModuleDepTag = platformBootclasspathDependencyTag{name: "module"}
+
+// The tag used for the dependency between the platform bootclasspath and bootclasspath_fragments.
+var platformBootclasspathFragmentDepTag = platformBootclasspathDependencyTag{name: "fragment"}
+
+var _ android.ExcludeFromVisibilityEnforcementTag = platformBootclasspathDependencyTag{}
+
+type platformBootclasspathModule struct {
+ android.ModuleBase
+ ClasspathFragmentBase
+
+ properties platformBootclasspathProperties
+
+ // The apex:module pairs obtained from the configured modules.
+ //
+ // Currently only for testing.
+ configuredModules []android.Module
+
+ // The apex:module pairs obtained from the fragments.
+ //
+ // Currently only for testing.
+ fragments []android.Module
+
+ // Path to the monolithic hiddenapi-flags.csv file.
+ hiddenAPIFlagsCSV android.OutputPath
+
+ // Path to the monolithic hiddenapi-index.csv file.
+ hiddenAPIIndexCSV android.OutputPath
+
+ // Path to the monolithic hiddenapi-unsupported.csv file.
+ hiddenAPIMetadataCSV android.OutputPath
+}
+
+// ApexVariantReference specifies a particular apex variant of a module.
+type ApexVariantReference struct {
+ // The name of the module apex variant, i.e. the apex containing the module variant.
+ //
+ // If this is not specified then it defaults to "platform" which will cause a dependency to be
+ // added to the module's platform variant.
+ Apex *string
+
+ // The name of the module.
+ Module *string
+}
+
+type platformBootclasspathProperties struct {
+ // The names of the bootclasspath_fragment modules that form part of this
+ // platform_bootclasspath.
+ Fragments []ApexVariantReference
+
+ Hidden_api HiddenAPIFlagFileProperties
+}
+
+func platformBootclasspathFactory() android.Module {
+ m := &platformBootclasspathModule{}
+ m.AddProperties(&m.properties)
+ // TODO(satayev): split systemserver and apex jars into separate configs.
+ initClasspathFragment(m)
+ android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+ return m
+}
+
+var _ android.OutputFileProducer = (*platformBootclasspathModule)(nil)
+
+func (b *platformBootclasspathModule) AndroidMkEntries() (entries []android.AndroidMkEntries) {
+ entries = append(entries, android.AndroidMkEntries{
+ Class: "FAKE",
+ // Need at least one output file in order for this to take effect.
+ OutputFile: android.OptionalPathForPath(b.hiddenAPIFlagsCSV),
+ Include: "$(BUILD_PHONY_PACKAGE)",
+ })
+ entries = append(entries, b.classpathFragmentBase().getAndroidMkEntries()...)
+ return
+}
+
+// Make the hidden API files available from the platform-bootclasspath module.
+func (b *platformBootclasspathModule) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case "hiddenapi-flags.csv":
+ return android.Paths{b.hiddenAPIFlagsCSV}, nil
+ case "hiddenapi-index.csv":
+ return android.Paths{b.hiddenAPIIndexCSV}, nil
+ case "hiddenapi-metadata.csv":
+ return android.Paths{b.hiddenAPIMetadataCSV}, nil
+ }
+
+ return nil, fmt.Errorf("unknown tag %s", tag)
+}
+
+func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+ if SkipDexpreoptBootJars(ctx) {
+ return
+ }
+
+ // Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
+ // path is retrieved from the dependency by GetGlobalSoongConfig(ctx).
+ dexpreopt.RegisterToolDeps(ctx)
+}
+
+func platformBootclasspathDepsMutator(ctx android.BottomUpMutatorContext) {
+ m := ctx.Module()
+ if p, ok := m.(*platformBootclasspathModule); ok {
+ // Add dependencies on all the modules configured in the "art" boot image.
+ artImageConfig := genBootImageConfigs(ctx)[artBootImageName]
+ addDependenciesOntoBootImageModules(ctx, artImageConfig.modules)
+
+ // Add dependencies on all the modules configured in the "boot" boot image. That does not
+ // include modules configured in the "art" boot image.
+ bootImageConfig := p.getImageConfig(ctx)
+ addDependenciesOntoBootImageModules(ctx, bootImageConfig.modules)
+
+ // Add dependencies on all the updatable modules.
+ updatableModules := dexpreopt.GetGlobalConfig(ctx).UpdatableBootJars
+ addDependenciesOntoBootImageModules(ctx, updatableModules)
+
+ // Add dependencies on all the fragments.
+ addDependencyOntoApexVariants(ctx, "fragments", p.properties.Fragments, platformBootclasspathFragmentDepTag)
+ }
+}
+
+func addDependencyOntoApexVariants(ctx android.BottomUpMutatorContext, propertyName string, refs []ApexVariantReference, tag blueprint.DependencyTag) {
+ for i, ref := range refs {
+ apex := proptools.StringDefault(ref.Apex, "platform")
+
+ if ref.Module == nil {
+ ctx.PropertyErrorf(propertyName, "missing module name at position %d", i)
+ continue
+ }
+ name := proptools.String(ref.Module)
+
+ addDependencyOntoApexModulePair(ctx, apex, name, tag)
+ }
+}
+
+func addDependencyOntoApexModulePair(ctx android.BottomUpMutatorContext, apex string, name string, tag blueprint.DependencyTag) {
+ var variations []blueprint.Variation
+ if apex != "platform" {
+ // Pick the correct apex variant.
+ variations = []blueprint.Variation{
+ {Mutator: "apex", Variation: apex},
+ }
+ }
+
+ addedDep := false
+ if ctx.OtherModuleDependencyVariantExists(variations, name) {
+ ctx.AddFarVariationDependencies(variations, tag, name)
+ addedDep = true
+ }
+
+ // Add a dependency on the prebuilt module if it exists.
+ prebuiltName := android.PrebuiltNameFromSource(name)
+ if ctx.OtherModuleDependencyVariantExists(variations, prebuiltName) {
+ ctx.AddVariationDependencies(variations, tag, prebuiltName)
+ addedDep = true
+ }
+
+ // If no appropriate variant existing for this, so no dependency could be added, then it is an
+ // error, unless missing dependencies are allowed. The simplest way to handle that is to add a
+ // dependency that will not be satisfied and the default behavior will handle it.
+ if !addedDep {
+ // Add dependency on the unprefixed (i.e. source or renamed prebuilt) module which we know does
+ // not exist. The resulting error message will contain useful information about the available
+ // variants.
+ reportMissingVariationDependency(ctx, variations, name)
+
+ // Add dependency on the missing prefixed prebuilt variant too if a module with that name exists
+ // so that information about its available variants will be reported too.
+ if ctx.OtherModuleExists(prebuiltName) {
+ reportMissingVariationDependency(ctx, variations, prebuiltName)
+ }
+ }
+}
+
+// reportMissingVariationDependency intentionally adds a dependency on a missing variation in order
+// to generate an appropriate error message with information about the available variations.
+func reportMissingVariationDependency(ctx android.BottomUpMutatorContext, variations []blueprint.Variation, name string) {
+ modules := ctx.AddFarVariationDependencies(variations, nil, name)
+ if len(modules) != 1 {
+ panic(fmt.Errorf("Internal Error: expected one module, found %d", len(modules)))
+ return
+ }
+ if modules[0] != nil {
+ panic(fmt.Errorf("Internal Error: expected module to be missing but was found: %q", modules[0]))
+ return
+ }
+}
+
+func addDependenciesOntoBootImageModules(ctx android.BottomUpMutatorContext, modules android.ConfiguredJarList) {
+ for i := 0; i < modules.Len(); i++ {
+ apex := modules.Apex(i)
+ name := modules.Jar(i)
+
+ addDependencyOntoApexModulePair(ctx, apex, name, platformBootclasspathModuleDepTag)
+ }
+}
+
+func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ b.classpathFragmentBase().generateAndroidBuildActions(ctx)
+
+ ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) {
+ tag := ctx.OtherModuleDependencyTag(module)
+ if tag == platformBootclasspathModuleDepTag {
+ b.configuredModules = append(b.configuredModules, module)
+ } else if tag == platformBootclasspathFragmentDepTag {
+ b.fragments = append(b.fragments, module)
+ }
+ })
+
+ b.generateHiddenAPIBuildActions(ctx, b.configuredModules)
+
+ // Nothing to do if skipping the dexpreopt of boot image jars.
+ if SkipDexpreoptBootJars(ctx) {
+ return
+ }
+
+ // Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
+ // GenerateSingletonBuildActions method as it cannot create it for itself.
+ dexpreopt.GetGlobalSoongConfig(ctx)
+
+ imageConfig := b.getImageConfig(ctx)
+ if imageConfig == nil {
+ return
+ }
+
+ // Construct the boot image info from the config.
+ info := BootImageInfo{imageConfig: imageConfig}
+
+ // Make it available for other modules.
+ ctx.SetProvider(BootImageInfoProvider, info)
+}
+
+func (b *platformBootclasspathModule) getImageConfig(ctx android.EarlyModuleContext) *bootImageConfig {
+ return defaultBootImageConfig(ctx)
+}
+
+// generateHiddenAPIBuildActions generates all the hidden API related build rules.
+func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module) {
+
+ // Save the paths to the monolithic files for retrieval via OutputFiles().
+ b.hiddenAPIFlagsCSV = hiddenAPISingletonPaths(ctx).flags
+ b.hiddenAPIIndexCSV = hiddenAPISingletonPaths(ctx).index
+ b.hiddenAPIMetadataCSV = hiddenAPISingletonPaths(ctx).metadata
+
+ // Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true. This is a performance
+ // optimization that can be used to reduce the incremental build time but as its name suggests it
+ // can be unsafe to use, e.g. when the changes affect anything that goes on the bootclasspath.
+ if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+ paths := android.OutputPaths{b.hiddenAPIFlagsCSV, b.hiddenAPIIndexCSV, b.hiddenAPIMetadataCSV}
+ for _, path := range paths {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Touch,
+ Output: path,
+ })
+ }
+ return
+ }
+
+ hiddenAPISupportingModules := []hiddenAPISupportingModule{}
+ for _, module := range modules {
+ if h, ok := module.(hiddenAPISupportingModule); ok {
+ if h.bootDexJar() == nil {
+ ctx.ModuleErrorf("module %s does not provide a bootDexJar file", module)
+ }
+ if h.flagsCSV() == nil {
+ ctx.ModuleErrorf("module %s does not provide a flagsCSV file", module)
+ }
+ if h.indexCSV() == nil {
+ ctx.ModuleErrorf("module %s does not provide an indexCSV file", module)
+ }
+ if h.metadataCSV() == nil {
+ ctx.ModuleErrorf("module %s does not provide a metadataCSV file", module)
+ }
+
+ if ctx.Failed() {
+ continue
+ }
+
+ hiddenAPISupportingModules = append(hiddenAPISupportingModules, h)
+ } else {
+ ctx.ModuleErrorf("module %s of type %s does not support hidden API processing", module, ctx.OtherModuleType(module))
+ }
+ }
+
+ moduleSpecificFlagsPaths := android.Paths{}
+ for _, module := range hiddenAPISupportingModules {
+ moduleSpecificFlagsPaths = append(moduleSpecificFlagsPaths, module.flagsCSV())
+ }
+
+ augmentationInfo := b.properties.Hidden_api.hiddenAPIFlagFileInfo(ctx)
+
+ outputPath := hiddenAPISingletonPaths(ctx).flags
+ baseFlagsPath := hiddenAPISingletonPaths(ctx).stubFlags
+ ruleToGenerateHiddenApiFlags(ctx, outputPath, baseFlagsPath, moduleSpecificFlagsPaths, augmentationInfo)
+
+ b.generateHiddenAPIIndexRules(ctx, hiddenAPISupportingModules)
+ b.generatedHiddenAPIMetadataRules(ctx, hiddenAPISupportingModules)
+}
+
+func (b *platformBootclasspathModule) generateHiddenAPIIndexRules(ctx android.ModuleContext, modules []hiddenAPISupportingModule) {
+ indexes := android.Paths{}
+ for _, module := range modules {
+ indexes = append(indexes, module.indexCSV())
+ }
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ BuiltTool("merge_csv").
+ Flag("--key_field signature").
+ FlagWithArg("--header=", "signature,file,startline,startcol,endline,endcol,properties").
+ FlagWithOutput("--output=", hiddenAPISingletonPaths(ctx).index).
+ Inputs(indexes)
+ rule.Build("platform-bootclasspath-monolithic-hiddenapi-index", "monolithic hidden API index")
+}
+
+func (b *platformBootclasspathModule) generatedHiddenAPIMetadataRules(ctx android.ModuleContext, modules []hiddenAPISupportingModule) {
+ metadataCSVFiles := android.Paths{}
+ for _, module := range modules {
+ metadataCSVFiles = append(metadataCSVFiles, module.metadataCSV())
+ }
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+
+ outputPath := hiddenAPISingletonPaths(ctx).metadata
+
+ rule.Command().
+ BuiltTool("merge_csv").
+ Flag("--key_field signature").
+ FlagWithOutput("--output=", outputPath).
+ Inputs(metadataCSVFiles)
+
+ rule.Build("platform-bootclasspath-monolithic-hiddenapi-metadata", "monolithic hidden API metadata")
+}
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
new file mode 100644
index 0000000..e51b049
--- /dev/null
+++ b/java/platform_bootclasspath_test.go
@@ -0,0 +1,316 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "testing"
+
+ "android/soong/android"
+ "android/soong/dexpreopt"
+)
+
+// Contains some simple tests for platform_bootclasspath.
+
+var prepareForTestWithPlatformBootclasspath = android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ dexpreopt.PrepareForTestByEnablingDexpreopt,
+)
+
+func TestPlatformBootclasspath(t *testing.T) {
+ preparer := android.GroupFixturePreparers(
+ prepareForTestWithPlatformBootclasspath,
+ FixtureConfigureBootJars("platform:foo", "platform:bar"),
+ android.FixtureWithRootAndroidBp(`
+ platform_bootclasspath {
+ name: "platform-bootclasspath",
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["a.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ }
+ `),
+ )
+
+ var addSourceBootclassPathModule = android.FixtureAddTextFile("source/Android.bp", `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ }
+ `)
+
+ var addPrebuiltBootclassPathModule = android.FixtureAddTextFile("prebuilt/Android.bp", `
+ java_import {
+ name: "foo",
+ jars: ["a.jar"],
+ compile_dex: true,
+ prefer: false,
+ }
+ `)
+
+ var addPrebuiltPreferredBootclassPathModule = android.FixtureAddTextFile("prebuilt/Android.bp", `
+ java_import {
+ name: "foo",
+ jars: ["a.jar"],
+ compile_dex: true,
+ prefer: true,
+ }
+ `)
+
+ t.Run("missing", func(t *testing.T) {
+ preparer.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`"platform-bootclasspath" depends on undefined module "foo"`)).
+ RunTest(t)
+ })
+
+ t.Run("source", func(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ preparer,
+ addSourceBootclassPathModule,
+ ).RunTest(t)
+
+ CheckPlatformBootclasspathModules(t, result, "platform-bootclasspath", []string{
+ "platform:foo",
+ "platform:bar",
+ })
+ })
+
+ t.Run("prebuilt", func(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ preparer,
+ addPrebuiltBootclassPathModule,
+ ).RunTest(t)
+
+ CheckPlatformBootclasspathModules(t, result, "platform-bootclasspath", []string{
+ "platform:prebuilt_foo",
+ "platform:bar",
+ })
+ })
+
+ t.Run("source+prebuilt - source preferred", func(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ preparer,
+ addSourceBootclassPathModule,
+ addPrebuiltBootclassPathModule,
+ ).RunTest(t)
+
+ CheckPlatformBootclasspathModules(t, result, "platform-bootclasspath", []string{
+ "platform:foo",
+ "platform:bar",
+ })
+ })
+
+ t.Run("source+prebuilt - prebuilt preferred", func(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ preparer,
+ addSourceBootclassPathModule,
+ addPrebuiltPreferredBootclassPathModule,
+ ).RunTest(t)
+
+ CheckPlatformBootclasspathModules(t, result, "platform-bootclasspath", []string{
+ "platform:prebuilt_foo",
+ "platform:bar",
+ })
+ })
+}
+
+func TestPlatformBootclasspathVariant(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithPlatformBootclasspath,
+ android.FixtureWithRootAndroidBp(`
+ platform_bootclasspath {
+ name: "platform-bootclasspath",
+ }
+ `),
+ ).RunTest(t)
+
+ variants := result.ModuleVariantsForTests("platform-bootclasspath")
+ android.AssertIntEquals(t, "expect 1 variant", 1, len(variants))
+}
+
+func TestPlatformBootclasspath_ClasspathFragmentPaths(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithPlatformBootclasspath,
+ android.FixtureWithRootAndroidBp(`
+ platform_bootclasspath {
+ name: "platform-bootclasspath",
+ }
+ `),
+ ).RunTest(t)
+
+ p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
+ android.AssertStringEquals(t, "output filepath", p.Name()+".pb", p.ClasspathFragmentBase.outputFilepath.Base())
+ android.AssertPathRelativeToTopEquals(t, "install filepath", "out/soong/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath)
+}
+
+func TestPlatformBootclasspathModule_AndroidMkEntries(t *testing.T) {
+ preparer := android.GroupFixturePreparers(
+ prepareForTestWithPlatformBootclasspath,
+ android.FixtureWithRootAndroidBp(`
+ platform_bootclasspath {
+ name: "platform-bootclasspath",
+ }
+ `),
+ )
+
+ t.Run("AndroidMkEntries", func(t *testing.T) {
+ result := preparer.RunTest(t)
+
+ p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
+
+ entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)
+ android.AssertIntEquals(t, "AndroidMkEntries count", 2, len(entries))
+ })
+
+ t.Run("hiddenapi-flags-entry", func(t *testing.T) {
+ result := preparer.RunTest(t)
+
+ p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
+
+ entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)
+ got := entries[0].OutputFile
+ android.AssertBoolEquals(t, "valid output path", true, got.Valid())
+ android.AssertSame(t, "output filepath", p.hiddenAPIFlagsCSV, got.Path())
+ })
+
+ t.Run("classpath-fragment-entry", func(t *testing.T) {
+ result := preparer.RunTest(t)
+
+ want := map[string][]string{
+ "LOCAL_MODULE": {"platform-bootclasspath"},
+ "LOCAL_MODULE_CLASS": {"ETC"},
+ "LOCAL_INSTALLED_MODULE_STEM": {"platform-bootclasspath.pb"},
+ // Output and Install paths are tested separately in TestPlatformBootclasspath_ClasspathFragmentPaths
+ }
+
+ p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
+
+ entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)
+ got := entries[1]
+ for k, expectedValue := range want {
+ if value, ok := got.EntryMap[k]; ok {
+ android.AssertDeepEquals(t, k, expectedValue, value)
+ } else {
+ t.Errorf("No %s defined, saw %q", k, got.EntryMap)
+ }
+ }
+ })
+}
+
+func TestPlatformBootclasspath_Dist(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithPlatformBootclasspath,
+ FixtureConfigureBootJars("platform:foo", "platform:bar"),
+ android.PrepareForTestWithAndroidMk,
+ android.FixtureWithRootAndroidBp(`
+ platform_bootclasspath {
+ name: "platform-bootclasspath",
+ dists: [
+ {
+ targets: ["droidcore"],
+ tag: "hiddenapi-flags.csv",
+ },
+ ],
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["a.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ }
+
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ }
+ `),
+ ).RunTest(t)
+
+ platformBootclasspath := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
+ entries := android.AndroidMkEntriesForTest(t, result.TestContext, platformBootclasspath)
+ goals := entries[0].GetDistForGoals(platformBootclasspath)
+ android.AssertStringEquals(t, "platform dist goals phony", ".PHONY: droidcore\n", goals[0])
+ android.AssertStringEquals(t, "platform dist goals call", "$(call dist-for-goals,droidcore,out/soong/hiddenapi/hiddenapi-flags.csv:hiddenapi-flags.csv)\n", android.StringRelativeToTop(result.Config, goals[1]))
+}
+
+func TestPlatformBootclasspath_HiddenAPIMonolithicFiles(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ hiddenApiFixtureFactory,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("bar"),
+ FixtureConfigureBootJars("platform:foo", "platform:bar"),
+ ).RunTestWithBp(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ compile_dex: true,
+
+ hiddenapi_additional_annotations: [
+ "foo-hiddenapi-annotations",
+ ],
+ }
+
+ java_library {
+ name: "foo-hiddenapi-annotations",
+ srcs: ["a.java"],
+ compile_dex: true,
+ }
+
+ java_import {
+ name: "foo",
+ jars: ["a.jar"],
+ compile_dex: true,
+ prefer: false,
+ }
+
+ java_sdk_library {
+ name: "bar",
+ srcs: ["a.java"],
+ compile_dex: true,
+ }
+
+ platform_bootclasspath {
+ name: "myplatform-bootclasspath",
+ }
+ `)
+
+ platformBootclasspath := result.ModuleForTests("myplatform-bootclasspath", "android_common")
+ indexRule := platformBootclasspath.Rule("platform-bootclasspath-monolithic-hiddenapi-index")
+ CheckHiddenAPIRuleInputs(t, `
+.intermediates/bar/android_common/hiddenapi/index.csv
+.intermediates/foo/android_common/hiddenapi/index.csv
+`,
+ indexRule)
+
+ // Make sure that the foo-hiddenapi-annotations.jar is included in the inputs to the rules that
+ // creates the index.csv file.
+ foo := result.ModuleForTests("foo", "android_common")
+ indexParams := foo.Output("hiddenapi/index.csv")
+ CheckHiddenAPIRuleInputs(t, `
+.intermediates/foo-hiddenapi-annotations/android_common/javac/foo-hiddenapi-annotations.jar
+.intermediates/foo/android_common/javac/foo.jar
+`, indexParams)
+}
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 9bc821d..edfa146 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -15,30 +15,45 @@
package java
import (
+ "path/filepath"
+
"android/soong/android"
+ "github.com/google/blueprint"
+
"fmt"
)
func init() {
- android.RegisterSingletonType("platform_compat_config_singleton", platformCompatConfigSingletonFactory)
- android.RegisterModuleType("platform_compat_config", PlatformCompatConfigFactory)
- android.RegisterModuleType("global_compat_config", globalCompatConfigFactory)
+ registerPlatformCompatConfigBuildComponents(android.InitRegistrationContext)
+
+ android.RegisterSdkMemberType(&compatConfigMemberType{
+ SdkMemberTypeBase: android.SdkMemberTypeBase{
+ PropertyName: "compat_configs",
+ SupportsSdk: true,
+ },
+ })
}
+func registerPlatformCompatConfigBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterSingletonType("platform_compat_config_singleton", platformCompatConfigSingletonFactory)
+ ctx.RegisterModuleType("platform_compat_config", PlatformCompatConfigFactory)
+ ctx.RegisterModuleType("prebuilt_platform_compat_config", prebuiltCompatConfigFactory)
+ ctx.RegisterModuleType("global_compat_config", globalCompatConfigFactory)
+}
+
+var PrepareForTestWithPlatformCompatConfig = android.FixtureRegisterWithContext(registerPlatformCompatConfigBuildComponents)
+
func platformCompatConfigPath(ctx android.PathContext) android.OutputPath {
return android.PathForOutput(ctx, "compat_config", "merged_compat_config.xml")
}
-type platformCompatConfigSingleton struct {
- metadata android.Path
-}
-
type platformCompatConfigProperties struct {
Src *string `android:"path"`
}
type platformCompatConfig struct {
android.ModuleBase
+ android.SdkBase
properties platformCompatConfigProperties
installDirPath android.InstallPath
@@ -46,7 +61,7 @@
metadataFile android.OutputPath
}
-func (p *platformCompatConfig) compatConfigMetadata() android.OutputPath {
+func (p *platformCompatConfig) compatConfigMetadata() android.Path {
return p.metadataFile
}
@@ -58,24 +73,180 @@
return "compatconfig"
}
+type platformCompatConfigMetadataProvider interface {
+ compatConfigMetadata() android.Path
+}
+
type PlatformCompatConfigIntf interface {
android.Module
- compatConfigMetadata() android.OutputPath
CompatConfig() android.OutputPath
// Sub dir under etc dir.
SubDir() string
}
var _ PlatformCompatConfigIntf = (*platformCompatConfig)(nil)
+var _ platformCompatConfigMetadataProvider = (*platformCompatConfig)(nil)
+
+func (p *platformCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ rule := android.NewRuleBuilder(pctx, ctx)
+
+ configFileName := p.Name() + ".xml"
+ metadataFileName := p.Name() + "_meta.xml"
+ p.configFile = android.PathForModuleOut(ctx, configFileName).OutputPath
+ p.metadataFile = android.PathForModuleOut(ctx, metadataFileName).OutputPath
+ path := android.PathForModuleSrc(ctx, String(p.properties.Src))
+
+ rule.Command().
+ BuiltTool("process-compat-config").
+ FlagWithInput("--jar ", path).
+ FlagWithOutput("--device-config ", p.configFile).
+ FlagWithOutput("--merged-config ", p.metadataFile)
+
+ p.installDirPath = android.PathForModuleInstall(ctx, "etc", "compatconfig")
+ rule.Build(configFileName, "Extract compat/compat_config.xml and install it")
+
+}
+
+func (p *platformCompatConfig) AndroidMkEntries() []android.AndroidMkEntries {
+ return []android.AndroidMkEntries{android.AndroidMkEntries{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(p.configFile),
+ Include: "$(BUILD_PREBUILT)",
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base())
+ },
+ },
+ }}
+}
+
+func PlatformCompatConfigFactory() android.Module {
+ module := &platformCompatConfig{}
+ module.AddProperties(&module.properties)
+ android.InitSdkAwareModule(module)
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ return module
+}
+
+type compatConfigMemberType struct {
+ android.SdkMemberTypeBase
+}
+
+func (b *compatConfigMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
+ mctx.AddVariationDependencies(nil, dependencyTag, names...)
+}
+
+func (b *compatConfigMemberType) IsInstance(module android.Module) bool {
+ _, ok := module.(*platformCompatConfig)
+ return ok
+}
+
+func (b *compatConfigMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
+ return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_platform_compat_config")
+}
+
+func (b *compatConfigMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
+ return &compatConfigSdkMemberProperties{}
+}
+
+type compatConfigSdkMemberProperties struct {
+ android.SdkMemberPropertiesBase
+
+ Metadata android.Path
+}
+
+func (b *compatConfigSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
+ module := variant.(*platformCompatConfig)
+ b.Metadata = module.metadataFile
+}
+
+func (b *compatConfigSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
+ builder := ctx.SnapshotBuilder()
+ if b.Metadata != nil {
+ snapshotRelativePath := filepath.Join("compat_configs", ctx.Name(), b.Metadata.Base())
+ builder.CopyToSnapshot(b.Metadata, snapshotRelativePath)
+ propertySet.AddProperty("metadata", snapshotRelativePath)
+ }
+}
+
+var _ android.SdkMemberType = (*compatConfigMemberType)(nil)
+
+// A prebuilt version of the platform compat config module.
+type prebuiltCompatConfigModule struct {
+ android.ModuleBase
+ android.SdkBase
+ prebuilt android.Prebuilt
+
+ properties prebuiltCompatConfigProperties
+
+ metadataFile android.Path
+}
+
+type prebuiltCompatConfigProperties struct {
+ Metadata *string `android:"path"`
+}
+
+func (module *prebuiltCompatConfigModule) Prebuilt() *android.Prebuilt {
+ return &module.prebuilt
+}
+
+func (module *prebuiltCompatConfigModule) Name() string {
+ return module.prebuilt.Name(module.ModuleBase.Name())
+}
+
+func (module *prebuiltCompatConfigModule) compatConfigMetadata() android.Path {
+ return module.metadataFile
+}
+
+var _ platformCompatConfigMetadataProvider = (*prebuiltCompatConfigModule)(nil)
+
+func (module *prebuiltCompatConfigModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ module.metadataFile = module.prebuilt.SingleSourcePath(ctx)
+}
+
+// A prebuilt version of platform_compat_config that provides the metadata.
+func prebuiltCompatConfigFactory() android.Module {
+ m := &prebuiltCompatConfigModule{}
+ m.AddProperties(&m.properties)
+ android.InitSingleSourcePrebuiltModule(m, &m.properties, "Metadata")
+ android.InitSdkAwareModule(m)
+ android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+ return m
+}
// compat singleton rules
+type platformCompatConfigSingleton struct {
+ metadata android.Path
+}
+
+// isModulePreferredByCompatConfig checks to see whether the module is preferred for use by
+// platform compat config.
+func isModulePreferredByCompatConfig(module android.Module) bool {
+ // A versioned prebuilt_platform_compat_config, i.e. foo-platform-compat-config@current should be
+ // ignored.
+ if s, ok := module.(android.SdkAware); ok {
+ if !s.ContainingSdk().Unversioned() {
+ return false
+ }
+ }
+
+ return android.IsModulePreferred(module)
+}
+
func (p *platformCompatConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
var compatConfigMetadata android.Paths
ctx.VisitAllModules(func(module android.Module) {
- if c, ok := module.(PlatformCompatConfigIntf); ok {
+ if !module.Enabled() {
+ return
+ }
+ if c, ok := module.(platformCompatConfigMetadataProvider); ok {
+ if !isModulePreferredByCompatConfig(module) {
+ return
+ }
metadata := c.compatConfigMetadata()
compatConfigMetadata = append(compatConfigMetadata, metadata)
}
@@ -105,51 +276,10 @@
}
}
-func (p *platformCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- rule := android.NewRuleBuilder(pctx, ctx)
-
- configFileName := p.Name() + ".xml"
- metadataFileName := p.Name() + "_meta.xml"
- p.configFile = android.PathForModuleOut(ctx, configFileName).OutputPath
- p.metadataFile = android.PathForModuleOut(ctx, metadataFileName).OutputPath
- path := android.PathForModuleSrc(ctx, String(p.properties.Src))
-
- rule.Command().
- BuiltTool("process-compat-config").
- FlagWithInput("--jar ", path).
- FlagWithOutput("--device-config ", p.configFile).
- FlagWithOutput("--merged-config ", p.metadataFile)
-
- p.installDirPath = android.PathForModuleInstall(ctx, "etc", "compatconfig")
- rule.Build(configFileName, "Extract compat/compat_config.xml and install it")
-
-}
-
-func (p *platformCompatConfig) AndroidMkEntries() []android.AndroidMkEntries {
- return []android.AndroidMkEntries{android.AndroidMkEntries{
- Class: "ETC",
- OutputFile: android.OptionalPathForPath(p.configFile),
- Include: "$(BUILD_PREBUILT)",
- ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
- entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base())
- },
- },
- }}
-}
-
func platformCompatConfigSingletonFactory() android.Singleton {
return &platformCompatConfigSingleton{}
}
-func PlatformCompatConfigFactory() android.Module {
- module := &platformCompatConfig{}
- module.AddProperties(&module.properties)
- android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
- return module
-}
-
//============== merged_compat_config =================
type globalCompatConfigProperties struct {
// name of the file into which the metadata will be copied.
diff --git a/java/platform_compat_config_test.go b/java/platform_compat_config_test.go
new file mode 100644
index 0000000..80d991c
--- /dev/null
+++ b/java/platform_compat_config_test.go
@@ -0,0 +1,44 @@
+// Copyright 2021 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 (
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestPlatformCompatConfig(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithPlatformCompatConfig,
+ android.FixtureWithRootAndroidBp(`
+ platform_compat_config {
+ name: "myconfig2",
+ }
+ platform_compat_config {
+ name: "myconfig1",
+ }
+ platform_compat_config {
+ name: "myconfig3",
+ }
+ `),
+ ).RunTest(t)
+
+ CheckMergedCompatConfigInputs(t, result, "myconfig",
+ "out/soong/.intermediates/myconfig1/myconfig1_meta.xml",
+ "out/soong/.intermediates/myconfig2/myconfig2_meta.xml",
+ "out/soong/.intermediates/myconfig3/myconfig3_meta.xml",
+ )
+}
diff --git a/java/plugin.go b/java/plugin.go
index 947c286..297ac2c 100644
--- a/java/plugin.go
+++ b/java/plugin.go
@@ -17,7 +17,11 @@
import "android/soong/android"
func init() {
- android.RegisterModuleType("java_plugin", PluginFactory)
+ registerJavaPluginBuildComponents(android.InitRegistrationContext)
+}
+
+func registerJavaPluginBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("java_plugin", PluginFactory)
}
// A java_plugin module describes a host java library that will be used by javac as an annotation processor.
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index bcc6cc0..c33e6c2 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -15,12 +15,14 @@
package java
import (
- "sort"
+ "fmt"
+ "strconv"
"strings"
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/genrule"
)
func init() {
@@ -35,6 +37,12 @@
// list of api version directories
Api_dirs []string
+ // The next API directory can optionally point to a directory where
+ // files incompatibility-tracking files are stored for the current
+ // "in progress" API. Each module present in one of the api_dirs will have
+ // a <module>-incompatibilities.api.<scope>.latest module created.
+ Next_api_dir *string
+
// The sdk_version of java_import modules generated based on jar files.
// Defaults to "current"
Imports_sdk_version *string
@@ -98,28 +106,50 @@
mctx.CreateModule(ImportFactory, &props)
}
-func createFilegroup(mctx android.LoadHookContext, module string, scope string, apiver string, path string) {
- fgName := module + ".api." + scope + "." + apiver
- filegroupProps := struct {
+func createApiModule(mctx android.LoadHookContext, name string, path string) {
+ genruleProps := struct {
Name *string
Srcs []string
+ Out []string
+ Cmd *string
}{}
- filegroupProps.Name = proptools.StringPtr(fgName)
- filegroupProps.Srcs = []string{path}
- mctx.CreateModule(android.FileGroupFactory, &filegroupProps)
+ genruleProps.Name = proptools.StringPtr(name)
+ genruleProps.Srcs = []string{path}
+ genruleProps.Out = []string{name}
+ genruleProps.Cmd = proptools.StringPtr("cp $(in) $(out)")
+ mctx.CreateModule(genrule.GenRuleFactory, &genruleProps)
+}
+
+func createEmptyFile(mctx android.LoadHookContext, name string) {
+ props := struct {
+ Name *string
+ Cmd *string
+ Out []string
+ }{}
+ props.Name = proptools.StringPtr(name)
+ props.Out = []string{name}
+ props.Cmd = proptools.StringPtr("touch $(genDir)/" + name)
+ mctx.CreateModule(genrule.GenRuleFactory, &props)
}
func getPrebuiltFiles(mctx android.LoadHookContext, p *prebuiltApis, name string) []string {
- mydir := mctx.ModuleDir() + "/"
var files []string
for _, apiver := range p.properties.Api_dirs {
- for _, scope := range []string{"public", "system", "test", "core", "module-lib", "system-server"} {
- vfiles, err := mctx.GlobWithDeps(mydir+apiver+"/"+scope+"/"+name, nil)
- if err != nil {
- mctx.ModuleErrorf("failed to glob %s files under %q: %s", name, mydir+apiver+"/"+scope, err)
- }
- files = append(files, vfiles...)
+ files = append(files, getPrebuiltFilesInSubdir(mctx, apiver, name)...)
+ }
+ return files
+}
+
+func getPrebuiltFilesInSubdir(mctx android.LoadHookContext, subdir string, name string) []string {
+ var files []string
+ dir := mctx.ModuleDir() + "/" + subdir
+ for _, scope := range []string{"public", "system", "test", "core", "module-lib", "system-server"} {
+ glob := fmt.Sprintf("%s/%s/%s", dir, scope, name)
+ vfiles, err := mctx.GlobWithDeps(glob, nil)
+ if err != nil {
+ mctx.ModuleErrorf("failed to glob %s files under %q: %s", name, dir+"/"+scope, err)
}
+ files = append(files, vfiles...)
}
return files
}
@@ -148,7 +178,7 @@
props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", "public", apiver))
props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", "public", apiver))
- mctx.CreateModule(SystemModulesFactory, &props)
+ mctx.CreateModule(systemModulesImportFactory, &props)
}
func prebuiltSdkSystemModules(mctx android.LoadHookContext, p *prebuiltApis) {
@@ -173,41 +203,69 @@
// construct a map to find out the latest api file path
// for each (<module>, <scope>) pair.
type latestApiInfo struct {
- module string
- scope string
- apiver string
- path string
+ module string
+ scope string
+ version int
+ path string
+ }
+
+ // Create modules for all (<module>, <scope, <version>) triplets,
+ // and a "latest" module variant for each (<module>, <scope>) pair
+ apiModuleName := func(module, scope, version string) string {
+ return module + ".api." + scope + "." + version
}
m := make(map[string]latestApiInfo)
-
for _, f := range files {
- // create a filegroup for each api txt file
localPath := strings.TrimPrefix(f, mydir)
module, apiver, scope := parseApiFilePath(mctx, localPath)
- createFilegroup(mctx, module, scope, apiver, localPath)
+ createApiModule(mctx, apiModuleName(module, scope, apiver), localPath)
- // find the latest apiver
- key := module + "." + scope
- info, ok := m[key]
- if !ok {
- m[key] = latestApiInfo{module, scope, apiver, localPath}
- } else if len(apiver) > len(info.apiver) || (len(apiver) == len(info.apiver) &&
- strings.Compare(apiver, info.apiver) > 0) {
- info.apiver = apiver
- info.path = localPath
- m[key] = info
+ version, err := strconv.Atoi(apiver)
+ if err != nil {
+ mctx.ModuleErrorf("Found finalized API files in non-numeric dir %v", apiver)
+ return
+ }
+
+ // Track latest version of each module/scope, except for incompatibilities
+ if !strings.HasSuffix(module, "incompatibilities") {
+ key := module + "." + scope
+ info, ok := m[key]
+ if !ok {
+ m[key] = latestApiInfo{module, scope, version, localPath}
+ } else if version > info.version {
+ info.version = version
+ info.path = localPath
+ m[key] = info
+ }
}
}
- // create filegroups for the latest version of (<module>, <scope>) pairs
- // sort the keys in order to make build.ninja stable
- keys := make([]string, 0, len(m))
- for k := range m {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- for _, k := range keys {
+
+ // Sort the keys in order to make build.ninja stable
+ for _, k := range android.SortedStringKeys(m) {
info := m[k]
- createFilegroup(mctx, info.module, info.scope, "latest", info.path)
+ name := apiModuleName(info.module, info.scope, "latest")
+ createApiModule(mctx, name, info.path)
+ }
+
+ // Create incompatibilities tracking files for all modules, if we have a "next" api.
+ incompatibilities := make(map[string]bool)
+ if nextApiDir := String(p.properties.Next_api_dir); nextApiDir != "" {
+ files := getPrebuiltFilesInSubdir(mctx, nextApiDir, "api/*incompatibilities.txt")
+ for _, f := range files {
+ localPath := strings.TrimPrefix(f, mydir)
+ filename, _, scope := parseApiFilePath(mctx, localPath)
+ referencedModule := strings.TrimSuffix(filename, "-incompatibilities")
+
+ createApiModule(mctx, apiModuleName(referencedModule+"-incompatibilities", scope, "latest"), localPath)
+
+ incompatibilities[referencedModule+"."+scope] = true
+ }
+ }
+ // Create empty incompatibilities files for remaining modules
+ for _, k := range android.SortedStringKeys(m) {
+ if _, ok := incompatibilities[k]; !ok {
+ createEmptyFile(mctx, apiModuleName(m[k].module+"-incompatibilities", m[k].scope, "latest"))
+ }
}
}
@@ -219,10 +277,10 @@
}
}
-// prebuilt_apis is a meta-module that generates filegroup modules for all
-// API txt files found under the directory where the Android.bp is located.
+// prebuilt_apis is a meta-module that generates modules for all API txt files
+// found under the directory where the Android.bp is located.
// Specifically, an API file located at ./<ver>/<scope>/api/<module>.txt
-// generates a filegroup module named <module>-api.<scope>.<ver>.
+// generates a module named <module>-api.<scope>.<ver>.
//
// It also creates <module>-api.<scope>.latest for the latest <ver>.
//
diff --git a/java/proto.go b/java/proto.go
index dc5519f..8731822 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -82,7 +82,7 @@
case "lite", "":
ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-lite")
case "full":
- if ctx.Host() {
+ if ctx.Host() || ctx.BazelConversionMode() {
ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-full")
} else {
ctx.PropertyErrorf("proto.type", "full java protos only supported on the host")
@@ -94,7 +94,7 @@
}
}
-func protoFlags(ctx android.ModuleContext, j *CompilerProperties, p *android.ProtoProperties,
+func protoFlags(ctx android.ModuleContext, j *CommonProperties, p *android.ProtoProperties,
flags javaBuilderFlags) javaBuilderFlags {
flags.proto = android.GetProtoFlags(ctx, p)
diff --git a/java/robolectric.go b/java/robolectric.go
index c821e5b..00f233e 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -31,13 +31,15 @@
}
var robolectricDefaultLibs = []string{
- "Robolectric_all-target",
"mockito-robolectric-prebuilt",
"truth-prebuilt",
// TODO(ccross): this is not needed at link time
"junitxml",
}
+const robolectricCurrentLib = "Robolectric_all-target"
+const robolectricPrebuiltLibPattern = "platform-robolectric-%s-prebuilt"
+
var (
roboCoverageLibsTag = dependencyTag{name: "roboCoverageLibs"}
roboRuntimesTag = dependencyTag{name: "roboRuntimes"}
@@ -57,6 +59,10 @@
// Number of shards to use when running the tests.
Shards *int64
}
+
+ // The version number of a robolectric prebuilt to use from prebuilts/misc/common/robolectric
+ // instead of the one built from source in external/robolectric-shadows.
+ Robolectric_prebuilt_version *string
}
type robolectricTest struct {
@@ -94,6 +100,12 @@
ctx.PropertyErrorf("instrumentation_for", "missing required instrumented module")
}
+ if v := String(r.robolectricProperties.Robolectric_prebuilt_version); v != "" {
+ ctx.AddVariationDependencies(nil, libTag, fmt.Sprintf(robolectricPrebuiltLibPattern, v))
+ } else {
+ ctx.AddVariationDependencies(nil, libTag, robolectricCurrentLib)
+ }
+
ctx.AddVariationDependencies(nil, libTag, robolectricDefaultLibs...)
ctx.AddVariationDependencies(nil, roboCoverageLibsTag, r.robolectricProperties.Coverage_libs...)
@@ -148,10 +160,10 @@
}
for _, dep := range ctx.GetDirectDepsWithTag(libTag) {
- m := dep.(Dependency)
- r.libs = append(r.libs, m.BaseModuleName())
- if !android.InList(m.BaseModuleName(), config.FrameworkLibraries) {
- combinedJarJars = append(combinedJarJars, m.ImplementationAndResourcesJars()...)
+ m := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+ r.libs = append(r.libs, ctx.OtherModuleName(dep))
+ if !android.InList(ctx.OtherModuleName(dep), config.FrameworkLibraries) {
+ combinedJarJars = append(combinedJarJars, m.ImplementationAndResourcesJars...)
}
}
@@ -245,10 +257,10 @@
srcJarDeps := append(android.Paths(nil), instrumentedApp.srcJarDeps...)
for _, m := range ctx.GetDirectDepsWithTag(roboCoverageLibsTag) {
- if dep, ok := m.(Dependency); ok {
- depSrcJarArgs, depSrcJarDeps := dep.SrcJarArgs()
- srcJarArgs = append(srcJarArgs, depSrcJarArgs...)
- srcJarDeps = append(srcJarDeps, depSrcJarDeps...)
+ if ctx.OtherModuleHasProvider(m, JavaInfoProvider) {
+ dep := ctx.OtherModuleProvider(m, JavaInfoProvider).(JavaInfo)
+ srcJarArgs = append(srcJarArgs, dep.SrcJarArgs...)
+ srcJarDeps = append(srcJarDeps, dep.SrcJarDeps...)
}
}
@@ -298,7 +310,11 @@
if t := r.robolectricProperties.Test_options.Timeout; t != nil {
fmt.Fprintln(w, "LOCAL_ROBOTEST_TIMEOUT :=", *t)
}
- fmt.Fprintln(w, "-include external/robolectric-shadows/run_robotests.mk")
+ if v := String(r.robolectricProperties.Robolectric_prebuilt_version); v != "" {
+ fmt.Fprintf(w, "-include prebuilts/misc/common/robolectric/%s/run_robotests.mk\n", v)
+ } else {
+ fmt.Fprintln(w, "-include external/robolectric-shadows/run_robotests.mk")
+ }
}
// An android_robolectric_test module compiles tests against the Robolectric framework that can run on the local host
diff --git a/java/rro.go b/java/rro.go
index 98cd379..2e58c04 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -55,7 +55,11 @@
// only when the ro.boot.vendor.overlay.theme system property is set to the same value.
Theme *string
- // if not blank, set to the version of the sdk to compile against.
+ // If not blank, set to the version of the sdk to compile against. This
+ // can be either an API version (e.g. "29" for API level 29 AKA Android 10)
+ // or special subsets of the current platform, for example "none", "current",
+ // "core", "system", "test". See build/soong/java/sdk.go for the full and
+ // up-to-date list of possible values.
// Defaults to compiling against the current platform.
Sdk_version *string
@@ -87,7 +91,7 @@
}
func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) {
- sdkDep := decodeSdkDep(ctx, sdkContext(r))
+ sdkDep := decodeSdkDep(ctx, android.SdkContext(r))
if sdkDep.hasFrameworkLibs() {
r.aapt.deps(ctx, sdkDep)
}
@@ -137,23 +141,23 @@
ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile)
}
-func (r *RuntimeResourceOverlay) sdkVersion() sdkSpec {
- return sdkSpecFrom(String(r.properties.Sdk_version))
+func (r *RuntimeResourceOverlay) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ return android.SdkSpecFrom(ctx, String(r.properties.Sdk_version))
}
-func (r *RuntimeResourceOverlay) systemModules() string {
+func (r *RuntimeResourceOverlay) SystemModules() string {
return ""
}
-func (r *RuntimeResourceOverlay) minSdkVersion() sdkSpec {
+func (r *RuntimeResourceOverlay) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
if r.properties.Min_sdk_version != nil {
- return sdkSpecFrom(*r.properties.Min_sdk_version)
+ return android.SdkSpecFrom(ctx, *r.properties.Min_sdk_version)
}
- return r.sdkVersion()
+ return r.SdkVersion(ctx)
}
-func (r *RuntimeResourceOverlay) targetSdkVersion() sdkSpec {
- return r.sdkVersion()
+func (r *RuntimeResourceOverlay) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+ return r.SdkVersion(ctx)
}
func (r *RuntimeResourceOverlay) Certificate() Certificate {
diff --git a/java/rro_test.go b/java/rro_test.go
index 345f2ee..bad60bc 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -20,10 +20,11 @@
"testing"
"android/soong/android"
+ "android/soong/shared"
)
func TestRuntimeResourceOverlay(t *testing.T) {
- fs := map[string][]byte{
+ fs := android.MockFS{
"baz/res/res/values/strings.xml": nil,
"bar/res/res/values/strings.xml": nil,
}
@@ -56,12 +57,15 @@
sdk_version: "current",
resource_dirs: ["baz/res"],
}
- `
- config := testAppConfig(nil, bp, fs)
- ctx := testContext(config)
- run(t, ctx, config)
+ `
- m := ctx.ModuleForTests("foo", "android_common")
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithOverlayBuildComponents,
+ fs.AddToFixture(),
+ ).RunTestWithBp(t, bp)
+
+ m := result.ModuleForTests("foo", "android_common")
// Check AAPT2 link flags.
aapt2Flags := m.Output("package-res.apk").Args["flags"]
@@ -72,14 +76,14 @@
}
// Check overlay.list output for static_libs dependency.
- overlayList := m.Output("aapt2/overlay.list").Inputs.Strings()
- staticLibPackage := buildDir + "/.intermediates/bar/android_common/package-res.apk"
+ overlayList := android.PathsRelativeToTop(m.Output("aapt2/overlay.list").Inputs)
+ staticLibPackage := "out/soong/.intermediates/bar/android_common/package-res.apk"
if !inList(staticLibPackage, overlayList) {
t.Errorf("Stactic lib res package %q missing in overlay list: %q", staticLibPackage, overlayList)
}
// Check AAPT2 link flags for resource_libs dependency.
- resourceLibFlag := "-I " + buildDir + "/.intermediates/baz/android_common/package-res.apk"
+ resourceLibFlag := "-I " + "out/soong/.intermediates/baz/android_common/package-res.apk"
if !strings.Contains(aapt2Flags, resourceLibFlag) {
t.Errorf("Resource lib flag %q missing in aapt2 link flags: %q", resourceLibFlag, aapt2Flags)
}
@@ -96,7 +100,7 @@
if expected != signingFlag {
t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
}
- androidMkEntries := android.AndroidMkEntriesForTest(t, config, "", m.Module())[0]
+ androidMkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0]
path := androidMkEntries.EntryMap["LOCAL_CERTIFICATE"]
expectedPath := []string{"build/make/target/product/security/platform.x509.pem"}
if !reflect.DeepEqual(path, expectedPath) {
@@ -105,19 +109,15 @@
// Check device location.
path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
- expectedPath = []string{"/tmp/target/product/test_device/product/overlay"}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
- }
+ expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay")}
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
// A themed module has a different device location
- m = ctx.ModuleForTests("foo_themed", "android_common")
- androidMkEntries = android.AndroidMkEntriesForTest(t, config, "", m.Module())[0]
+ m = result.ModuleForTests("foo_themed", "android_common")
+ androidMkEntries = android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0]
path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
- expectedPath = []string{"/tmp/target/product/test_device/product/overlay/faza"}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
- }
+ expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay/faza")}
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
overrides := androidMkEntries.EntryMap["LOCAL_OVERRIDES_PACKAGES"]
expectedOverrides := []string{"foo"}
@@ -159,11 +159,9 @@
}
// Check device location.
- path := android.AndroidMkEntriesForTest(t, config, "", m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
- expectedPath := []string{"/tmp/target/product/test_device/product/overlay/default_theme"}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_MODULE_PATH value: %q, expected: %q", path, expectedPath)
- }
+ path := android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
+ expectedPath := []string{shared.JoinPath("out/target/product/test_device/product/overlay/default_theme")}
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path)
//
// RRO module without defaults
@@ -178,11 +176,9 @@
}
// Check device location.
- path = android.AndroidMkEntriesForTest(t, config, "", m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
- expectedPath = []string{"/tmp/target/product/test_device/system/overlay"}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
- }
+ path = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
+ expectedPath = []string{shared.JoinPath("out/target/product/test_device/system/overlay")}
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path)
}
func TestOverrideRuntimeResourceOverlay(t *testing.T) {
@@ -213,7 +209,7 @@
}{
{
variantName: "android_common",
- apkPath: "/target/product/test_device/product/overlay/foo_overlay.apk",
+ apkPath: "out/soong/target/product/test_device/product/overlay/foo_overlay.apk",
overrides: nil,
targetVariant: "android_common",
packageFlag: "",
@@ -221,7 +217,7 @@
},
{
variantName: "android_common_bar_overlay",
- apkPath: "/target/product/test_device/product/overlay/bar_overlay.apk",
+ apkPath: "out/soong/target/product/test_device/product/overlay/bar_overlay.apk",
overrides: []string{"foo_overlay"},
targetVariant: "android_common_bar",
packageFlag: "com.android.bar.overlay",
@@ -232,18 +228,7 @@
variant := ctx.ModuleForTests("foo_overlay", expected.variantName)
// Check the final apk name
- outputs := variant.AllOutputs()
- expectedApkPath := buildDir + expected.apkPath
- found := false
- for _, o := range outputs {
- if o == expectedApkPath {
- found = true
- break
- }
- }
- if !found {
- t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs)
- }
+ variant.Output(expected.apkPath)
// Check if the overrides field values are correctly aggregated.
mod := variant.Module().(*RuntimeResourceOverlay)
@@ -263,54 +248,41 @@
func TestEnforceRRO_propagatesToDependencies(t *testing.T) {
testCases := []struct {
- name string
- enforceRROTargets []string
- enforceRROExemptTargets []string
- rroDirs map[string][]string
+ name string
+ enforceRROTargets []string
+ rroDirs map[string][]string
}{
{
- name: "no RRO",
- enforceRROTargets: nil,
- enforceRROExemptTargets: nil,
+ name: "no RRO",
+ enforceRROTargets: nil,
rroDirs: map[string][]string{
"foo": nil,
"bar": nil,
},
},
{
- name: "enforce RRO on all",
- enforceRROTargets: []string{"*"},
- enforceRROExemptTargets: nil,
+ name: "enforce RRO on all",
+ enforceRROTargets: []string{"*"},
rroDirs: map[string][]string{
"foo": {"product/vendor/blah/overlay/lib2/res"},
"bar": {"product/vendor/blah/overlay/lib2/res"},
},
},
{
- name: "enforce RRO on foo",
- enforceRROTargets: []string{"foo"},
- enforceRROExemptTargets: nil,
+ name: "enforce RRO on foo",
+ enforceRROTargets: []string{"foo"},
rroDirs: map[string][]string{
"foo": {"product/vendor/blah/overlay/lib2/res"},
"bar": {"product/vendor/blah/overlay/lib2/res"},
},
},
- {
- name: "enforce RRO on foo, bar exempted",
- enforceRROTargets: []string{"foo"},
- enforceRROExemptTargets: []string{"bar"},
- rroDirs: map[string][]string{
- "foo": {"product/vendor/blah/overlay/lib2/res"},
- "bar": nil,
- },
- },
}
productResourceOverlays := []string{
"product/vendor/blah/overlay",
}
- fs := map[string][]byte{
+ fs := android.MockFS{
"lib2/res/values/strings.xml": nil,
"product/vendor/blah/overlay/lib2/res/values/strings.xml": nil,
}
@@ -346,22 +318,22 @@
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
- config := testAppConfig(nil, bp, fs)
- config.TestProductVariables.ProductResourceOverlays = productResourceOverlays
- if testCase.enforceRROTargets != nil {
- config.TestProductVariables.EnforceRROTargets = testCase.enforceRROTargets
- }
- if testCase.enforceRROExemptTargets != nil {
- config.TestProductVariables.EnforceRROExemptedTargets = testCase.enforceRROExemptTargets
- }
-
- ctx := testContext(config)
- run(t, ctx, config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithOverlayBuildComponents,
+ fs.AddToFixture(),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.ProductResourceOverlays = productResourceOverlays
+ if testCase.enforceRROTargets != nil {
+ variables.EnforceRROTargets = testCase.enforceRROTargets
+ }
+ }),
+ ).RunTestWithBp(t, bp)
modules := []string{"foo", "bar"}
for _, moduleName := range modules {
- module := ctx.ModuleForTests(moduleName, "android_common")
- mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
+ module := result.ModuleForTests(moduleName, "android_common")
+ mkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, module.Module())[0]
actualRRODirs := mkEntries.EntryMap["LOCAL_SOONG_PRODUCT_RRO_DIRS"]
if !reflect.DeepEqual(actualRRODirs, testCase.rroDirs[moduleName]) {
t.Errorf("exected %s LOCAL_SOONG_PRODUCT_RRO_DIRS entry: %v\ngot:%q",
diff --git a/java/sdk.go b/java/sdk.go
index a68abfb..d6e20a7 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -19,7 +19,6 @@
"path/filepath"
"sort"
"strconv"
- "strings"
"android/soong/android"
"android/soong/java/config"
@@ -38,19 +37,6 @@
var nonUpdatableFrameworkAidlPathKey = android.NewOnceKey("nonUpdatableFrameworkAidlPathKey")
var apiFingerprintPathKey = android.NewOnceKey("apiFingerprintPathKey")
-type sdkContext interface {
- // sdkVersion returns sdkSpec that corresponds to the sdk_version property of the current module
- sdkVersion() sdkSpec
- // systemModules returns the system_modules property of the current module, or an empty string if it is not set.
- systemModules() string
- // minSdkVersion returns sdkSpec that corresponds to the min_sdk_version property of the current module,
- // or from sdk_version if it is not set.
- minSdkVersion() sdkSpec
- // targetSdkVersion returns the sdkSpec that corresponds to the target_sdk_version property of the current module,
- // or from sdk_version if it is not set.
- targetSdkVersion() sdkSpec
-}
-
func UseApiFingerprint(ctx android.BaseModuleContext) bool {
if ctx.Config().UnbundledBuild() &&
!ctx.Config().AlwaysUsePrebuiltSdks() &&
@@ -60,318 +46,41 @@
return false
}
-// sdkKind represents a particular category of an SDK spec like public, system, test, etc.
-type sdkKind int
-
-const (
- sdkInvalid sdkKind = iota
- sdkNone
- sdkCore
- sdkCorePlatform
- sdkPublic
- sdkSystem
- sdkTest
- sdkModule
- sdkSystemServer
- sdkPrivate
-)
-
-// String returns the string representation of this sdkKind
-func (k sdkKind) String() string {
- switch k {
- case sdkPrivate:
- return "private"
- case sdkNone:
- return "none"
- case sdkPublic:
- return "public"
- case sdkSystem:
- return "system"
- case sdkTest:
- return "test"
- case sdkCore:
- return "core"
- case sdkCorePlatform:
- return "core_platform"
- case sdkModule:
- return "module-lib"
- case sdkSystemServer:
- return "system-server"
- default:
- return "invalid"
- }
-}
-
-// sdkVersion represents a specific version number of an SDK spec of a particular kind
-type sdkVersion int
-
-const (
- // special version number for a not-yet-frozen SDK
- sdkVersionCurrent sdkVersion = sdkVersion(android.FutureApiLevelInt)
- // special version number to be used for SDK specs where version number doesn't
- // make sense, e.g. "none", "", etc.
- sdkVersionNone sdkVersion = sdkVersion(0)
-)
-
-// isCurrent checks if the sdkVersion refers to the not-yet-published version of an sdkKind
-func (v sdkVersion) isCurrent() bool {
- return v == sdkVersionCurrent
-}
-
-// isNumbered checks if the sdkVersion refers to the published (a.k.a numbered) version of an sdkKind
-func (v sdkVersion) isNumbered() bool {
- return !v.isCurrent() && v != sdkVersionNone
-}
-
-// String returns the string representation of this sdkVersion.
-func (v sdkVersion) String() string {
- if v.isCurrent() {
- return "current"
- } else if v.isNumbered() {
- return strconv.Itoa(int(v))
- }
- return "(no version)"
-}
-
-func (v sdkVersion) ApiLevel(ctx android.EarlyModuleContext) android.ApiLevel {
- return android.ApiLevelOrPanic(ctx, v.String())
-}
-
-// asNumberString directly converts the numeric value of this sdk version as a string.
-// When isNumbered() is true, this method is the same as String(). However, for sdkVersionCurrent
-// and sdkVersionNone, this returns 10000 and 0 while String() returns "current" and "(no version"),
-// respectively.
-func (v sdkVersion) asNumberString() string {
- return strconv.Itoa(int(v))
-}
-
-// sdkSpec represents the kind and the version of an SDK for a module to build against
-type sdkSpec struct {
- kind sdkKind
- version sdkVersion
- raw string
-}
-
-func (s sdkSpec) String() string {
- return fmt.Sprintf("%s_%s", s.kind, s.version)
-}
-
-// valid checks if this sdkSpec is well-formed. Note however that true doesn't mean that the
-// specified SDK actually exists.
-func (s sdkSpec) valid() bool {
- return s.kind != sdkInvalid
-}
-
-// specified checks if this sdkSpec is well-formed and is not "".
-func (s sdkSpec) specified() bool {
- return s.valid() && s.kind != sdkPrivate
-}
-
-// whether the API surface is managed and versioned, i.e. has .txt file that
-// get frozen on SDK freeze and changes get reviewed by API council.
-func (s sdkSpec) stable() bool {
- if !s.specified() {
- return false
- }
- switch s.kind {
- case sdkNone:
- // there is nothing to manage and version in this case; de facto stable API.
- return true
- case sdkCore, sdkPublic, sdkSystem, sdkModule, sdkSystemServer:
- return true
- case sdkCorePlatform, sdkTest, sdkPrivate:
- return false
- default:
- panic(fmt.Errorf("unknown sdkKind=%v", s.kind))
- }
- return false
-}
-
-// prebuiltSdkAvailableForUnbundledBuilt tells whether this sdkSpec can have a prebuilt SDK
-// that can be used for unbundled builds.
-func (s sdkSpec) prebuiltSdkAvailableForUnbundledBuild() bool {
- // "", "none", and "core_platform" are not available for unbundled build
- // as we don't/can't have prebuilt stub for the versions
- return s.kind != sdkPrivate && s.kind != sdkNone && s.kind != sdkCorePlatform
-}
-
-func (s sdkSpec) forVendorPartition(ctx android.EarlyModuleContext) sdkSpec {
- // If BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES has a numeric value,
- // use it instead of "current" for the vendor partition.
- currentSdkVersion := ctx.DeviceConfig().CurrentApiLevelForVendorModules()
- if currentSdkVersion == "current" {
- return s
- }
-
- if s.kind == sdkPublic || s.kind == sdkSystem {
- if s.version.isCurrent() {
- if i, err := strconv.Atoi(currentSdkVersion); err == nil {
- version := sdkVersion(i)
- return sdkSpec{s.kind, version, s.raw}
- }
- panic(fmt.Errorf("BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES must be either \"current\" or a number, but was %q", currentSdkVersion))
- }
- }
- return s
-}
-
-// usePrebuilt determines whether prebuilt SDK should be used for this sdkSpec with the given context.
-func (s sdkSpec) usePrebuilt(ctx android.EarlyModuleContext) bool {
- if s.version.isCurrent() {
- // "current" can be built from source and be from prebuilt SDK
- return ctx.Config().AlwaysUsePrebuiltSdks()
- } else if s.version.isNumbered() {
- // validation check
- if s.kind != sdkPublic && s.kind != sdkSystem && s.kind != sdkTest && s.kind != sdkModule {
- panic(fmt.Errorf("prebuilt SDK is not not available for sdkKind=%q", s.kind))
- return false
- }
- // numbered SDKs are always from prebuilt
- return true
- }
- // "", "none", "core_platform" fall here
- return false
-}
-
-// effectiveVersion converts an sdkSpec into the concrete sdkVersion that the module
-// should use. For modules targeting an unreleased SDK (meaning it does not yet have a number)
-// it returns android.FutureApiLevel(10000).
-func (s sdkSpec) effectiveVersion(ctx android.EarlyModuleContext) (sdkVersion, error) {
- if !s.valid() {
- return s.version, fmt.Errorf("invalid sdk version %q", s.raw)
- }
-
- if ctx.DeviceSpecific() || ctx.SocSpecific() {
- s = s.forVendorPartition(ctx)
- }
- if s.version.isNumbered() {
- return s.version, nil
- }
- return sdkVersion(ctx.Config().DefaultAppTargetSdk(ctx).FinalOrFutureInt()), nil
-}
-
-// effectiveVersionString converts an sdkSpec into the concrete version string that the module
-// should use. For modules targeting an unreleased SDK (meaning it does not yet have a number)
-// it returns the codename (P, Q, R, etc.)
-func (s sdkSpec) effectiveVersionString(ctx android.EarlyModuleContext) (string, error) {
- ver, err := s.effectiveVersion(ctx)
- if err == nil && int(ver) == ctx.Config().DefaultAppTargetSdk(ctx).FinalOrFutureInt() {
- return ctx.Config().DefaultAppTargetSdk(ctx).String(), nil
- }
- return ver.String(), err
-}
-
-func (s sdkSpec) defaultJavaLanguageVersion(ctx android.EarlyModuleContext) javaVersion {
- sdk, err := s.effectiveVersion(ctx)
+func defaultJavaLanguageVersion(ctx android.EarlyModuleContext, s android.SdkSpec) javaVersion {
+ sdk, err := s.EffectiveVersion(ctx)
if err != nil {
ctx.PropertyErrorf("sdk_version", "%s", err)
}
- if sdk <= 23 {
+ if sdk.FinalOrFutureInt() <= 23 {
return JAVA_VERSION_7
- } else if sdk <= 29 {
+ } else if sdk.FinalOrFutureInt() <= 29 {
return JAVA_VERSION_8
} else {
return JAVA_VERSION_9
}
}
-func sdkSpecFrom(str string) sdkSpec {
- switch str {
- // special cases first
- case "":
- return sdkSpec{sdkPrivate, sdkVersionNone, str}
- case "none":
- return sdkSpec{sdkNone, sdkVersionNone, str}
- case "core_platform":
- return sdkSpec{sdkCorePlatform, sdkVersionNone, str}
- default:
- // the syntax is [kind_]version
- sep := strings.LastIndex(str, "_")
-
- var kindString string
- if sep == 0 {
- return sdkSpec{sdkInvalid, sdkVersionNone, str}
- } else if sep == -1 {
- kindString = ""
- } else {
- kindString = str[0:sep]
- }
- versionString := str[sep+1 : len(str)]
-
- var kind sdkKind
- switch kindString {
- case "":
- kind = sdkPublic
- case "core":
- kind = sdkCore
- case "system":
- kind = sdkSystem
- case "test":
- kind = sdkTest
- case "module":
- kind = sdkModule
- case "system_server":
- kind = sdkSystemServer
- default:
- return sdkSpec{sdkInvalid, sdkVersionNone, str}
- }
-
- var version sdkVersion
- if versionString == "current" {
- version = sdkVersionCurrent
- } else if i, err := strconv.Atoi(versionString); err == nil {
- version = sdkVersion(i)
- } else {
- return sdkSpec{sdkInvalid, sdkVersionNone, str}
- }
-
- return sdkSpec{kind, version, str}
- }
-}
-
-func (s sdkSpec) validateSystemSdk(ctx android.EarlyModuleContext) bool {
- // Ensures that the specified system SDK version is one of BOARD_SYSTEMSDK_VERSIONS (for vendor/product Java module)
- // Assuming that BOARD_SYSTEMSDK_VERSIONS := 28 29,
- // sdk_version of the modules in vendor/product that use system sdk must be either system_28, system_29 or system_current
- if s.kind != sdkSystem || !s.version.isNumbered() {
- return true
- }
- allowedVersions := ctx.DeviceConfig().PlatformSystemSdkVersions()
- if ctx.DeviceSpecific() || ctx.SocSpecific() || (ctx.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
- systemSdkVersions := ctx.DeviceConfig().SystemSdkVersions()
- if len(systemSdkVersions) > 0 {
- allowedVersions = systemSdkVersions
- }
- }
- if len(allowedVersions) > 0 && !android.InList(s.version.String(), allowedVersions) {
- ctx.PropertyErrorf("sdk_version", "incompatible sdk version %q. System SDK version should be one of %q",
- s.raw, allowedVersions)
- return false
- }
- return true
-}
-
-func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext sdkContext) sdkDep {
- sdkVersion := sdkContext.sdkVersion()
- if !sdkVersion.valid() {
- ctx.PropertyErrorf("sdk_version", "invalid version %q", sdkVersion.raw)
+func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) sdkDep {
+ sdkVersion := sdkContext.SdkVersion(ctx)
+ if !sdkVersion.Valid() {
+ ctx.PropertyErrorf("sdk_version", "invalid version %q", sdkVersion.Raw)
return sdkDep{}
}
if ctx.DeviceSpecific() || ctx.SocSpecific() {
- sdkVersion = sdkVersion.forVendorPartition(ctx)
+ sdkVersion = sdkVersion.ForVendorPartition(ctx)
}
- if !sdkVersion.validateSystemSdk(ctx) {
+ if !sdkVersion.ValidateSystemSdk(ctx) {
return sdkDep{}
}
- if sdkVersion.usePrebuilt(ctx) {
- dir := filepath.Join("prebuilts", "sdk", sdkVersion.version.String(), sdkVersion.kind.String())
+ if sdkVersion.UsePrebuilt(ctx) {
+ dir := filepath.Join("prebuilts", "sdk", sdkVersion.ApiLevel.String(), sdkVersion.Kind.String())
jar := filepath.Join(dir, "android.jar")
// There's no aidl for other SDKs yet.
// TODO(77525052): Add aidl files for other SDKs too.
- publicDir := filepath.Join("prebuilts", "sdk", sdkVersion.version.String(), "public")
+ publicDir := filepath.Join("prebuilts", "sdk", sdkVersion.ApiLevel.String(), "public")
aidl := filepath.Join(publicDir, "framework.aidl")
jarPath := android.ExistentPathForSource(ctx, jar)
aidlPath := android.ExistentPathForSource(ctx, aidl)
@@ -380,23 +89,23 @@
if (!jarPath.Valid() || !aidlPath.Valid()) && ctx.Config().AllowMissingDependencies() {
return sdkDep{
invalidVersion: true,
- bootclasspath: []string{fmt.Sprintf("sdk_%s_%s_android", sdkVersion.kind, sdkVersion.version.String())},
+ bootclasspath: []string{fmt.Sprintf("sdk_%s_%s_android", sdkVersion.Kind, sdkVersion.ApiLevel.String())},
}
}
if !jarPath.Valid() {
- ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdkVersion.raw, jar)
+ ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdkVersion.Raw, jar)
return sdkDep{}
}
if !aidlPath.Valid() {
- ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdkVersion.raw, aidl)
+ ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdkVersion.Raw, aidl)
return sdkDep{}
}
var systemModules string
- if sdkVersion.defaultJavaLanguageVersion(ctx).usesJavaModules() {
- systemModules = "sdk_public_" + sdkVersion.version.String() + "_system_modules"
+ if defaultJavaLanguageVersion(ctx, sdkVersion).usesJavaModules() {
+ systemModules = "sdk_public_" + sdkVersion.ApiLevel.String() + "_system_modules"
}
return sdkDep{
@@ -418,8 +127,8 @@
}
}
- switch sdkVersion.kind {
- case sdkPrivate:
+ switch sdkVersion.Kind {
+ case android.SdkPrivate:
return sdkDep{
useModule: true,
systemModules: corePlatformSystemModules(ctx),
@@ -427,8 +136,8 @@
classpath: config.FrameworkLibraries,
frameworkResModule: "framework-res",
}
- case sdkNone:
- systemModules := sdkContext.systemModules()
+ case android.SdkNone:
+ systemModules := sdkContext.SystemModules()
if systemModules == "" {
ctx.PropertyErrorf("sdk_version",
`system_modules is required to be set to a non-empty value when sdk_version is "none", did you mean sdk_version: "core_platform"?`)
@@ -444,34 +153,34 @@
systemModules: systemModules,
bootclasspath: []string{systemModules},
}
- case sdkCorePlatform:
+ case android.SdkCorePlatform:
return sdkDep{
useModule: true,
systemModules: corePlatformSystemModules(ctx),
bootclasspath: corePlatformBootclasspathLibraries(ctx),
noFrameworksLibs: true,
}
- case sdkPublic:
+ case android.SdkPublic:
return toModule([]string{"android_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
- case sdkSystem:
+ case android.SdkSystem:
return toModule([]string{"android_system_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
- case sdkTest:
+ case android.SdkTest:
return toModule([]string{"android_test_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
- case sdkCore:
+ case android.SdkCore:
return sdkDep{
useModule: true,
bootclasspath: []string{"core.current.stubs", config.DefaultLambdaStubsLibrary},
systemModules: "core-current-stubs-system-modules",
noFrameworksLibs: true,
}
- case sdkModule:
+ case android.SdkModule:
// TODO(146757305): provide .apk and .aidl that have more APIs for modules
return toModule([]string{"android_module_lib_stubs_current"}, "framework-res", nonUpdatableFrameworkAidlPath(ctx))
- case sdkSystemServer:
+ case android.SdkSystemServer:
// TODO(146757305): provide .apk and .aidl that have more APIs for modules
return toModule([]string{"android_system_server_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
default:
- panic(fmt.Errorf("invalid sdk %q", sdkVersion.raw))
+ panic(fmt.Errorf("invalid sdk %q", sdkVersion.Raw))
}
}
@@ -566,10 +275,11 @@
ctx.VisitAllModules(func(module android.Module) {
// Collect dex jar paths for the modules listed above.
- if j, ok := module.(Dependency); ok {
+ if ctx.ModuleHasProvider(module, JavaInfoProvider) {
+ j := ctx.ModuleProvider(module, JavaInfoProvider).(JavaInfo)
name := ctx.ModuleName(module)
if i := android.IndexList(name, stubsModules); i != -1 {
- stubsJars[i] = j.HeaderJars()
+ stubsJars[i] = j.HeaderJars
}
}
})
@@ -640,14 +350,26 @@
if ctx.Config().PlatformSdkCodename() == "REL" {
cmd.Text("echo REL >").Output(out)
- } else if !ctx.Config().AlwaysUsePrebuiltSdks() {
- in, err := ctx.GlobWithDeps("frameworks/base/api/*current.txt", nil)
- if err != nil {
- ctx.Errorf("error globbing API files: %s", err)
+ } else if ctx.Config().FrameworksBaseDirExists(ctx) && !ctx.Config().AlwaysUsePrebuiltSdks() {
+ cmd.Text("cat")
+ apiTxtFileModules := []string{
+ "frameworks-base-api-current.txt",
+ "frameworks-base-api-system-current.txt",
+ "frameworks-base-api-module-lib-current.txt",
}
-
- cmd.Text("cat").
- Inputs(android.PathsForSource(ctx, in)).
+ count := 0
+ ctx.VisitAllModules(func(module android.Module) {
+ name := ctx.ModuleName(module)
+ if android.InList(name, apiTxtFileModules) {
+ cmd.Inputs(android.OutputFilesForModule(ctx, module, ""))
+ count++
+ }
+ })
+ if count != len(apiTxtFileModules) {
+ ctx.Errorf("Could not find all the expected API modules %v, found %d\n", apiTxtFileModules, count)
+ return
+ }
+ cmd.Input(android.PathForSource(ctx, "frameworks/base/services/api/current.txt")).
Text("| md5sum | cut -d' ' -f1 >").
Output(out)
} else {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index f279b95..e5ee397 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -60,12 +60,12 @@
apiScope *apiScope
// Function for extracting appropriate path information from the dependency.
- depInfoExtractor func(paths *scopePaths, dep android.Module) error
+ depInfoExtractor func(paths *scopePaths, ctx android.ModuleContext, dep android.Module) error
}
// Extract tag specific information from the dependency.
func (tag scopeDependencyTag) extractDepInfo(ctx android.ModuleContext, dep android.Module, paths *scopePaths) {
- err := tag.depInfoExtractor(paths, dep)
+ err := tag.depInfoExtractor(paths, ctx, dep)
if err != nil {
ctx.ModuleErrorf("has an invalid {scopeDependencyTag: %s} dependency on module %s: %s", tag.name, ctx.OtherModuleName(dep), err.Error())
}
@@ -201,8 +201,12 @@
return scope
}
+func (scope *apiScope) stubsLibraryModuleNameSuffix() string {
+ return ".stubs" + scope.moduleSuffix
+}
+
func (scope *apiScope) stubsLibraryModuleName(baseName string) string {
- return baseName + ".stubs" + scope.moduleSuffix
+ return baseName + scope.stubsLibraryModuleNameSuffix()
}
func (scope *apiScope) stubsSourceModuleName(baseName string) string {
@@ -452,6 +456,7 @@
// that references the latest released API and remove API specification files.
// * API specification filegroup -> <dist-stem>.api.<scope>.latest
// * Removed API specification filegroup -> <dist-stem>-removed.api.<scope>.latest
+ // * API incompatibilities baseline filegroup -> <dist-stem>-incompatibilities.api.<scope>.latest
Dist_stem *string
// A compatibility mode that allows historical API-tracking files to not exist.
@@ -539,13 +544,14 @@
stubsSrcJar android.OptionalPath
}
-func (paths *scopePaths) extractStubsLibraryInfoFromDependency(dep android.Module) error {
- if lib, ok := dep.(Dependency); ok {
- paths.stubsHeaderPath = lib.HeaderJars()
- paths.stubsImplPath = lib.ImplementationJars()
+func (paths *scopePaths) extractStubsLibraryInfoFromDependency(ctx android.ModuleContext, dep android.Module) error {
+ if ctx.OtherModuleHasProvider(dep, JavaInfoProvider) {
+ lib := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+ paths.stubsHeaderPath = lib.HeaderJars
+ paths.stubsImplPath = lib.ImplementationJars
return nil
} else {
- return fmt.Errorf("expected module that implements Dependency, e.g. java_library")
+ return fmt.Errorf("expected module that has JavaInfoProvider, e.g. java_library")
}
}
@@ -572,7 +578,7 @@
paths.removedApiFilePath = android.OptionalPathForPath(provider.RemovedApiFilePath())
}
-func (paths *scopePaths) extractApiInfoFromDep(dep android.Module) error {
+func (paths *scopePaths) extractApiInfoFromDep(ctx android.ModuleContext, dep android.Module) error {
return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) {
paths.extractApiInfoFromApiStubsProvider(provider)
})
@@ -582,13 +588,13 @@
paths.stubsSrcJar = android.OptionalPathForPath(provider.StubsSrcJar())
}
-func (paths *scopePaths) extractStubsSourceInfoFromDep(dep android.Module) error {
+func (paths *scopePaths) extractStubsSourceInfoFromDep(ctx android.ModuleContext, dep android.Module) error {
return paths.treatDepAsApiStubsSrcProvider(dep, func(provider ApiStubsSrcProvider) {
paths.extractStubsSourceInfoFromApiStubsProviders(provider)
})
}
-func (paths *scopePaths) extractStubsSourceAndApiInfoFromApiStubsProvider(dep android.Module) error {
+func (paths *scopePaths) extractStubsSourceAndApiInfoFromApiStubsProvider(ctx android.ModuleContext, dep android.Module) error {
return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) {
paths.extractApiInfoFromApiStubsProvider(provider)
paths.extractStubsSourceInfoFromApiStubsProviders(provider)
@@ -812,22 +818,22 @@
return nil
}
-func (c *commonToSdkLibraryAndImport) selectHeaderJarsForSdkVersion(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
+func (c *commonToSdkLibraryAndImport) selectHeaderJarsForSdkVersion(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths {
// If a specific numeric version has been requested then use prebuilt versions of the sdk.
- if sdkVersion.version.isNumbered() {
+ if !sdkVersion.ApiLevel.IsPreview() {
return PrebuiltJars(ctx, c.moduleBase.BaseModuleName(), sdkVersion)
}
var apiScope *apiScope
- switch sdkVersion.kind {
- case sdkSystem:
+ switch sdkVersion.Kind {
+ case android.SdkSystem:
apiScope = apiScopeSystem
- case sdkModule:
+ case android.SdkModule:
apiScope = apiScopeModuleLib
- case sdkTest:
+ case android.SdkTest:
apiScope = apiScopeTest
- case sdkSystemServer:
+ case android.SdkSystemServer:
apiScope = apiScopeSystemServer
default:
apiScope = apiScopePublic
@@ -930,14 +936,14 @@
//
// These are turbine generated jars so they only change if the externals of the
// class changes but it does not contain and implementation or JavaDoc.
- SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths
+ SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths
// Get the implementation jars appropriate for the supplied sdk version.
//
// These are either the implementation jar for the whole sdk library or the implementation
// jars for the stubs. The latter should only be needed when generating JavaDoc as otherwise
// they are identical to the corresponding header jars.
- SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths
+ SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths
}
type SdkLibrary struct {
@@ -951,7 +957,6 @@
commonToSdkLibraryAndImport
}
-var _ Dependency = (*SdkLibrary)(nil)
var _ SdkLibraryDependency = (*SdkLibrary)(nil)
func (module *SdkLibrary) generateTestAndSystemScopesByDefault() bool {
@@ -1048,6 +1053,33 @@
// Add other dependencies as normal.
func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
+ var missingApiModules []string
+ for _, apiScope := range module.getGeneratedApiScopes(ctx) {
+ if apiScope.unstable {
+ continue
+ }
+ if m := android.SrcIsModule(module.latestApiFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
+ missingApiModules = append(missingApiModules, m)
+ }
+ if m := android.SrcIsModule(module.latestRemovedApiFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
+ missingApiModules = append(missingApiModules, m)
+ }
+ if m := android.SrcIsModule(module.latestIncompatibilitiesFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
+ missingApiModules = append(missingApiModules, m)
+ }
+ }
+ if len(missingApiModules) != 0 && !module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api {
+ m := module.Name() + " is missing tracking files for previously released library versions.\n"
+ m += "You need to do one of the following:\n"
+ m += "- Add `unsafe_ignore_missing_latest_api: true` to your blueprint (to disable compat tracking)\n"
+ m += "- Add a set of prebuilt txt files representing the last released version of this library for compat checking.\n"
+ m += " (the current set of API files can be used as a seed for this compatibility tracking\n"
+ m += "\n"
+ m += "The following filegroup modules are missing:\n "
+ m += strings.Join(missingApiModules, "\n ") + "\n"
+ m += "Please see the documentation of the prebuilt_apis module type (and a usage example in prebuilts/sdk) for a convenient way to generate these."
+ ctx.ModuleErrorf(m)
+ }
if module.requiresRuntimeImplementationLibrary() {
// Only add the deps for the library if it is actually going to be built.
module.Library.deps(ctx)
@@ -1119,7 +1151,7 @@
return proptools.String(scopeProperties.Sdk_version)
}
- sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
+ sdkDep := decodeSdkDep(mctx, android.SdkContext(&module.Library))
if sdkDep.hasStandardLibs() {
// If building against a standard sdk then use the sdk version appropriate for the scope.
return apiScope.sdkVersion
@@ -1141,6 +1173,10 @@
return ":" + module.distStem() + "-removed.api." + apiScope.name + ".latest"
}
+func (module *SdkLibrary) latestIncompatibilitiesFilegroupName(apiScope *apiScope) string {
+ return ":" + module.distStem() + "-incompatibilities.api." + apiScope.name + ".latest"
+}
+
func childModuleVisibility(childVisibility []string) []string {
if childVisibility == nil {
// No child visibility set. The child will use the visibility of the sdk_library.
@@ -1271,10 +1307,10 @@
Merge_annotations_dirs []string
Merge_inclusion_annotations_dirs []string
Generate_stubs *bool
+ Previous_api *string
Check_api struct {
- Current ApiToCheck
- Last_released ApiToCheck
- Ignore_missing_latest_api *bool
+ Current ApiToCheck
+ Last_released ApiToCheck
Api_lint struct {
Enabled *bool
@@ -1357,15 +1393,16 @@
// check against the not-yet-release API
props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
- // TODO(b/176092454): change true to module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api
- props.Check_api.Ignore_missing_latest_api = proptools.BoolPtr(true)
- if !apiScope.unstable {
+ if !(apiScope.unstable || module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api) {
// check against the latest released API
latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
+ props.Previous_api = latestApiFilegroupName
props.Check_api.Last_released.Api_file = latestApiFilegroupName
props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
module.latestRemovedApiFilegroupName(apiScope))
+ props.Check_api.Last_released.Baseline_file = proptools.StringPtr(
+ module.latestIncompatibilitiesFilegroupName(apiScope))
if proptools.Bool(module.sdkLibraryProperties.Api_lint.Enabled) {
// Enable api lint.
@@ -1432,17 +1469,17 @@
mctx.CreateModule(sdkLibraryXmlFactory, &props)
}
-func PrebuiltJars(ctx android.BaseModuleContext, baseName string, s sdkSpec) android.Paths {
- var ver sdkVersion
- var kind sdkKind
- if s.usePrebuilt(ctx) {
- ver = s.version
- kind = s.kind
+func PrebuiltJars(ctx android.BaseModuleContext, baseName string, s android.SdkSpec) android.Paths {
+ var ver android.ApiLevel
+ var kind android.SdkKind
+ if s.UsePrebuilt(ctx) {
+ ver = s.ApiLevel
+ kind = s.Kind
} else {
// We don't have prebuilt SDK for the specific sdkVersion.
// Instead of breaking the build, fallback to use "system_current"
- ver = sdkVersionCurrent
- kind = sdkSystem
+ ver = android.FutureApiLevel
+ kind = android.SdkSystem
}
dir := filepath.Join("prebuilts", "sdk", ver.String(), kind.String())
@@ -1452,7 +1489,7 @@
if ctx.Config().AllowMissingDependencies() {
return android.Paths{android.PathForSource(ctx, jar)}
} else {
- ctx.PropertyErrorf("sdk_library", "invalid sdk version %q, %q does not exist", s.raw, jar)
+ ctx.PropertyErrorf("sdk_library", "invalid sdk version %q, %q does not exist", s.Raw, jar)
}
return nil
}
@@ -1469,13 +1506,13 @@
return len(otherApexInfo.InApexes) > 0 && reflect.DeepEqual(apexInfo.InApexes, otherApexInfo.InApexes)
}
-func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion sdkSpec, headerJars bool) android.Paths {
+func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec, headerJars bool) android.Paths {
// If the client doesn't set sdk_version, but if this library prefers stubs over
// the impl library, let's provide the widest API surface possible. To do so,
// force override sdk_version to module_current so that the closest possible API
// surface could be found in selectHeaderJarsForSdkVersion
- if module.defaultsToStubs() && !sdkVersion.specified() {
- sdkVersion = sdkSpecFrom("module_current")
+ if module.defaultsToStubs() && !sdkVersion.Specified() {
+ sdkVersion = android.SdkSpecFrom(ctx, "module_current")
}
// Only provide access to the implementation library if it is actually built.
@@ -1485,7 +1522,7 @@
// Only allow access to the implementation library in the following condition:
// * No sdk_version specified on the referencing module.
// * The referencing module is in the same apex as this.
- if sdkVersion.kind == sdkPrivate || withinSameApexesAs(ctx, module) {
+ if sdkVersion.Kind == android.SdkPrivate || withinSameApexesAs(ctx, module) {
if headerJars {
return module.HeaderJars()
} else {
@@ -1498,12 +1535,12 @@
}
// to satisfy SdkLibraryDependency interface
-func (module *SdkLibrary) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
+func (module *SdkLibrary) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths {
return module.sdkJars(ctx, sdkVersion, true /*headerJars*/)
}
// to satisfy SdkLibraryDependency interface
-func (module *SdkLibrary) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
+func (module *SdkLibrary) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths {
return module.sdkJars(ctx, sdkVersion, false /*headerJars*/)
}
@@ -1535,7 +1572,7 @@
// If this builds against standard libraries (i.e. is not part of the core libraries)
// then assume it provides both system and test apis.
- sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
+ sdkDep := decodeSdkDep(mctx, android.SdkContext(&module.Library))
hasSystemAndTestApis := sdkDep.hasStandardLibs()
module.sdkLibraryProperties.Generate_system_and_test_apis = hasSystemAndTestApis
@@ -1648,19 +1685,23 @@
var _ sdkLibraryComponentNamingScheme = (*defaultNamingScheme)(nil)
-func moduleStubLinkType(name string) (stub bool, ret linkType) {
+func moduleStubLinkType(name string) (stub bool, ret sdkLinkType) {
// This suffix-based approach is fragile and could potentially mis-trigger.
// TODO(b/155164730): Clean this up when modules no longer reference sdk_lib stubs directly.
- if strings.HasSuffix(name, ".stubs.public") || strings.HasSuffix(name, "-stubs-publicapi") {
+ if strings.HasSuffix(name, apiScopePublic.stubsLibraryModuleNameSuffix()) {
+ if name == "hwbinder.stubs" || name == "libcore_private.stubs" {
+ // Due to a previous bug, these modules were not considered stubs, so we retain that.
+ return false, javaPlatform
+ }
return true, javaSdk
}
- if strings.HasSuffix(name, ".stubs.system") || strings.HasSuffix(name, "-stubs-systemapi") {
+ if strings.HasSuffix(name, apiScopeSystem.stubsLibraryModuleNameSuffix()) {
return true, javaSystem
}
- if strings.HasSuffix(name, ".stubs.module_lib") || strings.HasSuffix(name, "-stubs-module_libs_api") {
+ if strings.HasSuffix(name, apiScopeModuleLib.stubsLibraryModuleNameSuffix()) {
return true, javaModule
}
- if strings.HasSuffix(name, ".stubs.test") {
+ if strings.HasSuffix(name, apiScopeTest.stubsLibraryModuleNameSuffix()) {
return true, javaSystem
}
return false, javaPlatform
@@ -1749,6 +1790,8 @@
android.ApexModuleBase
android.SdkBase
+ hiddenAPI
+
properties sdkLibraryImportProperties
// Map from api scope to the scope specific property structure.
@@ -1763,6 +1806,9 @@
// The reference to the xml permissions module created by the source module.
// Is nil if the source module does not exist.
xmlPermissionsFileModule *sdkLibraryXml
+
+ // Path to the dex implementation jar obtained from the prebuilt_apex, if any.
+ dexJarFile android.Path
}
var _ SdkLibraryDependency = (*SdkLibraryImport)(nil)
@@ -1907,11 +1953,11 @@
}
// Add dependencies to the prebuilt stubs library
- ctx.AddVariationDependencies(nil, apiScope.stubsTag, "prebuilt_"+module.stubsLibraryModuleName(apiScope))
+ ctx.AddVariationDependencies(nil, apiScope.stubsTag, android.PrebuiltNameFromSource(module.stubsLibraryModuleName(apiScope)))
if len(scopeProperties.Stub_srcs) > 0 {
// Add dependencies to the prebuilt stubs source library
- ctx.AddVariationDependencies(nil, apiScope.stubsSourceTag, "prebuilt_"+module.stubsSourceModuleName(apiScope))
+ ctx.AddVariationDependencies(nil, apiScope.stubsSourceTag, android.PrebuiltNameFromSource(module.stubsSourceModuleName(apiScope)))
}
}
}
@@ -1959,6 +2005,8 @@
func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
module.generateCommonBuildActions(ctx)
+ var deapexerModule android.Module
+
// Record the paths to the prebuilt stubs library and stubs source.
ctx.VisitDirectDeps(func(to android.Module) {
tag := ctx.OtherModuleDependencyTag(to)
@@ -1984,6 +2032,11 @@
ctx.ModuleErrorf("xml permissions file module must be of type *sdkLibraryXml but was %T", to)
}
}
+
+ // Save away the `deapexer` module on which this depends, if any.
+ if tag == android.DeapexerTag {
+ deapexerModule = to
+ }
})
// Populate the scope paths with information from the properties.
@@ -1996,9 +2049,35 @@
paths.currentApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Current_api)
paths.removedApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Removed_api)
}
+
+ if ctx.Device() {
+ // If this is a variant created for a prebuilt_apex then use the dex implementation jar
+ // obtained from the associated deapexer module.
+ ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ if ai.ForPrebuiltApex {
+ if deapexerModule == nil {
+ // This should never happen as a variant for a prebuilt_apex is only created if the
+ // deapxer module has been configured to export the dex implementation jar for this module.
+ ctx.ModuleErrorf("internal error: module %q does not depend on a `deapexer` module for prebuilt_apex %q",
+ module.Name(), ai.ApexVariationName)
+ }
+
+ // Get the path of the dex implementation jar from the `deapexer` module.
+ di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
+ if dexOutputPath := di.PrebuiltExportPath(module.BaseModuleName(), ".dexjar"); dexOutputPath != nil {
+ module.dexJarFile = dexOutputPath
+ module.initHiddenAPI(ctx, module.configurationName)
+ module.hiddenAPIExtractInformation(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0])
+ } else {
+ // This should never happen as a variant for a prebuilt_apex is only created if the
+ // prebuilt_apex has been configured to export the java library dex file.
+ ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt_apex %q", deapexerModule.Name())
+ }
+ }
+ }
}
-func (module *SdkLibraryImport) sdkJars(ctx android.BaseModuleContext, sdkVersion sdkSpec, headerJars bool) android.Paths {
+func (module *SdkLibraryImport) sdkJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec, headerJars bool) android.Paths {
// For consistency with SdkLibrary make the implementation jar available to libraries that
// are within the same APEX.
@@ -2015,19 +2094,24 @@
}
// to satisfy SdkLibraryDependency interface
-func (module *SdkLibraryImport) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
+func (module *SdkLibraryImport) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths {
// This module is just a wrapper for the prebuilt stubs.
return module.sdkJars(ctx, sdkVersion, true)
}
// to satisfy SdkLibraryDependency interface
-func (module *SdkLibraryImport) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
+func (module *SdkLibraryImport) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths {
// This module is just a wrapper for the stubs.
return module.sdkJars(ctx, sdkVersion, false)
}
// to satisfy UsesLibraryDependency interface
func (module *SdkLibraryImport) DexJarBuildPath() android.Path {
+ // The dex implementation jar extracted from the .apex file should be used in preference to the
+ // source.
+ if module.dexJarFile != nil {
+ return module.dexJarFile
+ }
if module.implLibraryModule == nil {
return nil
} else {
@@ -2215,7 +2299,7 @@
Class: "ETC",
OutputFile: android.OptionalPathForPath(module.outputFilePath),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_TAGS", "optional")
entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.ToMakePath().String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", module.outputFilePath.Base())
diff --git a/java/sdk_library_external.go b/java/sdk_library_external.go
index 2934936..0acaa13 100644
--- a/java/sdk_library_external.go
+++ b/java/sdk_library_external.go
@@ -75,10 +75,15 @@
return inList(j.Name(), ctx.Config().InterPartitionJavaLibraryAllowList())
}
+func (j *Module) syspropWithPublicStubs() bool {
+ return j.deviceProperties.SyspropPublicStub != ""
+}
+
type javaSdkLibraryEnforceContext interface {
Name() string
allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool
partitionGroup(ctx android.EarlyModuleContext) partitionGroup
+ syspropWithPublicStubs() bool
}
var _ javaSdkLibraryEnforceContext = (*Module)(nil)
@@ -88,6 +93,10 @@
return
}
+ if dep.syspropWithPublicStubs() {
+ return
+ }
+
// If product interface is not enforced, skip check between system and product partition.
// But still need to check between product and vendor partition because product interface flag
// just represents enforcement between product and system, and vendor interface enforcement
diff --git a/java/sdk_test.go b/java/sdk_test.go
index dc90ea3..2b18465 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -16,7 +16,6 @@
import (
"path/filepath"
- "reflect"
"strings"
"testing"
@@ -27,6 +26,7 @@
)
func TestClasspath(t *testing.T) {
+ const frameworkAidl = "-I" + defaultJavaDir + "/framework/aidl"
var classpathTestcases = []struct {
name string
unbundled bool
@@ -52,7 +52,7 @@
system: config.StableCorePlatformSystemModules,
java8classpath: config.FrameworkLibraries,
java9classpath: config.FrameworkLibraries,
- aidl: "-Iframework/aidl",
+ aidl: frameworkAidl,
},
{
name: `sdk_version:"core_platform"`,
@@ -69,7 +69,7 @@
system: config.StableCorePlatformSystemModules,
java8classpath: config.FrameworkLibraries,
java9classpath: config.FrameworkLibraries,
- aidl: "-Iframework/aidl",
+ aidl: frameworkAidl,
},
{
@@ -97,7 +97,7 @@
bootclasspath: []string{"android_stubs_current", "core-lambda-stubs"},
system: "core-current-stubs-system-modules",
java9classpath: []string{"android_stubs_current"},
- aidl: "-p" + buildDir + "/framework.aidl",
+ aidl: "-pout/soong/framework.aidl",
},
{
@@ -106,7 +106,7 @@
bootclasspath: []string{"android_system_stubs_current", "core-lambda-stubs"},
system: "core-current-stubs-system-modules",
java9classpath: []string{"android_system_stubs_current"},
- aidl: "-p" + buildDir + "/framework.aidl",
+ aidl: "-pout/soong/framework.aidl",
},
{
@@ -134,7 +134,7 @@
bootclasspath: []string{"android_test_stubs_current", "core-lambda-stubs"},
system: "core-current-stubs-system-modules",
java9classpath: []string{"android_test_stubs_current"},
- aidl: "-p" + buildDir + "/framework.aidl",
+ aidl: "-pout/soong/framework.aidl",
},
{
@@ -221,7 +221,7 @@
bootclasspath: []string{"android_module_lib_stubs_current", "core-lambda-stubs"},
system: "core-current-stubs-system-modules",
java9classpath: []string{"android_module_lib_stubs_current"},
- aidl: "-p" + buildDir + "/framework_non_updatable.aidl",
+ aidl: "-pout/soong/framework_non_updatable.aidl",
},
{
name: "system_server_current",
@@ -229,7 +229,7 @@
bootclasspath: []string{"android_system_server_stubs_current", "core-lambda-stubs"},
system: "core-current-stubs-system-modules",
java9classpath: []string{"android_system_server_stubs_current"},
- aidl: "-p" + buildDir + "/framework.aidl",
+ aidl: "-pout/soong/framework.aidl",
},
}
@@ -263,7 +263,7 @@
convertModulesToPaths := func(cp []string) []string {
ret := make([]string, len(cp))
for i, e := range cp {
- ret[i] = moduleToPath(e)
+ ret[i] = defaultModuleToPath(e)
}
return ret
}
@@ -299,24 +299,26 @@
dir := ""
if strings.HasPrefix(testcase.system, "sdk_public_") {
dir = "prebuilts/sdk"
+ } else {
+ dir = defaultJavaDir
}
- system = "--system=" + filepath.Join(buildDir, ".intermediates", dir, testcase.system, "android_common", "system")
+ system = "--system=" + filepath.Join("out", "soong", ".intermediates", dir, testcase.system, "android_common", "system")
// The module-relative parts of these paths are hardcoded in system_modules.go:
systemDeps = []string{
- filepath.Join(buildDir, ".intermediates", dir, testcase.system, "android_common", "system", "lib", "modules"),
- filepath.Join(buildDir, ".intermediates", dir, testcase.system, "android_common", "system", "lib", "jrt-fs.jar"),
- filepath.Join(buildDir, ".intermediates", dir, testcase.system, "android_common", "system", "release"),
+ filepath.Join("out", "soong", ".intermediates", dir, testcase.system, "android_common", "system", "lib", "modules"),
+ filepath.Join("out", "soong", ".intermediates", dir, testcase.system, "android_common", "system", "lib", "jrt-fs.jar"),
+ filepath.Join("out", "soong", ".intermediates", dir, testcase.system, "android_common", "system", "release"),
}
}
- checkClasspath := func(t *testing.T, ctx *android.TestContext, isJava8 bool) {
- foo := ctx.ModuleForTests("foo", variant)
+ checkClasspath := func(t *testing.T, result *android.TestResult, isJava8 bool) {
+ foo := result.ModuleForTests("foo", variant)
javac := foo.Rule("javac")
var deps []string
aidl := foo.MaybeRule("aidl")
if aidl.Rule != nil {
- deps = append(deps, aidl.Output.String())
+ deps = append(deps, android.PathRelativeToTop(aidl.Output))
}
got := javac.Args["bootClasspath"]
@@ -344,83 +346,74 @@
t.Errorf("classpath expected %q != got %q", expected, got)
}
- if !reflect.DeepEqual(javac.Implicits.Strings(), deps) {
- t.Errorf("implicits expected %q != got %q", deps, javac.Implicits.Strings())
- }
+ android.AssertPathsRelativeToTopEquals(t, "implicits", deps, javac.Implicits)
}
+ fixtureFactory := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ FixtureWithPrebuiltApis(map[string][]string{
+ "29": {},
+ "30": {},
+ "current": {},
+ }),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ if testcase.unbundled {
+ variables.Unbundled_build = proptools.BoolPtr(true)
+ variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
+ }
+ }),
+ android.FixtureModifyEnv(func(env map[string]string) {
+ if env["ANDROID_JAVA8_HOME"] == "" {
+ env["ANDROID_JAVA8_HOME"] = "jdk8"
+ }
+ }),
+ )
+
// Test with legacy javac -source 1.8 -target 1.8
t.Run("Java language level 8", func(t *testing.T) {
- config := testConfig(nil, bpJava8, nil)
- if testcase.unbundled {
- config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
- config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
- }
- ctx := testContext(config)
- run(t, ctx, config)
+ result := fixtureFactory.RunTestWithBp(t, bpJava8)
- checkClasspath(t, ctx, true /* isJava8 */)
+ checkClasspath(t, result, true /* isJava8 */)
if testcase.host != android.Host {
- aidl := ctx.ModuleForTests("foo", variant).Rule("aidl")
+ aidl := result.ModuleForTests("foo", variant).Rule("aidl")
- if g, w := aidl.RuleParams.Command, testcase.aidl+" -I."; !strings.Contains(g, w) {
- t.Errorf("want aidl command to contain %q, got %q", w, g)
- }
+ android.AssertStringDoesContain(t, "aidl command", aidl.RuleParams.Command, testcase.aidl+" -I.")
}
})
// Test with default javac -source 9 -target 9
t.Run("Java language level 9", func(t *testing.T) {
- config := testConfig(nil, bp, nil)
- if testcase.unbundled {
- config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
- config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
- }
- ctx := testContext(config)
- run(t, ctx, config)
+ result := fixtureFactory.RunTestWithBp(t, bp)
- checkClasspath(t, ctx, false /* isJava8 */)
+ checkClasspath(t, result, false /* isJava8 */)
if testcase.host != android.Host {
- aidl := ctx.ModuleForTests("foo", variant).Rule("aidl")
+ aidl := result.ModuleForTests("foo", variant).Rule("aidl")
- if g, w := aidl.RuleParams.Command, testcase.aidl+" -I."; !strings.Contains(g, w) {
- t.Errorf("want aidl command to contain %q, got %q", w, g)
- }
+ android.AssertStringDoesContain(t, "aidl command", aidl.RuleParams.Command, testcase.aidl+" -I.")
}
})
+ prepareWithPlatformVersionRel := android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Platform_sdk_codename = proptools.StringPtr("REL")
+ variables.Platform_sdk_final = proptools.BoolPtr(true)
+ })
+
// Test again with PLATFORM_VERSION_CODENAME=REL, javac -source 8 -target 8
t.Run("REL + Java language level 8", func(t *testing.T) {
- config := testConfig(nil, bpJava8, nil)
- config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("REL")
- config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(true)
+ result := android.GroupFixturePreparers(
+ fixtureFactory, prepareWithPlatformVersionRel).RunTestWithBp(t, bpJava8)
- if testcase.unbundled {
- config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
- config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
- }
- ctx := testContext(config)
- run(t, ctx, config)
-
- checkClasspath(t, ctx, true /* isJava8 */)
+ checkClasspath(t, result, true /* isJava8 */)
})
// Test again with PLATFORM_VERSION_CODENAME=REL, javac -source 9 -target 9
t.Run("REL + Java language level 9", func(t *testing.T) {
- config := testConfig(nil, bp, nil)
- config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("REL")
- config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(true)
+ result := android.GroupFixturePreparers(
+ fixtureFactory, prepareWithPlatformVersionRel).RunTestWithBp(t, bp)
- if testcase.unbundled {
- config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
- config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
- }
- ctx := testContext(config)
- run(t, ctx, config)
-
- checkClasspath(t, ctx, false /* isJava8 */)
+ checkClasspath(t, result, false /* isJava8 */)
})
})
}
diff --git a/java/sysprop.go b/java/sysprop.go
deleted file mode 100644
index e41aef6..0000000
--- a/java/sysprop.go
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package java
-
-// This file contains a map to redirect dependencies towards sysprop_library. If a sysprop_library
-// is owned by Platform, and the client module links against system API, the public stub of the
-// sysprop_library should be used. The map will contain public stub names of sysprop_libraries.
-
-import (
- "sync"
-
- "android/soong/android"
-)
-
-type syspropLibraryInterface interface {
- BaseModuleName() string
- Owner() string
- HasPublicStub() bool
- JavaPublicStubName() string
-}
-
-var (
- syspropPublicStubsKey = android.NewOnceKey("syspropPublicStubsJava")
- syspropPublicStubsLock sync.Mutex
-)
-
-func init() {
- android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.BottomUp("sysprop_java", SyspropMutator).Parallel()
- })
-}
-
-func syspropPublicStubs(config android.Config) map[string]string {
- return config.Once(syspropPublicStubsKey, func() interface{} {
- return make(map[string]string)
- }).(map[string]string)
-}
-
-// gather list of sysprop libraries owned by platform.
-func SyspropMutator(mctx android.BottomUpMutatorContext) {
- if m, ok := mctx.Module().(syspropLibraryInterface); ok {
- if m.Owner() != "Platform" || !m.HasPublicStub() {
- return
- }
-
- syspropPublicStubs := syspropPublicStubs(mctx.Config())
- syspropPublicStubsLock.Lock()
- defer syspropPublicStubsLock.Unlock()
-
- syspropPublicStubs[m.BaseModuleName()] = m.JavaPublicStubName()
- }
-}
diff --git a/java/system_modules.go b/java/system_modules.go
index 7394fd5..320a2bb 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -160,8 +160,8 @@
var jars android.Paths
ctx.VisitDirectDepsWithTag(systemModulesLibsTag, func(module android.Module) {
- dep, _ := module.(Dependency)
- jars = append(jars, dep.HeaderJars()...)
+ dep, _ := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+ jars = append(jars, dep.HeaderJars...)
})
system.headerJars = jars
@@ -169,7 +169,13 @@
system.outputDir, system.outputDeps = TransformJarsToSystemModules(ctx, jars)
}
-func (system *SystemModules) DepsMutator(ctx android.BottomUpMutatorContext) {
+// ComponentDepsMutator is called before prebuilt modules without a corresponding source module are
+// renamed so unless the supplied libs specifically includes the prebuilt_ prefix this is guaranteed
+// to only add dependencies on source modules.
+//
+// The systemModuleLibsTag will prevent the prebuilt mutators from replacing this dependency so it
+// will never be changed to depend on a prebuilt either.
+func (system *SystemModules) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
ctx.AddVariationDependencies(nil, systemModulesLibsTag, system.properties.Libs...)
}
@@ -192,6 +198,7 @@
fmt.Fprintln(w, name+":", "$("+makevar+")")
fmt.Fprintln(w, ".PHONY:", name)
+ // TODO(b/151177513): Licenses: Doesn't go through base_rules. May have to generate meta_lic and meta_module here.
},
}
}
@@ -224,6 +231,15 @@
return &system.prebuilt
}
+// ComponentDepsMutator is called before prebuilt modules without a corresponding source module are
+// renamed so as this adds a prebuilt_ prefix this is guaranteed to only add dependencies on source
+// modules.
+func (system *systemModulesImport) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
+ for _, lib := range system.properties.Libs {
+ ctx.AddVariationDependencies(nil, systemModulesLibsTag, android.PrebuiltNameFromSource(lib))
+ }
+}
+
type systemModulesSdkMemberType struct {
android.SdkMemberTypeBase
}
diff --git a/java/system_modules_test.go b/java/system_modules_test.go
new file mode 100644
index 0000000..7b5a386
--- /dev/null
+++ b/java/system_modules_test.go
@@ -0,0 +1,113 @@
+// Copyright 2021 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 (
+ "testing"
+
+ "android/soong/android"
+)
+
+func getModuleHeaderJarsAsRelativeToTopPaths(result *android.TestResult, moduleNames ...string) []string {
+ paths := []string{}
+ for _, moduleName := range moduleNames {
+ module := result.Module(moduleName, "android_common")
+ info := result.ModuleProvider(module, JavaInfoProvider).(JavaInfo)
+ paths = append(paths, info.HeaderJars.RelativeToTop().Strings()...)
+ }
+ return paths
+}
+
+var addSourceSystemModules = android.FixtureAddTextFile("source/Android.bp", `
+ java_system_modules {
+ name: "system-modules",
+ libs: ["system-module1", "system-module2"],
+ }
+ java_library {
+ name: "system-module1",
+ srcs: ["a.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ }
+ java_library {
+ name: "system-module2",
+ srcs: ["b.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ }
+`)
+
+func TestJavaSystemModules(t *testing.T) {
+ result := android.GroupFixturePreparers(prepareForJavaTest, addSourceSystemModules).RunTest(t)
+
+ // check the existence of the source module
+ sourceSystemModules := result.ModuleForTests("system-modules", "android_common")
+ sourceInputs := sourceSystemModules.Rule("jarsTosystemModules").Inputs
+
+ // The expected paths are the header jars from the source input modules.
+ expectedSourcePaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "system-module1", "system-module2")
+ android.AssertArrayString(t, "source system modules inputs", expectedSourcePaths, sourceInputs.RelativeToTop().Strings())
+}
+
+var addPrebuiltSystemModules = android.FixtureAddTextFile("prebuilts/Android.bp", `
+ java_system_modules_import {
+ name: "system-modules",
+ libs: ["system-module1", "system-module2"],
+ }
+ java_import {
+ name: "system-module1",
+ jars: ["a.jar"],
+ }
+ java_import {
+ name: "system-module2",
+ jars: ["b.jar"],
+ }
+`)
+
+func TestJavaSystemModulesImport(t *testing.T) {
+ result := android.GroupFixturePreparers(prepareForJavaTest, addPrebuiltSystemModules).RunTest(t)
+
+ // check the existence of the renamed prebuilt module
+ prebuiltSystemModules := result.ModuleForTests("system-modules", "android_common")
+ prebuiltInputs := prebuiltSystemModules.Rule("jarsTosystemModules").Inputs
+
+ // The expected paths are the header jars from the renamed prebuilt input modules.
+ expectedPrebuiltPaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "system-module1", "system-module2")
+ android.AssertArrayString(t, "renamed prebuilt system modules inputs", expectedPrebuiltPaths, prebuiltInputs.RelativeToTop().Strings())
+}
+
+func TestJavaSystemModulesMixSourceAndPrebuilt(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ addSourceSystemModules,
+ addPrebuiltSystemModules,
+ ).RunTest(t)
+
+ // check the existence of the source module
+ sourceSystemModules := result.ModuleForTests("system-modules", "android_common")
+ sourceInputs := sourceSystemModules.Rule("jarsTosystemModules").Inputs
+
+ // The expected paths are the header jars from the source input modules.
+ expectedSourcePaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "system-module1", "system-module2")
+ android.AssertArrayString(t, "source system modules inputs", expectedSourcePaths, sourceInputs.RelativeToTop().Strings())
+
+ // check the existence of the renamed prebuilt module
+ prebuiltSystemModules := result.ModuleForTests("prebuilt_system-modules", "android_common")
+ prebuiltInputs := prebuiltSystemModules.Rule("jarsTosystemModules").Inputs
+
+ // The expected paths are the header jars from the renamed prebuilt input modules.
+ expectedPrebuiltPaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "prebuilt_system-module1", "prebuilt_system-module2")
+ android.AssertArrayString(t, "prebuilt system modules inputs", expectedPrebuiltPaths, prebuiltInputs.RelativeToTop().Strings())
+}
diff --git a/java/testing.go b/java/testing.go
index fc4e477..aee0710 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -18,66 +18,146 @@
"fmt"
"reflect"
"sort"
+ "strings"
"testing"
"android/soong/android"
"android/soong/cc"
"android/soong/dexpreopt"
- "android/soong/python"
-
"github.com/google/blueprint"
)
-func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) android.Config {
- bp += GatherRequiredDepsForTest()
+const defaultJavaDir = "default/java"
- mockFS := map[string][]byte{
- "api/current.txt": nil,
- "api/removed.txt": nil,
- "api/system-current.txt": nil,
- "api/system-removed.txt": nil,
- "api/test-current.txt": nil,
- "api/test-removed.txt": nil,
+// Test fixture preparer that will register most java build components.
+//
+// Singletons and mutators should only be added here if they are needed for a majority of java
+// module types, otherwise they should be added under a separate preparer to allow them to be
+// selected only when needed to reduce test execution time.
+//
+// Module types do not have much of an overhead unless they are used so this should include as many
+// module types as possible. The exceptions are those module types that require mutators and/or
+// singletons in order to function in which case they should be kept together in a separate
+// preparer.
+var PrepareForTestWithJavaBuildComponents = android.GroupFixturePreparers(
+ // Make sure that mutators and module types, e.g. prebuilt mutators available.
+ android.PrepareForTestWithAndroidBuildComponents,
+ // Make java build components available to the test.
+ android.FixtureRegisterWithContext(registerRequiredBuildComponentsForTest),
+ android.FixtureRegisterWithContext(registerJavaPluginBuildComponents),
+ // Additional files needed in tests that disallow non-existent source files.
+ // This includes files that are needed by all, or at least most, instances of a java module type.
+ android.MockFS{
+ // Needed for linter used by java_library.
+ "build/soong/java/lint_defaults.txt": nil,
+ // Needed for apps that do not provide their own.
+ "build/make/target/product/security": nil,
+ }.AddToFixture(),
+)
- "prebuilts/sdk/tools/core-lambda-stubs.jar": nil,
- "prebuilts/sdk/Android.bp": []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "30", "current"], imports_sdk_version: "none", imports_compile_dex:true,}`),
+// Test fixture preparer that will define default java modules, e.g. standard prebuilt modules.
+var PrepareForTestWithJavaDefaultModules = android.GroupFixturePreparers(
+ // Make sure that all the module types used in the defaults are registered.
+ PrepareForTestWithJavaBuildComponents,
+ // Additional files needed when test disallows non-existent source.
+ android.MockFS{
+ // Needed for framework-res
+ defaultJavaDir + "/AndroidManifest.xml": nil,
+ // Needed for framework
+ defaultJavaDir + "/framework/aidl": nil,
+ // Needed for various deps defined in GatherRequiredDepsForTest()
+ defaultJavaDir + "/a.java": nil,
+ }.AddToFixture(),
+ // The java default module definitions.
+ android.FixtureAddTextFile(defaultJavaDir+"/Android.bp", gatherRequiredDepsForTest()),
+ // Add dexpreopt compat libs (android.test.base, etc.) and a fake dex2oatd module.
+ dexpreopt.PrepareForTestWithDexpreoptCompatLibs,
+ dexpreopt.PrepareForTestWithFakeDex2oatd,
+)
- "bin.py": nil,
- python.StubTemplateHost: []byte(`PYTHON_BINARY = '%interpreter%'
- MAIN_FILE = '%main%'`),
+// Provides everything needed by dexpreopt.
+var PrepareForTestWithDexpreopt = android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ dexpreopt.PrepareForTestByEnablingDexpreopt,
+)
- // For java_sdk_library
- "api/module-lib-current.txt": nil,
- "api/module-lib-removed.txt": nil,
- "api/system-server-current.txt": nil,
- "api/system-server-removed.txt": nil,
+var PrepareForTestWithOverlayBuildComponents = android.FixtureRegisterWithContext(registerOverlayBuildComponents)
+
+// Prepare a fixture to use all java module types, mutators and singletons fully.
+//
+// This should only be used by tests that want to run with as much of the build enabled as possible.
+var PrepareForIntegrationTestWithJava = android.GroupFixturePreparers(
+ cc.PrepareForIntegrationTestWithCc,
+ PrepareForTestWithJavaDefaultModules,
+)
+
+// Prepare a fixture with the standard files required by a java_sdk_library module.
+var PrepareForTestWithJavaSdkLibraryFiles = android.FixtureMergeMockFs(android.MockFS{
+ "api/current.txt": nil,
+ "api/removed.txt": nil,
+ "api/system-current.txt": nil,
+ "api/system-removed.txt": nil,
+ "api/test-current.txt": nil,
+ "api/test-removed.txt": nil,
+ "api/module-lib-current.txt": nil,
+ "api/module-lib-removed.txt": nil,
+ "api/system-server-current.txt": nil,
+ "api/system-server-removed.txt": nil,
+})
+
+// FixtureWithLastReleaseApis creates a preparer that creates prebuilt versions of the specified
+// modules for the `last` API release. By `last` it just means last in the list of supplied versions
+// and as this only provides one version it can be any value.
+//
+// This uses FixtureWithPrebuiltApis under the covers so the limitations of that apply to this.
+func FixtureWithLastReleaseApis(moduleNames ...string) android.FixturePreparer {
+ return FixtureWithPrebuiltApis(map[string][]string{
+ "30": moduleNames,
+ })
+}
+
+// PrepareForTestWithPrebuiltsOfCurrentApi is a preparer that creates prebuilt versions of the
+// standard modules for the current version.
+//
+// This uses FixtureWithPrebuiltApis under the covers so the limitations of that apply to this.
+var PrepareForTestWithPrebuiltsOfCurrentApi = FixtureWithPrebuiltApis(map[string][]string{
+ "current": {},
+ // Can't have current on its own as it adds a prebuilt_apis module but doesn't add any
+ // .txt files which causes the prebuilt_apis module to fail.
+ "30": {},
+})
+
+// FixtureWithPrebuiltApis creates a preparer that will define prebuilt api modules for the
+// specified releases and modules.
+//
+// The supplied map keys are the releases, e.g. current, 29, 30, etc. The values are a list of
+// modules for that release. Due to limitations in the prebuilt_apis module which this preparer
+// uses the set of releases must include at least one numbered release, i.e. it cannot just include
+// "current".
+//
+// This defines a file in the mock file system in a predefined location (prebuilts/sdk/Android.bp)
+// and so only one instance of this can be used in each fixture.
+func FixtureWithPrebuiltApis(release2Modules map[string][]string) android.FixturePreparer {
+ mockFS := android.MockFS{}
+ path := "prebuilts/sdk/Android.bp"
+
+ bp := fmt.Sprintf(`
+ prebuilt_apis {
+ name: "sdk",
+ api_dirs: ["%s"],
+ imports_sdk_version: "none",
+ imports_compile_dex: true,
+ }
+ `, strings.Join(android.SortedStringKeys(release2Modules), `", "`))
+
+ for release, modules := range release2Modules {
+ libs := append([]string{"android", "core-for-system-modules"}, modules...)
+ mockFS.Merge(prebuiltApisFilesForLibs([]string{release}, libs))
}
-
- levels := []string{"14", "28", "29", "30", "current"}
- libs := []string{
- "android", "foo", "bar", "sdklib", "barney", "betty", "foo-shared_library",
- "foo-no_shared_library", "core-for-system-modules", "quuz", "qux", "fred",
- "runtime-library",
- }
- for k, v := range prebuiltApisFilesForLibs(levels, libs) {
- mockFS[k] = v
- }
-
- cc.GatherRequiredFilesForTest(mockFS)
-
- for k, v := range fs {
- mockFS[k] = v
- }
-
- if env == nil {
- env = make(map[string]string)
- }
- if env["ANDROID_JAVA8_HOME"] == "" {
- env["ANDROID_JAVA8_HOME"] = "jdk8"
- }
- config := android.TestArchConfig(buildDir, env, bp, mockFS)
-
- return config
+ return android.GroupFixturePreparers(
+ android.FixtureAddTextFile(path, bp),
+ android.FixtureMergeMockFs(mockFS),
+ )
}
func prebuiltApisFilesForLibs(apiLevels []string, sdkLibs []string) map[string][]byte {
@@ -86,8 +166,11 @@
for _, lib := range sdkLibs {
for _, scope := range []string{"public", "system", "module-lib", "system-server", "test"} {
fs[fmt.Sprintf("prebuilts/sdk/%s/%s/%s.jar", level, scope, lib)] = nil
- fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s.txt", level, scope, lib)] = nil
- fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s-removed.txt", level, scope, lib)] = nil
+ // No finalized API files for "current"
+ if level != "current" {
+ fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s.txt", level, scope, lib)] = nil
+ fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s-removed.txt", level, scope, lib)] = nil
+ }
}
}
fs[fmt.Sprintf("prebuilts/sdk/%s/public/framework.aidl", level)] = nil
@@ -95,7 +178,74 @@
return fs
}
-func GatherRequiredDepsForTest() string {
+// FixtureConfigureBootJars configures the boot jars in both the dexpreopt.GlobalConfig and
+// Config.productVariables structs. As a side effect that enables dexpreopt.
+func FixtureConfigureBootJars(bootJars ...string) android.FixturePreparer {
+ artBootJars := []string{}
+ for _, j := range bootJars {
+ artApex := false
+ for _, artApexName := range artApexNames {
+ if strings.HasPrefix(j, artApexName+":") {
+ artApex = true
+ break
+ }
+ }
+ if artApex {
+ artBootJars = append(artBootJars, j)
+ }
+ }
+ return android.GroupFixturePreparers(
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.BootJars = android.CreateTestConfiguredJarList(bootJars)
+ }),
+ dexpreopt.FixtureSetBootJars(bootJars...),
+ dexpreopt.FixtureSetArtBootJars(artBootJars...),
+ )
+}
+
+// FixtureConfigureUpdatableBootJars configures the updatable boot jars in both the
+// dexpreopt.GlobalConfig and Config.productVariables structs. As a side effect that enables
+// dexpreopt.
+func FixtureConfigureUpdatableBootJars(bootJars ...string) android.FixturePreparer {
+ return android.GroupFixturePreparers(
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.UpdatableBootJars = android.CreateTestConfiguredJarList(bootJars)
+ }),
+ dexpreopt.FixtureSetUpdatableBootJars(bootJars...),
+ )
+}
+
+// registerRequiredBuildComponentsForTest registers the build components used by
+// PrepareForTestWithJavaDefaultModules.
+//
+// As functionality is moved out of here into separate FixturePreparer instances they should also
+// be moved into GatherRequiredDepsForTest for use by tests that have not yet switched to use test
+// fixtures.
+func registerRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
+ RegisterAARBuildComponents(ctx)
+ RegisterAppBuildComponents(ctx)
+ RegisterAppImportBuildComponents(ctx)
+ RegisterAppSetBuildComponents(ctx)
+ RegisterBootImageBuildComponents(ctx)
+ RegisterDexpreoptBootJarsComponents(ctx)
+ RegisterDocsBuildComponents(ctx)
+ RegisterGenRuleBuildComponents(ctx)
+ registerJavaBuildComponents(ctx)
+ registerPlatformBootclasspathBuildComponents(ctx)
+ RegisterPrebuiltApisBuildComponents(ctx)
+ RegisterRuntimeResourceOverlayBuildComponents(ctx)
+ RegisterSdkLibraryBuildComponents(ctx)
+ RegisterStubsBuildComponents(ctx)
+ RegisterSystemModulesBuildComponents(ctx)
+}
+
+// gatherRequiredDepsForTest gathers the module definitions used by
+// PrepareForTestWithJavaDefaultModules.
+//
+// As functionality is moved out of here into separate FixturePreparer instances they should also
+// be moved into GatherRequiredDepsForTest for use by tests that have not yet switched to use test
+// fixtures.
+func gatherRequiredDepsForTest() string {
var bp string
extraModules := []string{
@@ -127,24 +277,6 @@
`, extra)
}
- // For class loader context and <uses-library> tests.
- dexpreoptModules := []string{"android.test.runner"}
- dexpreoptModules = append(dexpreoptModules, dexpreopt.CompatUsesLibs...)
- dexpreoptModules = append(dexpreoptModules, dexpreopt.OptionalCompatUsesLibs...)
-
- for _, extra := range dexpreoptModules {
- bp += fmt.Sprintf(`
- java_library {
- name: "%s",
- srcs: ["a.java"],
- sdk_version: "none",
- system_modules: "stable-core-platform-api-stubs-system-modules",
- compile_dex: true,
- installable: true,
- }
- `, extra)
- }
-
bp += `
java_library {
name: "framework",
@@ -181,6 +313,13 @@
`, extra)
}
+ // Make sure that the dex_bootjars singleton module is instantiated for the tests.
+ bp += `
+ dex_bootjars {
+ name: "dex_bootjars",
+ }
+`
+
return bp
}
@@ -197,3 +336,61 @@
t.Errorf("expected %#q, found %#q", expected, actual)
}
}
+
+// CheckPlatformBootclasspathModules returns the apex:module pair for the modules depended upon by
+// the platform-bootclasspath module.
+func CheckPlatformBootclasspathModules(t *testing.T, result *android.TestResult, name string, expected []string) {
+ t.Helper()
+ platformBootclasspath := result.Module(name, "android_common").(*platformBootclasspathModule)
+ pairs := ApexNamePairsFromModules(result.TestContext, platformBootclasspath.configuredModules)
+ android.AssertDeepEquals(t, fmt.Sprintf("%s modules", "platform-bootclasspath"), expected, pairs)
+}
+
+// ApexNamePairsFromModules returns the apex:module pair for the supplied modules.
+func ApexNamePairsFromModules(ctx *android.TestContext, modules []android.Module) []string {
+ pairs := []string{}
+ for _, module := range modules {
+ pairs = append(pairs, apexNamePairFromModule(ctx, module))
+ }
+ return pairs
+}
+
+func apexNamePairFromModule(ctx *android.TestContext, module android.Module) string {
+ name := module.Name()
+ var apex string
+ apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+ if apexInfo.IsForPlatform() {
+ apex = "platform"
+ } else {
+ apex = apexInfo.InApexes[0]
+ }
+
+ return fmt.Sprintf("%s:%s", apex, name)
+}
+
+// CheckPlatformBootclasspathFragments returns the apex:module pair for the fragments depended upon
+// by the platform-bootclasspath module.
+func CheckPlatformBootclasspathFragments(t *testing.T, result *android.TestResult, name string, expected []string) {
+ t.Helper()
+ platformBootclasspath := result.Module(name, "android_common").(*platformBootclasspathModule)
+ pairs := ApexNamePairsFromModules(result.TestContext, platformBootclasspath.fragments)
+ android.AssertDeepEquals(t, fmt.Sprintf("%s fragments", "platform-bootclasspath"), expected, pairs)
+}
+
+func CheckHiddenAPIRuleInputs(t *testing.T, expected string, hiddenAPIRule android.TestingBuildParams) {
+ t.Helper()
+ actual := strings.TrimSpace(strings.Join(android.NormalizePathsForTesting(hiddenAPIRule.Implicits), "\n"))
+ expected = strings.TrimSpace(expected)
+ if actual != expected {
+ t.Errorf("Expected hiddenapi rule inputs:\n%s\nactual inputs:\n%s", expected, actual)
+ }
+}
+
+// Check that the merged file create by platform_compat_config_singleton has the correct inputs.
+func CheckMergedCompatConfigInputs(t *testing.T, result *android.TestResult, message string, expectedPaths ...string) {
+ sourceGlobalCompatConfig := result.SingletonForTests("platform_compat_config_singleton")
+ allOutputs := sourceGlobalCompatConfig.AllOutputs()
+ android.AssertIntEquals(t, message+": output len", 1, len(allOutputs))
+ output := sourceGlobalCompatConfig.Output(allOutputs[0])
+ android.AssertPathsRelativeToTopEquals(t, message+": inputs", expectedPaths, output.Implicits)
+}
diff --git a/kernel/Android.bp b/kernel/Android.bp
new file mode 100644
index 0000000..91e7490
--- /dev/null
+++ b/kernel/Android.bp
@@ -0,0 +1,22 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-kernel",
+ pkgPath: "android/soong/kernel",
+ deps: [
+ "blueprint",
+ "soong",
+ "soong-android",
+ "soong-cc",
+ "soong-cc-config",
+ ],
+ srcs: [
+ "prebuilt_kernel_modules.go",
+ ],
+ testSrcs: [
+ "prebuilt_kernel_modules_test.go",
+ ],
+ pluginFor: ["soong_build"],
+}
diff --git a/kernel/prebuilt_kernel_modules.go b/kernel/prebuilt_kernel_modules.go
new file mode 100644
index 0000000..5bcca04
--- /dev/null
+++ b/kernel/prebuilt_kernel_modules.go
@@ -0,0 +1,170 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package kernel
+
+import (
+ "fmt"
+ "path/filepath"
+ "strings"
+
+ "android/soong/android"
+ _ "android/soong/cc/config"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+func init() {
+ pctx.Import("android/soong/cc/config")
+ registerKernelBuildComponents(android.InitRegistrationContext)
+}
+
+func registerKernelBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("prebuilt_kernel_modules", prebuiltKernelModulesFactory)
+}
+
+type prebuiltKernelModules struct {
+ android.ModuleBase
+
+ properties prebuiltKernelModulesProperties
+
+ installDir android.InstallPath
+}
+
+type prebuiltKernelModulesProperties struct {
+ // List or filegroup of prebuilt kernel module files. Should have .ko suffix.
+ Srcs []string `android:"path,arch_variant"`
+
+ // Kernel version that these modules are for. Kernel modules are installed to
+ // /lib/modules/<kernel_version> directory in the corresponding partition. Default is "".
+ Kernel_version *string
+}
+
+// prebuilt_kernel_modules installs a set of prebuilt kernel module files to the correct directory.
+// In addition, this module builds modules.load, modules.dep, modules.softdep and modules.alias
+// using depmod and installs them as well.
+func prebuiltKernelModulesFactory() android.Module {
+ module := &prebuiltKernelModules{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ return module
+}
+
+func (pkm *prebuiltKernelModules) KernelVersion() string {
+ return proptools.StringDefault(pkm.properties.Kernel_version, "")
+}
+
+func (pkm *prebuiltKernelModules) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // do nothing
+}
+
+func (pkm *prebuiltKernelModules) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ modules := android.PathsForModuleSrc(ctx, pkm.properties.Srcs)
+
+ depmodOut := runDepmod(ctx, modules)
+ strippedModules := stripDebugSymbols(ctx, modules)
+
+ installDir := android.PathForModuleInstall(ctx, "lib", "modules")
+ if pkm.KernelVersion() != "" {
+ installDir = installDir.Join(ctx, pkm.KernelVersion())
+ }
+
+ for _, m := range strippedModules {
+ ctx.InstallFile(installDir, filepath.Base(m.String()), m)
+ }
+ ctx.InstallFile(installDir, "modules.load", depmodOut.modulesLoad)
+ ctx.InstallFile(installDir, "modules.dep", depmodOut.modulesDep)
+ ctx.InstallFile(installDir, "modules.softdep", depmodOut.modulesSoftdep)
+ ctx.InstallFile(installDir, "modules.alias", depmodOut.modulesAlias)
+}
+
+var (
+ pctx = android.NewPackageContext("android/soong/kernel")
+
+ stripRule = pctx.AndroidStaticRule("strip",
+ blueprint.RuleParams{
+ Command: "$stripCmd -o $out --strip-debug $in",
+ CommandDeps: []string{"$stripCmd"},
+ }, "stripCmd")
+)
+
+func stripDebugSymbols(ctx android.ModuleContext, modules android.Paths) android.OutputPaths {
+ dir := android.PathForModuleOut(ctx, "stripped").OutputPath
+ var outputs android.OutputPaths
+
+ for _, m := range modules {
+ stripped := dir.Join(ctx, filepath.Base(m.String()))
+ ctx.Build(pctx, android.BuildParams{
+ Rule: stripRule,
+ Input: m,
+ Output: stripped,
+ Args: map[string]string{
+ "stripCmd": "${config.ClangBin}/llvm-strip",
+ },
+ })
+ outputs = append(outputs, stripped)
+ }
+
+ return outputs
+}
+
+type depmodOutputs struct {
+ modulesLoad android.OutputPath
+ modulesDep android.OutputPath
+ modulesSoftdep android.OutputPath
+ modulesAlias android.OutputPath
+}
+
+func runDepmod(ctx android.ModuleContext, modules android.Paths) depmodOutputs {
+ baseDir := android.PathForModuleOut(ctx, "depmod").OutputPath
+ fakeVer := "0.0" // depmod demands this anyway
+ modulesDir := baseDir.Join(ctx, "lib", "modules", fakeVer)
+
+ builder := android.NewRuleBuilder(pctx, ctx)
+
+ // Copy the module files to a temporary dir
+ builder.Command().Text("rm").Flag("-rf").Text(modulesDir.String())
+ builder.Command().Text("mkdir").Flag("-p").Text(modulesDir.String())
+ for _, m := range modules {
+ builder.Command().Text("cp").Input(m).Text(modulesDir.String())
+ }
+
+ // Enumerate modules to load
+ modulesLoad := modulesDir.Join(ctx, "modules.load")
+ var basenames []string
+ for _, m := range modules {
+ basenames = append(basenames, filepath.Base(m.String()))
+ }
+ builder.Command().
+ Text("echo").Flag("\"" + strings.Join(basenames, " ") + "\"").
+ Text("|").Text("tr").Flag("\" \"").Flag("\"\\n\"").
+ Text(">").Output(modulesLoad)
+
+ // Run depmod to build modules.dep/softdep/alias files
+ modulesDep := modulesDir.Join(ctx, "modules.dep")
+ modulesSoftdep := modulesDir.Join(ctx, "modules.softdep")
+ modulesAlias := modulesDir.Join(ctx, "modules.alias")
+ builder.Command().
+ BuiltTool("depmod").
+ FlagWithArg("-b ", baseDir.String()).
+ Text(fakeVer).
+ ImplicitOutput(modulesDep).
+ ImplicitOutput(modulesSoftdep).
+ ImplicitOutput(modulesAlias)
+
+ builder.Build("depmod", fmt.Sprintf("depmod %s", ctx.ModuleName()))
+
+ return depmodOutputs{modulesLoad, modulesDep, modulesSoftdep, modulesAlias}
+}
diff --git a/kernel/prebuilt_kernel_modules_test.go b/kernel/prebuilt_kernel_modules_test.go
new file mode 100644
index 0000000..90b9886
--- /dev/null
+++ b/kernel/prebuilt_kernel_modules_test.go
@@ -0,0 +1,62 @@
+// Copyright 2021 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 kernel
+
+import (
+ "os"
+ "testing"
+
+ "android/soong/android"
+ "android/soong/cc"
+)
+
+func TestKernelModulesFilelist(t *testing.T) {
+ ctx := android.GroupFixturePreparers(
+ cc.PrepareForTestWithCcDefaultModules,
+ android.FixtureRegisterWithContext(registerKernelBuildComponents),
+ android.MockFS{
+ "depmod.cpp": nil,
+ "mod1.ko": nil,
+ "mod2.ko": nil,
+ }.AddToFixture(),
+ ).RunTestWithBp(t, `
+ prebuilt_kernel_modules {
+ name: "foo",
+ srcs: ["*.ko"],
+ kernel_version: "5.10",
+ }
+ `)
+
+ expected := []string{
+ "lib/modules/5.10/mod1.ko",
+ "lib/modules/5.10/mod2.ko",
+ "lib/modules/5.10/modules.load",
+ "lib/modules/5.10/modules.dep",
+ "lib/modules/5.10/modules.softdep",
+ "lib/modules/5.10/modules.alias",
+ }
+
+ var actual []string
+ for _, ps := range ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().PackagingSpecs() {
+ actual = append(actual, ps.RelPathInPackage())
+ }
+ actual = android.SortedUniqueStrings(actual)
+ expected = android.SortedUniqueStrings(expected)
+ android.AssertDeepEquals(t, "foo packaging specs", expected, actual)
+}
+
+func TestMain(m *testing.M) {
+ os.Exit(m.Run())
+}
diff --git a/licenses/Android.bp b/licenses/Android.bp
new file mode 100644
index 0000000..c70d6bd
--- /dev/null
+++ b/licenses/Android.bp
@@ -0,0 +1,1256 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_visibility: ["//visibility:public"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+license {
+ name: "Android-Apache-2.0",
+ license_kinds: ["SPDX-license-identifier-Apache-2.0"],
+ copyright_notice: "Copyright (C) The Android Open Source Project",
+ license_text: ["LICENSE"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-0BSD",
+ conditions: ["unencumbered"],
+ url: "https://spdx.org/licenses/0BSD",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AFL-1.1",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/AFL-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AFL-1.2",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/AFL-1.2.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AFL-2.0",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/AFL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AFL-2.1",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/AFL-2.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AFL-3.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/AFL-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/AGPL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-1.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/AGPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-1.0-only",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/AGPL-1.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-1.0-or-later",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/AGPL-1.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-3.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/AGPL-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-3.0-only",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/AGPL-3.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-3.0-or-later",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/AGPL-3.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-APSL-1.1",
+ conditions: [
+ "reciprocal",
+ ],
+ url: "https://spdx.org/licenses/APSL-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-APSL-2.0",
+ conditions: [
+ "reciprocal",
+ ],
+ url: "https://spdx.org/licenses/APSL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Apache",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Apache-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Apache-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Apache-1.1",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Apache-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Apache-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Apache-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Artistic",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Artistic-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Artistic-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Artistic-1.0-Perl",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Artistic-1.0-Perl.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Artistic-1.0-cl8",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Artistic-1.0-cl8.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Artistic-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Artistic-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-1-Clause",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-1-Clause.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-2-Clause",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-2-Clause.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-2-Clause-FreeBSD",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-2-Clause-FreeBSD.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-2-Clause-NetBSD",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-2-Clause-NetBSD.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-2-Clause-Patent",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-2-Clause-Patent.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-Attribution",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-Attribution.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-Clear",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-Clear.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-LBNL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-LBNL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-No-Nuclear-License",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-No-Nuclear-License-2014",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-No-Nuclear-Warranty",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-Open-MPI",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-Open-MPI.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-4-Clause",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-4-Clause.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-4-Clause-UC",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-4-Clause-UC.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-Protection",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-Protection.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-Source-Code",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-Source-Code.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSL-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Beerware",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Beerware.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/CC-BY-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/CC-BY-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-2.5",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/CC-BY-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-3.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/CC-BY-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-4.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/CC-BY-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-1.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/CC-BY-NC-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-2.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/CC-BY-NC-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-2.5",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/CC-BY-NC-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-3.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/CC-BY-NC-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-4.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/CC-BY-NC-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-ND-1.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/CC-BY-NC-ND-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-ND-2.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/CC-BY-NC-ND-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-ND-2.5",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/CC-BY-NC-ND-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-ND-3.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/CC-BY-NC-ND-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-ND-4.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/CC-BY-NC-ND-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-SA-1.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/CC-BY-NC-SA-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-SA-2.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/CC-BY-NC-SA-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-SA-2.5",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/CC-BY-NC-SA-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-SA-3.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/CC-BY-NC-SA-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-SA-4.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/CC-BY-NC-SA-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND-1.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-ND-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND-2.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-ND-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND-2.5",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-ND-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND-3.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-ND-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND-4.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-ND-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-1.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-SA-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-2.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-SA-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-2.5",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-SA-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-3.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-SA-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-4.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-SA-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-ND",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC0-1.0",
+ conditions: ["unencumbered"],
+ url: "https://spdx.org/licenses/CC0-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CDDL",
+ conditions: ["reciprocal"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CDDL-1.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/CDLL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CDDL-1.1",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/CDLL-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CPAL-1.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/CPAL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CPL-1.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/CPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EPL",
+ conditions: ["reciprocal"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EPL-1.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/EPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EPL-2.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/EPL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EUPL",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EUPL-1.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/EUPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EUPL-1.1",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/EUPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EUPL-1.2",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/EUPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-FSFAP",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/FSFAP",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-FTL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/FTL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GFDL",
+ conditions: ["by_exception_only"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-1.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-1.0+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-1.0+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-1.0-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-1.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-1.0-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-1.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-with-GCC-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-with-GCC-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-with-autoconf-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-with-autoconf-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-with-bison-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-with-bison-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-with-classpath-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-with-classpath-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-with-font-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-with-font-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0-with-GCC-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0-with-GCC-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0-with-autoconf-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0-with-autoconf-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-with-classpath-exception",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-HPND",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/HPND.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ICU",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ICU.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ISC",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ISC.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-JSON",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/JSON.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.0+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.0+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.0-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.0-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.1",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.1+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.1+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.1-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.1-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.1-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.1-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-3.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-3.0+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-3.0+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-3.0-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-3.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-3.0-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-3.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPLLR",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPLLR.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LPL-1.02",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/LPL-1.02.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT-0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MIT-0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT-CMU",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MIT-CMU.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT-advertising",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MIT-advertising.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT-enna",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MIT-enna.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT-feh",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MIT-feh.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MITNFA",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MITNFA.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MPL",
+ conditions: ["reciprocal"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MPL-1.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/MPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MPL-1.1",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/MPL-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MPL-2.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/MPL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MPL-2.0-no-copyleft-exception",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/MPL-2.0-no-copyleft-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MS-PL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MS-PL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MS-RL",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/MS-RL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-NCSA",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/NCSA.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL",
+ conditions: ["by_exception_only"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.0",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.0-RFN",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.0-RFN.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.0-no-RFN",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.0-no-RFN.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.1",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.1-RFN",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.1-RFN.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.1-no-RFN",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.1-no-RFN.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OpenSSL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/OpenSSL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-PSF-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/PSF-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-SISSL",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/SISSL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-SISSL-1.2",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/SISSL-1.2.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-SPL-1.0",
+ conditions: [
+ "by_exception_only",
+ "reciprocal",
+ ],
+ url: "https://spdx.org/licenses/SPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-SSPL",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/SSPL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-UPL-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/UPL-1.-.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Unicode-DFS",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Unicode-DFS-2015",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Unicode-DFS-2015.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Unicode-DFS-2016",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Unicode-DFS-2016.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Unlicense",
+ conditions: ["unencumbered"],
+ url: "https://spdx.org/licenses/Unlicense.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-W3C",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/W3C.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-W3C-19980720",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/W3C-19980720.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-W3C-20150513",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/W3C-20150513.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-WTFPL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/WTFPL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Watcom-1.0",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/Watcom-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Xnet",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Xnet.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ZPL",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ZPL-1.1",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ZPL-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ZPL-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ZPL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ZPL-2.1",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ZPL-2.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Zend-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Zend-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Zlib",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Zlib.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-libtiff",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/libtiff.html",
+}
+
+// Legacy license kinds -- do not add new references -- use an spdx kind instead.
+license_kind {
+ name: "legacy_unknown",
+ conditions: ["by_exception_only"],
+}
+
+license_kind {
+ name: "legacy_unencumbered",
+ conditions: ["unencumbered"],
+}
+
+license_kind {
+ name: "legacy_permissive",
+ conditions: ["permissive"],
+}
+
+license_kind {
+ name: "legacy_notice",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "legacy_reciprocal",
+ conditions: ["reciprocal"],
+}
+
+license_kind {
+ name: "legacy_restricted",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "legacy_by_exception_only",
+ conditions: ["by_exception_only"],
+}
+
+license_kind {
+ name: "legacy_not_a_contribution",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+}
+
+license_kind {
+ name: "legacy_not_allowed",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+}
+
+license_kind {
+ name: "legacy_proprietary",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ "proprietary",
+ ],
+}
diff --git a/licenses/LICENSE b/licenses/LICENSE
new file mode 100644
index 0000000..dae0406
--- /dev/null
+++ b/licenses/LICENSE
@@ -0,0 +1,214 @@
+
+ Copyright (c) The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/licenses/OWNERS b/licenses/OWNERS
new file mode 100644
index 0000000..fddfc48
--- /dev/null
+++ b/licenses/OWNERS
@@ -0,0 +1,2 @@
+per-file * = bbadour@google.com
+
diff --git a/linkerconfig/Android.bp b/linkerconfig/Android.bp
index 8807a2e..9161f0e 100644
--- a/linkerconfig/Android.bp
+++ b/linkerconfig/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-linkerconfig",
pkgPath: "android/soong/linkerconfig",
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index d538ce4..241cac6 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -17,6 +17,7 @@
import (
"android/soong/android"
"android/soong/etc"
+ "fmt"
"github.com/google/blueprint/proptools"
)
@@ -27,7 +28,11 @@
func init() {
pctx.HostBinToolVariable("conv_linker_config", "conv_linker_config")
- android.RegisterModuleType("linker_config", linkerConfigFactory)
+ registerLinkerConfigBuildComponent(android.InitRegistrationContext)
+}
+
+func registerLinkerConfigBuildComponent(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("linker_config", linkerConfigFactory)
}
type linkerConfigProperties struct {
@@ -64,6 +69,17 @@
return l.outputFilePath
}
+var _ android.OutputFileProducer = (*linkerConfig)(nil)
+
+func (l *linkerConfig) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case "":
+ return android.Paths{l.outputFilePath}, nil
+ default:
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+ }
+}
+
func (l *linkerConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
inputFile := android.PathForModuleSrc(ctx, android.String(l.properties.Src))
l.outputFilePath = android.PathForModuleOut(ctx, "linker.config.pb").OutputPath
@@ -77,9 +93,10 @@
linkerConfigRule.Build("conv_linker_config",
"Generate linker config protobuf "+l.outputFilePath.String())
- if proptools.BoolDefault(l.properties.Installable, true) {
- ctx.InstallFile(l.installDirPath, l.outputFilePath.Base(), l.outputFilePath)
+ if !proptools.BoolDefault(l.properties.Installable, true) {
+ l.SkipInstall()
}
+ ctx.InstallFile(l.installDirPath, l.outputFilePath.Base(), l.outputFilePath)
}
// linker_config generates protobuf file from json file. This protobuf file will be used from
@@ -98,7 +115,7 @@
Class: "ETC",
OutputFile: android.OptionalPathForPath(l.outputFilePath),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_PATH", l.installDirPath.ToMakePath().String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.outputFilePath.Base())
entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !installable)
diff --git a/linkerconfig/linkerconfig_test.go b/linkerconfig/linkerconfig_test.go
index 01f4657..939e4bb 100644
--- a/linkerconfig/linkerconfig_test.go
+++ b/linkerconfig/linkerconfig_test.go
@@ -15,65 +15,29 @@
package linkerconfig
import (
- "android/soong/android"
- "io/ioutil"
"os"
"reflect"
"testing"
+
+ "android/soong/android"
)
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_etc_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ os.Exit(m.Run())
}
-func testContext(t *testing.T, bp string) (*android.TestContext, android.Config) {
- t.Helper()
-
- fs := map[string][]byte{
- "linker.config.json": nil,
- }
-
- config := android.TestArchConfig(buildDir, nil, bp, fs)
-
- ctx := android.NewTestArchContext(config)
- ctx.RegisterModuleType("linker_config", linkerConfigFactory)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
-
- return ctx, config
-}
+var prepareForLinkerConfigTest = android.GroupFixturePreparers(
+ android.PrepareForTestWithAndroidBuildComponents,
+ android.FixtureRegisterWithContext(registerLinkerConfigBuildComponent),
+ android.FixtureAddFile("linker.config.json", nil),
+)
func TestBaseLinkerConfig(t *testing.T) {
- ctx, config := testContext(t, `
- linker_config {
- name: "linker-config-base",
- src: "linker.config.json",
- }
+ result := prepareForLinkerConfigTest.RunTestWithBp(t, `
+ linker_config {
+ name: "linker-config-base",
+ src: "linker.config.json",
+ }
`)
expected := map[string][]string{
@@ -82,13 +46,13 @@
"LOCAL_INSTALLED_MODULE_STEM": {"linker.config.pb"},
}
- p := ctx.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
+ p := result.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
if p.outputFilePath.Base() != "linker.config.pb" {
t.Errorf("expected linker.config.pb, got %q", p.outputFilePath.Base())
}
- entries := android.AndroidMkEntriesForTest(t, config, "", p)[0]
+ entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)[0]
for k, expectedValue := range expected {
if value, ok := entries.EntryMap[k]; ok {
if !reflect.DeepEqual(value, expectedValue) {
@@ -105,18 +69,18 @@
}
func TestUninstallableLinkerConfig(t *testing.T) {
- ctx, config := testContext(t, `
- linker_config {
- name: "linker-config-base",
- src: "linker.config.json",
- installable: false,
- }
+ result := prepareForLinkerConfigTest.RunTestWithBp(t, `
+ linker_config {
+ name: "linker-config-base",
+ src: "linker.config.json",
+ installable: false,
+ }
`)
expected := []string{"true"}
- p := ctx.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
- entries := android.AndroidMkEntriesForTest(t, config, "", p)[0]
+ p := result.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
+ entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)[0]
if value, ok := entries.EntryMap["LOCAL_UNINSTALLABLE_MODULE"]; ok {
if !reflect.DeepEqual(value, expected) {
t.Errorf("LOCAL_UNINSTALLABLE_MODULE is expected to be true but %s", value)
diff --git a/linkerconfig/proto/Android.bp b/linkerconfig/proto/Android.bp
index 4d97128..3b1e4ab 100644
--- a/linkerconfig/proto/Android.bp
+++ b/linkerconfig/proto/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_library_static {
name: "lib_linker_config_proto_lite",
host_supported: true,
diff --git a/makedeps/Android.bp b/makedeps/Android.bp
index b77b08f..62bdfd5 100644
--- a/makedeps/Android.bp
+++ b/makedeps/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-makedeps",
pkgPath: "android/soong/makedeps",
diff --git a/partner/Android.bp b/partner/Android.bp
index f2ced8d..7fc873e 100644
--- a/partner/Android.bp
+++ b/partner/Android.bp
@@ -16,6 +16,10 @@
// Sample project for creating an extended androidmk
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "partner_androidmk",
srcs: [
diff --git a/phony/Android.bp b/phony/Android.bp
index 2c423ef..db5efc9 100644
--- a/phony/Android.bp
+++ b/phony/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-phony",
pkgPath: "android/soong/phony",
diff --git a/phony/phony.go b/phony/phony.go
index cb60b9f..a31d402 100644
--- a/phony/phony.go
+++ b/phony/phony.go
@@ -52,6 +52,7 @@
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name)
+ data.Entries.WriteLicenseVariables(w)
if p.Host() {
fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
}
diff --git a/python/Android.bp b/python/Android.bp
index ffd03fe..e49fa6a 100644
--- a/python/Android.bp
+++ b/python/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-python",
pkgPath: "android/soong/python",
@@ -16,6 +20,7 @@
"proto.go",
"python.go",
"test.go",
+ "testing.go",
],
testSrcs: [
"python_test.go",
diff --git a/python/androidmk.go b/python/androidmk.go
index 60637d3..13b4172 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -48,27 +48,29 @@
func (p *binaryDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
entries.Class = "EXECUTABLES"
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
- entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
- })
+ entries.ExtraEntries = append(entries.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
+ })
base.subAndroidMk(entries, p.pythonInstaller)
}
func (p *testDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
entries.Class = "NATIVE_TESTS"
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
- entries.AddCompatibilityTestSuites(p.binaryDecorator.binaryProperties.Test_suites...)
- if p.testConfig != nil {
- entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
- }
+ entries.ExtraEntries = append(entries.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.AddCompatibilityTestSuites(p.binaryDecorator.binaryProperties.Test_suites...)
+ if p.testConfig != nil {
+ entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
+ }
- entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
+ entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
- entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
+ entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
- entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(p.testProperties.Test_options.Unit_test))
- })
+ entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(p.testProperties.Test_options.Unit_test))
+ })
base.subAndroidMk(entries, p.binaryDecorator.pythonInstaller)
}
@@ -80,14 +82,15 @@
}
entries.Required = append(entries.Required, "libc++")
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
- path, file := filepath.Split(installer.path.ToMakePath().String())
- stem := strings.TrimSuffix(file, filepath.Ext(file))
+ entries.ExtraEntries = append(entries.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ path, file := filepath.Split(installer.path.ToMakePath().String())
+ stem := strings.TrimSuffix(file, filepath.Ext(file))
- entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
- entries.SetString("LOCAL_MODULE_PATH", path)
- entries.SetString("LOCAL_MODULE_STEM", stem)
- entries.AddStrings("LOCAL_SHARED_LIBRARIES", installer.androidMkSharedLibs...)
- entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
- })
+ entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
+ entries.SetString("LOCAL_MODULE_PATH", path)
+ entries.SetString("LOCAL_MODULE_STEM", stem)
+ entries.AddStrings("LOCAL_SHARED_LIBRARIES", installer.androidMkSharedLibs...)
+ entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
+ })
}
diff --git a/python/binary.go b/python/binary.go
index 416a7ee..e955492 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -20,10 +20,99 @@
"fmt"
"android/soong/android"
+ "android/soong/bazel"
+
+ "github.com/google/blueprint/proptools"
)
func init() {
- android.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
+ registerPythonBinaryComponents(android.InitRegistrationContext)
+ android.RegisterBp2BuildMutator("python_binary_host", PythonBinaryBp2Build)
+}
+
+func registerPythonBinaryComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
+}
+
+type bazelPythonBinaryAttributes struct {
+ Main string
+ Srcs bazel.LabelListAttribute
+ Data bazel.LabelListAttribute
+ Python_version string
+}
+
+type bazelPythonBinary struct {
+ android.BazelTargetModuleBase
+ bazelPythonBinaryAttributes
+}
+
+func BazelPythonBinaryFactory() android.Module {
+ module := &bazelPythonBinary{}
+ module.AddProperties(&module.bazelPythonBinaryAttributes)
+ android.InitBazelTargetModule(module)
+ return module
+}
+
+func (m *bazelPythonBinary) Name() string {
+ return m.BaseModuleName()
+}
+
+func (m *bazelPythonBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+
+func PythonBinaryBp2Build(ctx android.TopDownMutatorContext) {
+ m, ok := ctx.Module().(*Module)
+ if !ok || !m.ConvertWithBp2build(ctx) {
+ return
+ }
+
+ // a Module can be something other than a python_binary_host
+ if ctx.ModuleType() != "python_binary_host" {
+ return
+ }
+
+ var main string
+ for _, propIntf := range m.GetProperties() {
+ if props, ok := propIntf.(*BinaryProperties); ok {
+ // main is optional.
+ if props.Main != nil {
+ main = *props.Main
+ break
+ }
+ }
+ }
+ // TODO(b/182306917): this doesn't fully handle all nested props versioned
+ // by the python version, which would have been handled by the version split
+ // mutator. This is sufficient for very simple python_binary_host modules
+ // under Bionic.
+ py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
+ py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
+ var python_version string
+ if py3Enabled && py2Enabled {
+ panic(fmt.Errorf(
+ "error for '%s' module: bp2build's python_binary_host converter does not support "+
+ "converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name()))
+ } else if py2Enabled {
+ python_version = "PY2"
+ } else {
+ // do nothing, since python_version defaults to PY3.
+ }
+
+ srcs := android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
+ data := android.BazelLabelForModuleSrc(ctx, m.properties.Data)
+
+ attrs := &bazelPythonBinaryAttributes{
+ Main: main,
+ Srcs: bazel.MakeLabelListAttribute(srcs),
+ Data: bazel.MakeLabelListAttribute(data),
+ Python_version: python_version,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ // Use the native py_binary rule.
+ Rule_class: "py_binary",
+ }
+
+ ctx.CreateBazelTargetModule(BazelPythonBinaryFactory, m.Name(), props, attrs)
}
type BinaryProperties struct {
@@ -81,6 +170,8 @@
func PythonBinaryHostFactory() android.Module {
module, _ := NewBinary(android.HostSupported)
+ android.InitBazelModule(module)
+
return module.init()
}
diff --git a/python/library.go b/python/library.go
index b724d2b..9663b3c 100644
--- a/python/library.go
+++ b/python/library.go
@@ -21,8 +21,12 @@
)
func init() {
- android.RegisterModuleType("python_library_host", PythonLibraryHostFactory)
- android.RegisterModuleType("python_library", PythonLibraryFactory)
+ registerPythonLibraryComponents(android.InitRegistrationContext)
+}
+
+func registerPythonLibraryComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("python_library_host", PythonLibraryHostFactory)
+ ctx.RegisterModuleType("python_library", PythonLibraryFactory)
}
func PythonLibraryHostFactory() android.Module {
diff --git a/python/python.go b/python/python.go
index b3e3d13..4444a70 100644
--- a/python/python.go
+++ b/python/python.go
@@ -29,7 +29,11 @@
)
func init() {
- android.PreDepsMutators(RegisterPythonPreDepsMutators)
+ registerPythonMutators(android.InitRegistrationContext)
+}
+
+func registerPythonMutators(ctx android.RegistrationContext) {
+ ctx.PreDepsMutators(RegisterPythonPreDepsMutators)
}
// Exported to support other packages using Python modules in tests.
@@ -125,6 +129,7 @@
type Module struct {
android.ModuleBase
android.DefaultableModuleBase
+ android.BazelModuleBase
properties BaseProperties
protoProperties android.ProtoProperties
diff --git a/python/python_test.go b/python/python_test.go
index 5c4efa7..f57f504 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -15,21 +15,15 @@
package python
import (
- "errors"
"fmt"
- "io/ioutil"
"os"
"path/filepath"
- "reflect"
- "sort"
- "strings"
+ "regexp"
"testing"
"android/soong/android"
)
-var buildDir string
-
type pyModule struct {
name string
actualVersion string
@@ -56,7 +50,7 @@
data = []struct {
desc string
- mockFiles map[string][]byte
+ mockFiles android.MockFS
errors []string
expectedBinaries []pyModule
@@ -64,7 +58,6 @@
{
desc: "module without any src files",
mockFiles: map[string][]byte{
- bpFile: []byte(`subdirs = ["dir"]`),
filepath.Join("dir", bpFile): []byte(
`python_library_host {
name: "lib1",
@@ -79,7 +72,6 @@
{
desc: "module with bad src file ext",
mockFiles: map[string][]byte{
- bpFile: []byte(`subdirs = ["dir"]`),
filepath.Join("dir", bpFile): []byte(
`python_library_host {
name: "lib1",
@@ -98,7 +90,6 @@
{
desc: "module with bad data file ext",
mockFiles: map[string][]byte{
- bpFile: []byte(`subdirs = ["dir"]`),
filepath.Join("dir", bpFile): []byte(
`python_library_host {
name: "lib1",
@@ -121,7 +112,6 @@
{
desc: "module with bad pkg_path format",
mockFiles: map[string][]byte{
- bpFile: []byte(`subdirs = ["dir"]`),
filepath.Join("dir", bpFile): []byte(
`python_library_host {
name: "lib1",
@@ -159,7 +149,6 @@
{
desc: "module with bad runfile src path format",
mockFiles: map[string][]byte{
- bpFile: []byte(`subdirs = ["dir"]`),
filepath.Join("dir", bpFile): []byte(
`python_library_host {
name: "lib1",
@@ -187,7 +176,6 @@
{
desc: "module with duplicate runfile path",
mockFiles: map[string][]byte{
- bpFile: []byte(`subdirs = ["dir"]`),
filepath.Join("dir", bpFile): []byte(
`python_library_host {
name: "lib1",
@@ -207,21 +195,32 @@
"lib1",
],
}
+
+ python_binary_host {
+ name: "bin",
+ pkg_path: "e/",
+ srcs: [
+ "bin.py",
+ ],
+ libs: [
+ "lib2",
+ ],
+ }
`,
),
"dir/c/file1.py": nil,
"dir/file1.py": nil,
+ "dir/bin.py": nil,
},
errors: []string{
- fmt.Sprintf(dupRunfileErrTemplate, "dir/Android.bp:9:6",
- "lib2", "PY3", "a/b/c/file1.py", "lib2", "dir/file1.py",
+ fmt.Sprintf(dupRunfileErrTemplate, "dir/Android.bp:20:6",
+ "bin", "PY3", "a/b/c/file1.py", "bin", "dir/file1.py",
"lib1", "dir/c/file1.py"),
},
},
{
desc: "module for testing dependencies",
mockFiles: map[string][]byte{
- bpFile: []byte(`subdirs = ["dir"]`),
filepath.Join("dir", bpFile): []byte(
`python_defaults {
name: "default_lib",
@@ -314,10 +313,10 @@
"e/default_py3.py",
"e/file4.py",
},
- srcsZip: "@prefix@/.intermediates/dir/bin/PY3/bin.py.srcszip",
+ srcsZip: "out/soong/.intermediates/dir/bin/PY3/bin.py.srcszip",
depsSrcsZips: []string{
- "@prefix@/.intermediates/dir/lib5/PY3/lib5.py.srcszip",
- "@prefix@/.intermediates/dir/lib6/PY3/lib6.py.srcszip",
+ "out/soong/.intermediates/dir/lib5/PY3/lib5.py.srcszip",
+ "out/soong/.intermediates/dir/lib6/PY3/lib6.py.srcszip",
},
},
},
@@ -327,60 +326,36 @@
func TestPythonModule(t *testing.T) {
for _, d := range data {
+ if d.desc != "module with duplicate runfile path" {
+ continue
+ }
+ errorPatterns := make([]string, len(d.errors))
+ for i, s := range d.errors {
+ errorPatterns[i] = regexp.QuoteMeta(s)
+ }
+
t.Run(d.desc, func(t *testing.T) {
- config := android.TestConfig(buildDir, nil, "", d.mockFiles)
- ctx := android.NewTestContext(config)
- ctx.PreDepsMutators(RegisterPythonPreDepsMutators)
- ctx.RegisterModuleType("python_library_host", PythonLibraryHostFactory)
- ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
- ctx.RegisterModuleType("python_defaults", defaultsFactory)
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- ctx.Register()
- _, testErrs := ctx.ParseBlueprintsFiles(bpFile)
- android.FailIfErrored(t, testErrs)
- _, actErrs := ctx.PrepareBuildActions(config)
- if len(actErrs) > 0 {
- testErrs = append(testErrs, expectErrors(t, actErrs, d.errors)...)
- } else {
- for _, e := range d.expectedBinaries {
- testErrs = append(testErrs,
- expectModule(t, ctx, buildDir, e.name,
- e.actualVersion,
- e.srcsZip,
- e.pyRunfiles,
- e.depsSrcsZips)...)
- }
+ result := android.GroupFixturePreparers(
+ android.PrepareForTestWithDefaults,
+ PrepareForTestWithPythonBuildComponents,
+ d.mockFiles.AddToFixture(),
+ ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(errorPatterns)).
+ RunTest(t)
+
+ if len(result.Errs) > 0 {
+ return
}
- android.FailIfErrored(t, testErrs)
+
+ for _, e := range d.expectedBinaries {
+ t.Run(e.name, func(t *testing.T) {
+ expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles, e.depsSrcsZips)
+ })
+ }
})
}
}
-func expectErrors(t *testing.T, actErrs []error, expErrs []string) (testErrs []error) {
- actErrStrs := []string{}
- for _, v := range actErrs {
- actErrStrs = append(actErrStrs, v.Error())
- }
- sort.Strings(actErrStrs)
- if len(actErrStrs) != len(expErrs) {
- t.Errorf("got (%d) errors, expected (%d) errors!", len(actErrStrs), len(expErrs))
- for _, v := range actErrStrs {
- testErrs = append(testErrs, errors.New(v))
- }
- } else {
- sort.Strings(expErrs)
- for i, v := range actErrStrs {
- if !strings.Contains(v, expErrs[i]) {
- testErrs = append(testErrs, errors.New(v))
- }
- }
- }
-
- return
-}
-
-func expectModule(t *testing.T, ctx *android.TestContext, buildDir, name, variant, expectedSrcsZip string,
- expectedPyRunfiles, expectedDepsSrcsZips []string) (testErrs []error) {
+func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles, expectedDepsSrcsZips []string) {
module := ctx.ModuleForTests(name, variant)
base, baseOk := module.Module().(*Module)
@@ -393,56 +368,13 @@
actualPyRunfiles = append(actualPyRunfiles, path.dest)
}
- if !reflect.DeepEqual(actualPyRunfiles, expectedPyRunfiles) {
- testErrs = append(testErrs, errors.New(fmt.Sprintf(
- `binary "%s" variant "%s" has unexpected pyRunfiles: %q! (expected: %q)`,
- base.Name(),
- base.properties.Actual_version,
- actualPyRunfiles,
- expectedPyRunfiles)))
- }
+ android.AssertDeepEquals(t, "pyRunfiles", expectedPyRunfiles, actualPyRunfiles)
- if base.srcsZip.String() != strings.Replace(expectedSrcsZip, "@prefix@", buildDir, 1) {
- testErrs = append(testErrs, errors.New(fmt.Sprintf(
- `binary "%s" variant "%s" has unexpected srcsZip: %q!`,
- base.Name(),
- base.properties.Actual_version,
- base.srcsZip)))
- }
+ android.AssertPathRelativeToTopEquals(t, "srcsZip", expectedSrcsZip, base.srcsZip)
- for i, _ := range expectedDepsSrcsZips {
- expectedDepsSrcsZips[i] = strings.Replace(expectedDepsSrcsZips[i], "@prefix@", buildDir, 1)
- }
- if !reflect.DeepEqual(base.depsSrcsZips.Strings(), expectedDepsSrcsZips) {
- testErrs = append(testErrs, errors.New(fmt.Sprintf(
- `binary "%s" variant "%s" has unexpected depsSrcsZips: %q!`,
- base.Name(),
- base.properties.Actual_version,
- base.depsSrcsZips)))
- }
-
- return
-}
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_python_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
+ android.AssertPathsRelativeToTopEquals(t, "depsSrcsZips", expectedDepsSrcsZips, base.depsSrcsZips)
}
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ os.Exit(m.Run())
}
diff --git a/python/test.go b/python/test.go
index b7cd475..6713189 100644
--- a/python/test.go
+++ b/python/test.go
@@ -22,8 +22,12 @@
// This file contains the module types for building Python test.
func init() {
- android.RegisterModuleType("python_test_host", PythonTestHostFactory)
- android.RegisterModuleType("python_test", PythonTestFactory)
+ registerPythonTestComponents(android.InitRegistrationContext)
+}
+
+func registerPythonTestComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("python_test_host", PythonTestHostFactory)
+ ctx.RegisterModuleType("python_test", PythonTestFactory)
}
// Test option struct.
diff --git a/python/testing.go b/python/testing.go
new file mode 100644
index 0000000..ce1a5ab
--- /dev/null
+++ b/python/testing.go
@@ -0,0 +1,24 @@
+// Copyright 2021 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 python
+
+import "android/soong/android"
+
+var PrepareForTestWithPythonBuildComponents = android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(registerPythonBinaryComponents),
+ android.FixtureRegisterWithContext(registerPythonLibraryComponents),
+ android.FixtureRegisterWithContext(registerPythonTestComponents),
+ android.FixtureRegisterWithContext(registerPythonMutators),
+)
diff --git a/python/tests/Android.bp b/python/tests/Android.bp
index c8bf420..0e8eef6 100644
--- a/python/tests/Android.bp
+++ b/python/tests/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
python_test_host {
name: "par_test",
main: "par_test.py",
diff --git a/remoteexec/Android.bp b/remoteexec/Android.bp
index fc2c0e3..0d55168 100644
--- a/remoteexec/Android.bp
+++ b/remoteexec/Android.bp
@@ -1,10 +1,10 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-remoteexec",
pkgPath: "android/soong/remoteexec",
- deps: [
- "blueprint",
- "soong-android",
- ],
srcs: [
"remoteexec.go",
],
diff --git a/remoteexec/remoteexec.go b/remoteexec/remoteexec.go
index d6e2c0a..ef4672a 100644
--- a/remoteexec/remoteexec.go
+++ b/remoteexec/remoteexec.go
@@ -17,10 +17,6 @@
import (
"sort"
"strings"
-
- "android/soong/android"
-
- "github.com/google/blueprint"
)
const (
@@ -56,7 +52,6 @@
var (
defaultLabels = map[string]string{"type": "tool"}
defaultExecStrategy = LocalExecStrategy
- pctx = android.NewPackageContext("android/soong/remoteexec")
)
// REParams holds information pertinent to the remote execution of a rule.
@@ -69,9 +64,8 @@
ExecStrategy string
// Inputs is a list of input paths or ninja variables.
Inputs []string
- // RSPFile is the name of the ninja variable used by the rule as a placeholder for an rsp
- // input.
- RSPFile string
+ // RSPFiles is the name of the files used by the rule as a placeholder for an rsp input.
+ RSPFiles []string
// OutputFiles is a list of output file paths or ninja variables as placeholders for rule
// outputs.
OutputFiles []string
@@ -81,31 +75,24 @@
// ToolchainInputs is a list of paths or ninja variables pointing to the location of
// toolchain binaries used by the rule.
ToolchainInputs []string
+ // EnvironmentVariables is a list of environment variables whose values should be passed through
+ // to the remote execution.
+ EnvironmentVariables []string
}
func init() {
- pctx.VariableFunc("Wrapper", func(ctx android.PackageVarContext) string {
- return wrapper(ctx.Config())
- })
-}
-
-func wrapper(cfg android.Config) string {
- if override := cfg.Getenv("RBE_WRAPPER"); override != "" {
- return override
- }
- return DefaultWrapperPath
}
// Template generates the remote execution wrapper template to be added as a prefix to the rule's
// command.
func (r *REParams) Template() string {
- return "${remoteexec.Wrapper}" + r.wrapperArgs()
+ return "${android.RBEWrapper}" + r.wrapperArgs()
}
// NoVarTemplate generates the remote execution wrapper template without variables, to be used in
// RuleBuilder.
-func (r *REParams) NoVarTemplate(cfg android.Config) string {
- return wrapper(cfg) + r.wrapperArgs()
+func (r *REParams) NoVarTemplate(wrapper string) string {
+ return wrapper + r.wrapperArgs()
}
func (r *REParams) wrapperArgs() string {
@@ -146,8 +133,8 @@
args += " --inputs=" + strings.Join(r.Inputs, ",")
}
- if r.RSPFile != "" {
- args += " --input_list_paths=" + r.RSPFile
+ if len(r.RSPFiles) > 0 {
+ args += " --input_list_paths=" + strings.Join(r.RSPFiles, ",")
}
if len(r.OutputFiles) > 0 {
@@ -162,45 +149,9 @@
args += " --toolchain_inputs=" + strings.Join(r.ToolchainInputs, ",")
}
+ if len(r.EnvironmentVariables) > 0 {
+ args += " --env_var_allowlist=" + strings.Join(r.EnvironmentVariables, ",")
+ }
+
return args + " -- "
}
-
-// StaticRules returns a pair of rules based on the given RuleParams, where the first rule is a
-// locally executable rule and the second rule is a remotely executable rule. commonArgs are args
-// used for both the local and remotely executable rules. reArgs are used only for remote
-// execution.
-func StaticRules(ctx android.PackageContext, name string, ruleParams blueprint.RuleParams, reParams *REParams, commonArgs []string, reArgs []string) (blueprint.Rule, blueprint.Rule) {
- ruleParamsRE := ruleParams
- ruleParams.Command = strings.ReplaceAll(ruleParams.Command, "$reTemplate", "")
- ruleParamsRE.Command = strings.ReplaceAll(ruleParamsRE.Command, "$reTemplate", reParams.Template())
-
- return ctx.AndroidStaticRule(name, ruleParams, commonArgs...),
- ctx.AndroidRemoteStaticRule(name+"RE", android.RemoteRuleSupports{RBE: true}, ruleParamsRE, append(commonArgs, reArgs...)...)
-}
-
-// MultiCommandStaticRules returns a pair of rules based on the given RuleParams, where the first
-// rule is a locally executable rule and the second rule is a remotely executable rule. This
-// function supports multiple remote execution wrappers placed in the template when commands are
-// chained together with &&. commonArgs are args used for both the local and remotely executable
-// rules. reArgs are args used only for remote execution.
-func MultiCommandStaticRules(ctx android.PackageContext, name string, ruleParams blueprint.RuleParams, reParams map[string]*REParams, commonArgs []string, reArgs []string) (blueprint.Rule, blueprint.Rule) {
- ruleParamsRE := ruleParams
- for k, v := range reParams {
- ruleParams.Command = strings.ReplaceAll(ruleParams.Command, k, "")
- ruleParamsRE.Command = strings.ReplaceAll(ruleParamsRE.Command, k, v.Template())
- }
-
- return ctx.AndroidStaticRule(name, ruleParams, commonArgs...),
- ctx.AndroidRemoteStaticRule(name+"RE", android.RemoteRuleSupports{RBE: true}, ruleParamsRE, append(commonArgs, reArgs...)...)
-}
-
-// EnvOverrideFunc retrieves a variable func that evaluates to the value of the given environment
-// variable if set, otherwise the given default.
-func EnvOverrideFunc(envVar, defaultVal string) func(ctx android.PackageVarContext) string {
- return func(ctx android.PackageVarContext) string {
- if override := ctx.Config().Getenv(envVar); override != "" {
- return override
- }
- return defaultVal
- }
-}
diff --git a/remoteexec/remoteexec_test.go b/remoteexec/remoteexec_test.go
index 56985d3..b117b89 100644
--- a/remoteexec/remoteexec_test.go
+++ b/remoteexec/remoteexec_test.go
@@ -17,8 +17,6 @@
import (
"fmt"
"testing"
-
- "android/soong/android"
)
func TestTemplate(t *testing.T) {
@@ -38,7 +36,7 @@
PoolKey: "default",
},
},
- want: fmt.Sprintf("${remoteexec.Wrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out -- ", DefaultImage),
+ want: fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out -- ", DefaultImage),
},
{
name: "all params",
@@ -47,14 +45,14 @@
Inputs: []string{"$in"},
OutputFiles: []string{"$out"},
ExecStrategy: "remote",
- RSPFile: "$out.rsp",
+ RSPFiles: []string{"$out.rsp", "out2.rsp"},
ToolchainInputs: []string{"clang++"},
Platform: map[string]string{
ContainerImageKey: DefaultImage,
PoolKey: "default",
},
},
- want: fmt.Sprintf("${remoteexec.Wrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=remote --inputs=$in --input_list_paths=$out.rsp --output_files=$out --toolchain_inputs=clang++ -- ", DefaultImage),
+ want: fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=remote --inputs=$in --input_list_paths=$out.rsp,out2.rsp --output_files=$out --toolchain_inputs=clang++ -- ", DefaultImage),
},
}
for _, test := range tests {
@@ -77,7 +75,7 @@
},
}
want := fmt.Sprintf("prebuilts/remoteexecution-client/live/rewrapper --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out -- ", DefaultImage)
- if got := params.NoVarTemplate(android.NullConfig("")); got != want {
+ if got := params.NoVarTemplate(DefaultWrapperPath); got != want {
t.Errorf("NoVarTemplate() returned\n%s\nwant\n%s", got, want)
}
}
@@ -92,7 +90,7 @@
PoolKey: "default",
},
}
- want := fmt.Sprintf("${remoteexec.Wrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out -- ", DefaultImage)
+ want := fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out -- ", DefaultImage)
for i := 0; i < 1000; i++ {
if got := r.Template(); got != want {
t.Fatalf("Template() returned\n%s\nwant\n%s", got, want)
diff --git a/response/Android.bp b/response/Android.bp
new file mode 100644
index 0000000..e19981f
--- /dev/null
+++ b/response/Android.bp
@@ -0,0 +1,16 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-response",
+ pkgPath: "android/soong/response",
+ deps: [
+ ],
+ srcs: [
+ "response.go",
+ ],
+ testSrcs: [
+ "response_test.go",
+ ],
+}
diff --git a/response/response.go b/response/response.go
new file mode 100644
index 0000000..b65503e
--- /dev/null
+++ b/response/response.go
@@ -0,0 +1,112 @@
+// Copyright 2021 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 response
+
+import (
+ "io"
+ "io/ioutil"
+ "strings"
+ "unicode"
+)
+
+const noQuote = '\x00'
+
+// ReadRspFile reads a file in Ninja's response file format and returns its contents.
+func ReadRspFile(r io.Reader) ([]string, error) {
+ var files []string
+ var file []byte
+
+ buf, err := ioutil.ReadAll(r)
+ if err != nil {
+ return nil, err
+ }
+
+ isEscaping := false
+ quotingStart := byte(noQuote)
+ for _, c := range buf {
+ switch {
+ case isEscaping:
+ if quotingStart == '"' {
+ if !(c == '"' || c == '\\') {
+ // '\"' or '\\' will be escaped under double quoting.
+ file = append(file, '\\')
+ }
+ }
+ file = append(file, c)
+ isEscaping = false
+ case c == '\\' && quotingStart != '\'':
+ isEscaping = true
+ case quotingStart == noQuote && (c == '\'' || c == '"'):
+ quotingStart = c
+ case quotingStart != noQuote && c == quotingStart:
+ quotingStart = noQuote
+ case quotingStart == noQuote && unicode.IsSpace(rune(c)):
+ // Current character is a space outside quotes
+ if len(file) != 0 {
+ files = append(files, string(file))
+ }
+ file = file[:0]
+ default:
+ file = append(file, c)
+ }
+ }
+
+ if len(file) != 0 {
+ files = append(files, string(file))
+ }
+
+ return files, nil
+}
+
+func rspUnsafeChar(r rune) bool {
+ switch {
+ case 'A' <= r && r <= 'Z',
+ 'a' <= r && r <= 'z',
+ '0' <= r && r <= '9',
+ r == '_',
+ r == '+',
+ r == '-',
+ r == '.',
+ r == '/':
+ return false
+ default:
+ return true
+ }
+}
+
+var rspEscaper = strings.NewReplacer(`'`, `'\''`)
+
+// WriteRspFile writes a list of files to a file in Ninja's response file format.
+func WriteRspFile(w io.Writer, files []string) error {
+ for i, f := range files {
+ if i != 0 {
+ _, err := io.WriteString(w, " ")
+ if err != nil {
+ return err
+ }
+ }
+
+ if strings.IndexFunc(f, rspUnsafeChar) != -1 {
+ f = `'` + rspEscaper.Replace(f) + `'`
+ }
+
+ _, err := io.WriteString(w, f)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/response/response_test.go b/response/response_test.go
new file mode 100644
index 0000000..4d1fb41
--- /dev/null
+++ b/response/response_test.go
@@ -0,0 +1,123 @@
+// Copyright 2021 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 response
+
+import (
+ "bytes"
+ "reflect"
+ "testing"
+)
+
+func TestReadRspFile(t *testing.T) {
+ testCases := []struct {
+ name, in string
+ out []string
+ }{
+ {
+ name: "single quoting test case 1",
+ in: `./cmd '"'-C`,
+ out: []string{"./cmd", `"-C`},
+ },
+ {
+ name: "single quoting test case 2",
+ in: `./cmd '-C`,
+ out: []string{"./cmd", `-C`},
+ },
+ {
+ name: "single quoting test case 3",
+ in: `./cmd '\"'-C`,
+ out: []string{"./cmd", `\"-C`},
+ },
+ {
+ name: "single quoting test case 4",
+ in: `./cmd '\\'-C`,
+ out: []string{"./cmd", `\\-C`},
+ },
+ {
+ name: "none quoting test case 1",
+ in: `./cmd \'-C`,
+ out: []string{"./cmd", `'-C`},
+ },
+ {
+ name: "none quoting test case 2",
+ in: `./cmd \\-C`,
+ out: []string{"./cmd", `\-C`},
+ },
+ {
+ name: "none quoting test case 3",
+ in: `./cmd \"-C`,
+ out: []string{"./cmd", `"-C`},
+ },
+ {
+ name: "double quoting test case 1",
+ in: `./cmd "'"-C`,
+ out: []string{"./cmd", `'-C`},
+ },
+ {
+ name: "double quoting test case 2",
+ in: `./cmd "\\"-C`,
+ out: []string{"./cmd", `\-C`},
+ },
+ {
+ name: "double quoting test case 3",
+ in: `./cmd "\""-C`,
+ out: []string{"./cmd", `"-C`},
+ },
+ {
+ name: "ninja rsp file",
+ in: "'a'\nb\n'@'\n'foo'\\''bar'\n'foo\"bar'",
+ out: []string{"a", "b", "@", "foo'bar", `foo"bar`},
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ got, err := ReadRspFile(bytes.NewBuffer([]byte(testCase.in)))
+ if err != nil {
+ t.Errorf("unexpected error: %q", err)
+ }
+ if !reflect.DeepEqual(got, testCase.out) {
+ t.Errorf("expected %q got %q", testCase.out, got)
+ }
+ })
+ }
+}
+
+func TestWriteRspFile(t *testing.T) {
+ testCases := []struct {
+ name string
+ in []string
+ out string
+ }{
+ {
+ name: "ninja rsp file",
+ in: []string{"a", "b", "@", "foo'bar", `foo"bar`},
+ out: "a b '@' 'foo'\\''bar' 'foo\"bar'",
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ buf := &bytes.Buffer{}
+ err := WriteRspFile(buf, testCase.in)
+ if err != nil {
+ t.Errorf("unexpected error: %q", err)
+ }
+ if buf.String() != testCase.out {
+ t.Errorf("expected %q got %q", testCase.out, buf.String())
+ }
+ })
+ }
+}
diff --git a/rust/Android.bp b/rust/Android.bp
index df731db..a6c4e07 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -1,20 +1,27 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-rust",
pkgPath: "android/soong/rust",
deps: [
"soong",
"soong-android",
+ "soong-bloaty",
"soong-cc",
"soong-rust-config",
],
srcs: [
"androidmk.go",
+ "benchmark.go",
"binary.go",
"bindgen.go",
"builder.go",
"clippy.go",
"compiler.go",
"coverage.go",
+ "fuzz.go",
"image.go",
"library.go",
"prebuilt.go",
@@ -22,18 +29,21 @@
"project_json.go",
"protobuf.go",
"rust.go",
+ "sanitize.go",
"strip.go",
"source_provider.go",
"test.go",
"testing.go",
],
testSrcs: [
+ "benchmark_test.go",
"binary_test.go",
"bindgen_test.go",
"builder_test.go",
"clippy_test.go",
"compiler_test.go",
"coverage_test.go",
+ "fuzz_test.go",
"image_test.go",
"library_test.go",
"project_json_test.go",
diff --git a/rust/androidmk.go b/rust/androidmk.go
index e9da6fa..5f89d73 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -18,6 +18,7 @@
"path/filepath"
"android/soong/android"
+ "android/soong/cc"
)
type AndroidMkContext interface {
@@ -49,16 +50,20 @@
}
ret := android.AndroidMkEntries{
- OutputFile: mod.outputFile,
+ OutputFile: mod.unstrippedOutputFile,
Include: "$(BUILD_SYSTEM)/soong_rust_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.AddStrings("LOCAL_RLIB_LIBRARIES", mod.Properties.AndroidMkRlibs...)
entries.AddStrings("LOCAL_DYLIB_LIBRARIES", mod.Properties.AndroidMkDylibs...)
entries.AddStrings("LOCAL_PROC_MACRO_LIBRARIES", mod.Properties.AndroidMkProcMacroLibs...)
entries.AddStrings("LOCAL_SHARED_LIBRARIES", mod.Properties.AndroidMkSharedLibs...)
entries.AddStrings("LOCAL_STATIC_LIBRARIES", mod.Properties.AndroidMkStaticLibs...)
entries.AddStrings("LOCAL_SOONG_LINK_TYPE", mod.makeLinkType)
+ if mod.UseVndk() {
+ entries.SetBool("LOCAL_USE_VNDK", true)
+ }
+
},
},
}
@@ -69,6 +74,12 @@
// If the compiler is disabled, this is a SourceProvider.
mod.SubAndroidMk(&ret, mod.sourceProvider)
}
+
+ if mod.sanitize != nil {
+ mod.SubAndroidMk(&ret, mod.sanitize)
+ }
+
+ ret.SubName += mod.Properties.RustSubName
ret.SubName += mod.Properties.SubName
return []android.AndroidMkEntries{ret}
@@ -82,23 +93,37 @@
}
ret.Class = "EXECUTABLES"
- ret.ExtraEntries = append(ret.ExtraEntries, func(entries *android.AndroidMkEntries) {
- entries.SetOptionalPath("LOCAL_PREBUILT_COVERAGE_ARCHIVE", binary.coverageOutputZipFile)
- })
}
func (test *testDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
- test.binaryDecorator.AndroidMk(ctx, ret)
+ ctx.SubAndroidMk(ret, test.binaryDecorator)
+
ret.Class = "NATIVE_TESTS"
- ret.ExtraEntries = append(ret.ExtraEntries, func(entries *android.AndroidMkEntries) {
- entries.AddCompatibilityTestSuites(test.Properties.Test_suites...)
- if test.testConfig != nil {
- entries.SetString("LOCAL_FULL_TEST_CONFIG", test.testConfig.String())
- }
- entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(test.Properties.Auto_gen_config, true))
- entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(test.Properties.Test_options.Unit_test))
- })
- // TODO(chh): add test data with androidMkWriteTestData(test.data, ctx, ret)
+ ret.ExtraEntries = append(ret.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.AddCompatibilityTestSuites(test.Properties.Test_suites...)
+ if test.testConfig != nil {
+ entries.SetString("LOCAL_FULL_TEST_CONFIG", test.testConfig.String())
+ }
+ entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(test.Properties.Auto_gen_config, true))
+ entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(test.Properties.Test_options.Unit_test))
+ })
+
+ cc.AndroidMkWriteTestData(test.data, ret)
+}
+
+func (benchmark *benchmarkDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
+ benchmark.binaryDecorator.AndroidMk(ctx, ret)
+ ret.Class = "NATIVE_TESTS"
+ ret.ExtraEntries = append(ret.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.AddCompatibilityTestSuites(benchmark.Properties.Test_suites...)
+ if benchmark.testConfig != nil {
+ entries.SetString("LOCAL_FULL_TEST_CONFIG", benchmark.testConfig.String())
+ }
+ entries.SetBool("LOCAL_NATIVE_BENCHMARK", true)
+ entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(benchmark.Properties.Auto_gen_config, true))
+ })
}
func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
@@ -117,10 +142,6 @@
if library.distFile.Valid() {
ret.DistFiles = android.MakeDefaultDistFiles(library.distFile.Path())
}
-
- ret.ExtraEntries = append(ret.ExtraEntries, func(entries *android.AndroidMkEntries) {
- entries.SetOptionalPath("LOCAL_PREBUILT_COVERAGE_ARCHIVE", library.coverageOutputZipFile)
- })
}
func (procMacro *procMacroDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
@@ -138,13 +159,14 @@
ret.Class = "ETC"
ret.OutputFile = android.OptionalPathForPath(outFile)
ret.SubName += sourceProvider.subName
- ret.ExtraEntries = append(ret.ExtraEntries, func(entries *android.AndroidMkEntries) {
- _, file := filepath.Split(outFile.String())
- stem, suffix, _ := android.SplitFileExt(file)
- entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
- entries.SetString("LOCAL_MODULE_STEM", stem)
- entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
- })
+ ret.ExtraEntries = append(ret.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ _, file := filepath.Split(outFile.String())
+ stem, suffix, _ := android.SplitFileExt(file)
+ entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
+ entries.SetString("LOCAL_MODULE_STEM", stem)
+ entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+ })
}
func (bindgen *bindgenDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
@@ -169,12 +191,13 @@
unstrippedOutputFile = ret.OutputFile
ret.OutputFile = compiler.strippedOutputFile
}
- ret.ExtraEntries = append(ret.ExtraEntries, func(entries *android.AndroidMkEntries) {
- entries.SetOptionalPath("LOCAL_SOONG_UNSTRIPPED_BINARY", unstrippedOutputFile)
- path, file := filepath.Split(compiler.path.ToMakePath().String())
- stem, suffix, _ := android.SplitFileExt(file)
- entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
- entries.SetString("LOCAL_MODULE_PATH", path)
- entries.SetString("LOCAL_MODULE_STEM", stem)
- })
+ ret.ExtraEntries = append(ret.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetOptionalPath("LOCAL_SOONG_UNSTRIPPED_BINARY", unstrippedOutputFile)
+ path, file := filepath.Split(compiler.path.ToMakePath().String())
+ stem, suffix, _ := android.SplitFileExt(file)
+ entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
+ entries.SetString("LOCAL_MODULE_PATH", path)
+ entries.SetString("LOCAL_MODULE_STEM", stem)
+ })
}
diff --git a/rust/benchmark.go b/rust/benchmark.go
new file mode 100644
index 0000000..b89f5cd
--- /dev/null
+++ b/rust/benchmark.go
@@ -0,0 +1,129 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+ "android/soong/android"
+ "android/soong/tradefed"
+)
+
+type BenchmarkProperties struct {
+ // Disables the creation of a test-specific directory when used with
+ // relative_install_path. Useful if several tests need to be in the same
+ // directory, but test_per_src doesn't work.
+ No_named_install_directory *bool
+
+ // the name of the test configuration (for example "AndroidBenchmark.xml") that should be
+ // installed with the module.
+ Test_config *string `android:"path,arch_variant"`
+
+ // the name of the test configuration template (for example "AndroidBenchmarkTemplate.xml") that
+ // should be installed with the module.
+ Test_config_template *string `android:"path,arch_variant"`
+
+ // list of compatibility suites (for example "cts", "vts") that the module should be
+ // installed into.
+ Test_suites []string `android:"arch_variant"`
+
+ // Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
+ // doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
+ // explicitly.
+ Auto_gen_config *bool
+}
+
+type benchmarkDecorator struct {
+ *binaryDecorator
+ Properties BenchmarkProperties
+ testConfig android.Path
+}
+
+func NewRustBenchmark(hod android.HostOrDeviceSupported) (*Module, *benchmarkDecorator) {
+ // Build both 32 and 64 targets for device benchmarks.
+ // Cannot build both for host benchmarks yet if the benchmark depends on
+ // something like proc-macro2 that cannot be built for both.
+ multilib := android.MultilibBoth
+ if hod != android.DeviceSupported && hod != android.HostAndDeviceSupported {
+ multilib = android.MultilibFirst
+ }
+ module := newModule(hod, multilib)
+
+ benchmark := &benchmarkDecorator{
+ binaryDecorator: &binaryDecorator{
+ baseCompiler: NewBaseCompiler("nativebench", "nativebench64", InstallInData),
+ },
+ }
+
+ module.compiler = benchmark
+ module.AddProperties(&benchmark.Properties)
+ return module, benchmark
+}
+
+func init() {
+ android.RegisterModuleType("rust_benchmark", RustBenchmarkFactory)
+ android.RegisterModuleType("rust_benchmark_host", RustBenchmarkHostFactory)
+}
+
+func RustBenchmarkFactory() android.Module {
+ module, _ := NewRustBenchmark(android.HostAndDeviceSupported)
+ return module.Init()
+}
+
+func RustBenchmarkHostFactory() android.Module {
+ module, _ := NewRustBenchmark(android.HostSupported)
+ return module.Init()
+}
+
+func (benchmark *benchmarkDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
+ return rlibAutoDep
+}
+
+func (benchmark *benchmarkDecorator) stdLinkage(ctx *depsContext) RustLinkage {
+ return RlibLinkage
+}
+
+func (benchmark *benchmarkDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
+ flags = benchmark.binaryDecorator.compilerFlags(ctx, flags)
+ return flags
+}
+
+func (benchmark *benchmarkDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+ deps = benchmark.binaryDecorator.compilerDeps(ctx, deps)
+
+ deps.Rustlibs = append(deps.Rustlibs, "libcriterion")
+
+ return deps
+}
+
+func (benchmark *benchmarkDecorator) compilerProps() []interface{} {
+ return append(benchmark.binaryDecorator.compilerProps(), &benchmark.Properties)
+}
+
+func (benchmark *benchmarkDecorator) install(ctx ModuleContext) {
+ benchmark.testConfig = tradefed.AutoGenRustBenchmarkConfig(ctx,
+ benchmark.Properties.Test_config,
+ benchmark.Properties.Test_config_template,
+ benchmark.Properties.Test_suites,
+ nil,
+ benchmark.Properties.Auto_gen_config)
+
+ // default relative install path is module name
+ if !Bool(benchmark.Properties.No_named_install_directory) {
+ benchmark.baseCompiler.relative = ctx.ModuleName()
+ } else if String(benchmark.baseCompiler.Properties.Relative_install_path) == "" {
+ ctx.PropertyErrorf("no_named_install_directory", "Module install directory may only be disabled if relative_install_path is set")
+ }
+
+ benchmark.binaryDecorator.install(ctx)
+}
diff --git a/rust/benchmark_test.go b/rust/benchmark_test.go
new file mode 100644
index 0000000..734dda7
--- /dev/null
+++ b/rust/benchmark_test.go
@@ -0,0 +1,54 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestRustBenchmark(t *testing.T) {
+ ctx := testRust(t, `
+ rust_benchmark_host {
+ name: "my_bench",
+ srcs: ["foo.rs"],
+ }`)
+
+ testingModule := ctx.ModuleForTests("my_bench", "linux_glibc_x86_64")
+ expectedOut := "my_bench/linux_glibc_x86_64/my_bench"
+ outPath := testingModule.Output("my_bench").Output.String()
+ if !strings.Contains(outPath, expectedOut) {
+ t.Errorf("wrong output path: %v; expected: %v", outPath, expectedOut)
+ }
+}
+
+func TestRustBenchmarkLinkage(t *testing.T) {
+ ctx := testRust(t, `
+ rust_benchmark {
+ name: "my_bench",
+ srcs: ["foo.rs"],
+ }`)
+
+ testingModule := ctx.ModuleForTests("my_bench", "android_arm64_armv8-a").Module().(*Module)
+
+ if !android.InList("libcriterion.rlib-std", testingModule.Properties.AndroidMkRlibs) {
+ t.Errorf("rlib-std variant for libcriterion not detected as a rustlib-defined rlib dependency for device rust_benchmark module")
+ }
+ if !android.InList("libstd", testingModule.Properties.AndroidMkRlibs) {
+ t.Errorf("Device rust_benchmark module 'my_bench' does not link libstd as an rlib")
+ }
+}
diff --git a/rust/binary.go b/rust/binary.go
index c2d97f3..dfe8744 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -87,7 +87,7 @@
deps = binary.baseCompiler.compilerDeps(ctx, deps)
if ctx.toolchain().Bionic() {
- deps = bionicDeps(deps, Bool(binary.Properties.Static_executable))
+ deps = bionicDeps(ctx, deps, Bool(binary.Properties.Static_executable))
if Bool(binary.Properties.Static_executable) {
deps.CrtBegin = "crtbegin_static"
} else {
@@ -119,9 +119,10 @@
outputFile := android.PathForModuleOut(ctx, fileName)
flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
+ flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
- outputs := TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+ TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
if binary.stripper.NeedsStrip(ctx) {
strippedOutputFile := android.PathForModuleOut(ctx, "stripped", fileName)
@@ -129,25 +130,10 @@
binary.strippedOutputFile = android.OptionalPathForPath(strippedOutputFile)
}
- binary.coverageFile = outputs.coverageFile
-
- var coverageFiles android.Paths
- if outputs.coverageFile != nil {
- coverageFiles = append(coverageFiles, binary.coverageFile)
- }
- if len(deps.coverageFiles) > 0 {
- coverageFiles = append(coverageFiles, deps.coverageFiles...)
- }
- binary.coverageOutputZipFile = TransformCoverageFilesToZip(ctx, coverageFiles, binary.getStem(ctx))
-
return outputFile
}
-func (binary *binaryDecorator) coverageOutputZipPath() android.OptionalPath {
- return binary.coverageOutputZipFile
-}
-
-func (binary *binaryDecorator) autoDep(ctx BaseModuleContext) autoDep {
+func (binary *binaryDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
// Binaries default to dylib dependencies for device, rlib for host.
if binary.preferRlib() {
return rlibAutoDep
@@ -164,3 +150,7 @@
}
return binary.baseCompiler.stdLinkage(ctx)
}
+
+func (binary *binaryDecorator) isDependencyRoot() bool {
+ return true
+}
diff --git a/rust/binary_test.go b/rust/binary_test.go
index b44a5bc..86f50d3 100644
--- a/rust/binary_test.go
+++ b/rust/binary_test.go
@@ -130,6 +130,9 @@
if !strings.Contains(flags, "-C relocation-model=static") {
t.Errorf("static binary missing '-C relocation-model=static' in rustcFlags, found: %#v", flags)
}
+ if !strings.Contains(flags, "-C panic=abort") {
+ t.Errorf("static binary missing '-C panic=abort' in rustcFlags, found: %#v", flags)
+ }
if !strings.Contains(linkFlags, "-static") {
t.Errorf("static binary missing '-static' in linkFlags, found: %#v", flags)
}
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 35a807b..bcc26b8 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -29,14 +29,21 @@
defaultBindgenFlags = []string{""}
// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
- bindgenClangVersion = "clang-r399163b"
+ bindgenClangVersion = "clang-r412851"
+
+ _ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
+ if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
+ return override
+ }
+ return bindgenClangVersion
+ })
//TODO(b/160803703) Use a prebuilt bindgen instead of the built bindgen.
_ = pctx.HostBinToolVariable("bindgenCmd", "bindgen")
_ = pctx.SourcePathVariable("bindgenClang",
- "${cc_config.ClangBase}/${config.HostPrebuiltTag}/"+bindgenClangVersion+"/bin/clang")
+ "${cc_config.ClangBase}/${config.HostPrebuiltTag}/${bindgenClangVersion}/bin/clang")
_ = pctx.SourcePathVariable("bindgenLibClang",
- "${cc_config.ClangBase}/${config.HostPrebuiltTag}/"+bindgenClangVersion+"/lib64/")
+ "${cc_config.ClangBase}/${config.HostPrebuiltTag}/${bindgenClangVersion}/lib64/")
//TODO(ivanlozano) Switch this to RuleBuilder
bindgen = pctx.AndroidStaticRule("bindgen",
@@ -253,7 +260,7 @@
func (b *bindgenDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
deps = b.BaseSourceProvider.SourceProviderDeps(ctx, deps)
if ctx.toolchain().Bionic() {
- deps = bionicDeps(deps, false)
+ deps = bionicDeps(ctx, deps, false)
}
deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs...)
diff --git a/rust/builder.go b/rust/builder.go
index e5f0ab5..208b734 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -19,10 +19,8 @@
"strings"
"github.com/google/blueprint"
- "github.com/google/blueprint/pathtools"
"android/soong/android"
- "android/soong/cc"
"android/soong/rust/config"
)
@@ -52,9 +50,12 @@
Command: "$envVars $clippyCmd " +
// Because clippy-driver uses rustc as backend, we need to have some output even during the linting.
// Use the metadata output as it has the smallest footprint.
- "--emit metadata -o $out $in ${libFlags} " +
- "$rustcFlags $clippyFlags",
+ "--emit metadata -o $out --emit dep-info=$out.d.raw $in ${libFlags} " +
+ "$rustcFlags $clippyFlags" +
+ " && grep \"^$out:\" $out.d.raw > $out.d",
CommandDeps: []string{"$clippyCmd"},
+ Deps: blueprint.DepsGCC,
+ Depfile: "$out.d",
},
"rustcFlags", "libFlags", "clippyFlags", "envVars")
@@ -76,8 +77,7 @@
)
type buildOutput struct {
- outputFile android.Path
- coverageFile android.Path
+ outputFile android.Path
}
func init() {
@@ -86,7 +86,7 @@
func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, linkDirs []string) buildOutput {
- flags.RustFlags = append(flags.RustFlags, "-C lto")
+ flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin", linkDirs)
}
@@ -103,13 +103,13 @@
func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, linkDirs []string) buildOutput {
- flags.RustFlags = append(flags.RustFlags, "-C lto")
+ flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib", linkDirs)
}
func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, linkDirs []string) buildOutput {
- flags.RustFlags = append(flags.RustFlags, "-C lto")
+ flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib", linkDirs)
}
@@ -188,37 +188,15 @@
implicits = append(implicits, rustLibsToPaths(deps.DyLibs)...)
implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...)
implicits = append(implicits, deps.StaticLibs...)
- implicits = append(implicits, deps.SharedLibs...)
+ implicits = append(implicits, deps.SharedLibDeps...)
implicits = append(implicits, deps.srcProviderFiles...)
if deps.CrtBegin.Valid() {
implicits = append(implicits, deps.CrtBegin.Path(), deps.CrtEnd.Path())
}
- if flags.Coverage {
- var gcnoFile android.WritablePath
- // Provide consistency with cc gcda output, see cc/builder.go init()
- profileEmitArg := strings.TrimPrefix(cc.PwdPrefix(), "PWD=") + "/"
-
- if outputFile.Ext() != "" {
- // rustc seems to split the output filename at the first '.' when determining the gcno filename
- // so we need to do the same here.
- gcnoFile = android.PathForModuleOut(ctx, strings.Split(outputFile.Base(), ".")[0]+".gcno")
- rustcFlags = append(rustcFlags, "-Z profile-emit="+profileEmitArg+android.PathForModuleOut(
- ctx, pathtools.ReplaceExtension(outputFile.Base(), "gcda")).String())
- } else {
- gcnoFile = android.PathForModuleOut(ctx, outputFile.Base()+".gcno")
- rustcFlags = append(rustcFlags, "-Z profile-emit="+profileEmitArg+android.PathForModuleOut(
- ctx, outputFile.Base()+".gcda").String())
- }
-
- implicitOutputs = append(implicitOutputs, gcnoFile)
- output.coverageFile = gcnoFile
- }
-
if len(deps.SrcDeps) > 0 {
- genSubDir := "out/"
- moduleGenDir := android.PathForModuleOut(ctx, genSubDir)
+ moduleGenDir := ctx.RustModule().compiler.CargoOutDir()
var outputs android.WritablePaths
for _, genSrc := range deps.SrcDeps {
@@ -231,7 +209,7 @@
ctx.Build(pctx, android.BuildParams{
Rule: cp,
- Description: "cp " + moduleGenDir.Rel(),
+ Description: "cp " + moduleGenDir.Path().Rel(),
Outputs: outputs,
Inputs: deps.SrcDeps,
Args: map[string]string{
@@ -253,6 +231,8 @@
envVars = append(envVars, "OUT_DIR="+filepath.Join(outDirPrefix, moduleGenDir.String()))
}
+ envVars = append(envVars, "ANDROID_RUST_VERSION="+config.RustDefaultVersion)
+
if flags.Clippy {
clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
ctx.Build(pctx, android.BuildParams{
@@ -292,21 +272,3 @@
return output
}
-
-func TransformCoverageFilesToZip(ctx ModuleContext,
- covFiles android.Paths, baseName string) android.OptionalPath {
- if len(covFiles) > 0 {
-
- outputFile := android.PathForModuleOut(ctx, baseName+".zip")
-
- ctx.Build(pctx, android.BuildParams{
- Rule: zip,
- Description: "zip " + outputFile.Base(),
- Inputs: covFiles,
- Output: outputFile,
- })
-
- return android.OptionalPathForPath(outputFile)
- }
- return android.OptionalPath{}
-}
diff --git a/rust/clippy_test.go b/rust/clippy_test.go
index e24f666..bd3bfb1 100644
--- a/rust/clippy_test.go
+++ b/rust/clippy_test.go
@@ -18,7 +18,6 @@
"testing"
"android/soong/android"
- "android/soong/cc"
)
func TestClippy(t *testing.T) {
@@ -45,15 +44,6 @@
clippy_lints: "none",
}`
- bp = bp + GatherRequiredDepsForTest()
- bp = bp + cc.GatherRequiredDepsForTest(android.NoOsType)
-
- fs := map[string][]byte{
- // Reuse the same blueprint file for subdirectories.
- "external/Android.bp": []byte(bp),
- "hardware/Android.bp": []byte(bp),
- }
-
var clippyLintTests = []struct {
modulePath string
fooFlags string
@@ -66,29 +56,22 @@
for _, tc := range clippyLintTests {
t.Run("path="+tc.modulePath, func(t *testing.T) {
- config := android.TestArchConfig(buildDir, nil, bp, fs)
- ctx := CreateTestContext(config)
- ctx.Register()
- _, errs := ctx.ParseFileList(".", []string{tc.modulePath + "Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ // Test with the blueprint file in different directories.
+ android.FixtureAddTextFile(tc.modulePath+"Android.bp", bp),
+ ).RunTest(t)
- r := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
- if r.Args["clippyFlags"] != tc.fooFlags {
- t.Errorf("Incorrect flags for libfoo: %q, want %q", r.Args["clippyFlags"], tc.fooFlags)
- }
+ r := result.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+ android.AssertStringEquals(t, "libfoo flags", tc.fooFlags, r.Args["clippyFlags"])
- r = ctx.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
- if r.Args["clippyFlags"] != "${config.ClippyDefaultLints}" {
- t.Errorf("Incorrect flags for libbar: %q, want %q", r.Args["clippyFlags"], "${config.ClippyDefaultLints}")
- }
+ r = result.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+ android.AssertStringEquals(t, "libbar flags", "${config.ClippyDefaultLints}", r.Args["clippyFlags"])
- r = ctx.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+ r = result.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
if r.Rule != nil {
t.Errorf("libfoobar is setup to use clippy when explicitly disabled: clippyFlags=%q", r.Args["clippyFlags"])
}
-
})
}
}
diff --git a/rust/compiler.go b/rust/compiler.go
index ee88a27..bc034d7 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -60,6 +60,7 @@
InstallInData = iota
incorrectSourcesError = "srcs can only contain one path for a rust file and source providers prefixed by \":\""
+ genSubDir = "out/"
)
type BaseCompilerProperties struct {
@@ -75,7 +76,7 @@
// errors). The default value is "default".
Lints *string
- // flags to pass to rustc
+ // flags to pass to rustc. To enable configuration options or features, use the "cfgs" or "features" properties.
Flags []string `android:"path,arch_variant"`
// flags to pass to the linker
@@ -96,9 +97,25 @@
// list of C shared library dependencies
Shared_libs []string `android:"arch_variant"`
- // list of C static library dependencies
+ // list of C static library dependencies. These dependencies do not normally propagate to dependents
+ // and may need to be redeclared. See whole_static_libs for bundling static dependencies into a library.
Static_libs []string `android:"arch_variant"`
+ // Similar to static_libs, but will bundle the static library dependency into a library. This is helpful
+ // to avoid having to redeclare the dependency for dependents of this library, but in some cases may also
+ // result in bloat if multiple dependencies all include the same static library whole.
+ //
+ // The common use case for this is when the static library is unlikely to be a dependency of other modules to avoid
+ // having to redeclare the static library dependency for every dependent module.
+ // If you are not sure what to, for rust_library modules most static dependencies should go in static_libraries,
+ // and for rust_ffi modules most static dependencies should go into whole_static_libraries.
+ //
+ // For rust_ffi static variants, these libraries will be included in the resulting static library archive.
+ //
+ // For rust_library rlib variants, these libraries will be bundled into the resulting rlib library. This will
+ // include all of the static libraries symbols in any dylibs or binaries which use this rlib as well.
+ Whole_static_libs []string `android:"arch_variant"`
+
// crate name, required for modules which produce Rust libraries: rust_library, rust_ffi and SourceProvider
// modules which create library variants (rust_bindgen). This must be the expected extern crate name used in
// source, and is required to conform to an enforced format matching library output files (if the output file is
@@ -108,6 +125,9 @@
// list of features to enable for this crate
Features []string `android:"arch_variant"`
+ // list of configuration options to enable for this crate. To enable features, use the "features" property.
+ Cfgs []string `android:"arch_variant"`
+
// specific rust edition that should be used if the default version is not desired
Edition *string `android:"arch_variant"`
@@ -136,8 +156,7 @@
}
type baseCompiler struct {
- Properties BaseCompilerProperties
- coverageFile android.Path //rustc generates a single gcno file
+ Properties BaseCompilerProperties
// Install related
dir string
@@ -146,11 +165,15 @@
relative string
path android.InstallPath
location installLocation
+ sanitize *sanitize
- coverageOutputZipFile android.OptionalPath
- distFile android.OptionalPath
+ distFile android.OptionalPath
// Stripped output file. If Valid(), this file will be installed instead of outputFile.
strippedOutputFile android.OptionalPath
+
+ // If a crate has a source-generated dependency, a copy of the source file
+ // will be available in cargoOutDir (equivalent to Cargo OUT_DIR).
+ cargoOutDir android.ModuleOutPath
}
func (compiler *baseCompiler) Disabled() bool {
@@ -190,9 +213,17 @@
return []interface{}{&compiler.Properties}
}
-func (compiler *baseCompiler) featuresToFlags(features []string) []string {
+func (compiler *baseCompiler) cfgsToFlags() []string {
flags := []string{}
- for _, feature := range features {
+ for _, cfg := range compiler.Properties.Cfgs {
+ flags = append(flags, "--cfg '"+cfg+"'")
+ }
+ return flags
+}
+
+func (compiler *baseCompiler) featuresToFlags() []string {
+ flags := []string{}
+ for _, feature := range compiler.Properties.Features {
flags = append(flags, "--cfg 'feature=\""+feature+"\"'")
}
return flags
@@ -206,7 +237,8 @@
}
flags.RustFlags = append(flags.RustFlags, lintFlags)
flags.RustFlags = append(flags.RustFlags, compiler.Properties.Flags...)
- flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags(compiler.Properties.Features)...)
+ flags.RustFlags = append(flags.RustFlags, compiler.cfgsToFlags()...)
+ flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags()...)
flags.RustFlags = append(flags.RustFlags, "--edition="+compiler.edition())
flags.LinkFlags = append(flags.LinkFlags, compiler.Properties.Ld_flags...)
flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...)
@@ -229,6 +261,10 @@
flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpathPrefix+"../"+rpath)
}
+ if ctx.RustModule().UseVndk() {
+ flags.RustFlags = append(flags.RustFlags, "--cfg 'android_vndk'")
+ }
+
return flags
}
@@ -236,28 +272,44 @@
panic(fmt.Errorf("baseCrater doesn't know how to crate things!"))
}
+func (compiler *baseCompiler) initialize(ctx ModuleContext) {
+ compiler.cargoOutDir = android.PathForModuleOut(ctx, genSubDir)
+}
+
+func (compiler *baseCompiler) CargoOutDir() android.OptionalPath {
+ return android.OptionalPathForPath(compiler.cargoOutDir)
+}
+
+func (compiler *baseCompiler) isDependencyRoot() bool {
+ return false
+}
+
+func (compiler *baseCompiler) strippedOutputFilePath() android.OptionalPath {
+ return compiler.strippedOutputFile
+}
+
func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
deps.Rlibs = append(deps.Rlibs, compiler.Properties.Rlibs...)
deps.Dylibs = append(deps.Dylibs, compiler.Properties.Dylibs...)
deps.Rustlibs = append(deps.Rustlibs, compiler.Properties.Rustlibs...)
deps.ProcMacros = append(deps.ProcMacros, compiler.Properties.Proc_macros...)
deps.StaticLibs = append(deps.StaticLibs, compiler.Properties.Static_libs...)
+ deps.WholeStaticLibs = append(deps.WholeStaticLibs, compiler.Properties.Whole_static_libs...)
deps.SharedLibs = append(deps.SharedLibs, compiler.Properties.Shared_libs...)
if !Bool(compiler.Properties.No_stdlibs) {
for _, stdlib := range config.Stdlibs {
// If we're building for the primary arch of the build host, use the compiler's stdlibs
- if ctx.Target().Os == android.BuildOs && ctx.TargetPrimary() {
+ if ctx.Target().Os == android.BuildOs {
stdlib = stdlib + "_" + ctx.toolchain().RustTriple()
}
-
deps.Stdlibs = append(deps.Stdlibs, stdlib)
}
}
return deps
}
-func bionicDeps(deps Deps, static bool) Deps {
+func bionicDeps(ctx DepsContext, deps Deps, static bool) Deps {
bionicLibs := []string{}
bionicLibs = append(bionicLibs, "liblog")
bionicLibs = append(bionicLibs, "libc")
@@ -270,9 +322,9 @@
deps.SharedLibs = append(deps.SharedLibs, bionicLibs...)
}
- //TODO(b/141331117) libstd requires libgcc on Android
- deps.StaticLibs = append(deps.StaticLibs, "libgcc")
-
+ if libRuntimeBuiltins := config.BuiltinsRuntimeLibrary(ctx.toolchain()); libRuntimeBuiltins != "" {
+ deps.StaticLibs = append(deps.StaticLibs, libRuntimeBuiltins)
+ }
return deps
}
@@ -291,6 +343,10 @@
if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
dir = filepath.Join(dir, ctx.Arch().ArchType.String())
}
+
+ if compiler.location == InstallInData && ctx.RustModule().UseVndk() {
+ dir = filepath.Join(dir, "vendor")
+ }
return android.PathForModuleInstall(ctx, dir, compiler.subDir,
compiler.relativeInstallPath(), compiler.relative)
}
@@ -300,10 +356,7 @@
}
func (compiler *baseCompiler) install(ctx ModuleContext) {
- path := ctx.RustModule().outputFile
- if compiler.strippedOutputFile.Valid() {
- path = compiler.strippedOutputFile
- }
+ path := ctx.RustModule().OutputFile()
compiler.path = ctx.InstallFile(compiler.installDir(ctx), path.Path().Base(), path.Path())
}
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index 2b40727..5ca9e7f 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -19,7 +19,6 @@
"testing"
"android/soong/android"
- "android/soong/cc"
)
// Test that feature flags are being correctly generated.
@@ -43,6 +42,27 @@
}
}
+// Test that cfgs flags are being correctly generated.
+func TestCfgsToFlags(t *testing.T) {
+ ctx := testRust(t, `
+ rust_library_host {
+ name: "libfoo",
+ srcs: ["foo.rs"],
+ crate_name: "foo",
+ cfgs: [
+ "std",
+ "cfg1=\"one\""
+ ],
+ }`)
+
+ libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
+
+ if !strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'std'") ||
+ !strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'cfg1=\"one\"'") {
+ t.Fatalf("missing std and cfg1 flags for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
+ }
+}
+
// Test that we reject multiple source files.
func TestEnforceSingleSourceFile(t *testing.T) {
@@ -132,15 +152,6 @@
lints: "none",
}`
- bp = bp + GatherRequiredDepsForTest()
- bp = bp + cc.GatherRequiredDepsForTest(android.NoOsType)
-
- fs := map[string][]byte{
- // Reuse the same blueprint file for subdirectories.
- "external/Android.bp": []byte(bp),
- "hardware/Android.bp": []byte(bp),
- }
-
var lintTests = []struct {
modulePath string
fooFlags string
@@ -153,29 +164,20 @@
for _, tc := range lintTests {
t.Run("path="+tc.modulePath, func(t *testing.T) {
- config := android.TestArchConfig(buildDir, nil, bp, fs)
- ctx := CreateTestContext(config)
- ctx.Register()
- _, errs := ctx.ParseFileList(".", []string{tc.modulePath + "Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ // Test with the blueprint file in different directories.
+ android.FixtureAddTextFile(tc.modulePath+"Android.bp", bp),
+ ).RunTest(t)
- r := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
- if !strings.Contains(r.Args["rustcFlags"], tc.fooFlags) {
- t.Errorf("Incorrect flags for libfoo: %q, want %q", r.Args["rustcFlags"], tc.fooFlags)
- }
+ r := result.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
+ android.AssertStringDoesContain(t, "libfoo flags", r.Args["rustcFlags"], tc.fooFlags)
- r = ctx.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
- if !strings.Contains(r.Args["rustcFlags"], "${config.RustDefaultLints}") {
- t.Errorf("Incorrect flags for libbar: %q, want %q", r.Args["rustcFlags"], "${config.RustDefaultLints}")
- }
+ r = result.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
+ android.AssertStringDoesContain(t, "libbar flags", r.Args["rustcFlags"], "${config.RustDefaultLints}")
- r = ctx.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
- if !strings.Contains(r.Args["rustcFlags"], "${config.RustAllowAllLints}") {
- t.Errorf("Incorrect flags for libfoobar: %q, want %q", r.Args["rustcFlags"], "${config.RustAllowAllLints}")
- }
-
+ r = result.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
+ android.AssertStringDoesContain(t, "libfoobar flags", r.Args["rustcFlags"], "${config.RustAllowAllLints}")
})
}
}
diff --git a/rust/config/Android.bp b/rust/config/Android.bp
index 1f0109f..5b121c3 100644
--- a/rust/config/Android.bp
+++ b/rust/config/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-rust-config",
pkgPath: "android/soong/rust/config",
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 21df024..394fcc5 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -14,12 +14,16 @@
"external/rust",
"external/vm_tools/p9",
"frameworks/native/libs/binder/rust",
+ "frameworks/proto_logging/stats",
"packages/modules/DnsResolver",
"packages/modules/Virtualization",
"prebuilts/rust",
"system/bt",
+ "system/core/libstats/pull_rust",
"system/extras/profcollectd",
+ "system/extras/simpleperf",
"system/hardware/interfaces/keystore2",
+ "system/logging/rust",
"system/security",
"system/tools/aidl",
}
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
index 21b22a4..186e571 100644
--- a/rust/config/arm64_device.go
+++ b/rust/config/arm64_device.go
@@ -26,9 +26,10 @@
Arm64LinkFlags = []string{}
Arm64ArchVariantRustFlags = map[string][]string{
- "armv8-a": []string{},
- "armv8-2a": []string{},
- "armv8-2a-dotprod": []string{},
+ "armv8-a": []string{},
+ "armv8-a-branchprot": []string{},
+ "armv8-2a": []string{},
+ "armv8-2a-dotprod": []string{},
}
)
@@ -71,6 +72,10 @@
return true
}
+func (toolchainArm64) LibclangRuntimeLibraryArch() string {
+ return "aarch64"
+}
+
func Arm64ToolchainFactory(arch android.Arch) Toolchain {
archVariant := arch.ArchVariant
if archVariant == "" {
diff --git a/rust/config/arm_device.go b/rust/config/arm_device.go
index adfe917..42c1c02 100644
--- a/rust/config/arm_device.go
+++ b/rust/config/arm_device.go
@@ -72,6 +72,10 @@
return true
}
+func (toolchainArm) LibclangRuntimeLibraryArch() string {
+ return "arm"
+}
+
func ArmToolchainFactory(arch android.Arch) Toolchain {
toolchainRustFlags := []string{
"${config.ArmToolchainRustFlags}",
diff --git a/rust/config/global.go b/rust/config/global.go
index 22d9567..18776ab 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
var pctx = android.NewPackageContext("android/soong/rust/config")
var (
- RustDefaultVersion = "1.48.0"
+ RustDefaultVersion = "1.51.0"
RustDefaultBase = "prebuilts/rust/"
DefaultEdition = "2018"
Stdlibs = []string{
@@ -44,11 +44,17 @@
GlobalRustFlags = []string{
"--remap-path-prefix $$(pwd)=",
"-C codegen-units=1",
+ "-C debuginfo=2",
"-C opt-level=3",
"-C relocation-model=pic",
+ // Use v0 mangling to distinguish from C++ symbols
+ "-Z symbol-mangling-version=v0",
}
- deviceGlobalRustFlags = []string{}
+ deviceGlobalRustFlags = []string{
+ "-C panic=abort",
+ "-Z link-native-libraries=no",
+ }
deviceGlobalLinkFlags = []string{
// Prepend the lld flags from cc_config so we stay in sync with cc
@@ -57,7 +63,7 @@
// Override cc's --no-undefined-version to allow rustc's generated alloc functions
"-Wl,--undefined-version",
- "-Bdynamic",
+ "-Wl,-Bdynamic",
"-nostdlib",
"-Wl,--pack-dyn-relocs=android+relr",
"-Wl,--use-android-relr-tags",
diff --git a/rust/config/lints.go b/rust/config/lints.go
index 06bb668..ef6b315 100644
--- a/rust/config/lints.go
+++ b/rust/config/lints.go
@@ -52,6 +52,9 @@
// deny.
defaultClippyLints = []string{
"-A clippy::type-complexity",
+ "-A clippy::unnecessary-wraps",
+ "-A clippy::unusual-byte-groupings",
+ "-A clippy::upper-case-acronyms",
}
// Rust lints for vendor code.
diff --git a/rust/config/toolchain.go b/rust/config/toolchain.go
index 616d88b..a769f12 100644
--- a/rust/config/toolchain.go
+++ b/rust/config/toolchain.go
@@ -34,6 +34,8 @@
Supported() bool
Bionic() bool
+
+ LibclangRuntimeLibraryArch() string
}
type toolchainBase struct {
@@ -106,6 +108,40 @@
return false
}
+func (toolchainBase) LibclangRuntimeLibraryArch() string {
+ return ""
+}
+
+func BuiltinsRuntimeLibrary(t Toolchain) string {
+ return LibclangRuntimeLibrary(t, "builtins")
+}
+
+func LibFuzzerRuntimeLibrary(t Toolchain) string {
+ return LibclangRuntimeLibrary(t, "fuzzer")
+}
+
+func LibclangRuntimeLibrary(t Toolchain, library string) string {
+ arch := t.LibclangRuntimeLibraryArch()
+ if arch == "" {
+ return ""
+ }
+ if !t.Bionic() {
+ return "libclang_rt." + library + "-" + arch
+ }
+ return "libclang_rt." + library + "-" + arch + "-android"
+}
+
+func LibRustRuntimeLibrary(t Toolchain, library string) string {
+ arch := t.LibclangRuntimeLibraryArch()
+ if arch == "" {
+ return ""
+ }
+ if !t.Bionic() {
+ return "librustc_rt." + library + "-" + arch
+ }
+ return "librustc_rt." + library + "-" + arch + "-android"
+}
+
func toolchainBaseFactory() Toolchain {
return &toolchainBase{}
}
diff --git a/rust/config/x86_64_device.go b/rust/config/x86_64_device.go
index 5f6e85a..94b719f 100644
--- a/rust/config/x86_64_device.go
+++ b/rust/config/x86_64_device.go
@@ -77,6 +77,10 @@
return true
}
+func (toolchainX86_64) LibclangRuntimeLibraryArch() string {
+ return "x86_64"
+}
+
func x86_64ToolchainFactory(arch android.Arch) Toolchain {
toolchainRustFlags := []string{
"${config.X86_64ToolchainRustFlags}",
diff --git a/rust/config/x86_device.go b/rust/config/x86_device.go
index daeeb14..aae1125 100644
--- a/rust/config/x86_device.go
+++ b/rust/config/x86_device.go
@@ -80,6 +80,10 @@
return true
}
+func (toolchainX86) LibclangRuntimeLibraryArch() string {
+ return "i686"
+}
+
func x86ToolchainFactory(arch android.Arch) Toolchain {
toolchainRustFlags := []string{
"${config.X86ToolchainRustFlags}",
diff --git a/rust/config/x86_linux_host.go b/rust/config/x86_linux_host.go
index e7f26ce..b63e14d 100644
--- a/rust/config/x86_linux_host.go
+++ b/rust/config/x86_linux_host.go
@@ -103,6 +103,14 @@
return "x86"
}
+func (toolchainLinuxX86) LibclangRuntimeLibraryArch() string {
+ return "i386"
+}
+
+func (toolchainLinuxX8664) LibclangRuntimeLibraryArch() string {
+ return "x86_64"
+}
+
func (t *toolchainLinuxX86) RustTriple() string {
return "i686-unknown-linux-gnu"
}
diff --git a/rust/coverage.go b/rust/coverage.go
index 26375f5..dac526a 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -20,7 +20,9 @@
"android/soong/cc"
)
-var CovLibraryName = "libprofile-extras"
+var CovLibraryName = "libprofile-clang-extras"
+
+const profileInstrFlag = "-fprofile-instr-generate=/data/misc/trace/clang-%p-%m.profraw"
type coverage struct {
Properties cc.CoverageProperties
@@ -53,9 +55,9 @@
flags.Coverage = true
coverage := ctx.GetDirectDepWithTag(CovLibraryName, cc.CoverageDepTag).(cc.LinkableInterface)
flags.RustFlags = append(flags.RustFlags,
- "-Z profile", "-g", "-C opt-level=0", "-C link-dead-code")
+ "-Z instrument-coverage", "-g", "-C link-dead-code")
flags.LinkFlags = append(flags.LinkFlags,
- "--coverage", "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,getenv")
+ profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
}
diff --git a/rust/coverage_test.go b/rust/coverage_test.go
index e7f873e..4b6c9d4 100644
--- a/rust/coverage_test.go
+++ b/rust/coverage_test.go
@@ -56,7 +56,7 @@
fizzCov := ctx.ModuleForTests("fizz_cov", "android_arm64_armv8-a_cov").Rule("rustc")
buzzNoCov := ctx.ModuleForTests("buzzNoCov", "android_arm64_armv8-a").Rule("rustc")
- rustcCoverageFlags := []string{"-Z profile", " -g ", "-C opt-level=0", "-C link-dead-code"}
+ rustcCoverageFlags := []string{"-Z instrument-coverage", " -g ", "-C link-dead-code"}
for _, flag := range rustcCoverageFlags {
missingErrorStr := "missing rustc flag '%s' for '%s' module with coverage enabled; rustcFlags: %#v"
containsErrorStr := "contains rustc flag '%s' for '%s' module with coverage disabled; rustcFlags: %#v"
@@ -75,7 +75,7 @@
}
}
- linkCoverageFlags := []string{"--coverage", " -g "}
+ linkCoverageFlags := []string{"-fprofile-instr-generate=/data/misc/trace/clang-%p-%m.profraw", " -g "}
for _, flag := range linkCoverageFlags {
missingErrorStr := "missing rust linker flag '%s' for '%s' module with coverage enabled; rustcFlags: %#v"
containsErrorStr := "contains rust linker flag '%s' for '%s' module with coverage disabled; rustcFlags: %#v"
@@ -96,83 +96,6 @@
}
-// Test coverage files are included correctly
-func TestCoverageZip(t *testing.T) {
- ctx := testRustCov(t, `
- rust_library {
- name: "libfoo",
- srcs: ["foo.rs"],
- rlibs: ["librlib"],
- crate_name: "foo",
- }
- rust_ffi_static {
- name: "libbaz",
- srcs: ["foo.rs"],
- rlibs: ["librlib"],
- crate_name: "baz",
- }
- rust_library_rlib {
- name: "librlib",
- srcs: ["foo.rs"],
- crate_name: "rlib",
- }
- rust_binary {
- name: "fizz",
- rlibs: ["librlib"],
- static_libs: ["libbaz"],
- srcs: ["foo.rs"],
- }
- cc_binary {
- name: "buzz",
- static_libs: ["libbaz"],
- srcs: ["foo.c"],
- }
- cc_library {
- name: "libbar",
- static_libs: ["libbaz"],
- compile_multilib: "64",
- srcs: ["foo.c"],
- }`)
-
- fizzZipInputs := ctx.ModuleForTests("fizz", "android_arm64_armv8-a_cov").Rule("zip").Inputs.Strings()
- libfooZipInputs := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib_cov").Rule("zip").Inputs.Strings()
- buzzZipInputs := ctx.ModuleForTests("buzz", "android_arm64_armv8-a_cov").Rule("zip").Inputs.Strings()
- libbarZipInputs := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared_cov").Rule("zip").Inputs.Strings()
-
- // Make sure the expected number of input files are included.
- if len(fizzZipInputs) != 3 {
- t.Fatalf("expected only 3 coverage inputs for rust 'fizz' binary, got %#v: %#v", len(fizzZipInputs), fizzZipInputs)
- }
- if len(libfooZipInputs) != 2 {
- t.Fatalf("expected only 2 coverage inputs for rust 'libfoo' library, got %#v: %#v", len(libfooZipInputs), libfooZipInputs)
- }
- if len(buzzZipInputs) != 2 {
- t.Fatalf("expected only 2 coverage inputs for cc 'buzz' binary, got %#v: %#v", len(buzzZipInputs), buzzZipInputs)
- }
- if len(libbarZipInputs) != 2 {
- t.Fatalf("expected only 2 coverage inputs for cc 'libbar' library, got %#v: %#v", len(libbarZipInputs), libbarZipInputs)
- }
-
- // Make sure the expected inputs are provided to the zip rule.
- if !android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_rlib_dylib-std_cov/librlib.gcno") ||
- !android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_static_cov/libbaz.gcno") ||
- !android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_cov/fizz.gcno") {
- t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", fizzZipInputs)
- }
- if !android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_rlib_dylib-std_cov/librlib.gcno") ||
- !android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_dylib_cov/libfoo.gcno") {
- t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", libfooZipInputs)
- }
- if !android.SuffixInList(buzzZipInputs, "android_arm64_armv8-a_cov/obj/foo.gcno") ||
- !android.SuffixInList(buzzZipInputs, "android_arm64_armv8-a_static_cov/libbaz.gcno") {
- t.Fatalf("missing expected coverage files for cc 'buzz' binary: %#v", buzzZipInputs)
- }
- if !android.SuffixInList(libbarZipInputs, "android_arm64_armv8-a_static_cov/obj/foo.gcno") ||
- !android.SuffixInList(libbarZipInputs, "android_arm64_armv8-a_static_cov/libbaz.gcno") {
- t.Fatalf("missing expected coverage files for cc 'libbar' library: %#v", libbarZipInputs)
- }
-}
-
func TestCoverageDeps(t *testing.T) {
ctx := testRustCov(t, `
rust_binary {
@@ -181,7 +104,7 @@
}`)
fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a_cov").Rule("rustc")
- if !strings.Contains(fizz.Args["linkFlags"], "libprofile-extras.a") {
- t.Fatalf("missing expected coverage 'libprofile-extras' dependency in linkFlags: %#v", fizz.Args["linkFlags"])
+ if !strings.Contains(fizz.Args["linkFlags"], "libprofile-clang-extras.a") {
+ t.Fatalf("missing expected coverage 'libprofile-clang-extras' dependency in linkFlags: %#v", fizz.Args["linkFlags"])
}
}
diff --git a/rust/fuzz.go b/rust/fuzz.go
new file mode 100644
index 0000000..d699971
--- /dev/null
+++ b/rust/fuzz.go
@@ -0,0 +1,95 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/rust/config"
+)
+
+func init() {
+ android.RegisterModuleType("rust_fuzz", RustFuzzFactory)
+}
+
+type fuzzDecorator struct {
+ *binaryDecorator
+
+ Properties cc.FuzzProperties
+ dictionary android.Path
+ corpus android.Paths
+ corpusIntermediateDir android.Path
+ config android.Path
+ data android.Paths
+ dataIntermediateDir android.Path
+}
+
+var _ compiler = (*binaryDecorator)(nil)
+
+// rust_binary produces a binary that is runnable on a device.
+func RustFuzzFactory() android.Module {
+ module, _ := NewRustFuzz(android.HostAndDeviceSupported)
+ return module.Init()
+}
+
+func NewRustFuzz(hod android.HostOrDeviceSupported) (*Module, *fuzzDecorator) {
+ module, binary := NewRustBinary(hod)
+ fuzz := &fuzzDecorator{
+ binaryDecorator: binary,
+ }
+
+ // Change the defaults for the binaryDecorator's baseCompiler
+ fuzz.binaryDecorator.baseCompiler.dir = "fuzz"
+ fuzz.binaryDecorator.baseCompiler.dir64 = "fuzz"
+ fuzz.binaryDecorator.baseCompiler.location = InstallInData
+ module.sanitize.SetSanitizer(cc.Fuzzer, true)
+ module.compiler = fuzz
+ return module, fuzz
+}
+
+func (fuzzer *fuzzDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
+ flags = fuzzer.binaryDecorator.compilerFlags(ctx, flags)
+
+ // `../lib` for installed fuzz targets (both host and device), and `./lib` for fuzz target packages.
+ flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
+ flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/lib`)
+
+ return flags
+}
+
+func (fuzzer *fuzzDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+ if libFuzzerRuntimeLibrary := config.LibFuzzerRuntimeLibrary(ctx.toolchain()); libFuzzerRuntimeLibrary != "" {
+ deps.StaticLibs = append(deps.StaticLibs, libFuzzerRuntimeLibrary)
+ }
+ deps.SharedLibs = append(deps.SharedLibs, "libc++")
+ deps.Rlibs = append(deps.Rlibs, "liblibfuzzer_sys")
+
+ deps = fuzzer.binaryDecorator.compilerDeps(ctx, deps)
+
+ return deps
+}
+
+func (fuzzer *fuzzDecorator) compilerProps() []interface{} {
+ return append(fuzzer.binaryDecorator.compilerProps(),
+ &fuzzer.Properties)
+}
+
+func (fuzzer *fuzzDecorator) stdLinkage(ctx *depsContext) RustLinkage {
+ return RlibLinkage
+}
+
+func (fuzzer *fuzzDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
+ return rlibAutoDep
+}
diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go
new file mode 100644
index 0000000..2524f91
--- /dev/null
+++ b/rust/fuzz_test.go
@@ -0,0 +1,63 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestRustFuzz(t *testing.T) {
+ ctx := testRust(t, `
+ rust_library {
+ name: "libtest_fuzzing",
+ crate_name: "test_fuzzing",
+ srcs: ["foo.rs"],
+ }
+ rust_fuzz {
+ name: "fuzz_libtest",
+ srcs: ["foo.rs"],
+ rustlibs: ["libtest_fuzzing"],
+ }
+ `)
+
+ // Check that appropriate dependencies are added and that the rustlib linkage is correct.
+ fuzz_libtest_mod := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Module().(*Module)
+ if !android.InList("liblibfuzzer_sys.rlib-std", fuzz_libtest_mod.Properties.AndroidMkRlibs) {
+ t.Errorf("liblibfuzzer_sys rlib library dependency missing for rust_fuzz module. %#v", fuzz_libtest_mod.Properties.AndroidMkRlibs)
+ }
+ if !android.InList("libtest_fuzzing.rlib-std", fuzz_libtest_mod.Properties.AndroidMkRlibs) {
+ t.Errorf("rustlibs not linked as rlib for rust_fuzz module.")
+ }
+
+ // Check that compiler flags are set appropriately .
+ fuzz_libtest := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Output("fuzz_libtest")
+ if !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-Z sanitizer=hwaddress") ||
+ !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov'") ||
+ !strings.Contains(fuzz_libtest.Args["rustcFlags"], "--cfg fuzzing") {
+ t.Errorf("rust_fuzz module does not contain the expected flags (sancov, cfg fuzzing, hwaddress sanitizer).")
+
+ }
+
+ // Check that dependencies have 'fuzzer' variants produced for them as well.
+ libtest_fuzzer := ctx.ModuleForTests("libtest_fuzzing", "android_arm64_armv8-a_rlib_rlib-std_fuzzer").Output("libtest_fuzzing.rlib")
+ if !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-Z sanitizer=hwaddress") ||
+ !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-C passes='sancov'") ||
+ !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "--cfg fuzzing") {
+ t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov, cfg fuzzing, hwaddress sanitizer).")
+ }
+}
diff --git a/rust/image.go b/rust/image.go
index af8c3b2..900842e 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -23,10 +23,72 @@
var _ android.ImageInterface = (*Module)(nil)
-func (mod *Module) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+var _ cc.ImageMutatableModule = (*Module)(nil)
+
+func (mod *Module) VendorAvailable() bool {
+ return Bool(mod.VendorProperties.Vendor_available)
+}
+
+func (mod *Module) OdmAvailable() bool {
+ return Bool(mod.VendorProperties.Odm_available)
+}
+
+func (mod *Module) ProductAvailable() bool {
return false
}
+func (mod *Module) RamdiskAvailable() bool {
+ return false
+}
+
+func (mod *Module) VendorRamdiskAvailable() bool {
+ return Bool(mod.Properties.Vendor_ramdisk_available)
+}
+
+func (mod *Module) AndroidModuleBase() *android.ModuleBase {
+ return &mod.ModuleBase
+}
+
+func (mod *Module) RecoveryAvailable() bool {
+ return false
+}
+
+func (mod *Module) ExtraVariants() []string {
+ return mod.Properties.ExtraVariants
+}
+
+func (mod *Module) AppendExtraVariant(extraVariant string) {
+ mod.Properties.ExtraVariants = append(mod.Properties.ExtraVariants, extraVariant)
+}
+
+func (mod *Module) SetRamdiskVariantNeeded(b bool) {
+ if b {
+ panic("Setting ramdisk variant needed for Rust module is unsupported: " + mod.BaseModuleName())
+ }
+}
+
+func (mod *Module) SetVendorRamdiskVariantNeeded(b bool) {
+ mod.Properties.VendorRamdiskVariantNeeded = b
+}
+
+func (mod *Module) SetRecoveryVariantNeeded(b bool) {
+ if b {
+ panic("Setting recovery variant needed for Rust module is unsupported: " + mod.BaseModuleName())
+ }
+}
+
+func (mod *Module) SetCoreVariantNeeded(b bool) {
+ mod.Properties.CoreVariantNeeded = b
+}
+
+func (mod *Module) SnapshotVersion(mctx android.BaseModuleContext) string {
+ panic("Rust modules do not support snapshotting: " + mod.BaseModuleName())
+}
+
+func (mod *Module) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return mod.Properties.VendorRamdiskVariantNeeded
+}
+
func (mod *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
return mod.Properties.CoreVariantNeeded
}
@@ -35,6 +97,10 @@
return mod.InRamdisk()
}
+func (mod *Module) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
func (mod *Module) RecoveryVariantNeeded(android.BaseModuleContext) bool {
return mod.InRecovery()
}
@@ -43,6 +109,29 @@
return mod.Properties.ExtraVariants
}
+func (mod *Module) IsSnapshotPrebuilt() bool {
+ // Rust does not support prebuilts in its snapshots
+ return false
+}
+
+func (ctx *moduleContext) SocSpecific() bool {
+ // Additionally check if this module is inVendor() that means it is a "vendor" variant of a
+ // module. As well as SoC specific modules, vendor variants must be installed to /vendor
+ // unless they have "odm_available: true".
+ return ctx.ModuleContext.SocSpecific() || (ctx.RustModule().InVendor() && !ctx.RustModule().VendorVariantToOdm())
+}
+
+func (ctx *moduleContext) DeviceSpecific() bool {
+ // Some vendor variants want to be installed to /odm by setting "odm_available: true".
+ return ctx.ModuleContext.DeviceSpecific() || (ctx.RustModule().InVendor() && ctx.RustModule().VendorVariantToOdm())
+}
+
+// Returns true when this module creates a vendor variant and wants to install the vendor variant
+// to the odm partition.
+func (c *Module) VendorVariantToOdm() bool {
+ return Bool(c.VendorProperties.Odm_available)
+}
+
func (ctx *moduleContext) ProductSpecific() bool {
return false
}
@@ -52,6 +141,10 @@
return false
}
+func (mod *Module) InVendorRamdisk() bool {
+ return mod.ModuleBase.InVendorRamdisk() || mod.ModuleBase.InstallInVendorRamdisk()
+}
+
func (mod *Module) OnlyInRamdisk() bool {
// TODO(b/165791368)
return false
@@ -68,20 +161,32 @@
// Returns true when this module is configured to have core and vendor variants.
func (mod *Module) HasVendorVariant() bool {
- return mod.IsVndk() || Bool(mod.VendorProperties.Vendor_available)
+ return Bool(mod.VendorProperties.Vendor_available) || Bool(mod.VendorProperties.Odm_available)
}
-func (c *Module) VendorAvailable() bool {
- return Bool(c.VendorProperties.Vendor_available)
+// Always returns false because rust modules do not support product variant.
+func (mod *Module) HasProductVariant() bool {
+ return Bool(mod.VendorProperties.Product_available)
}
-func (c *Module) InProduct() bool {
+func (mod *Module) HasNonSystemVariants() bool {
+ return mod.HasVendorVariant() || mod.HasProductVariant()
+}
+
+func (mod *Module) InProduct() bool {
return false
}
+// Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
+func (mod *Module) InVendor() bool {
+ return mod.Properties.ImageVariationPrefix == cc.VendorVariationPrefix
+}
+
func (mod *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
m := module.(*Module)
- if strings.HasPrefix(variant, cc.VendorVariationPrefix) {
+ if variant == android.VendorRamdiskVariation {
+ m.MakeAsPlatform()
+ } else if strings.HasPrefix(variant, cc.VendorVariationPrefix) {
m.Properties.ImageVariationPrefix = cc.VendorVariationPrefix
m.Properties.VndkVersion = strings.TrimPrefix(variant, cc.VendorVariationPrefix)
@@ -96,58 +201,30 @@
}
func (mod *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
- vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
- platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
-
// Rust does not support installing to the product image yet.
- if mod.VendorProperties.Product_available != nil {
+ if Bool(mod.VendorProperties.Product_available) {
mctx.PropertyErrorf("product_available",
"Rust modules do not yet support being available to the product image")
} else if mctx.ProductSpecific() {
mctx.PropertyErrorf("product_specific",
"Rust modules do not yet support installing to the product image.")
- } else if mod.VendorProperties.Double_loadable != nil {
+ } else if Bool(mod.VendorProperties.Double_loadable) {
mctx.PropertyErrorf("double_loadable",
"Rust modules do not yet support double loading")
}
-
- coreVariantNeeded := true
- var vendorVariants []string
-
- if mod.VendorProperties.Vendor_available != nil {
- if vendorSpecific {
- mctx.PropertyErrorf("vendor_available",
- "doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
- }
-
- if lib, ok := mod.compiler.(libraryInterface); ok {
- // Explicitly disallow rust_ffi variants which produce shared libraries from setting vendor_available.
- // Vendor variants do not produce an error for dylibs, rlibs with dylib-std linkage are disabled in the respective library
- // mutators until support is added.
- //
- // We can't check shared() here because image mutator is called before the library mutator, so we need to
- // check buildShared()
- if lib.buildShared() {
- mctx.PropertyErrorf("vendor_available",
- "vendor_available can only be set for rust_ffi_static modules.")
- } else if Bool(mod.VendorProperties.Vendor_available) == true {
- vendorVariants = append(vendorVariants, platformVndkVersion)
- }
+ if Bool(mod.Properties.Vendor_ramdisk_available) {
+ if lib, ok := mod.compiler.(libraryInterface); !ok || (ok && lib.buildShared()) {
+ mctx.PropertyErrorf("vendor_ramdisk_available", "cannot be set for rust_ffi or rust_ffi_shared modules.")
}
}
- if vendorSpecific {
- if lib, ok := mod.compiler.(libraryInterface); !ok || (ok && !lib.static()) {
- mctx.ModuleErrorf("Rust vendor specific modules are currently only supported for rust_ffi_static modules.")
- } else {
- coreVariantNeeded = false
- vendorVariants = append(vendorVariants, platformVndkVersion)
+ cc.MutateImage(mctx, mod)
+
+ if !mod.Properties.CoreVariantNeeded || mod.HasNonSystemVariants() {
+
+ if _, ok := mod.compiler.(*prebuiltLibraryDecorator); ok {
+ // Rust does not support prebuilt libraries on non-System images.
+ mctx.ModuleErrorf("Rust prebuilt modules not supported for non-system images.")
}
}
-
- mod.Properties.CoreVariantNeeded = coreVariantNeeded
- for _, variant := range android.FirstUniqueStrings(vendorVariants) {
- mod.Properties.ExtraVariants = append(mod.Properties.ExtraVariants, cc.VendorVariationPrefix+variant)
- }
-
}
diff --git a/rust/image_test.go b/rust/image_test.go
index 025b0fd..95e788f 100644
--- a/rust/image_test.go
+++ b/rust/image_test.go
@@ -15,15 +15,16 @@
package rust
import (
+ "strings"
"testing"
"android/soong/android"
"android/soong/cc"
)
-// Test that cc_binaries can link against rust_ffi_static libraries.
+// Test that cc modules can link against vendor_available rust_ffi_static libraries.
func TestVendorLinkage(t *testing.T) {
- ctx := testRust(t, `
+ ctx := testRustVndk(t, `
cc_binary {
name: "fizz_vendor",
static_libs: ["libfoo_vendor"],
@@ -37,37 +38,68 @@
}
`)
- vendorBinary := ctx.ModuleForTests("fizz_vendor", "android_arm64_armv8-a").Module().(*cc.Module)
+ vendorBinary := ctx.ModuleForTests("fizz_vendor", "android_vendor.29_arm64_armv8-a").Module().(*cc.Module)
- if !android.InList("libfoo_vendor", vendorBinary.Properties.AndroidMkStaticLibs) {
- t.Errorf("vendorBinary should have a dependency on libfoo_vendor")
+ if !android.InList("libfoo_vendor.vendor", vendorBinary.Properties.AndroidMkStaticLibs) {
+ t.Errorf("vendorBinary should have a dependency on libfoo_vendor: %#v", vendorBinary.Properties.AndroidMkStaticLibs)
}
}
-// Test that shared libraries cannot be made vendor available until proper support is added.
+// Test that variants which use the vndk emit the appropriate cfg flag.
+func TestImageVndkCfgFlag(t *testing.T) {
+ ctx := testRustVndk(t, `
+ rust_ffi_static {
+ name: "libfoo",
+ crate_name: "foo",
+ srcs: ["foo.rs"],
+ vendor_available: true,
+ }
+ `)
+
+ vendor := ctx.ModuleForTests("libfoo", "android_vendor.29_arm64_armv8-a_static").Rule("rustc")
+
+ if !strings.Contains(vendor.Args["rustcFlags"], "--cfg 'android_vndk'") {
+ t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
+ }
+}
+
+// Test that cc modules can link against vendor_ramdisk_available rust_ffi_static libraries.
+func TestVendorRamdiskLinkage(t *testing.T) {
+ ctx := testRustVndk(t, `
+ cc_library_static {
+ name: "libcc_vendor_ramdisk",
+ static_libs: ["libfoo_vendor_ramdisk"],
+ system_shared_libs: [],
+ vendor_ramdisk_available: true,
+ }
+ rust_ffi_static {
+ name: "libfoo_vendor_ramdisk",
+ crate_name: "foo",
+ srcs: ["foo.rs"],
+ vendor_ramdisk_available: true,
+ }
+ `)
+
+ vendorRamdiskLibrary := ctx.ModuleForTests("libcc_vendor_ramdisk", "android_vendor_ramdisk_arm64_armv8-a_static").Module().(*cc.Module)
+
+ if !android.InList("libfoo_vendor_ramdisk.vendor_ramdisk", vendorRamdiskLibrary.Properties.AndroidMkStaticLibs) {
+ t.Errorf("libcc_vendor_ramdisk should have a dependency on libfoo_vendor_ramdisk")
+ }
+}
+
+// Test that prebuilt libraries cannot be made vendor available.
func TestForbiddenVendorLinkage(t *testing.T) {
- testRustError(t, "vendor_available can only be set for rust_ffi_static modules", `
- rust_ffi_shared {
- name: "libfoo_vendor",
- crate_name: "foo",
- srcs: ["foo.rs"],
- vendor_available: true,
- }
- `)
- testRustError(t, "Rust vendor specific modules are currently only supported for rust_ffi_static modules.", `
- rust_ffi {
- name: "libfoo_vendor",
- crate_name: "foo",
- srcs: ["foo.rs"],
+ testRustVndkError(t, "Rust prebuilt modules not supported for non-system images.", `
+ rust_prebuilt_library {
+ name: "librust_prebuilt",
+ crate_name: "rust_prebuilt",
+ rlib: {
+ srcs: ["libtest.rlib"],
+ },
+ dylib: {
+ srcs: ["libtest.so"],
+ },
vendor: true,
}
- `)
- testRustError(t, "Rust vendor specific modules are currently only supported for rust_ffi_static modules.", `
- rust_library {
- name: "libfoo_vendor",
- crate_name: "foo",
- srcs: ["foo.rs"],
- vendor: true,
- }
- `)
+ `)
}
diff --git a/rust/library.go b/rust/library.go
index 4ac52b4..26c104c 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -219,13 +219,17 @@
library.MutatedProperties.VariantIsSource = true
}
-func (library *libraryDecorator) autoDep(ctx BaseModuleContext) autoDep {
+func (library *libraryDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
if library.preferRlib() {
return rlibAutoDep
} else if library.rlib() || library.static() {
return rlibAutoDep
} else if library.dylib() || library.shared() {
return dylibAutoDep
+ } else if ctx.BazelConversionMode() {
+ // In Bazel conversion mode, we are currently ignoring the deptag, so we just need to supply a
+ // compatible tag in order to add the dependency.
+ return rlibAutoDep
} else {
panic(fmt.Errorf("autoDep called on library %q that has no enabled variants.", ctx.ModuleName()))
}
@@ -400,7 +404,7 @@
deps = library.baseCompiler.compilerDeps(ctx, deps)
if ctx.toolchain().Bionic() && (library.dylib() || library.shared()) {
- deps = bionicDeps(deps, false)
+ deps = bionicDeps(ctx, deps, false)
deps.CrtBegin = "crtbegin_so"
deps.CrtEnd = "crtend_so"
}
@@ -439,6 +443,7 @@
}
flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
+ flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
if library.dylib() {
@@ -452,46 +457,32 @@
fileName = library.getStem(ctx) + ctx.toolchain().RlibSuffix()
outputFile = android.PathForModuleOut(ctx, fileName)
- outputs := TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
- library.coverageFile = outputs.coverageFile
+ TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
} else if library.dylib() {
fileName = library.getStem(ctx) + ctx.toolchain().DylibSuffix()
outputFile = android.PathForModuleOut(ctx, fileName)
- outputs := TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
- library.coverageFile = outputs.coverageFile
+ TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
} else if library.static() {
fileName = library.getStem(ctx) + ctx.toolchain().StaticLibSuffix()
outputFile = android.PathForModuleOut(ctx, fileName)
- outputs := TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
- library.coverageFile = outputs.coverageFile
+ TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
} else if library.shared() {
fileName = library.sharedLibFilename(ctx)
outputFile = android.PathForModuleOut(ctx, fileName)
- outputs := TransformSrctoShared(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
- library.coverageFile = outputs.coverageFile
+ TransformSrctoShared(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
}
- if !library.rlib() && library.stripper.NeedsStrip(ctx) {
+ if !library.rlib() && !library.static() && library.stripper.NeedsStrip(ctx) {
strippedOutputFile := android.PathForModuleOut(ctx, "stripped", fileName)
library.stripper.StripExecutableOrSharedLib(ctx, outputFile, strippedOutputFile)
library.strippedOutputFile = android.OptionalPathForPath(strippedOutputFile)
}
- var coverageFiles android.Paths
- if library.coverageFile != nil {
- coverageFiles = append(coverageFiles, library.coverageFile)
- }
- if len(deps.coverageFiles) > 0 {
- coverageFiles = append(coverageFiles, deps.coverageFiles...)
- }
- library.coverageOutputZipFile = TransformCoverageFilesToZip(ctx, coverageFiles, library.getStem(ctx))
-
if library.rlib() || library.dylib() {
library.flagExporter.exportLinkDirs(deps.linkDirs...)
- library.flagExporter.exportDepFlags(deps.depFlags...)
library.flagExporter.exportLinkObjects(deps.linkObjects...)
}
@@ -605,9 +596,9 @@
v.(*Module).compiler.(libraryInterface).setRlib()
case dylibVariation:
v.(*Module).compiler.(libraryInterface).setDylib()
- if v.(*Module).ModuleBase.ImageVariation().Variation != android.CoreVariation {
+ if v.(*Module).ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
// TODO(b/165791368)
- // Disable dylib non-core variations until we support these.
+ // Disable dylib Vendor Ramdisk variations until we support these.
v.(*Module).Disable()
}
case "source":
@@ -646,14 +637,14 @@
dylib := modules[1].(*Module)
rlib.compiler.(libraryInterface).setRlibStd()
dylib.compiler.(libraryInterface).setDylibStd()
- if dylib.ModuleBase.ImageVariation().Variation != android.CoreVariation {
+ if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
// TODO(b/165791368)
- // Disable rlibs that link against dylib-std on non-core variations until non-core dylib
+ // Disable rlibs that link against dylib-std on vendor ramdisk variations until those dylib
// variants are properly supported.
dylib.Disable()
}
- rlib.Properties.SubName += RlibStdlibSuffix
- dylib.Properties.SubName += DylibStdlibSuffix
+ rlib.Properties.RustSubName += RlibStdlibSuffix
+ dylib.Properties.RustSubName += DylibStdlibSuffix
}
}
}
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 0c6ec99..115045a 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -51,6 +51,8 @@
flagExporter: NewFlagExporter(),
}
+ // Don't sanitize procMacros
+ module.sanitize = nil
module.compiler = procMacro
return module, procMacro
@@ -77,6 +79,6 @@
return stem + String(procMacro.baseCompiler.Properties.Suffix)
}
-func (procMacro *procMacroDecorator) autoDep(ctx BaseModuleContext) autoDep {
+func (procMacro *procMacroDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
return rlibAutoDep
}
diff --git a/rust/project_json.go b/rust/project_json.go
index 32ce6f4..c28bc7b 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -45,11 +45,12 @@
}
type rustProjectCrate struct {
- DisplayName string `json:"display_name"`
- RootModule string `json:"root_module"`
- Edition string `json:"edition,omitempty"`
- Deps []rustProjectDep `json:"deps"`
- Cfgs []string `json:"cfgs"`
+ DisplayName string `json:"display_name"`
+ RootModule string `json:"root_module"`
+ Edition string `json:"edition,omitempty"`
+ Deps []rustProjectDep `json:"deps"`
+ Cfg []string `json:"cfg"`
+ Env map[string]string `json:"env"`
}
type rustProjectJson struct {
@@ -136,7 +137,7 @@
}
})
if !foundSource {
- fmt.Errorf("No valid source for source provider found: %v\n", rModule)
+ ctx.Errorf("No valid source for source provider found: %v\n", rModule)
}
return sourceSrc, foundSource
}
@@ -220,7 +221,7 @@
func (singleton *projectGeneratorSingleton) addCrate(ctx android.SingletonContext, rModule *Module, comp *baseCompiler) (int, bool) {
rootModule, ok := crateSource(ctx, rModule, comp)
if !ok {
- fmt.Errorf("Unable to find source for valid module: %v", rModule)
+ ctx.Errorf("Unable to find source for valid module: %v", rModule)
return 0, false
}
@@ -229,7 +230,16 @@
RootModule: rootModule,
Edition: comp.edition(),
Deps: make([]rustProjectDep, 0),
- Cfgs: make([]string, 0),
+ Cfg: make([]string, 0),
+ Env: make(map[string]string),
+ }
+
+ if comp.CargoOutDir().Valid() {
+ crate.Env["OUT_DIR"] = comp.CargoOutDir().String()
+ }
+
+ for _, feature := range comp.Properties.Features {
+ crate.Cfg = append(crate.Cfg, "feature=\""+feature+"\"")
}
deps := make(map[string]int)
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
index ba66215..09d30db 100644
--- a/rust/project_json_test.go
+++ b/rust/project_json_test.go
@@ -18,6 +18,7 @@
"encoding/json"
"io/ioutil"
"path/filepath"
+ "sort"
"strings"
"testing"
@@ -27,15 +28,15 @@
// testProjectJson run the generation of rust-project.json. It returns the raw
// content of the generated file.
func testProjectJson(t *testing.T, bp string) []byte {
- tctx := newTestRustCtx(t, bp)
- tctx.env = map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}
- tctx.generateConfig()
- tctx.parse(t)
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ android.FixtureMergeEnv(map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}),
+ ).RunTestWithBp(t, bp)
// The JSON file is generated via WriteFileToOutputDir. Therefore, it
// won't appear in the Output of the TestingSingleton. Manually verify
// it exists.
- content, err := ioutil.ReadFile(filepath.Join(buildDir, rustProjectJsonFileName))
+ content, err := ioutil.ReadFile(filepath.Join(result.Config.BuildDir(), rustProjectJsonFileName))
if err != nil {
t.Errorf("rust-project.json has not been generated")
}
@@ -116,6 +117,41 @@
validateJsonCrates(t, jsonContent)
}
+func TestProjectJsonFeature(t *testing.T) {
+ bp := `
+ rust_library {
+ name: "liba",
+ srcs: ["a/src/lib.rs"],
+ crate_name: "a",
+ features: ["f1", "f2"]
+ }
+ `
+ jsonContent := testProjectJson(t, bp)
+ crates := validateJsonCrates(t, jsonContent)
+ for _, c := range crates {
+ crate := validateCrate(t, c)
+ cfgs, ok := crate["cfg"].([]interface{})
+ if !ok {
+ t.Fatalf("Unexpected type for cfgs: %v", crate)
+ }
+ expectedCfgs := []string{"feature=\"f1\"", "feature=\"f2\""}
+ foundCfgs := []string{}
+ for _, cfg := range cfgs {
+ cfg, ok := cfg.(string)
+ if !ok {
+ t.Fatalf("Unexpected type for cfg: %v", cfg)
+ }
+ foundCfgs = append(foundCfgs, cfg)
+ }
+ sort.Strings(foundCfgs)
+ for i, foundCfg := range foundCfgs {
+ if foundCfg != expectedCfgs[i] {
+ t.Errorf("Incorrect features: got %v; want %v", foundCfg, expectedCfgs[i])
+ }
+ }
+ }
+}
+
func TestProjectJsonBinary(t *testing.T) {
bp := `
rust_binary {
@@ -190,8 +226,8 @@
}
}
}
- // Check that liba depends on libbindings1
if strings.Contains(rootModule, "d/src/lib.rs") {
+ // Check that libd depends on libbindings1
found := false
for _, depName := range validateDependencies(t, crate) {
if depName == "bindings1" {
@@ -200,8 +236,17 @@
}
}
if !found {
- t.Errorf("liba does not depend on libbindings1: %v", crate)
+ t.Errorf("libd does not depend on libbindings1: %v", crate)
}
+ // Check that OUT_DIR is populated.
+ env, ok := crate["env"].(map[string]interface{})
+ if !ok {
+ t.Errorf("libd does not have its environment variables set: %v", crate)
+ }
+ if _, ok = env["OUT_DIR"]; !ok {
+ t.Errorf("libd does not have its OUT_DIR set: %v", env)
+ }
+
}
}
}
diff --git a/rust/protobuf_test.go b/rust/protobuf_test.go
index 1ac66f3..f0f5ec0 100644
--- a/rust/protobuf_test.go
+++ b/rust/protobuf_test.go
@@ -101,7 +101,7 @@
}
// Check that we're including the exported directory from libprotobuf-cpp-full
- if w := "-Ilibprotobuf-cpp-full-includes"; !strings.Contains(cmd, w) {
+ if w := "-I" + rustDefaultsDir + "libprotobuf-cpp-full-includes"; !strings.Contains(cmd, w) {
t.Errorf("expected %q in %q", w, cmd)
}
diff --git a/rust/rust.go b/rust/rust.go
index 1053846..9738b46 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -22,6 +22,7 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/bloaty"
"android/soong/cc"
cc_config "android/soong/cc/config"
"android/soong/rust/config"
@@ -42,6 +43,10 @@
ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
ctx.BottomUp("rust_stdlinkage", LibstdMutator).Parallel()
ctx.BottomUp("rust_begin", BeginMutator).Parallel()
+
+ })
+ android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
})
pctx.Import("android/soong/rust/config")
pctx.ImportAs("cc_config", "android/soong/cc/config")
@@ -69,9 +74,22 @@
VndkVersion string `blueprint:"mutated"`
SubName string `blueprint:"mutated"`
+ // SubName is used by CC for tracking image variants / SDK versions. RustSubName is used for Rust-specific
+ // subnaming which shouldn't be visible to CC modules (such as the rlib stdlinkage subname). This should be
+ // appended before SubName.
+ RustSubName string `blueprint:"mutated"`
+
// Set by imageMutator
- CoreVariantNeeded bool `blueprint:"mutated"`
- ExtraVariants []string `blueprint:"mutated"`
+ CoreVariantNeeded bool `blueprint:"mutated"`
+ VendorRamdiskVariantNeeded bool `blueprint:"mutated"`
+ ExtraVariants []string `blueprint:"mutated"`
+
+ // Make this module available when building for vendor ramdisk.
+ // On device without a dedicated recovery partition, the module is only
+ // available after switching root into
+ // /first_stage_ramdisk. To expose the module before switching root, install
+ // the recovery variant instead (TODO(b/165791368) recovery not yet supported)
+ Vendor_ramdisk_available *bool
// Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
Min_sdk_version *string
@@ -97,23 +115,60 @@
compiler compiler
coverage *coverage
clippy *clippy
+ sanitize *sanitize
cachedToolchain config.Toolchain
sourceProvider SourceProvider
subAndroidMkOnce map[SubAndroidMkProvider]bool
- outputFile android.OptionalPath
+ // Unstripped output. This is usually used when this module is linked to another module
+ // as a library. The stripped output which is used for installation can be found via
+ // compiler.strippedOutputFile if it exists.
+ unstrippedOutputFile android.OptionalPath
hideApexVariantFromMake bool
}
+func (mod *Module) Header() bool {
+ //TODO: If Rust libraries provide header variants, this needs to be updated.
+ return false
+}
+
+func (mod *Module) SetPreventInstall() {
+ mod.Properties.PreventInstall = true
+}
+
+func (mod *Module) SetHideFromMake() {
+ mod.Properties.HideFromMake = true
+}
+
+func (mod *Module) SanitizePropDefined() bool {
+ // Because compiler is not set for some Rust modules where sanitize might be set, check that compiler is also not
+ // nil since we need compiler to actually sanitize.
+ return mod.sanitize != nil && mod.compiler != nil
+}
+
+func (mod *Module) IsDependencyRoot() bool {
+ if mod.compiler != nil {
+ return mod.compiler.isDependencyRoot()
+ }
+ panic("IsDependencyRoot called on a non-compiler Rust module")
+}
+
+func (mod *Module) IsPrebuilt() bool {
+ if _, ok := mod.compiler.(*prebuiltLibraryDecorator); ok {
+ return true
+ }
+ return false
+}
+
func (mod *Module) OutputFiles(tag string) (android.Paths, error) {
switch tag {
case "":
if mod.sourceProvider != nil && (mod.compiler == nil || mod.compiler.Disabled()) {
return mod.sourceProvider.Srcs(), nil
} else {
- if mod.outputFile.Valid() {
- return android.Paths{mod.outputFile.Path()}, nil
+ if mod.OutputFile().Valid() {
+ return android.Paths{mod.OutputFile().Path()}, nil
}
return android.Paths{}, nil
}
@@ -177,7 +232,11 @@
}
func (mod *Module) MustUseVendorVariant() bool {
- return false
+ return true
+}
+
+func (mod *Module) SubName() string {
+ return mod.Properties.SubName
}
func (mod *Module) IsVndk() bool {
@@ -201,10 +260,30 @@
return false
}
+func (m *Module) IsLlndkHeaders() bool {
+ return false
+}
+
+func (m *Module) IsLlndkLibrary() bool {
+ return false
+}
+
+func (mod *Module) KernelHeadersDecorator() bool {
+ return false
+}
+
+func (m *Module) HasLlndkStubs() bool {
+ return false
+}
+
func (mod *Module) SdkVersion() string {
return ""
}
+func (mod *Module) MinSdkVersion() string {
+ return ""
+}
+
func (mod *Module) AlwaysSdk() bool {
return false
}
@@ -218,28 +297,35 @@
}
type Deps struct {
- Dylibs []string
- Rlibs []string
- Rustlibs []string
- Stdlibs []string
- ProcMacros []string
- SharedLibs []string
- StaticLibs []string
- HeaderLibs []string
+ Dylibs []string
+ Rlibs []string
+ Rustlibs []string
+ Stdlibs []string
+ ProcMacros []string
+ SharedLibs []string
+ StaticLibs []string
+ WholeStaticLibs []string
+ HeaderLibs []string
CrtBegin, CrtEnd string
}
type PathDeps struct {
- DyLibs RustLibraries
- RLibs RustLibraries
- SharedLibs android.Paths
- StaticLibs android.Paths
- ProcMacros RustLibraries
+ DyLibs RustLibraries
+ RLibs RustLibraries
+ SharedLibs android.Paths
+ SharedLibDeps android.Paths
+ StaticLibs android.Paths
+ ProcMacros RustLibraries
+
+ // depFlags and depLinkFlags are rustc and linker (clang) flags.
+ depFlags []string
+ depLinkFlags []string
+
+ // linkDirs are link paths passed via -L to rustc. linkObjects are objects passed directly to the linker.
+ // Both of these are exported and propagate to dependencies.
linkDirs []string
- depFlags []string
linkObjects []string
- //ReexportedDeps android.Paths
// Used by bindgen modules which call clang
depClangFlags []string
@@ -247,8 +333,6 @@
depGeneratedHeaders android.Paths
depSystemIncludePaths android.Paths
- coverageFiles android.Paths
-
CrtBegin android.OptionalPath
CrtEnd android.OptionalPath
@@ -265,12 +349,16 @@
}
type compiler interface {
+ initialize(ctx ModuleContext)
compilerFlags(ctx ModuleContext, flags Flags) Flags
compilerProps() []interface{}
compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path
compilerDeps(ctx DepsContext, deps Deps) Deps
crateName() string
+ // Output directory in which source-generated code from dependencies is
+ // copied. This is equivalent to Cargo's OUT_DIR variable.
+ CargoOutDir() android.OptionalPath
inData() bool
install(ctx ModuleContext)
relativeInstallPath() string
@@ -281,16 +369,17 @@
SetDisabled()
stdLinkage(ctx *depsContext) RustLinkage
+ isDependencyRoot() bool
+
+ strippedOutputFilePath() android.OptionalPath
}
type exportedFlagsProducer interface {
exportLinkDirs(...string)
- exportDepFlags(...string)
exportLinkObjects(...string)
}
type flagExporter struct {
- depFlags []string
linkDirs []string
linkObjects []string
}
@@ -299,17 +388,12 @@
flagExporter.linkDirs = android.FirstUniqueStrings(append(flagExporter.linkDirs, dirs...))
}
-func (flagExporter *flagExporter) exportDepFlags(flags ...string) {
- flagExporter.depFlags = android.FirstUniqueStrings(append(flagExporter.depFlags, flags...))
-}
-
func (flagExporter *flagExporter) exportLinkObjects(flags ...string) {
flagExporter.linkObjects = android.FirstUniqueStrings(append(flagExporter.linkObjects, flags...))
}
func (flagExporter *flagExporter) setProvider(ctx ModuleContext) {
ctx.SetProvider(FlagExporterInfoProvider, FlagExporterInfo{
- Flags: flagExporter.depFlags,
LinkDirs: flagExporter.linkDirs,
LinkObjects: flagExporter.linkObjects,
})
@@ -371,6 +455,7 @@
module.AddProperties(
&BaseProperties{},
&cc.VendorProperties{},
+ &BenchmarkProperties{},
&BindgenProperties{},
&BaseCompilerProperties{},
&BinaryCompilerProperties{},
@@ -382,6 +467,7 @@
&cc.CoverageProperties{},
&cc.RustBindgenClangProperties{},
&ClippyProperties{},
+ &SanitizeProperties{},
)
android.InitDefaultsModule(module)
@@ -464,20 +550,15 @@
}
func (mod *Module) OutputFile() android.OptionalPath {
- return mod.outputFile
+ if mod.compiler != nil && mod.compiler.strippedOutputFilePath().Valid() {
+ return mod.compiler.strippedOutputFilePath()
+ }
+ return mod.unstrippedOutputFile
}
func (mod *Module) CoverageFiles() android.Paths {
if mod.compiler != nil {
- if !mod.compiler.nativeCoverage() {
- return android.Paths{}
- }
- if library, ok := mod.compiler.(*libraryDecorator); ok {
- if library.coverageFile != nil {
- return android.Paths{library.coverageFile}
- }
- return android.Paths{}
- }
+ return android.Paths{}
}
panic(fmt.Errorf("CoverageFiles called on non-library module: %q", mod.BaseModuleName()))
}
@@ -489,7 +570,7 @@
return false
}
- return mod.outputFile.Valid() && !mod.Properties.PreventInstall
+ return mod.OutputFile().Valid() && !mod.Properties.PreventInstall
}
var _ cc.LinkableInterface = (*Module)(nil)
@@ -510,6 +591,9 @@
if mod.sourceProvider != nil {
mod.AddProperties(mod.sourceProvider.SourceProviderProps()...)
}
+ if mod.sanitize != nil {
+ mod.AddProperties(mod.sanitize.props()...)
+ }
android.InitAndroidArchModule(mod, mod.hod, mod.multilib)
android.InitApexModule(mod)
@@ -528,6 +612,7 @@
module := newBaseModule(hod, multilib)
module.coverage = &coverage{}
module.clippy = &clippy{}
+ module.sanitize = &sanitize{}
return module
}
@@ -620,7 +705,9 @@
// Differentiate static libraries that are vendor available
if mod.UseVndk() {
- mod.Properties.SubName += ".vendor"
+ mod.Properties.SubName += cc.VendorSuffix
+ } else if mod.InVendorRamdisk() && !mod.OnlyInVendorRamdisk() {
+ mod.Properties.SubName += cc.VendorRamdiskSuffix
}
if !toolchain.Supported() {
@@ -642,6 +729,9 @@
if mod.clippy != nil {
flags, deps = mod.clippy.flags(ctx, flags, deps)
}
+ if mod.sanitize != nil {
+ flags, deps = mod.sanitize.flags(ctx, flags, deps)
+ }
// SourceProvider needs to call GenerateSource() before compiler calls
// compile() so it can provide the source. A SourceProvider has
@@ -660,9 +750,10 @@
}
if mod.compiler != nil && !mod.compiler.Disabled() {
- outputFile := mod.compiler.compile(ctx, flags, deps)
-
- mod.outputFile = android.OptionalPathForPath(outputFile)
+ mod.compiler.initialize(ctx)
+ unstrippedOutputFile := mod.compiler.compile(ctx, flags, deps)
+ mod.unstrippedOutputFile = android.OptionalPathForPath(unstrippedOutputFile)
+ bloaty.MeasureSizeForPaths(ctx, mod.compiler.strippedOutputFilePath(), mod.unstrippedOutputFile)
apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
if mod.installable(apexInfo) {
@@ -685,13 +776,17 @@
deps = mod.coverage.deps(ctx, deps)
}
+ if mod.sanitize != nil {
+ deps = mod.sanitize.deps(ctx, deps)
+ }
+
deps.Rlibs = android.LastUniqueStrings(deps.Rlibs)
deps.Dylibs = android.LastUniqueStrings(deps.Dylibs)
deps.Rustlibs = android.LastUniqueStrings(deps.Rustlibs)
deps.ProcMacros = android.LastUniqueStrings(deps.ProcMacros)
deps.SharedLibs = android.LastUniqueStrings(deps.SharedLibs)
deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs)
-
+ deps.WholeStaticLibs = android.LastUniqueStrings(deps.WholeStaticLibs)
return deps
}
@@ -725,6 +820,11 @@
return ok && tag == dylibDepTag
}
+func IsRlibDepTag(depTag blueprint.DependencyTag) bool {
+ tag, ok := depTag.(dependencyTag)
+ return ok && tag == rlibDepTag
+}
+
type autoDep struct {
variation string
depTag dependencyTag
@@ -738,13 +838,16 @@
)
type autoDeppable interface {
- autoDep(ctx BaseModuleContext) autoDep
+ autoDep(ctx android.BottomUpMutatorContext) autoDep
}
func (mod *Module) begin(ctx BaseModuleContext) {
if mod.coverage != nil {
mod.coverage.begin(ctx)
}
+ if mod.sanitize != nil {
+ mod.sanitize.begin(ctx)
+ }
}
func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
@@ -761,8 +864,10 @@
ctx.VisitDirectDeps(func(dep android.Module) {
depName := ctx.OtherModuleName(dep)
depTag := ctx.OtherModuleDependencyTag(dep)
+
if rustDep, ok := dep.(*Module); ok && !rustDep.CcLibraryInterface() {
//Handle Rust Modules
+ makeLibName := cc.MakeLibName(ctx, mod, rustDep, depName+rustDep.Properties.RustSubName)
switch depTag {
case dylibDepTag:
@@ -772,20 +877,19 @@
return
}
directDylibDeps = append(directDylibDeps, rustDep)
- mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, depName)
+ mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, makeLibName)
case rlibDepTag:
rlib, ok := rustDep.compiler.(libraryInterface)
if !ok || !rlib.rlib() {
- ctx.ModuleErrorf("mod %q not an rlib library", depName+rustDep.Properties.SubName)
+ ctx.ModuleErrorf("mod %q not an rlib library", makeLibName)
return
}
- depPaths.coverageFiles = append(depPaths.coverageFiles, rustDep.CoverageFiles()...)
directRlibDeps = append(directRlibDeps, rustDep)
- mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, depName+rustDep.Properties.SubName)
+ mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, makeLibName)
case procMacroDepTag:
directProcMacroDeps = append(directProcMacroDeps, rustDep)
- mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, depName)
+ mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName)
case android.SourceDepTag:
// Since these deps are added in path_properties.go via AddDependencies, we need to ensure the correct
// OS/Arch variant is used.
@@ -815,7 +919,7 @@
}
if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
- linkFile := rustDep.outputFile
+ linkFile := rustDep.unstrippedOutputFile
if !linkFile.Valid() {
ctx.ModuleErrorf("Invalid output file when adding dep %q to %q",
depName, ctx.ModuleName())
@@ -829,6 +933,7 @@
} else if ccDep, ok := dep.(cc.LinkableInterface); ok {
//Handle C dependencies
+ makeLibName := cc.MakeLibName(ctx, mod, ccDep, depName)
if _, ok := ccDep.(*Module); !ok {
if ccDep.Module().Target().Os != ctx.Os() {
ctx.ModuleErrorf("OS mismatch between %q and %q", ctx.ModuleName(), depName)
@@ -849,16 +954,28 @@
exportDep := false
switch {
case cc.IsStaticDepTag(depTag):
- depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
+ if cc.IsWholeStaticLib(depTag) {
+ // rustc will bundle static libraries when they're passed with "-lstatic=<lib>". This will fail
+ // if the library is not prefixed by "lib".
+ if libName, ok := libNameFromFilePath(linkObject.Path()); ok {
+ depPaths.depFlags = append(depPaths.depFlags, "-lstatic="+libName)
+ } else {
+ ctx.ModuleErrorf("'%q' cannot be listed as a whole_static_library in Rust modules unless the output is prefixed by 'lib'", depName, ctx.ModuleName())
+ }
+ }
+
+ // Add this to linkObjects to pass the library directly to the linker as well. This propagates
+ // to dependencies to avoid having to redeclare static libraries for dependents of the dylib variant.
depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
+ depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
+
exportedInfo := ctx.OtherModuleProvider(dep, cc.FlagExporterInfoProvider).(cc.FlagExporterInfo)
depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
- depPaths.coverageFiles = append(depPaths.coverageFiles, ccDep.CoverageFiles()...)
directStaticLibDeps = append(directStaticLibDeps, ccDep)
- mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, depName)
+ mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, makeLibName)
case cc.IsSharedDepTag(depTag):
depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
@@ -868,7 +985,7 @@
depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
directSharedLibDeps = append(directSharedLibDeps, ccDep)
- mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, depName)
+ mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, makeLibName)
exportDep = true
case cc.IsHeaderDepTag(depTag):
exportedInfo := ctx.OtherModuleProvider(dep, cc.FlagExporterInfoProvider).(cc.FlagExporterInfo)
@@ -899,15 +1016,15 @@
var rlibDepFiles RustLibraries
for _, dep := range directRlibDeps {
- rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.outputFile.Path(), CrateName: dep.CrateName()})
+ rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.unstrippedOutputFile.Path(), CrateName: dep.CrateName()})
}
var dylibDepFiles RustLibraries
for _, dep := range directDylibDeps {
- dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.outputFile.Path(), CrateName: dep.CrateName()})
+ dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.unstrippedOutputFile.Path(), CrateName: dep.CrateName()})
}
var procMacroDepFiles RustLibraries
for _, dep := range directProcMacroDeps {
- procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.outputFile.Path(), CrateName: dep.CrateName()})
+ procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.unstrippedOutputFile.Path(), CrateName: dep.CrateName()})
}
var staticLibDepFiles android.Paths
@@ -915,9 +1032,15 @@
staticLibDepFiles = append(staticLibDepFiles, dep.OutputFile().Path())
}
+ var sharedLibFiles android.Paths
var sharedLibDepFiles android.Paths
for _, dep := range directSharedLibDeps {
- sharedLibDepFiles = append(sharedLibDepFiles, dep.OutputFile().Path())
+ sharedLibFiles = append(sharedLibFiles, dep.OutputFile().Path())
+ if dep.Toc().Valid() {
+ sharedLibDepFiles = append(sharedLibDepFiles, dep.Toc().Path())
+ } else {
+ sharedLibDepFiles = append(sharedLibDepFiles, dep.OutputFile().Path())
+ }
}
var srcProviderDepFiles android.Paths
@@ -933,12 +1056,14 @@
depPaths.RLibs = append(depPaths.RLibs, rlibDepFiles...)
depPaths.DyLibs = append(depPaths.DyLibs, dylibDepFiles...)
depPaths.SharedLibs = append(depPaths.SharedLibs, sharedLibDepFiles...)
+ depPaths.SharedLibDeps = append(depPaths.SharedLibDeps, sharedLibDepFiles...)
depPaths.StaticLibs = append(depPaths.StaticLibs, staticLibDepFiles...)
depPaths.ProcMacros = append(depPaths.ProcMacros, procMacroDepFiles...)
depPaths.SrcDeps = append(depPaths.SrcDeps, srcProviderDepFiles...)
// Dedup exported flags from dependencies
depPaths.linkDirs = android.FirstUniqueStrings(depPaths.linkDirs)
+ depPaths.linkObjects = android.FirstUniqueStrings(depPaths.linkObjects)
depPaths.depFlags = android.FirstUniqueStrings(depPaths.depFlags)
depPaths.depClangFlags = android.FirstUniqueStrings(depPaths.depClangFlags)
depPaths.depIncludePaths = android.FirstUniquePaths(depPaths.depIncludePaths)
@@ -1014,7 +1139,10 @@
cc.SharedDepTag(), deps.SharedLibs...)
actx.AddVariationDependencies(append(commonDepVariations,
blueprint.Variation{Mutator: "link", Variation: "static"}),
- cc.StaticDepTag(), deps.StaticLibs...)
+ cc.StaticDepTag(false), deps.StaticLibs...)
+ actx.AddVariationDependencies(append(commonDepVariations,
+ blueprint.Variation{Mutator: "link", Variation: "static"}),
+ cc.StaticDepTag(true), deps.WholeStaticLibs...)
actx.AddVariationDependencies(nil, cc.HeaderDepTag(), deps.HeaderLibs...)
@@ -1157,6 +1285,16 @@
return false
}
+// If a library file has a "lib" prefix, extract the library name without the prefix.
+func libNameFromFilePath(filepath android.Path) (string, bool) {
+ libName := strings.TrimSuffix(filepath.Base(), filepath.Ext())
+ if strings.HasPrefix(libName, "lib") {
+ libName = libName[3:]
+ return libName, true
+ }
+ return "", false
+}
+
var Bool = proptools.Bool
var BoolDefault = proptools.BoolDefault
var String = proptools.String
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 48c8d74..6ae05d9 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -15,7 +15,6 @@
package rust
import (
- "io/ioutil"
"os"
"runtime"
"strings"
@@ -24,64 +23,112 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
- "android/soong/cc"
+ "android/soong/genrule"
)
-var (
- buildDir string
-)
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_rust_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
+ os.Exit(m.Run())
+}
- return m.Run()
- }
+var prepareForRustTest = android.GroupFixturePreparers(
+ android.PrepareForTestWithArchMutator,
+ android.PrepareForTestWithDefaults,
+ android.PrepareForTestWithPrebuilts,
- os.Exit(run())
+ genrule.PrepareForTestWithGenRuleBuildComponents,
+
+ PrepareForIntegrationTestWithRust,
+)
+
+var rustMockedFiles = android.MockFS{
+ "foo.rs": nil,
+ "foo.c": nil,
+ "src/bar.rs": nil,
+ "src/any.h": nil,
+ "proto.proto": nil,
+ "proto/buf.proto": nil,
+ "buf.proto": nil,
+ "foo.proto": nil,
+ "liby.so": nil,
+ "libz.so": nil,
+ "data.txt": nil,
}
// testRust returns a TestContext in which a basic environment has been setup.
-// This environment contains a few mocked files. See testRustCtx.useMockedFs
-// for the list of these files.
+// This environment contains a few mocked files. See rustMockedFiles for the list of these files.
func testRust(t *testing.T, bp string) *android.TestContext {
- tctx := newTestRustCtx(t, bp)
- tctx.useMockedFs()
- tctx.generateConfig()
- return tctx.parse(t)
+ skipTestIfOsNotSupported(t)
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ rustMockedFiles.AddToFixture(),
+ ).
+ RunTestWithBp(t, bp)
+ return result.TestContext
+}
+
+func testRustVndk(t *testing.T, bp string) *android.TestContext {
+ skipTestIfOsNotSupported(t)
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ rustMockedFiles.AddToFixture(),
+ android.FixtureModifyProductVariables(
+ func(variables android.FixtureProductVariables) {
+ variables.DeviceVndkVersion = StringPtr("current")
+ variables.ProductVndkVersion = StringPtr("current")
+ variables.Platform_vndk_version = StringPtr("29")
+ },
+ ),
+ ).RunTestWithBp(t, bp)
+ return result.TestContext
}
// testRustCov returns a TestContext in which a basic environment has been
// setup. This environment explicitly enables coverage.
func testRustCov(t *testing.T, bp string) *android.TestContext {
- tctx := newTestRustCtx(t, bp)
- tctx.useMockedFs()
- tctx.generateConfig()
- tctx.enableCoverage(t)
- return tctx.parse(t)
+ skipTestIfOsNotSupported(t)
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ rustMockedFiles.AddToFixture(),
+ android.FixtureModifyProductVariables(
+ func(variables android.FixtureProductVariables) {
+ variables.ClangCoverage = proptools.BoolPtr(true)
+ variables.Native_coverage = proptools.BoolPtr(true)
+ variables.NativeCoveragePaths = []string{"*"}
+ },
+ ),
+ ).RunTestWithBp(t, bp)
+ return result.TestContext
}
// testRustError ensures that at least one error was raised and its value
// matches the pattern provided. The error can be either in the parsing of the
// Blueprint or when generating the build actions.
func testRustError(t *testing.T, pattern string, bp string) {
- tctx := newTestRustCtx(t, bp)
- tctx.useMockedFs()
- tctx.generateConfig()
- tctx.parseError(t, pattern)
+ skipTestIfOsNotSupported(t)
+ android.GroupFixturePreparers(
+ prepareForRustTest,
+ rustMockedFiles.AddToFixture(),
+ ).
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
+ RunTestWithBp(t, bp)
+}
+
+// testRustVndkError is similar to testRustError, but can be used to test VNDK-related errors.
+func testRustVndkError(t *testing.T, pattern string, bp string) {
+ skipTestIfOsNotSupported(t)
+ android.GroupFixturePreparers(
+ prepareForRustTest,
+ rustMockedFiles.AddToFixture(),
+ android.FixtureModifyProductVariables(
+ func(variables android.FixtureProductVariables) {
+ variables.DeviceVndkVersion = StringPtr("current")
+ variables.ProductVndkVersion = StringPtr("current")
+ variables.Platform_vndk_version = StringPtr("VER")
+ },
+ ),
+ ).
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
+ RunTestWithBp(t, bp)
}
// testRustCtx is used to build a particular test environment. Unless your
@@ -94,89 +141,11 @@
config *android.Config
}
-// newTestRustCtx returns a new testRustCtx for the Blueprint definition argument.
-func newTestRustCtx(t *testing.T, bp string) *testRustCtx {
+func skipTestIfOsNotSupported(t *testing.T) {
// TODO (b/140435149)
if runtime.GOOS != "linux" {
t.Skip("Rust Soong tests can only be run on Linux hosts currently")
}
- return &testRustCtx{bp: bp}
-}
-
-// useMockedFs setup a default mocked filesystem for the test environment.
-func (tctx *testRustCtx) useMockedFs() {
- tctx.fs = map[string][]byte{
- "foo.rs": nil,
- "foo.c": nil,
- "src/bar.rs": nil,
- "src/any.h": nil,
- "proto.proto": nil,
- "proto/buf.proto": nil,
- "buf.proto": nil,
- "foo.proto": nil,
- "liby.so": nil,
- "libz.so": nil,
- }
-}
-
-// generateConfig creates the android.Config based on the bp, fs and env
-// attributes of the testRustCtx.
-func (tctx *testRustCtx) generateConfig() {
- tctx.bp = tctx.bp + GatherRequiredDepsForTest()
- tctx.bp = tctx.bp + cc.GatherRequiredDepsForTest(android.NoOsType)
- cc.GatherRequiredFilesForTest(tctx.fs)
- config := android.TestArchConfig(buildDir, tctx.env, tctx.bp, tctx.fs)
- tctx.config = &config
-}
-
-// enableCoverage configures the test to enable coverage.
-func (tctx *testRustCtx) enableCoverage(t *testing.T) {
- if tctx.config == nil {
- t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
- }
- tctx.config.TestProductVariables.GcovCoverage = proptools.BoolPtr(true)
- tctx.config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
- tctx.config.TestProductVariables.NativeCoveragePaths = []string{"*"}
-}
-
-// parse validates the configuration and parses the Blueprint file. It returns
-// a TestContext which can be used to retrieve the generated modules via
-// ModuleForTests.
-func (tctx testRustCtx) parse(t *testing.T) *android.TestContext {
- if tctx.config == nil {
- t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
- }
- ctx := CreateTestContext(*tctx.config)
- ctx.Register()
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(*tctx.config)
- android.FailIfErrored(t, errs)
- return ctx
-}
-
-// parseError parses the Blueprint file and ensure that at least one error
-// matching the provided pattern is observed.
-func (tctx testRustCtx) parseError(t *testing.T, pattern string) {
- if tctx.config == nil {
- t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
- }
- ctx := CreateTestContext(*tctx.config)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- if len(errs) > 0 {
- android.FailIfNoMatchingErrors(t, pattern, errs)
- return
- }
-
- _, errs = ctx.PrepareBuildActions(*tctx.config)
- if len(errs) > 0 {
- android.FailIfNoMatchingErrors(t, pattern, errs)
- return
- }
-
- t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
}
// Test that we can extract the link path from a lib path.
@@ -198,6 +167,11 @@
srcs: ["foo.rs"],
crate_name: "static",
}
+ rust_ffi_host_static {
+ name: "libwholestatic",
+ srcs: ["foo.rs"],
+ crate_name: "wholestatic",
+ }
rust_ffi_host_shared {
name: "libshared",
srcs: ["foo.rs"],
@@ -212,6 +186,8 @@
name: "librlib",
srcs: ["foo.rs"],
crate_name: "rlib",
+ static_libs: ["libstatic"],
+ whole_static_libs: ["libwholestatic"],
}
rust_proc_macro {
name: "libpm",
@@ -229,6 +205,7 @@
}
`)
module := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
+ rustc := ctx.ModuleForTests("librlib", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
// Since dependencies are added to AndroidMk* properties, we can check these to see if they've been picked up.
if !android.InList("libdylib", module.Properties.AndroidMkDylibs) {
@@ -250,6 +227,11 @@
if !android.InList("libstatic", module.Properties.AndroidMkStaticLibs) {
t.Errorf("Static library dependency not detected (dependency missing from AndroidMkStaticLibs)")
}
+
+ if !strings.Contains(rustc.Args["rustcFlags"], "-lstatic=wholestatic") {
+ t.Errorf("-lstatic flag not being passed to rustc for static library %#v", rustc.Args["rustcFlags"])
+ }
+
}
func TestSourceProviderDeps(t *testing.T) {
@@ -420,3 +402,17 @@
_ = ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std")
_ = ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_rlib_dylib-std")
}
+
+// Test that library size measurements are generated.
+func TestLibrarySizes(t *testing.T) {
+ ctx := testRust(t, `
+ rust_library_dylib {
+ name: "libwaldo",
+ srcs: ["foo.rs"],
+ crate_name: "waldo",
+ }`)
+
+ m := ctx.SingletonForTests("file_metrics")
+ m.Output("libwaldo.dylib.so.bloaty.csv")
+ m.Output("stripped/libwaldo.dylib.so.bloaty.csv")
+}
diff --git a/rust/sanitize.go b/rust/sanitize.go
new file mode 100644
index 0000000..0a53f98
--- /dev/null
+++ b/rust/sanitize.go
@@ -0,0 +1,322 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/rust/config"
+ "fmt"
+ "github.com/google/blueprint"
+)
+
+type SanitizeProperties struct {
+ // enable AddressSanitizer, HWAddressSanitizer, and others.
+ Sanitize struct {
+ Address *bool `android:"arch_variant"`
+ Hwaddress *bool `android:"arch_variant"`
+ Fuzzer *bool `android:"arch_variant"`
+ Never *bool `android:"arch_variant"`
+ }
+ SanitizerEnabled bool `blueprint:"mutated"`
+ SanitizeDep bool `blueprint:"mutated"`
+
+ // Used when we need to place libraries in their own directory, such as ASAN.
+ InSanitizerDir bool `blueprint:"mutated"`
+}
+
+var fuzzerFlags = []string{
+ "-C passes='sancov'",
+
+ "--cfg fuzzing",
+ "-C llvm-args=-sanitizer-coverage-level=3",
+ "-C llvm-args=-sanitizer-coverage-trace-compares",
+ "-C llvm-args=-sanitizer-coverage-inline-8bit-counters",
+ "-C llvm-args=-sanitizer-coverage-trace-geps",
+ "-C llvm-args=-sanitizer-coverage-prune-blocks=0",
+
+ // Sancov breaks with lto
+ // TODO: Remove when https://bugs.llvm.org/show_bug.cgi?id=41734 is resolved and sancov works with LTO
+ "-C lto=no",
+}
+
+var asanFlags = []string{
+ "-Z sanitizer=address",
+}
+
+var hwasanFlags = []string{
+ "-Z sanitizer=hwaddress",
+ "-C target-feature=+tagged-globals",
+}
+
+func boolPtr(v bool) *bool {
+ if v {
+ return &v
+ } else {
+ return nil
+ }
+}
+
+func init() {
+}
+func (sanitize *sanitize) props() []interface{} {
+ return []interface{}{&sanitize.Properties}
+}
+
+func (sanitize *sanitize) begin(ctx BaseModuleContext) {
+ s := sanitize.Properties.Sanitize
+
+ // TODO:(b/178369775)
+ // For now sanitizing is only supported on devices
+ if ctx.Os() == android.Android && Bool(s.Fuzzer) {
+ sanitize.Properties.SanitizerEnabled = true
+ }
+
+ if ctx.Os() == android.Android && Bool(s.Address) {
+ sanitize.Properties.SanitizerEnabled = true
+ }
+
+ // HWASan requires AArch64 hardware feature (top-byte-ignore).
+ if ctx.Arch().ArchType != android.Arm64 {
+ s.Hwaddress = nil
+ }
+
+ if ctx.Os() == android.Android && Bool(s.Hwaddress) {
+ sanitize.Properties.SanitizerEnabled = true
+ }
+}
+
+type sanitize struct {
+ Properties SanitizeProperties
+}
+
+func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
+ if !sanitize.Properties.SanitizerEnabled {
+ return flags, deps
+ }
+ if Bool(sanitize.Properties.Sanitize.Fuzzer) {
+ flags.RustFlags = append(flags.RustFlags, fuzzerFlags...)
+ if ctx.Arch().ArchType == android.Arm64 {
+ flags.RustFlags = append(flags.RustFlags, hwasanFlags...)
+ } else {
+ flags.RustFlags = append(flags.RustFlags, asanFlags...)
+ }
+ }
+ if Bool(sanitize.Properties.Sanitize.Address) {
+ flags.RustFlags = append(flags.RustFlags, asanFlags...)
+ }
+ if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+ flags.RustFlags = append(flags.RustFlags, hwasanFlags...)
+ }
+ return flags, deps
+}
+
+func (sanitize *sanitize) deps(ctx BaseModuleContext, deps Deps) Deps {
+ return deps
+}
+
+func rustSanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) {
+ if mod, ok := mctx.Module().(*Module); ok && mod.sanitize != nil {
+ if !mod.Enabled() {
+ return
+ }
+
+ variations := mctx.Target().Variations()
+ var depTag blueprint.DependencyTag
+ var deps []string
+
+ if mod.IsSanitizerEnabled(cc.Asan) ||
+ (mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType != android.Arm64) {
+ variations = append(variations,
+ blueprint.Variation{Mutator: "link", Variation: "shared"})
+ depTag = cc.SharedDepTag()
+ deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan")}
+ } else if mod.IsSanitizerEnabled(cc.Hwasan) ||
+ (mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType == android.Arm64) {
+ // TODO(b/180495975): HWASan for static Rust binaries isn't supported yet.
+ if binary, ok := mod.compiler.(*binaryDecorator); ok {
+ if Bool(binary.Properties.Static_executable) {
+ mctx.ModuleErrorf("HWASan is not supported for static Rust executables yet.")
+ }
+ }
+
+ if mod.StaticallyLinked() {
+ variations = append(variations,
+ blueprint.Variation{Mutator: "link", Variation: "static"})
+ depTag = cc.StaticDepTag(false)
+ deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "hwasan_static")}
+ } else {
+ variations = append(variations,
+ blueprint.Variation{Mutator: "link", Variation: "shared"})
+ depTag = cc.SharedDepTag()
+ deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "hwasan")}
+ }
+ }
+
+ mctx.AddFarVariationDependencies(variations, depTag, deps...)
+ }
+}
+
+func (sanitize *sanitize) SetSanitizer(t cc.SanitizerType, b bool) {
+ sanitizerSet := false
+ switch t {
+ case cc.Fuzzer:
+ sanitize.Properties.Sanitize.Fuzzer = boolPtr(b)
+ sanitizerSet = true
+ case cc.Asan:
+ sanitize.Properties.Sanitize.Address = boolPtr(b)
+ sanitizerSet = true
+ case cc.Hwasan:
+ sanitize.Properties.Sanitize.Hwaddress = boolPtr(b)
+ sanitizerSet = true
+ default:
+ panic(fmt.Errorf("setting unsupported sanitizerType %d", t))
+ }
+ if b && sanitizerSet {
+ sanitize.Properties.SanitizerEnabled = true
+ }
+}
+
+// Check if the sanitizer is explicitly disabled (as opposed to nil by
+// virtue of not being set).
+func (sanitize *sanitize) isSanitizerExplicitlyDisabled(t cc.SanitizerType) bool {
+ if sanitize == nil {
+ return false
+ }
+ if Bool(sanitize.Properties.Sanitize.Never) {
+ return true
+ }
+ sanitizerVal := sanitize.getSanitizerBoolPtr(t)
+ return sanitizerVal != nil && *sanitizerVal == false
+}
+
+// There isn't an analog of the method above (ie:isSanitizerExplicitlyEnabled)
+// because enabling a sanitizer either directly (via the blueprint) or
+// indirectly (via a mutator) sets the bool ptr to true, and you can't
+// distinguish between the cases. It isn't needed though - both cases can be
+// treated identically.
+func (sanitize *sanitize) isSanitizerEnabled(t cc.SanitizerType) bool {
+ if sanitize == nil || !sanitize.Properties.SanitizerEnabled {
+ return false
+ }
+
+ sanitizerVal := sanitize.getSanitizerBoolPtr(t)
+ return sanitizerVal != nil && *sanitizerVal == true
+}
+
+func (sanitize *sanitize) getSanitizerBoolPtr(t cc.SanitizerType) *bool {
+ switch t {
+ case cc.Fuzzer:
+ return sanitize.Properties.Sanitize.Fuzzer
+ case cc.Asan:
+ return sanitize.Properties.Sanitize.Address
+ case cc.Hwasan:
+ return sanitize.Properties.Sanitize.Hwaddress
+ default:
+ return nil
+ }
+}
+
+func (sanitize *sanitize) AndroidMk(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+ // Add a suffix for hwasan rlib libraries to allow surfacing both the sanitized and
+ // non-sanitized variants to make without a name conflict.
+ if entries.Class == "RLIB_LIBRARIES" || entries.Class == "STATIC_LIBRARIES" {
+ if sanitize.isSanitizerEnabled(cc.Hwasan) {
+ entries.SubName += ".hwasan"
+ }
+ }
+}
+
+func (mod *Module) SanitizerSupported(t cc.SanitizerType) bool {
+ if mod.Host() {
+ return false
+ }
+ switch t {
+ case cc.Fuzzer:
+ return true
+ case cc.Asan:
+ return true
+ case cc.Hwasan:
+ return true
+ default:
+ return false
+ }
+}
+
+func (mod *Module) IsSanitizerEnabled(t cc.SanitizerType) bool {
+ return mod.sanitize.isSanitizerEnabled(t)
+}
+
+func (mod *Module) IsSanitizerExplicitlyDisabled(t cc.SanitizerType) bool {
+ if mod.Host() {
+ return true
+ }
+
+ // TODO(b/178365482): Rust/CC interop doesn't work just yet; don't sanitize rust_ffi modules until
+ // linkage issues are resolved.
+ if lib, ok := mod.compiler.(libraryInterface); ok {
+ if lib.shared() || lib.static() {
+ return true
+ }
+ }
+
+ return mod.sanitize.isSanitizerExplicitlyDisabled(t)
+}
+
+func (mod *Module) SanitizeDep() bool {
+ return mod.sanitize.Properties.SanitizeDep
+}
+
+func (mod *Module) SetSanitizer(t cc.SanitizerType, b bool) {
+ if !Bool(mod.sanitize.Properties.Sanitize.Never) {
+ mod.sanitize.SetSanitizer(t, b)
+ }
+}
+
+func (mod *Module) SetSanitizeDep(b bool) {
+ mod.sanitize.Properties.SanitizeDep = b
+}
+
+func (mod *Module) StaticallyLinked() bool {
+ if lib, ok := mod.compiler.(libraryInterface); ok {
+ return lib.rlib() || lib.static()
+ } else if binary, ok := mod.compiler.(*binaryDecorator); ok {
+ return Bool(binary.Properties.Static_executable)
+ }
+ return false
+}
+
+func (mod *Module) SetInSanitizerDir() {
+ mod.sanitize.Properties.InSanitizerDir = true
+}
+
+func (mod *Module) SanitizeNever() bool {
+ return Bool(mod.sanitize.Properties.Sanitize.Never)
+}
+
+var _ cc.PlatformSanitizeable = (*Module)(nil)
+
+func IsSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
+ switch t := tag.(type) {
+ case dependencyTag:
+ return t.library
+ default:
+ return cc.IsSanitizableDependencyTag(tag)
+ }
+}
+
+func (m *Module) SanitizableDepTagChecker() cc.SantizableDependencyTagChecker {
+ return IsSanitizableDependencyTag
+}
diff --git a/rust/test.go b/rust/test.go
index 408e03a..6caa7b1 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -15,6 +15,8 @@
package rust
import (
+ "github.com/google/blueprint/proptools"
+
"android/soong/android"
"android/soong/tradefed"
)
@@ -43,6 +45,10 @@
// installed into.
Test_suites []string `android:"arch_variant"`
+ // list of files or filegroup modules that provide data that should be installed alongside
+ // the test
+ Data []string `android:"path,arch_variant"`
+
// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
// explicitly.
@@ -62,6 +68,12 @@
*binaryDecorator
Properties TestProperties
testConfig android.Path
+
+ data []android.DataPath
+}
+
+func (test *testDecorator) dataPaths() []android.DataPath {
+ return test.data
}
func (test *testDecorator) nativeCoverage() bool {
@@ -89,7 +101,6 @@
}
module.compiler = test
- module.AddProperties(&test.Properties)
return module, test
}
@@ -105,6 +116,12 @@
nil,
test.Properties.Auto_gen_config)
+ dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
+
+ for _, dataSrcPath := range dataSrcPaths {
+ test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
+ }
+
// default relative install path is module name
if !Bool(test.Properties.No_named_install_directory) {
test.baseCompiler.relative = ctx.ModuleName()
@@ -112,6 +129,9 @@
ctx.PropertyErrorf("no_named_install_directory", "Module install directory may only be disabled if relative_install_path is set")
}
+ if ctx.Host() && test.Properties.Test_options.Unit_test == nil {
+ test.Properties.Test_options.Unit_test = proptools.BoolPtr(true)
+ }
test.binaryDecorator.install(ctx)
}
@@ -120,10 +140,13 @@
if test.testHarness() {
flags.RustFlags = append(flags.RustFlags, "--test")
}
+ if ctx.Device() {
+ flags.RustFlags = append(flags.RustFlags, "-Z panic_abort_tests")
+ }
return flags
}
-func (test *testDecorator) autoDep(ctx BaseModuleContext) autoDep {
+func (test *testDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
return rlibAutoDep
}
diff --git a/rust/test_test.go b/rust/test_test.go
index fea2ad0..892761a 100644
--- a/rust/test_test.go
+++ b/rust/test_test.go
@@ -26,6 +26,7 @@
rust_test_host {
name: "my_test",
srcs: ["foo.rs"],
+ data: ["data.txt"],
}`)
testingModule := ctx.ModuleForTests("my_test", "linux_glibc_x86_64")
@@ -34,6 +35,12 @@
if !strings.Contains(outPath, expectedOut) {
t.Errorf("wrong output path: %v; expected: %v", outPath, expectedOut)
}
+
+ dataPaths := testingModule.Module().(*Module).compiler.(*testDecorator).dataPaths()
+ if len(dataPaths) != 1 {
+ t.Errorf("expected exactly one test data file. test data files: [%s]", dataPaths)
+ return
+ }
}
func TestRustTestLinkage(t *testing.T) {
diff --git a/rust/testing.go b/rust/testing.go
index 07f557a..a0f86b2 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -16,9 +16,35 @@
import (
"android/soong/android"
+ "android/soong/bloaty"
"android/soong/cc"
)
+// Preparer that will define all cc module types and a limited set of mutators and singletons that
+// make those module types usable.
+var PrepareForTestWithRustBuildComponents = android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(registerRequiredBuildComponentsForTest),
+)
+
+// The directory in which rust test default modules will be defined.
+//
+// Placing them here ensures that their location does not conflict with default test modules
+// defined by other packages.
+const rustDefaultsDir = "defaults/rust/"
+
+// Preparer that will define default rust modules, e.g. standard prebuilt modules.
+var PrepareForTestWithRustDefaultModules = android.GroupFixturePreparers(
+ cc.PrepareForTestWithCcDefaultModules,
+ bloaty.PrepareForTestWithBloatyDefaultModules,
+ PrepareForTestWithRustBuildComponents,
+ android.FixtureAddTextFile(rustDefaultsDir+"Android.bp", GatherRequiredDepsForTest()),
+)
+
+// Preparer that will allow use of all rust modules fully.
+var PrepareForIntegrationTestWithRust = android.GroupFixturePreparers(
+ PrepareForTestWithRustDefaultModules,
+)
+
func GatherRequiredDepsForTest() string {
bp := `
rust_prebuilt_library {
@@ -46,6 +72,30 @@
sysroot: true,
}
rust_prebuilt_library {
+ name: "libstd_i686-unknown-linux-gnu",
+ crate_name: "std",
+ rlib: {
+ srcs: ["libstd.rlib"],
+ },
+ dylib: {
+ srcs: ["libstd.so"],
+ },
+ host_supported: true,
+ sysroot: true,
+ }
+ rust_prebuilt_library {
+ name: "libtest_i686-unknown-linux-gnu",
+ crate_name: "test",
+ rlib: {
+ srcs: ["libtest.rlib"],
+ },
+ dylib: {
+ srcs: ["libtest.so"],
+ },
+ host_supported: true,
+ sysroot: true,
+ }
+ rust_prebuilt_library {
name: "libstd_x86_64-apple-darwin",
crate_name: "std",
rlib: {
@@ -79,6 +129,7 @@
system_shared_libs: [],
apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
min_sdk_version: "29",
+ vendor_available: true,
}
cc_library {
name: "libprotobuf-cpp-full",
@@ -87,6 +138,13 @@
system_shared_libs: [],
export_include_dirs: ["libprotobuf-cpp-full-includes"],
}
+ cc_library {
+ name: "libclang_rt.asan-aarch64-android",
+ no_libcrt: true,
+ nocrt: true,
+ system_shared_libs: [],
+ export_include_dirs: ["libprotobuf-cpp-full-includes"],
+ }
rust_library {
name: "libstd",
crate_name: "std",
@@ -94,7 +152,8 @@
no_stdlibs: true,
host_supported: true,
vendor_available: true,
- native_coverage: false,
+ vendor_ramdisk_available: true,
+ native_coverage: false,
sysroot: true,
apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
min_sdk_version: "29",
@@ -106,7 +165,8 @@
no_stdlibs: true,
host_supported: true,
vendor_available: true,
- native_coverage: false,
+ vendor_ramdisk_available: true,
+ native_coverage: false,
sysroot: true,
apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
min_sdk_version: "29",
@@ -129,12 +189,25 @@
srcs: ["foo.rs"],
host_supported: true,
}
-
+ rust_library {
+ name: "liblibfuzzer_sys",
+ crate_name: "libfuzzer_sys",
+ srcs:["foo.rs"],
+ host_supported: true,
+ }
+ rust_library {
+ name: "libcriterion",
+ crate_name: "criterion",
+ srcs:["foo.rs"],
+ host_supported: true,
+ }
`
return bp
}
-func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
+func registerRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("rust_benchmark", RustBenchmarkFactory)
+ ctx.RegisterModuleType("rust_benchmark_host", RustBenchmarkHostFactory)
ctx.RegisterModuleType("rust_binary", RustBinaryFactory)
ctx.RegisterModuleType("rust_binary_host", RustBinaryHostFactory)
ctx.RegisterModuleType("rust_bindgen", RustBindgenFactory)
@@ -147,6 +220,7 @@
ctx.RegisterModuleType("rust_library_host", RustLibraryHostFactory)
ctx.RegisterModuleType("rust_library_host_dylib", RustLibraryDylibHostFactory)
ctx.RegisterModuleType("rust_library_host_rlib", RustLibraryRlibHostFactory)
+ ctx.RegisterModuleType("rust_fuzz", RustFuzzFactory)
ctx.RegisterModuleType("rust_ffi", RustFFIFactory)
ctx.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory)
ctx.RegisterModuleType("rust_ffi_static", RustFFIStaticFactory)
@@ -167,13 +241,3 @@
})
ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
}
-
-func CreateTestContext(config android.Config) *android.TestContext {
- ctx := android.NewTestArchContext(config)
- android.RegisterPrebuiltMutators(ctx)
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- cc.RegisterRequiredBuildComponentsForTest(ctx)
- RegisterRequiredBuildComponentsForTest(ctx)
-
- return ctx
-}
diff --git a/scripts/Android.bp b/scripts/Android.bp
index dd03f28..c54b2bc 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
python_binary_host {
name: "check_boot_jars",
main: "check_boot_jars/check_boot_jars.py",
@@ -51,7 +55,9 @@
libs: [
"manifest_utils",
],
- test_suites: ["general-tests"],
+ test_options: {
+ unit_test: true,
+ },
}
python_library_host {
@@ -106,7 +112,9 @@
libs: [
"manifest_utils",
],
- test_suites: ["general-tests"],
+ test_options: {
+ unit_test: true,
+ },
}
python_binary_host {
@@ -205,13 +213,18 @@
test_suites: ["general-tests"],
}
+python_library_host {
+ name: "ninja_rsp",
+ srcs: ["ninja_rsp.py"],
+}
+
python_binary_host {
name: "lint-project-xml",
main: "lint-project-xml.py",
srcs: [
"lint-project-xml.py",
- "ninja_rsp.py",
],
+ libs: ["ninja_rsp"],
}
python_binary_host {
@@ -219,8 +232,8 @@
main: "gen-kotlin-build-file.py",
srcs: [
"gen-kotlin-build-file.py",
- "ninja_rsp.py",
],
+ libs: ["ninja_rsp"],
}
python_binary_host {
@@ -241,3 +254,22 @@
"linker_config_proto",
],
}
+
+python_binary_host {
+ name: "conv_classpaths_proto",
+ srcs: [
+ "conv_classpaths_proto.py",
+ ],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ },
+ libs: [
+ "classpaths_proto_python",
+ ],
+}
diff --git a/scripts/OWNERS b/scripts/OWNERS
index 8198083..2b9c2de 100644
--- a/scripts/OWNERS
+++ b/scripts/OWNERS
@@ -3,3 +3,4 @@
per-file build-aml-prebuilts.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com
per-file construct_context.py = ngeoffray@google.com,calin@google.com,mathieuc@google.com,skvadrik@google.com
per-file conv_linker_config.py = kiyoungkim@google.com, jiyong@google.com, jooyung@google.com
+per-file gen_ndk*.sh = sophiez@google.com, allenhair@google.com
diff --git a/scripts/TEST_MAPPING b/scripts/TEST_MAPPING
deleted file mode 100644
index 1b0a229..0000000
--- a/scripts/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "presubmit" : [
- {
- "name": "manifest_check_test",
- "host": true
- },
- {
- "name": "manifest_fixer_test",
- "host": true
- }
- ]
-}
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index 6db870f..30cb937 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -8,6 +8,7 @@
com.android.art.testing
com.android.conscrypt
com.android.i18n
+ com.android.os.statsd
com.android.runtime
com.android.tzdata
)
@@ -26,13 +27,17 @@
platform-mainline-test-exports
runtime-module-host-exports
runtime-module-sdk
+ statsd-module-sdk
+ statsd-module-sdk-for-art
+ tzdata-module-test-exports
)
# List of libraries installed on the platform that are needed for ART chroot
# testing.
PLATFORM_LIBRARIES=(
- liblog
+ heapprofd_client_api
libartpalette-system
+ liblog
)
# We want to create apex modules for all supported architectures.
@@ -60,6 +65,9 @@
esac
}
+# Make sure this build builds from source, regardless of the default.
+export SOONG_CONFIG_art_module_source_build=true
+
OUT_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var OUT_DIR)
DIST_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var DIST_DIR)
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index c27f098..a1fa48d 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -54,7 +54,6 @@
"Safestack": false,
"Ndk_abis": true,
- "Exclude_draft_ndk_apis": true,
"VendorVars": {
"art_module": {
diff --git a/scripts/check_do_not_merge.sh b/scripts/check_do_not_merge.sh
new file mode 100755
index 0000000..ad6a0a9
--- /dev/null
+++ b/scripts/check_do_not_merge.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if git show -s --format=%s $1 | grep -qE '(DO NOT MERGE)|(RESTRICT AUTOMERGE)'; then
+ cat >&2 <<EOF
+DO NOT MERGE and RESTRICT AUTOMERGE very often lead to unintended results
+and are not allowed to be used in this project.
+Please use the Merged-In tag to be more explicit about where this change
+should merge to. Google-internal documentation exists at go/merged-in
+
+If this check is mis-triggering or you know Merged-In is incorrect in this
+situation you can bypass this check with \`repo upload --no-verify\`.
+EOF
+ exit 1
+fi
diff --git a/scripts/construct_context.py b/scripts/construct_context.py
index 6f9edc4..f0658ba 100755
--- a/scripts/construct_context.py
+++ b/scripts/construct_context.py
@@ -66,9 +66,9 @@
if not args.sdk:
raise SystemExit('target sdk version is not set')
if not args.host_contexts:
- raise SystemExit('host context is not set')
+ args.host_contexts = []
if not args.target_contexts:
- raise SystemExit('target context is not set')
+ args.target_contexts = []
print(construct_contexts(args))
diff --git a/scripts/conv_classpaths_proto.py b/scripts/conv_classpaths_proto.py
new file mode 100644
index 0000000..f49fbbb
--- /dev/null
+++ b/scripts/conv_classpaths_proto.py
@@ -0,0 +1,76 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+
+import classpaths_pb2
+
+import google.protobuf.json_format as json_format
+import google.protobuf.text_format as text_format
+
+
+def encode(args):
+ pb = classpaths_pb2.ExportedClasspathsJars()
+ if args.format == 'json':
+ json_format.Parse(args.input.read(), pb)
+ else:
+ text_format.Parse(args.input.read(), pb)
+ args.output.write(pb.SerializeToString())
+ args.input.close()
+ args.output.close()
+
+
+def decode(args):
+ pb = classpaths_pb2.ExportedClasspathsJars()
+ pb.ParseFromString(args.input.read())
+ if args.format == 'json':
+ args.output.write(json_format.MessageToJson(pb))
+ else:
+ args.output.write(text_format.MessageToString(pb).encode('utf_8'))
+ args.input.close()
+ args.output.close()
+
+
+def main():
+ parser = argparse.ArgumentParser('Convert classpaths.proto messages between binary and '
+ 'human-readable formats.')
+ parser.add_argument('-f', '--format', default='textproto',
+ help='human-readable format, either json or text(proto), '
+ 'defaults to textproto')
+ parser.add_argument('-i', '--input',
+ nargs='?', type=argparse.FileType('rb'), default=sys.stdin.buffer)
+ parser.add_argument('-o', '--output',
+ nargs='?', type=argparse.FileType('wb'),
+ default=sys.stdout.buffer)
+
+ subparsers = parser.add_subparsers()
+
+ parser_encode = subparsers.add_parser('encode',
+ help='convert classpaths protobuf message from '
+ 'JSON to binary format',
+ parents=[parser], add_help=False)
+
+ parser_encode.set_defaults(func=encode)
+
+ parser_decode = subparsers.add_parser('decode',
+ help='print classpaths config in JSON format',
+ parents=[parser], add_help=False)
+ parser_decode.set_defaults(func=decode)
+
+ args = parser.parse_args()
+ args.func(args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/conv_linker_config.py b/scripts/conv_linker_config.py
index 22fe9f6..92f79da 100644
--- a/scripts/conv_linker_config.py
+++ b/scripts/conv_linker_config.py
@@ -78,6 +78,14 @@
with open(args.output, 'wb') as f:
f.write(pb.SerializeToString())
+def Merge(args):
+ pb = linker_config_pb2.LinkerConfig()
+ for other in args.input:
+ with open(other, 'rb') as f:
+ pb.MergeFromString(f.read())
+
+ with open(args.out, 'wb') as f:
+ f.write(pb.SerializeToString())
def GetArgParser():
parser = argparse.ArgumentParser()
@@ -161,6 +169,22 @@
help='Values of the libraries to append. If there are more than one it should be separated by empty space')
append.set_defaults(func=Append)
+ append = subparsers.add_parser(
+ 'merge', help='Merge configurations')
+ append.add_argument(
+ '-o',
+ '--out',
+ required=True,
+ type=str,
+ help='Ouptut linker configuration file to write in protobuf.')
+ append.add_argument(
+ '-i',
+ '--input',
+ nargs='+',
+ type=str,
+ help='Linker configuration files to merge.')
+ append.set_defaults(func=Merge)
+
return parser
diff --git a/scripts/gen_ndk_backedby_apex.sh b/scripts/gen_ndk_backedby_apex.sh
new file mode 100755
index 0000000..4abaaba
--- /dev/null
+++ b/scripts/gen_ndk_backedby_apex.sh
@@ -0,0 +1,72 @@
+#!/bin/bash -e
+
+# Copyright 2020 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.
+
+# Generates NDK API txt file used by Mainline modules. NDK APIs would have value
+# "UND" in Ndx column and have suffix "@LIB_NAME" in Name column.
+# For example, current line llvm-readelf output is:
+# 1: 00000000 0 FUNC GLOBAL DEFAULT UND dlopen@LIBC
+# After the parse function below "dlopen" would be write to the output file.
+printHelp() {
+ echo "**************************** Usage Instructions ****************************"
+ echo "This script is used to generate the Mainline modules backed-by NDK symbols."
+ echo ""
+ echo "To run this script use: ./gen_ndk_backed_by_apex.sh \$OUTPUT_FILE_PATH \$NDK_LIB_NAME_LIST \$MODULE_LIB1 \$MODULE_LIB2..."
+ echo "For example: If output write to /backedby.txt then the command would be:"
+ echo "./gen_ndk_backed_by_apex.sh /backedby.txt /ndkLibList.txt lib1.so lib2.so"
+ echo "If the module1 is backing lib1 then the backedby.txt would contains: "
+ echo "lib1"
+}
+
+contains() {
+ val="$1"
+ shift
+ for x in "$@"; do
+ if [ "$x" = "$val" ]; then
+ return 0
+ fi
+ done
+ return 1
+}
+
+
+genBackedByList() {
+ out="$1"
+ shift
+ ndk_list="$1"
+ shift
+ rm -f "$out"
+ touch "$out"
+ while IFS= read -r line
+ do
+ soFileName=$(echo "$line" | sed 's/\(.*so\).*/\1/')
+ if [[ ! -z "$soFileName" && "$soFileName" != *"#"* ]]
+ then
+ if contains "$soFileName" "$@"; then
+ echo "$soFileName" >> "$out"
+ fi
+ fi
+ done < "$ndk_list"
+}
+
+if [[ "$1" == "help" ]]
+then
+ printHelp
+elif [[ "$#" -lt 2 ]]
+then
+ echo "Wrong argument length. Expecting at least 2 argument representing output path, path to ndk library list, followed by a list of libraries in the Mainline module."
+else
+ genBackedByList "$@"
+fi
diff --git a/scripts/gen_ndk_usedby_apex.sh b/scripts/gen_ndk_usedby_apex.sh
index f143161..0d3ed5a 100755
--- a/scripts/gen_ndk_usedby_apex.sh
+++ b/scripts/gen_ndk_usedby_apex.sh
@@ -33,7 +33,7 @@
do
if [[ $line = *FUNC*GLOBAL*UND*@* ]] ;
then
- echo "$line" | sed -r 's/.*UND (.*)@.*/\1/g' >> "$2"
+ echo "$line" | sed -r 's/.*UND (.*@.*)/\1/g' >> "$2"
fi
done < "$1"
echo "" >> "$2"
diff --git a/scripts/gen_sorted_bss_symbols.sh b/scripts/gen_sorted_bss_symbols.sh
index 244ed0d..a9b61a1 100755
--- a/scripts/gen_sorted_bss_symbols.sh
+++ b/scripts/gen_sorted_bss_symbols.sh
@@ -18,11 +18,11 @@
# their sizes.
# Inputs:
# Environment:
-# CROSS_COMPILE: prefix added to nm tools
+# CLANG_BIN: path to the clang bin directory
# Arguments:
# $1: Input ELF file
# $2: Output symbol ordering file
set -o pipefail
-${CROSS_COMPILE}nm --size-sort $1 | awk '{if ($2 == "b" || $2 == "B") print $3}' > $2
+${CLANG_BIN}/llvm-nm --size-sort $1 | awk '{if ($2 == "b" || $2 == "B") print $3}' > $2
diff --git a/scripts/hiddenapi/Android.bp b/scripts/hiddenapi/Android.bp
new file mode 100644
index 0000000..af7e7fe
--- /dev/null
+++ b/scripts/hiddenapi/Android.bp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_binary_host {
+ name: "merge_csv",
+ main: "merge_csv.py",
+ srcs: ["merge_csv.py"],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ },
+}
+
+python_binary_host {
+ name: "generate_hiddenapi_lists",
+ main: "generate_hiddenapi_lists.py",
+ srcs: ["generate_hiddenapi_lists.py"],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ },
+}
diff --git a/scripts/hiddenapi/generate_hiddenapi_lists.py b/scripts/hiddenapi/generate_hiddenapi_lists.py
new file mode 100755
index 0000000..6816475
--- /dev/null
+++ b/scripts/hiddenapi/generate_hiddenapi_lists.py
@@ -0,0 +1,383 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Generate API lists for non-SDK API enforcement."""
+import argparse
+from collections import defaultdict, namedtuple
+import functools
+import os
+import re
+import sys
+
+# Names of flags recognized by the `hiddenapi` tool.
+FLAG_SDK = 'sdk'
+FLAG_UNSUPPORTED = 'unsupported'
+FLAG_BLOCKED = 'blocked'
+FLAG_MAX_TARGET_O = 'max-target-o'
+FLAG_MAX_TARGET_P = 'max-target-p'
+FLAG_MAX_TARGET_Q = 'max-target-q'
+FLAG_MAX_TARGET_R = 'max-target-r'
+FLAG_CORE_PLATFORM_API = 'core-platform-api'
+FLAG_PUBLIC_API = 'public-api'
+FLAG_SYSTEM_API = 'system-api'
+FLAG_TEST_API = 'test-api'
+
+# List of all known flags.
+FLAGS_API_LIST = [
+ FLAG_SDK,
+ FLAG_UNSUPPORTED,
+ FLAG_BLOCKED,
+ FLAG_MAX_TARGET_O,
+ FLAG_MAX_TARGET_P,
+ FLAG_MAX_TARGET_Q,
+ FLAG_MAX_TARGET_R,
+]
+ALL_FLAGS = FLAGS_API_LIST + [
+ FLAG_CORE_PLATFORM_API,
+ FLAG_PUBLIC_API,
+ FLAG_SYSTEM_API,
+ FLAG_TEST_API,
+]
+
+FLAGS_API_LIST_SET = set(FLAGS_API_LIST)
+ALL_FLAGS_SET = set(ALL_FLAGS)
+
+# Option specified after one of FLAGS_API_LIST to indicate that
+# only known and otherwise unassigned entries should be assign the
+# given flag.
+# For example, the max-target-P list is checked in as it was in P,
+# but signatures have changes since then. The flag instructs this
+# script to skip any entries which do not exist any more.
+FLAG_IGNORE_CONFLICTS = "ignore-conflicts"
+
+# Option specified after one of FLAGS_API_LIST to express that all
+# apis within a given set of packages should be assign the given flag.
+FLAG_PACKAGES = "packages"
+
+# Option specified after one of FLAGS_API_LIST to indicate an extra
+# tag that should be added to the matching APIs.
+FLAG_TAG = "tag"
+
+# Regex patterns of fields/methods used in serialization. These are
+# considered public API despite being hidden.
+SERIALIZATION_PATTERNS = [
+ r'readObject\(Ljava/io/ObjectInputStream;\)V',
+ r'readObjectNoData\(\)V',
+ r'readResolve\(\)Ljava/lang/Object;',
+ r'serialVersionUID:J',
+ r'serialPersistentFields:\[Ljava/io/ObjectStreamField;',
+ r'writeObject\(Ljava/io/ObjectOutputStream;\)V',
+ r'writeReplace\(\)Ljava/lang/Object;',
+]
+
+# Single regex used to match serialization API. It combines all the
+# SERIALIZATION_PATTERNS into a single regular expression.
+SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$')
+
+# Predicates to be used with filter_apis.
+HAS_NO_API_LIST_ASSIGNED = lambda api, flags: not FLAGS_API_LIST_SET.intersection(flags)
+IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api)
+
+
+class StoreOrderedOptions(argparse.Action):
+ """An argparse action that stores a number of option arguments in the order that
+ they were specified.
+ """
+ def __call__(self, parser, args, values, option_string = None):
+ items = getattr(args, self.dest, None)
+ if items is None:
+ items = []
+ items.append([option_string.lstrip('-'), values])
+ setattr(args, self.dest, items)
+
+def get_args():
+ """Parses command line arguments.
+
+ Returns:
+ Namespace: dictionary of parsed arguments
+ """
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--output', required=True)
+ parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE',
+ help='CSV files to be merged into output')
+
+ for flag in ALL_FLAGS:
+ parser.add_argument('--' + flag, dest='ordered_flags', metavar='TXT_FILE',
+ action=StoreOrderedOptions, help='lists of entries with flag "' + flag + '"')
+ parser.add_argument('--' + FLAG_IGNORE_CONFLICTS, dest='ordered_flags', nargs=0,
+ action=StoreOrderedOptions, help='Indicates that only known and otherwise unassigned '
+ 'entries should be assign the given flag. Must follow a list of entries and applies '
+ 'to the preceding such list.')
+ parser.add_argument('--' + FLAG_PACKAGES, dest='ordered_flags', nargs=0,
+ action=StoreOrderedOptions, help='Indicates that the previous list of entries '
+ 'is a list of packages. All members in those packages will be given the flag. '
+ 'Must follow a list of entries and applies to the preceding such list.')
+ parser.add_argument('--' + FLAG_TAG, dest='ordered_flags', nargs=1,
+ action=StoreOrderedOptions, help='Adds an extra tag to the previous list of entries. '
+ 'Must follow a list of entries and applies to the preceding such list.')
+
+ return parser.parse_args()
+
+
+def read_lines(filename):
+ """Reads entire file and return it as a list of lines.
+
+ Lines which begin with a hash are ignored.
+
+ Args:
+ filename (string): Path to the file to read from.
+
+ Returns:
+ Lines of the file as a list of string.
+ """
+ with open(filename, 'r') as f:
+ lines = f.readlines();
+ lines = filter(lambda line: not line.startswith('#'), lines)
+ lines = map(lambda line: line.strip(), lines)
+ return set(lines)
+
+
+def write_lines(filename, lines):
+ """Writes list of lines into a file, overwriting the file if it exists.
+
+ Args:
+ filename (string): Path to the file to be writing into.
+ lines (list): List of strings to write into the file.
+ """
+ lines = map(lambda line: line + '\n', lines)
+ with open(filename, 'w') as f:
+ f.writelines(lines)
+
+
+def extract_package(signature):
+ """Extracts the package from a signature.
+
+ Args:
+ signature (string): JNI signature of a method or field.
+
+ Returns:
+ The package name of the class containing the field/method.
+ """
+ full_class_name = signature.split(";->")[0]
+ # Example: Landroid/hardware/radio/V1_2/IRadio$Proxy
+ if (full_class_name[0] != "L"):
+ raise ValueError("Expected to start with 'L': %s" % full_class_name)
+ full_class_name = full_class_name[1:]
+ # If full_class_name doesn't contain '/', then package_name will be ''.
+ package_name = full_class_name.rpartition("/")[0]
+ return package_name.replace('/', '.')
+
+
+class FlagsDict:
+ def __init__(self):
+ self._dict_keyset = set()
+ self._dict = defaultdict(set)
+
+ def _check_entries_set(self, keys_subset, source):
+ assert isinstance(keys_subset, set)
+ assert keys_subset.issubset(self._dict_keyset), (
+ "Error: {} specifies signatures not present in code:\n"
+ "{}"
+ "Please visit go/hiddenapi for more information.").format(
+ source, "".join(map(lambda x: " " + str(x) + "\n", keys_subset - self._dict_keyset)))
+
+ def _check_flags_set(self, flags_subset, source):
+ assert isinstance(flags_subset, set)
+ assert flags_subset.issubset(ALL_FLAGS_SET), (
+ "Error processing: {}\n"
+ "The following flags were not recognized: \n"
+ "{}\n"
+ "Please visit go/hiddenapi for more information.").format(
+ source, "\n".join(flags_subset - ALL_FLAGS_SET))
+
+ def filter_apis(self, filter_fn):
+ """Returns APIs which match a given predicate.
+
+ This is a helper function which allows to filter on both signatures (keys) and
+ flags (values). The built-in filter() invokes the lambda only with dict's keys.
+
+ Args:
+ filter_fn : Function which takes two arguments (signature/flags) and returns a boolean.
+
+ Returns:
+ A set of APIs which match the predicate.
+ """
+ return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset))
+
+ def get_valid_subset_of_unassigned_apis(self, api_subset):
+ """Sanitizes a key set input to only include keys which exist in the dictionary
+ and have not been assigned any API list flags.
+
+ Args:
+ entries_subset (set/list): Key set to be sanitized.
+
+ Returns:
+ Sanitized key set.
+ """
+ assert isinstance(api_subset, set)
+ return api_subset.intersection(self.filter_apis(HAS_NO_API_LIST_ASSIGNED))
+
+ def generate_csv(self):
+ """Constructs CSV entries from a dictionary.
+
+ Old versions of flags are used to generate the file.
+
+ Returns:
+ List of lines comprising a CSV file. See "parse_and_merge_csv" for format description.
+ """
+ lines = []
+ for api in self._dict:
+ flags = sorted(self._dict[api])
+ lines.append(",".join([api] + flags))
+ return sorted(lines)
+
+ def parse_and_merge_csv(self, csv_lines, source = "<unknown>"):
+ """Parses CSV entries and merges them into a given dictionary.
+
+ The expected CSV format is:
+ <api signature>,<flag1>,<flag2>,...,<flagN>
+
+ Args:
+ csv_lines (list of strings): Lines read from a CSV file.
+ source (string): Origin of `csv_lines`. Will be printed in error messages.
+
+ Throws:
+ AssertionError if parsed flags are invalid.
+ """
+ # Split CSV lines into arrays of values.
+ csv_values = [ line.split(',') for line in csv_lines ]
+
+ # Update the full set of API signatures.
+ self._dict_keyset.update([ csv[0] for csv in csv_values ])
+
+ # Check that all flags are known.
+ csv_flags = set()
+ for csv in csv_values:
+ csv_flags.update(csv[1:])
+ self._check_flags_set(csv_flags, source)
+
+ # Iterate over all CSV lines, find entry in dict and append flags to it.
+ for csv in csv_values:
+ flags = csv[1:]
+ if (FLAG_PUBLIC_API in flags) or (FLAG_SYSTEM_API in flags):
+ flags.append(FLAG_SDK)
+ self._dict[csv[0]].update(flags)
+
+ def assign_flag(self, flag, apis, source="<unknown>", tag = None):
+ """Assigns a flag to given subset of entries.
+
+ Args:
+ flag (string): One of ALL_FLAGS.
+ apis (set): Subset of APIs to receive the flag.
+ source (string): Origin of `entries_subset`. Will be printed in error messages.
+
+ Throws:
+ AssertionError if parsed API signatures of flags are invalid.
+ """
+ # Check that all APIs exist in the dict.
+ self._check_entries_set(apis, source)
+
+ # Check that the flag is known.
+ self._check_flags_set(set([ flag ]), source)
+
+ # Iterate over the API subset, find each entry in dict and assign the flag to it.
+ for api in apis:
+ self._dict[api].add(flag)
+ if tag:
+ self._dict[api].add(tag)
+
+
+FlagFile = namedtuple('FlagFile', ('flag', 'file', 'ignore_conflicts', 'packages', 'tag'))
+
+def parse_ordered_flags(ordered_flags):
+ r = []
+ currentflag, file, ignore_conflicts, packages, tag = None, None, False, False, None
+ for flag_value in ordered_flags:
+ flag, value = flag_value[0], flag_value[1]
+ if flag in ALL_FLAGS_SET:
+ if currentflag:
+ r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
+ ignore_conflicts, packages, tag = False, False, None
+ currentflag = flag
+ file = value
+ else:
+ if currentflag is None:
+ raise argparse.ArgumentError('--%s is only allowed after one of %s' % (
+ flag, ' '.join(['--%s' % f for f in ALL_FLAGS_SET])))
+ if flag == FLAG_IGNORE_CONFLICTS:
+ ignore_conflicts = True
+ elif flag == FLAG_PACKAGES:
+ packages = True
+ elif flag == FLAG_TAG:
+ tag = value[0]
+
+
+ if currentflag:
+ r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
+ return r
+
+
+def main(argv):
+ # Parse arguments.
+ args = vars(get_args())
+ flagfiles = parse_ordered_flags(args['ordered_flags'])
+
+ # Initialize API->flags dictionary.
+ flags = FlagsDict()
+
+ # Merge input CSV files into the dictionary.
+ # Do this first because CSV files produced by parsing API stubs will
+ # contain the full set of APIs. Subsequent additions from text files
+ # will be able to detect invalid entries, and/or filter all as-yet
+ # unassigned entries.
+ for filename in args["csv"]:
+ flags.parse_and_merge_csv(read_lines(filename), filename)
+
+ # Combine inputs which do not require any particular order.
+ # (1) Assign serialization API to SDK.
+ flags.assign_flag(FLAG_SDK, flags.filter_apis(IS_SERIALIZATION))
+
+ # (2) Merge text files with a known flag into the dictionary.
+ for info in flagfiles:
+ if (not info.ignore_conflicts) and (not info.packages):
+ flags.assign_flag(info.flag, read_lines(info.file), info.file, info.tag)
+
+ # Merge text files where conflicts should be ignored.
+ # This will only assign the given flag if:
+ # (a) the entry exists, and
+ # (b) it has not been assigned any other flag.
+ # Because of (b), this must run after all strict assignments have been performed.
+ for info in flagfiles:
+ if info.ignore_conflicts:
+ valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(info.file))
+ flags.assign_flag(info.flag, valid_entries, filename, info.tag)
+
+ # All members in the specified packages will be assigned the appropriate flag.
+ for info in flagfiles:
+ if info.packages:
+ packages_needing_list = set(read_lines(info.file))
+ should_add_signature_to_list = lambda sig,lists: extract_package(
+ sig) in packages_needing_list and not lists
+ valid_entries = flags.filter_apis(should_add_signature_to_list)
+ flags.assign_flag(info.flag, valid_entries, info.file, info.tag)
+
+ # Mark all remaining entries as blocked.
+ flags.assign_flag(FLAG_BLOCKED, flags.filter_apis(HAS_NO_API_LIST_ASSIGNED))
+
+ # Write output.
+ write_lines(args["output"], flags.generate_csv())
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/scripts/hiddenapi/generate_hiddenapi_lists_test.py b/scripts/hiddenapi/generate_hiddenapi_lists_test.py
new file mode 100755
index 0000000..ff3d708
--- /dev/null
+++ b/scripts/hiddenapi/generate_hiddenapi_lists_test.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Unit tests for Hidden API list generation."""
+import unittest
+from generate_hiddenapi_lists import *
+
+class TestHiddenapiListGeneration(unittest.TestCase):
+
+ def test_filter_apis(self):
+ # Initialize flags so that A and B are put on the allow list and
+ # C, D, E are left unassigned. Try filtering for the unassigned ones.
+ flags = FlagsDict()
+ flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B,' + FLAG_SDK,
+ 'C', 'D', 'E'])
+ filter_set = flags.filter_apis(lambda api, flags: not flags)
+ self.assertTrue(isinstance(filter_set, set))
+ self.assertEqual(filter_set, set([ 'C', 'D', 'E' ]))
+
+ def test_get_valid_subset_of_unassigned_keys(self):
+ # Create flags where only A is unassigned.
+ flags = FlagsDict()
+ flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B', 'C'])
+ flags.assign_flag(FLAG_UNSUPPORTED, set(['C']))
+ self.assertEqual(flags.generate_csv(),
+ [ 'A,' + FLAG_SDK, 'B', 'C,' + FLAG_UNSUPPORTED ])
+
+ # Check three things:
+ # (1) B is selected as valid unassigned
+ # (2) A is not selected because it is assigned to the allow list
+ # (3) D is not selected because it is not a valid key
+ self.assertEqual(
+ flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ]))
+
+ def test_parse_and_merge_csv(self):
+ flags = FlagsDict()
+
+ # Test empty CSV entry.
+ self.assertEqual(flags.generate_csv(), [])
+
+ # Test new additions.
+ flags.parse_and_merge_csv([
+ 'A,' + FLAG_UNSUPPORTED,
+ 'B,' + FLAG_BLOCKED + ',' + FLAG_MAX_TARGET_O,
+ 'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
+ 'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API,
+ 'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
+ ])
+ self.assertEqual(flags.generate_csv(), [
+ 'A,' + FLAG_UNSUPPORTED,
+ 'B,' + FLAG_BLOCKED + "," + FLAG_MAX_TARGET_O,
+ 'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
+ 'D,' + FLAG_TEST_API + ',' + FLAG_UNSUPPORTED,
+ 'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
+ ])
+
+ # Test unknown flag.
+ with self.assertRaises(AssertionError):
+ flags.parse_and_merge_csv([ 'Z,foo' ])
+
+ def test_assign_flag(self):
+ flags = FlagsDict()
+ flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B'])
+
+ # Test new additions.
+ flags.assign_flag(FLAG_UNSUPPORTED, set([ 'A', 'B' ]))
+ self.assertEqual(flags.generate_csv(),
+ [ 'A,' + FLAG_SDK + "," + FLAG_UNSUPPORTED, 'B,' + FLAG_UNSUPPORTED ])
+
+ # Test invalid API signature.
+ with self.assertRaises(AssertionError):
+ flags.assign_flag(FLAG_SDK, set([ 'C' ]))
+
+ # Test invalid flag.
+ with self.assertRaises(AssertionError):
+ flags.assign_flag('foo', set([ 'A' ]))
+
+ def test_extract_package(self):
+ signature = 'Lcom/foo/bar/Baz;->method1()Lcom/bar/Baz;'
+ expected_package = 'com.foo.bar'
+ self.assertEqual(extract_package(signature), expected_package)
+
+ signature = 'Lcom/foo1/bar/MyClass;->method2()V'
+ expected_package = 'com.foo1.bar'
+ self.assertEqual(extract_package(signature), expected_package)
+
+ signature = 'Lcom/foo_bar/baz/MyClass;->method3()V'
+ expected_package = 'com.foo_bar.baz'
+ self.assertEqual(extract_package(signature), expected_package)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/scripts/hiddenapi/merge_csv.py b/scripts/hiddenapi/merge_csv.py
new file mode 100755
index 0000000..b047aab
--- /dev/null
+++ b/scripts/hiddenapi/merge_csv.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Merge multiple CSV files, possibly with different columns.
+"""
+
+import argparse
+import csv
+import io
+import heapq
+import itertools
+import operator
+
+from zipfile import ZipFile
+
+args_parser = argparse.ArgumentParser(description='Merge given CSV files into a single one.')
+args_parser.add_argument('--header', help='Comma separated field names; '
+ 'if missing determines the header from input files.')
+args_parser.add_argument('--zip_input', help='Treat files as ZIP archives containing CSV files to merge.',
+ action="store_true")
+args_parser.add_argument('--key_field', help='The name of the field by which the rows should be sorted. '
+ 'Must be in the field names. '
+ 'Will be the first field in the output. '
+ 'All input files must be sorted by that field.')
+args_parser.add_argument('--output', help='Output file for merged CSV.',
+ default='-', type=argparse.FileType('w'))
+args_parser.add_argument('files', nargs=argparse.REMAINDER)
+args = args_parser.parse_args()
+
+
+def dict_reader(input):
+ return csv.DictReader(input, delimiter=',', quotechar='|')
+
+csv_readers = []
+if not(args.zip_input):
+ for file in args.files:
+ csv_readers.append(dict_reader(open(file, 'r')))
+else:
+ for file in args.files:
+ with ZipFile(file) as zip:
+ for entry in zip.namelist():
+ if entry.endswith('.uau'):
+ csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r'))))
+
+headers = set()
+if args.header:
+ fieldnames = args.header.split(',')
+else:
+ # Build union of all columns from source files:
+ for reader in csv_readers:
+ headers = headers.union(reader.fieldnames)
+ fieldnames = sorted(headers)
+
+# By default chain the csv readers together so that the resulting output is
+# the concatenation of the rows from each of them:
+all_rows = itertools.chain.from_iterable(csv_readers)
+
+if len(csv_readers) > 0:
+ keyField = args.key_field
+ if keyField:
+ assert keyField in fieldnames, (
+ "--key_field {} not found, must be one of {}\n").format(
+ keyField, ",".join(fieldnames))
+ # Make the key field the first field in the output
+ keyFieldIndex = fieldnames.index(args.key_field)
+ fieldnames.insert(0, fieldnames.pop(keyFieldIndex))
+ # Create an iterable that performs a lazy merge sort on the csv readers
+ # sorting the rows by the key field.
+ all_rows = heapq.merge(*csv_readers, key=operator.itemgetter(keyField))
+
+# Write all rows from the input files to the output:
+writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
+ dialect='unix', fieldnames=fieldnames)
+writer.writeheader()
+
+# Read all the rows from the input and write them to the output in the correct
+# order:
+for row in all_rows:
+ writer.writerow(row)
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index 9122da1..8168fbf 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -19,6 +19,9 @@
from __future__ import print_function
import argparse
+import json
+import re
+import subprocess
import sys
from xml.dom import minidom
@@ -48,29 +51,99 @@
dest='enforce_uses_libraries',
action='store_true',
help='check the uses-library entries known to the build system against the manifest')
+ parser.add_argument('--enforce-uses-libraries-relax',
+ dest='enforce_uses_libraries_relax',
+ action='store_true',
+ help='do not fail immediately, just save the error message to file')
+ parser.add_argument('--enforce-uses-libraries-status',
+ dest='enforce_uses_libraries_status',
+ help='output file to store check status (error message)')
parser.add_argument('--extract-target-sdk-version',
dest='extract_target_sdk_version',
action='store_true',
help='print the targetSdkVersion from the manifest')
+ parser.add_argument('--dexpreopt-config',
+ dest='dexpreopt_configs',
+ action='append',
+ help='a paths to a dexpreopt.config of some library')
+ parser.add_argument('--aapt',
+ dest='aapt',
+ help='path to aapt executable')
parser.add_argument('--output', '-o', dest='output', help='output AndroidManifest.xml file')
parser.add_argument('input', help='input AndroidManifest.xml file')
return parser.parse_args()
-def enforce_uses_libraries(doc, uses_libraries, optional_uses_libraries):
- """Verify that the <uses-library> tags in the manifest match those provided by the build system.
+def enforce_uses_libraries(manifest, required, optional, relax, is_apk, path):
+ """Verify that the <uses-library> tags in the manifest match those provided
+ by the build system.
Args:
- doc: The XML document.
- uses_libraries: The names of <uses-library> tags known to the build system
- optional_uses_libraries: The names of <uses-library> tags with required:fals
- known to the build system
- Raises:
- RuntimeError: Invalid manifest
- ManifestMismatchError: Manifest does not match
+ manifest: manifest (either parsed XML or aapt dump of APK)
+ required: required libs known to the build system
+ optional: optional libs known to the build system
+ relax: if true, suppress error on mismatch and just write it to file
+ is_apk: if the manifest comes from an APK or an XML file
"""
+ if is_apk:
+ manifest_required, manifest_optional, tags = extract_uses_libs_apk(manifest)
+ else:
+ manifest_required, manifest_optional, tags = extract_uses_libs_xml(manifest)
- manifest = parse_manifest(doc)
+ if manifest_required == required and manifest_optional == optional:
+ return None
+
+ errmsg = ''.join([
+ 'mismatch in the <uses-library> tags between the build system and the '
+ 'manifest:\n',
+ '\t- required libraries in build system: [%s]\n' % ', '.join(required),
+ '\t vs. in the manifest: [%s]\n' % ', '.join(manifest_required),
+ '\t- optional libraries in build system: [%s]\n' % ', '.join(optional),
+ '\t vs. in the manifest: [%s]\n' % ', '.join(manifest_optional),
+ '\t- tags in the manifest (%s):\n' % path,
+ '\t\t%s\n' % '\t\t'.join(tags),
+ 'note: the following options are available:\n',
+ '\t- to temporarily disable the check on command line, rebuild with ',
+ 'RELAX_USES_LIBRARY_CHECK=true (this will set compiler filter "verify" ',
+ 'and disable AOT-compilation in dexpreopt)\n',
+ '\t- to temporarily disable the check for the whole product, set ',
+ 'PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true in the product makefiles\n',
+ '\t- to fix the check, make build system properties coherent with the '
+ 'manifest\n',
+ '\t- see build/make/Changes.md for details\n'])
+
+ if not relax:
+ raise ManifestMismatchError(errmsg)
+
+ return errmsg
+
+
+def extract_uses_libs_apk(badging):
+ """Extract <uses-library> tags from the manifest of an APK."""
+
+ pattern = re.compile("^uses-library(-not-required)?:'(.*)'$", re.MULTILINE)
+
+ required = []
+ optional = []
+ lines = []
+ for match in re.finditer(pattern, badging):
+ lines.append(match.group(0))
+ libname = match.group(2)
+ if match.group(1) == None:
+ required.append(libname)
+ else:
+ optional.append(libname)
+
+ required = first_unique_elements(required)
+ optional = first_unique_elements(optional)
+ tags = first_unique_elements(lines)
+ return required, optional, tags
+
+
+def extract_uses_libs_xml(xml):
+ """Extract <uses-library> tags from the manifest."""
+
+ manifest = parse_manifest(xml)
elems = get_children_with_tag(manifest, 'application')
application = elems[0] if len(elems) == 1 else None
if len(elems) > 1:
@@ -80,54 +153,20 @@
raise ManifestMismatchError('no <application> tag found')
return
- verify_uses_library(application, uses_libraries, optional_uses_libraries)
-
-
-def verify_uses_library(application, uses_libraries, optional_uses_libraries):
- """Verify that the uses-library values known to the build system match the manifest.
-
- Args:
- application: the <application> tag in the manifest.
- uses_libraries: the names of expected <uses-library> tags.
- optional_uses_libraries: the names of expected <uses-library> tags with required="false".
- Raises:
- ManifestMismatchError: Manifest does not match
- """
-
- if uses_libraries is None:
- uses_libraries = []
-
- if optional_uses_libraries is None:
- optional_uses_libraries = []
-
- manifest_uses_libraries, manifest_optional_uses_libraries = parse_uses_library(application)
-
- err = []
- if manifest_uses_libraries != uses_libraries:
- err.append('Expected required <uses-library> tags "%s", got "%s"' %
- (', '.join(uses_libraries), ', '.join(manifest_uses_libraries)))
-
- if manifest_optional_uses_libraries != optional_uses_libraries:
- err.append('Expected optional <uses-library> tags "%s", got "%s"' %
- (', '.join(optional_uses_libraries), ', '.join(manifest_optional_uses_libraries)))
-
- if err:
- raise ManifestMismatchError('\n'.join(err))
-
-
-def parse_uses_library(application):
- """Extract uses-library tags from the manifest.
-
- Args:
- application: the <application> tag in the manifest.
- """
-
libs = get_children_with_tag(application, 'uses-library')
- uses_libraries = [uses_library_name(x) for x in libs if uses_library_required(x)]
- optional_uses_libraries = [uses_library_name(x) for x in libs if not uses_library_required(x)]
+ required = [uses_library_name(x) for x in libs if uses_library_required(x)]
+ optional = [uses_library_name(x) for x in libs if not uses_library_required(x)]
- return first_unique_elements(uses_libraries), first_unique_elements(optional_uses_libraries)
+ # render <uses-library> tags as XML for a pretty error message
+ tags = []
+ for lib in libs:
+ tags.append(lib.toprettyxml())
+
+ required = first_unique_elements(required)
+ optional = first_unique_elements(optional)
+ tags = first_unique_elements(tags)
+ return required, optional, tags
def first_unique_elements(l):
@@ -156,16 +195,34 @@
return (required.value == 'true') if required is not None else True
-def extract_target_sdk_version(doc):
+def extract_target_sdk_version(manifest, is_apk = False):
"""Returns the targetSdkVersion from the manifest.
Args:
- doc: The XML document.
- Raises:
- RuntimeError: invalid manifest
+ manifest: manifest (either parsed XML or aapt dump of APK)
+ is_apk: if the manifest comes from an APK or an XML file
"""
+ if is_apk:
+ return extract_target_sdk_version_apk(manifest)
+ else:
+ return extract_target_sdk_version_xml(manifest)
- manifest = parse_manifest(doc)
+
+def extract_target_sdk_version_apk(badging):
+ """Extract targetSdkVersion tags from the manifest of an APK."""
+
+ pattern = re.compile("^targetSdkVersion?:'(.*)'$", re.MULTILINE)
+
+ for match in re.finditer(pattern, badging):
+ return match.group(1)
+
+ raise RuntimeError('cannot find targetSdkVersion in the manifest')
+
+
+def extract_target_sdk_version_xml(xml):
+ """Extract targetSdkVersion tags from the manifest."""
+
+ manifest = parse_manifest(xml)
# Get or insert the uses-sdk element
uses_sdk = get_children_with_tag(manifest, 'uses-sdk')
@@ -187,24 +244,89 @@
return target_attr.value
+def load_dexpreopt_configs(configs):
+ """Load dexpreopt.config files and map module names to library names."""
+ module_to_libname = {}
+
+ if configs is None:
+ configs = []
+
+ for config in configs:
+ with open(config, 'r') as f:
+ contents = json.load(f)
+ module_to_libname[contents['Name']] = contents['ProvidesUsesLibrary']
+
+ return module_to_libname
+
+
+def translate_libnames(modules, module_to_libname):
+ """Translate module names into library names using the mapping."""
+ if modules is None:
+ modules = []
+
+ libnames = []
+ for name in modules:
+ if name in module_to_libname:
+ name = module_to_libname[name]
+ libnames.append(name)
+
+ return libnames
+
+
def main():
"""Program entry point."""
try:
args = parse_args()
- doc = minidom.parse(args.input)
+ # The input can be either an XML manifest or an APK, they are parsed and
+ # processed in different ways.
+ is_apk = args.input.endswith('.apk')
+ if is_apk:
+ aapt = args.aapt if args.aapt != None else "aapt"
+ manifest = subprocess.check_output([aapt, "dump", "badging", args.input])
+ else:
+ manifest = minidom.parse(args.input)
if args.enforce_uses_libraries:
- enforce_uses_libraries(doc,
- args.uses_libraries,
- args.optional_uses_libraries)
+ # Load dexpreopt.config files and build a mapping from module names to
+ # library names. This is necessary because build system addresses
+ # libraries by their module name (`uses_libs`, `optional_uses_libs`,
+ # `LOCAL_USES_LIBRARIES`, `LOCAL_OPTIONAL_LIBRARY_NAMES` all contain
+ # module names), while the manifest addresses libraries by their name.
+ mod_to_lib = load_dexpreopt_configs(args.dexpreopt_configs)
+ required = translate_libnames(args.uses_libraries, mod_to_lib)
+ optional = translate_libnames(args.optional_uses_libraries, mod_to_lib)
+
+ # Check if the <uses-library> lists in the build system agree with those
+ # in the manifest. Raise an exception on mismatch, unless the script was
+ # passed a special parameter to suppress exceptions.
+ errmsg = enforce_uses_libraries(manifest, required, optional,
+ args.enforce_uses_libraries_relax, is_apk, args.input)
+
+ # Create a status file that is empty on success, or contains an error
+ # message on failure. When exceptions are suppressed, dexpreopt command
+ # command will check file size to determine if the check has failed.
+ if args.enforce_uses_libraries_status:
+ with open(args.enforce_uses_libraries_status, 'w') as f:
+ if not errmsg == None:
+ f.write("%s\n" % errmsg)
if args.extract_target_sdk_version:
- print(extract_target_sdk_version(doc))
+ try:
+ print(extract_target_sdk_version(manifest, is_apk))
+ except:
+ # Failed; don't crash, return "any" SDK version. This will result in
+ # dexpreopt not adding any compatibility libraries.
+ print(10000)
if args.output:
+ # XML output is supposed to be written only when this script is invoked
+ # with XML input manifest, not with an APK.
+ if is_apk:
+ raise RuntimeError('cannot save APK manifest as XML')
+
with open(args.output, 'wb') as f:
- write_xml(f, doc)
+ write_xml(f, manifest)
# pylint: disable=broad-except
except Exception as err:
diff --git a/scripts/manifest_check_test.py b/scripts/manifest_check_test.py
index 7baad5d..7159bdd 100755
--- a/scripts/manifest_check_test.py
+++ b/scripts/manifest_check_test.py
@@ -25,26 +25,38 @@
sys.dont_write_bytecode = True
-def uses_library(name, attr=''):
+def uses_library_xml(name, attr=''):
return '<uses-library android:name="%s"%s />' % (name, attr)
-def required(value):
+def required_xml(value):
return ' android:required="%s"' % ('true' if value else 'false')
+def uses_library_apk(name, sfx=''):
+ return "uses-library%s:'%s'" % (sfx, name)
+
+
+def required_apk(value):
+ return '' if value else '-not-required'
+
+
class EnforceUsesLibrariesTest(unittest.TestCase):
"""Unit tests for add_extract_native_libs function."""
- def run_test(self, input_manifest, uses_libraries=None, optional_uses_libraries=None):
- doc = minidom.parseString(input_manifest)
+ def run_test(self, xml, apk, uses_libraries=[], optional_uses_libraries=[]):
+ doc = minidom.parseString(xml)
try:
- manifest_check.enforce_uses_libraries(doc, uses_libraries, optional_uses_libraries)
+ relax = False
+ manifest_check.enforce_uses_libraries(doc, uses_libraries,
+ optional_uses_libraries, relax, False, 'path/to/X/AndroidManifest.xml')
+ manifest_check.enforce_uses_libraries(apk, uses_libraries,
+ optional_uses_libraries, relax, True, 'path/to/X/X.apk')
return True
except manifest_check.ManifestMismatchError:
return False
- manifest_tmpl = (
+ xml_tmpl = (
'<?xml version="1.0" encoding="utf-8"?>\n'
'<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
' <application>\n'
@@ -52,115 +64,155 @@
' </application>\n'
'</manifest>\n')
+ apk_tmpl = (
+ "package: name='com.google.android.something' versionCode='100'\n"
+ "sdkVersion:'29'\n"
+ "targetSdkVersion:'29'\n"
+ "uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n"
+ "%s\n"
+ "densities: '160' '240' '320' '480' '640' '65534")
+
def test_uses_library(self):
- manifest_input = self.manifest_tmpl % (uses_library('foo'))
- matches = self.run_test(manifest_input, uses_libraries=['foo'])
+ xml = self.xml_tmpl % (uses_library_xml('foo'))
+ apk = self.apk_tmpl % (uses_library_apk('foo'))
+ matches = self.run_test(xml, apk, uses_libraries=['foo'])
self.assertTrue(matches)
def test_uses_library_required(self):
- manifest_input = self.manifest_tmpl % (uses_library('foo', required(True)))
- matches = self.run_test(manifest_input, uses_libraries=['foo'])
+ xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(True)))
+ apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(True)))
+ matches = self.run_test(xml, apk, uses_libraries=['foo'])
self.assertTrue(matches)
def test_optional_uses_library(self):
- manifest_input = self.manifest_tmpl % (uses_library('foo', required(False)))
- matches = self.run_test(manifest_input, optional_uses_libraries=['foo'])
+ xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False)))
+ apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False)))
+ matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
self.assertTrue(matches)
def test_expected_uses_library(self):
- manifest_input = self.manifest_tmpl % (uses_library('foo', required(False)))
- matches = self.run_test(manifest_input, uses_libraries=['foo'])
+ xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False)))
+ apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False)))
+ matches = self.run_test(xml, apk, uses_libraries=['foo'])
self.assertFalse(matches)
def test_expected_optional_uses_library(self):
- manifest_input = self.manifest_tmpl % (uses_library('foo'))
- matches = self.run_test(manifest_input, optional_uses_libraries=['foo'])
+ xml = self.xml_tmpl % (uses_library_xml('foo'))
+ apk = self.apk_tmpl % (uses_library_apk('foo'))
+ matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
self.assertFalse(matches)
def test_missing_uses_library(self):
- manifest_input = self.manifest_tmpl % ('')
- matches = self.run_test(manifest_input, uses_libraries=['foo'])
+ xml = self.xml_tmpl % ('')
+ apk = self.apk_tmpl % ('')
+ matches = self.run_test(xml, apk, uses_libraries=['foo'])
self.assertFalse(matches)
def test_missing_optional_uses_library(self):
- manifest_input = self.manifest_tmpl % ('')
- matches = self.run_test(manifest_input, optional_uses_libraries=['foo'])
+ xml = self.xml_tmpl % ('')
+ apk = self.apk_tmpl % ('')
+ matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
self.assertFalse(matches)
def test_extra_uses_library(self):
- manifest_input = self.manifest_tmpl % (uses_library('foo'))
- matches = self.run_test(manifest_input)
+ xml = self.xml_tmpl % (uses_library_xml('foo'))
+ apk = self.apk_tmpl % (uses_library_xml('foo'))
+ matches = self.run_test(xml, apk)
self.assertFalse(matches)
def test_extra_optional_uses_library(self):
- manifest_input = self.manifest_tmpl % (uses_library('foo', required(False)))
- matches = self.run_test(manifest_input)
+ xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False)))
+ apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False)))
+ matches = self.run_test(xml, apk)
self.assertFalse(matches)
def test_multiple_uses_library(self):
- manifest_input = self.manifest_tmpl % ('\n'.join([uses_library('foo'),
- uses_library('bar')]))
- matches = self.run_test(manifest_input, uses_libraries=['foo', 'bar'])
+ xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
+ uses_library_xml('bar')]))
+ apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
+ uses_library_apk('bar')]))
+ matches = self.run_test(xml, apk, uses_libraries=['foo', 'bar'])
self.assertTrue(matches)
def test_multiple_optional_uses_library(self):
- manifest_input = self.manifest_tmpl % ('\n'.join([uses_library('foo', required(False)),
- uses_library('bar', required(False))]))
- matches = self.run_test(manifest_input, optional_uses_libraries=['foo', 'bar'])
+ xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo', required_xml(False)),
+ uses_library_xml('bar', required_xml(False))]))
+ apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo', required_apk(False)),
+ uses_library_apk('bar', required_apk(False))]))
+ matches = self.run_test(xml, apk, optional_uses_libraries=['foo', 'bar'])
self.assertTrue(matches)
def test_order_uses_library(self):
- manifest_input = self.manifest_tmpl % ('\n'.join([uses_library('foo'),
- uses_library('bar')]))
- matches = self.run_test(manifest_input, uses_libraries=['bar', 'foo'])
+ xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
+ uses_library_xml('bar')]))
+ apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
+ uses_library_apk('bar')]))
+ matches = self.run_test(xml, apk, uses_libraries=['bar', 'foo'])
self.assertFalse(matches)
def test_order_optional_uses_library(self):
- manifest_input = self.manifest_tmpl % ('\n'.join([uses_library('foo', required(False)),
- uses_library('bar', required(False))]))
- matches = self.run_test(manifest_input, optional_uses_libraries=['bar', 'foo'])
+ xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo', required_xml(False)),
+ uses_library_xml('bar', required_xml(False))]))
+ apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo', required_apk(False)),
+ uses_library_apk('bar', required_apk(False))]))
+ matches = self.run_test(xml, apk, optional_uses_libraries=['bar', 'foo'])
self.assertFalse(matches)
def test_duplicate_uses_library(self):
- manifest_input = self.manifest_tmpl % ('\n'.join([uses_library('foo'),
- uses_library('foo')]))
- matches = self.run_test(manifest_input, uses_libraries=['foo'])
+ xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
+ uses_library_xml('foo')]))
+ apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
+ uses_library_apk('foo')]))
+ matches = self.run_test(xml, apk, uses_libraries=['foo'])
self.assertTrue(matches)
def test_duplicate_optional_uses_library(self):
- manifest_input = self.manifest_tmpl % ('\n'.join([uses_library('foo', required(False)),
- uses_library('foo', required(False))]))
- matches = self.run_test(manifest_input, optional_uses_libraries=['foo'])
+ xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo', required_xml(False)),
+ uses_library_xml('foo', required_xml(False))]))
+ apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo', required_apk(False)),
+ uses_library_apk('foo', required_apk(False))]))
+ matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
self.assertTrue(matches)
def test_mixed(self):
- manifest_input = self.manifest_tmpl % ('\n'.join([uses_library('foo'),
- uses_library('bar', required(False))]))
- matches = self.run_test(manifest_input, uses_libraries=['foo'],
+ xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
+ uses_library_xml('bar', required_xml(False))]))
+ apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
+ uses_library_apk('bar', required_apk(False))]))
+ matches = self.run_test(xml, apk, uses_libraries=['foo'],
optional_uses_libraries=['bar'])
self.assertTrue(matches)
class ExtractTargetSdkVersionTest(unittest.TestCase):
- def test_target_sdk_version(self):
- manifest = (
- '<?xml version="1.0" encoding="utf-8"?>\n'
- '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
- ' <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="29" />\n'
- '</manifest>\n')
- doc = minidom.parseString(manifest)
- target_sdk_version = manifest_check.extract_target_sdk_version(doc)
- self.assertEqual(target_sdk_version, '29')
+ def run_test(self, xml, apk, version):
+ doc = minidom.parseString(xml)
+ v = manifest_check.extract_target_sdk_version(doc, is_apk=False)
+ self.assertEqual(v, version)
+ v = manifest_check.extract_target_sdk_version(apk, is_apk=True)
+ self.assertEqual(v, version)
- def test_min_sdk_version(self):
- manifest = (
+ xml_tmpl = (
'<?xml version="1.0" encoding="utf-8"?>\n'
'<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
- ' <uses-sdk android:minSdkVersion="28" />\n'
+ ' <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="%s" />\n'
'</manifest>\n')
- doc = minidom.parseString(manifest)
- target_sdk_version = manifest_check.extract_target_sdk_version(doc)
- self.assertEqual(target_sdk_version, '28')
+
+ apk_tmpl = (
+ "package: name='com.google.android.something' versionCode='100'\n"
+ "sdkVersion:'28'\n"
+ "targetSdkVersion:'%s'\n"
+ "uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n")
+
+ def test_targert_sdk_version_28(self):
+ xml = self.xml_tmpl % "28"
+ apk = self.apk_tmpl % "28"
+ self.run_test(xml, apk, "28")
+
+ def test_targert_sdk_version_29(self):
+ xml = self.xml_tmpl % "29"
+ apk = self.apk_tmpl % "29"
+ self.run_test(xml, apk, "29")
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
index c59732b..55d0fd1 100755
--- a/scripts/manifest_fixer.py
+++ b/scripts/manifest_fixer.py
@@ -121,7 +121,7 @@
# is empty. Set it to something low so that it will be overriden by the
# main manifest, but high enough that it doesn't cause implicit
# permissions grants.
- target_attr.value = '15'
+ target_attr.value = '16'
else:
target_attr.value = target_sdk_version
element.setAttributeNode(target_attr)
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
index d6e7f26..3a0a25d 100755
--- a/scripts/manifest_fixer_test.py
+++ b/scripts/manifest_fixer_test.py
@@ -173,7 +173,7 @@
"""Tests inserting targetSdkVersion when minSdkVersion exists."""
manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
- expected = self.manifest_tmpl % self.uses_sdk(min='28', target='15')
+ expected = self.manifest_tmpl % self.uses_sdk(min='28', target='16')
output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
self.assertEqual(output, expected)
@@ -189,7 +189,7 @@
"""Tests inserting targetSdkVersion when minSdkVersion does not exist."""
manifest_input = self.manifest_tmpl % ''
- expected = self.manifest_tmpl % self.uses_sdk(min='28', target='15')
+ expected = self.manifest_tmpl % self.uses_sdk(min='28', target='16')
output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
self.assertEqual(output, expected)
diff --git a/scripts/strip.sh b/scripts/strip.sh
index 40f0184..e3e5273 100755
--- a/scripts/strip.sh
+++ b/scripts/strip.sh
@@ -18,7 +18,6 @@
# Inputs:
# Environment:
# CLANG_BIN: path to the clang bin directory
-# CROSS_COMPILE: prefix added to readelf, objcopy tools
# XZ: path to the xz binary
# Arguments:
# -i ${file}: input file (required)
@@ -69,7 +68,7 @@
KEEP_SYMBOLS="--strip-unneeded-symbol=* --keep-symbols="
KEEP_SYMBOLS+="${outfile}.symbolList"
- "${CROSS_COMPILE}objcopy" -w "${infile}" "${outfile}.tmp" ${KEEP_SYMBOLS}
+ "${CLANG_BIN}/llvm-objcopy" -w "${infile}" "${outfile}.tmp" ${KEEP_SYMBOLS}
}
do_strip_keep_mini_debug_info() {
@@ -78,18 +77,13 @@
"${CLANG_BIN}/llvm-strip" --strip-all --keep-section=.ARM.attributes --remove-section=.comment "${infile}" -o "${outfile}.tmp" || fail=true
if [ -z $fail ]; then
- # Current prebult llvm-objcopy does not support --only-keep-debug flag,
- # and cannot process object files that are produced with the flag. Use
- # GNU objcopy instead for now. (b/141010852)
- "${CROSS_COMPILE}objcopy" --only-keep-debug "${infile}" "${outfile}.debug"
+ "${CLANG_BIN}/llvm-objcopy" --only-keep-debug "${infile}" "${outfile}.debug"
"${CLANG_BIN}/llvm-nm" -D "${infile}" --format=posix --defined-only 2> /dev/null | awk '{ print $1 }' | sort >"${outfile}.dynsyms"
"${CLANG_BIN}/llvm-nm" "${infile}" --format=posix --defined-only | awk '{ if ($2 == "T" || $2 == "t" || $2 == "D") print $1 }' | sort > "${outfile}.funcsyms"
comm -13 "${outfile}.dynsyms" "${outfile}.funcsyms" > "${outfile}.keep_symbols"
echo >> "${outfile}.keep_symbols" # Ensure that the keep_symbols file is not empty.
- "${CROSS_COMPILE}objcopy" --rename-section .debug_frame=saved_debug_frame "${outfile}.debug" "${outfile}.mini_debuginfo"
- "${CROSS_COMPILE}objcopy" -S --remove-section .gdb_index --remove-section .comment --keep-symbols="${outfile}.keep_symbols" "${outfile}.mini_debuginfo"
- "${CROSS_COMPILE}objcopy" --rename-section saved_debug_frame=.debug_frame "${outfile}.mini_debuginfo"
- "${XZ}" "${outfile}.mini_debuginfo"
+ "${CLANG_BIN}/llvm-objcopy" -S --keep-section .debug_frame --keep-symbols="${outfile}.keep_symbols" "${outfile}.debug" "${outfile}.mini_debuginfo"
+ "${XZ}" --keep --block-size=64k --threads=0 "${outfile}.mini_debuginfo"
"${CLANG_BIN}/llvm-objcopy" --add-section .gnu_debugdata="${outfile}.mini_debuginfo.xz" "${outfile}.tmp"
rm -f "${outfile}.dynsyms" "${outfile}.funcsyms" "${outfile}.keep_symbols" "${outfile}.debug" "${outfile}.mini_debuginfo" "${outfile}.mini_debuginfo.xz"
@@ -196,7 +190,6 @@
cat <<EOF > "${depsfile}"
${outfile}: \
${infile} \
- ${CROSS_COMPILE}objcopy \
${CLANG_BIN}/llvm-nm \
${CLANG_BIN}/llvm-objcopy \
${CLANG_BIN}/llvm-readelf \
diff --git a/scripts/toc.sh b/scripts/toc.sh
index 8b1d25f..c6b7866 100755
--- a/scripts/toc.sh
+++ b/scripts/toc.sh
@@ -17,7 +17,7 @@
# Script to handle generating a .toc file from a .so file
# Inputs:
# Environment:
-# CROSS_COMPILE: prefix added to readelf tool
+# CLANG_BIN: path to the clang bin directory
# Arguments:
# -i ${file}: input file (required)
# -o ${file}: output file (required)
@@ -35,34 +35,34 @@
}
do_elf() {
- ("${CROSS_COMPILE}readelf" -d "${infile}" | grep SONAME || echo "No SONAME for ${infile}") > "${outfile}.tmp"
- "${CROSS_COMPILE}readelf" --dyn-syms "${infile}" | awk '{$2=""; $3=""; print}' >> "${outfile}.tmp"
+ ("${CLANG_BIN}/llvm-readelf" -d "${infile}" | grep SONAME || echo "No SONAME for ${infile}") > "${outfile}.tmp"
+ "${CLANG_BIN}/llvm-readelf" --dyn-syms "${infile}" | awk '{$2=""; $3=""; print}' >> "${outfile}.tmp"
cat <<EOF > "${depsfile}"
${outfile}: \\
- ${CROSS_COMPILE}readelf \\
+ ${CLANG_BIN}/llvm-readelf \\
EOF
}
do_macho() {
- "${CROSS_COMPILE}/otool" -l "${infile}" | grep LC_ID_DYLIB -A 5 > "${outfile}.tmp"
- "${CROSS_COMPILE}/nm" -gP "${infile}" | cut -f1-2 -d" " | (grep -v 'U$' >> "${outfile}.tmp" || true)
+ "${CLANG_BIN}/llvm-objdump" -p "${infile}" | grep LC_ID_DYLIB -A 5 > "${outfile}.tmp"
+ "${CLANG_BIN}/llvm-nm" -gP "${infile}" | cut -f1-2 -d" " | (grep -v 'U$' >> "${outfile}.tmp" || true)
cat <<EOF > "${depsfile}"
${outfile}: \\
- ${CROSS_COMPILE}/otool \\
- ${CROSS_COMPILE}/nm \\
+ ${CLANG_BIN}/llvm-objdump \\
+ ${CLANG_BIN}/llvm-nm \\
EOF
}
do_pe() {
- "${CROSS_COMPILE}objdump" -x "${infile}" | grep "^Name" | cut -f3 -d" " > "${outfile}.tmp"
- "${CROSS_COMPILE}nm" -g -f p "${infile}" | cut -f1-2 -d" " >> "${outfile}.tmp"
+ "${CLANG_BIN}/llvm-objdump" -x "${infile}" | grep "^Name" | cut -f3 -d" " > "${outfile}.tmp"
+ "${CLANG_BIN}/llvm-nm" -gP "${infile}" | cut -f1-2 -d" " >> "${outfile}.tmp"
cat <<EOF > "${depsfile}"
${outfile}: \\
- ${CROSS_COMPILE}objdump \\
- ${CROSS_COMPILE}nm \\
+ ${CLANG_BIN}/llvm-objdump \\
+ ${CLANG_BIN}/llvm-nm \\
EOF
}
@@ -98,8 +98,8 @@
usage
fi
-if [ -z "${CROSS_COMPILE:-}" ]; then
- echo "CROSS_COMPILE environment variable must be set"
+if [ -z "${CLANG_BIN:-}" ]; then
+ echo "CLANG_BIN environment variable must be set"
usage
fi
@@ -107,7 +107,7 @@
cat <<EOF > "${depsfile}"
${outfile}: \\
- ${CROSS_COMPILE}readelf \\
+ ${CLANG_BIN}/llvm-readelf \\
EOF
if [ -n "${elf:-}" ]; then
diff --git a/scripts/unpack-prebuilt-apex.sh b/scripts/unpack-prebuilt-apex.sh
new file mode 100755
index 0000000..1acdeb5
--- /dev/null
+++ b/scripts/unpack-prebuilt-apex.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+set -eu
+
+# Copyright 2020 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.
+
+# Tool to unpack an apex file and verify that the required files were extracted.
+if [ $# -lt 5 ]; then
+ echo "usage: $0 <deapaxer_path> <debugfs_path> <apex file> <output_dir> <required_files>+" >&2
+ exit 1
+fi
+
+DEAPEXER_PATH=$1
+DEBUGFS_PATH=$2
+APEX_FILE=$3
+OUTPUT_DIR=$4
+shift 4
+REQUIRED_PATHS=$@
+
+set -x 1
+
+rm -fr $OUTPUT_DIR
+mkdir -p $OUTPUT_DIR
+
+# Unpack the apex file contents.
+$DEAPEXER_PATH --debugfs_path $DEBUGFS_PATH extract $APEX_FILE $OUTPUT_DIR
+
+# Verify that the files that the build expects to be in the .apex file actually
+# exist, and make sure they have a fresh mtime to not confuse ninja.
+typeset -i FAILED=0
+for r in $REQUIRED_PATHS; do
+ if [ ! -f $r ]; then
+ echo "Required file $r not present in apex $APEX_FILE" >&2
+ FAILED=$FAILED+1
+ else
+ # TODO(http:/b/177646343) - deapexer extracts the files with a timestamp of 1 Jan 1970.
+ # touch the file so that ninja knows it has changed.
+ touch $r
+ fi
+done
+
+if [ $FAILED -gt 0 ]; then
+ echo "$FAILED required files were missing from $APEX_FILE" >&2
+ echo "Available files are:" >&2
+ find $OUTPUT_DIR -type f | sed "s|^| |" >&2
+ exit 1
+fi
diff --git a/scripts/update-apex-allowed-deps.sh b/scripts/update-apex-allowed-deps.sh
deleted file mode 100755
index 872d746..0000000
--- a/scripts/update-apex-allowed-deps.sh
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/bash -e
-#
-# The script to run locally to re-generate global allowed list of dependencies
-# for updatable modules.
-
-if [ ! -e "build/envsetup.sh" ]; then
- echo "ERROR: $0 must be run from the top of the tree"
- exit 1
-fi
-
-source build/envsetup.sh > /dev/null || exit 1
-
-readonly OUT_DIR=$(get_build_var OUT_DIR)
-
-readonly ALLOWED_DEPS_FILE="build/soong/apex/allowed_deps.txt"
-readonly NEW_ALLOWED_DEPS_FILE="${OUT_DIR}/soong/apex/depsinfo/new-allowed-deps.txt"
-
-# If the script is run after droidcore failure, ${NEW_ALLOWED_DEPS_FILE}
-# should already be built. If running the script manually, make sure it exists.
-m "${NEW_ALLOWED_DEPS_FILE}" -j
-
-cat > "${ALLOWED_DEPS_FILE}" << EndOfFileComment
-# A list of allowed dependencies for all updatable modules.
-#
-# The list tracks all direct and transitive dependencies that end up within any
-# of the updatable binaries; specifically excluding external dependencies
-# required to compile those binaries. This prevents potential regressions in
-# case a new dependency is not aware of the different functional and
-# non-functional requirements being part of an updatable module, for example
-# setting correct min_sdk_version.
-#
-# To update the list, run:
-# repo-root$ build/soong/scripts/update-apex-allowed-deps.sh
-#
-# See go/apex-allowed-deps-error for more details.
-# TODO(b/157465465): introduce automated quality signals and remove this list.
-EndOfFileComment
-
-cat "${NEW_ALLOWED_DEPS_FILE}" >> "${ALLOWED_DEPS_FILE}"
diff --git a/sdk/Android.bp b/sdk/Android.bp
index cb93351..7b034e6 100644
--- a/sdk/Android.bp
+++ b/sdk/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-sdk",
pkgPath: "android/soong/sdk",
@@ -16,8 +20,10 @@
"update.go",
],
testSrcs: [
+ "boot_image_sdk_test.go",
"bp_test.go",
"cc_sdk_test.go",
+ "compat_config_sdk_test.go",
"exports_test.go",
"java_sdk_test.go",
"sdk_test.go",
diff --git a/sdk/boot_image_sdk_test.go b/sdk/boot_image_sdk_test.go
new file mode 100644
index 0000000..5a03e34
--- /dev/null
+++ b/sdk/boot_image_sdk_test.go
@@ -0,0 +1,105 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sdk
+
+import (
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestSnapshotWithBootImage(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ android.FixtureWithRootAndroidBp(`
+ sdk {
+ name: "mysdk",
+ boot_images: ["mybootimage"],
+ }
+
+ boot_image {
+ name: "mybootimage",
+ image_name: "art",
+ }
+ `),
+ ).RunTest(t)
+
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_boot_image {
+ name: "mybootimage",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ image_name: "art",
+}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_boot_image {
+ name: "mysdk_mybootimage@current",
+ sdk_member_name: "mybootimage",
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ image_name: "art",
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ visibility: ["//visibility:public"],
+ boot_images: ["mysdk_mybootimage@current"],
+}
+`),
+ checkAllCopyRules(""))
+}
+
+// Test that boot_image works with sdk.
+func TestBasicSdkWithBootImage(t *testing.T) {
+ android.GroupFixturePreparers(
+ prepareForSdkTestWithApex,
+ prepareForSdkTestWithJava,
+ android.FixtureWithRootAndroidBp(`
+ sdk {
+ name: "mysdk",
+ boot_images: ["mybootimage"],
+ }
+
+ boot_image {
+ name: "mybootimage",
+ image_name: "art",
+ apex_available: ["myapex"],
+ }
+
+ sdk_snapshot {
+ name: "mysdk@1",
+ boot_images: ["mybootimage_mysdk_1"],
+ }
+
+ prebuilt_boot_image {
+ name: "mybootimage_mysdk_1",
+ sdk_member_name: "mybootimage",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: [
+ "myapex",
+ ],
+ image_name: "art",
+ }
+ `),
+ ).RunTest(t)
+}
diff --git a/sdk/bp_test.go b/sdk/bp_test.go
index e1edc51..c620ac2 100644
--- a/sdk/bp_test.go
+++ b/sdk/bp_test.go
@@ -54,26 +54,25 @@
return str
}
-func checkPropertySetFixture(h *TestHelper, val interface{}, hasTags bool) {
+func checkPropertySetFixture(t *testing.T, val interface{}, hasTags bool) {
set := val.(*bpPropertySet)
- h.AssertDeepEquals("wrong x value", "taxi", set.getValue("x"))
- h.AssertDeepEquals("wrong y value", 1729, set.getValue("y"))
+ android.AssertDeepEquals(t, "wrong x value", "taxi", set.getValue("x"))
+ android.AssertDeepEquals(t, "wrong y value", 1729, set.getValue("y"))
subset := set.getValue("sub").(*bpPropertySet)
- h.AssertDeepEquals("wrong sub.x value", "taxi", subset.getValue("x"))
- h.AssertDeepEquals("wrong sub.y value", 1729, subset.getValue("y"))
+ android.AssertDeepEquals(t, "wrong sub.x value", "taxi", subset.getValue("x"))
+ android.AssertDeepEquals(t, "wrong sub.y value", 1729, subset.getValue("y"))
if hasTags {
- h.AssertDeepEquals("wrong y tag", "tag_y", set.getTag("y"))
- h.AssertDeepEquals("wrong sub.x tag", "tag_x", subset.getTag("x"))
+ android.AssertDeepEquals(t, "wrong y tag", "tag_y", set.getTag("y"))
+ android.AssertDeepEquals(t, "wrong sub.x tag", "tag_x", subset.getTag("x"))
} else {
- h.AssertDeepEquals("wrong y tag", nil, set.getTag("y"))
- h.AssertDeepEquals("wrong sub.x tag", nil, subset.getTag("x"))
+ android.AssertDeepEquals(t, "wrong y tag", nil, set.getTag("y"))
+ android.AssertDeepEquals(t, "wrong sub.x tag", nil, subset.getTag("x"))
}
}
func TestAddPropertySimple(t *testing.T) {
- h := &TestHelper{t}
set := newPropertySet()
for name, val := range map[string]interface{}{
"x": "taxi",
@@ -83,16 +82,15 @@
"arr": []string{"a", "b", "c"},
} {
set.AddProperty(name, val)
- h.AssertDeepEquals("wrong value", val, set.getValue(name))
+ android.AssertDeepEquals(t, "wrong value", val, set.getValue(name))
}
- h.AssertPanic("adding x again should panic",
+ android.AssertPanicMessageContains(t, "adding x again should panic", `Property "x" already exists in property set`,
func() { set.AddProperty("x", "taxi") })
- h.AssertPanic("adding arr again should panic",
+ android.AssertPanicMessageContains(t, "adding arr again should panic", `Property "arr" already exists in property set`,
func() { set.AddProperty("arr", []string{"d"}) })
}
func TestAddPropertySubset(t *testing.T) {
- h := &TestHelper{t}
getFixtureMap := map[string]func() interface{}{
"property set": propertySetFixture,
"property struct": propertyStructFixture,
@@ -103,8 +101,8 @@
t.Run(name, func(t *testing.T) {
set := propertySetFixture().(*bpPropertySet)
set.AddProperty("new", getFixture())
- checkPropertySetFixture(h, set, true)
- checkPropertySetFixture(h, set.getValue("new"), name == "property set")
+ checkPropertySetFixture(t, set, true)
+ checkPropertySetFixture(t, set.getValue("new"), name == "property set")
})
}
})
@@ -118,40 +116,38 @@
subset.AddPropertySet("sub")
set.AddProperty("sub", getFixture())
merged := set.getValue("sub").(*bpPropertySet)
- h.AssertDeepEquals("wrong flag value", false, merged.getValue("flag"))
- checkPropertySetFixture(h, merged, name == "property set")
+ android.AssertDeepEquals(t, "wrong flag value", false, merged.getValue("flag"))
+ checkPropertySetFixture(t, merged, name == "property set")
})
}
})
t.Run("add conflicting subset", func(t *testing.T) {
set := propertySetFixture().(*bpPropertySet)
- h.AssertPanic("adding x again should panic",
+ android.AssertPanicMessageContains(t, "adding x again should panic", `Property "x" already exists in property set`,
func() { set.AddProperty("x", propertySetFixture()) })
})
t.Run("add non-pointer struct", func(t *testing.T) {
set := propertySetFixture().(*bpPropertySet)
str := propertyStructFixture().(*propertyStruct)
- h.AssertPanic("adding a non-pointer struct should panic",
+ android.AssertPanicMessageContains(t, "adding a non-pointer struct should panic", "Value is a struct, not a pointer to one:",
func() { set.AddProperty("new", *str) })
})
}
func TestAddPropertySetNew(t *testing.T) {
- h := &TestHelper{t}
set := newPropertySet()
subset := set.AddPropertySet("sub")
subset.AddProperty("new", "d^^b")
- h.AssertDeepEquals("wrong sub.new value", "d^^b", set.getValue("sub").(*bpPropertySet).getValue("new"))
+ android.AssertDeepEquals(t, "wrong sub.new value", "d^^b", set.getValue("sub").(*bpPropertySet).getValue("new"))
}
func TestAddPropertySetExisting(t *testing.T) {
- h := &TestHelper{t}
set := propertySetFixture().(*bpPropertySet)
subset := set.AddPropertySet("sub")
subset.AddProperty("new", "d^^b")
- h.AssertDeepEquals("wrong sub.new value", "d^^b", set.getValue("sub").(*bpPropertySet).getValue("new"))
+ android.AssertDeepEquals(t, "wrong sub.new value", "d^^b", set.getValue("sub").(*bpPropertySet).getValue("new"))
}
type removeFredTransformation struct {
@@ -180,9 +176,6 @@
}
func TestTransformRemoveProperty(t *testing.T) {
-
- helper := &TestHelper{t}
-
set := newPropertySet()
set.AddProperty("name", "name")
set.AddProperty("fred", "12")
@@ -191,13 +184,10 @@
contents := &generatedContents{}
outputPropertySet(contents, set)
- helper.AssertTrimmedStringEquals("removing property failed", "name: \"name\",\n", contents.content.String())
+ android.AssertTrimmedStringEquals(t, "removing property failed", "name: \"name\",\n", contents.content.String())
}
func TestTransformRemovePropertySet(t *testing.T) {
-
- helper := &TestHelper{t}
-
set := newPropertySet()
set.AddProperty("name", "name")
set.AddPropertySet("fred")
@@ -206,5 +196,5 @@
contents := &generatedContents{}
outputPropertySet(contents, set)
- helper.AssertTrimmedStringEquals("removing property set failed", "name: \"name\",\n", contents.content.String())
+ android.AssertTrimmedStringEquals(t, "removing property set failed", "name: \"name\",\n", contents.content.String())
}
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index b1eebe9..b19fcc5 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -21,18 +21,18 @@
"android/soong/cc"
)
-var ccTestFs = map[string][]byte{
- "Test.cpp": nil,
- "include/Test.h": nil,
- "include-android/AndroidTest.h": nil,
- "include-host/HostTest.h": nil,
- "arm64/include/Arm64Test.h": nil,
- "libfoo.so": nil,
- "aidl/foo/bar/Test.aidl": nil,
- "some/where/stubslib.map.txt": nil,
+var ccTestFs = android.MockFS{
+ "Test.cpp": nil,
+ "myinclude/Test.h": nil,
+ "myinclude-android/AndroidTest.h": nil,
+ "myinclude-host/HostTest.h": nil,
+ "arm64/include/Arm64Test.h": nil,
+ "libfoo.so": nil,
+ "aidl/foo/bar/Test.aidl": nil,
+ "some/where/stubslib.map.txt": nil,
}
-func testSdkWithCc(t *testing.T, bp string) *testSdkResult {
+func testSdkWithCc(t *testing.T, bp string) *android.TestResult {
t.Helper()
return testSdkWithFs(t, bp, ccTestFs)
}
@@ -101,17 +101,16 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
- name: "mysdk_sdkmember@current",
- sdk_member_name: "sdkmember",
+ name: "sdkmember",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
host_supported: true,
- installable: false,
stl: "none",
compile_multilib: "64",
target: {
@@ -127,13 +126,17 @@
},
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
- name: "sdkmember",
- prefer: false,
+ name: "mysdk_sdkmember@current",
+ sdk_member_name: "sdkmember",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
host_supported: true,
+ installable: false,
stl: "none",
compile_multilib: "64",
target: {
@@ -248,6 +251,7 @@
uses_sdks: ["mysdk@1"],
key: "myapex.key",
certificate: ":myapex.cert",
+ updatable: false,
}
apex {
@@ -256,6 +260,7 @@
uses_sdks: ["mysdk@2"],
key: "myapex.key",
certificate: ":myapex.cert",
+ updatable: false,
}
apex {
@@ -263,6 +268,7 @@
native_shared_libs: ["sdkmember"],
key: "myapex.key",
certificate: ":myapex.cert",
+ updatable: false,
}
`)
@@ -347,13 +353,13 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_object {
- name: "mysdk_crtobj@current",
- sdk_member_name: "crtobj",
+ name: "crtobj",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
stl: "none",
@@ -370,10 +376,14 @@
},
},
}
+`),
+ // Make sure that the generated sdk_snapshot uses the native_objects property.
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
cc_prebuilt_object {
- name: "crtobj",
- prefer: false,
+ name: "mysdk_crtobj@current",
+ sdk_member_name: "crtobj",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
stl: "none",
@@ -416,7 +426,7 @@
srcs: [
"Test.cpp",
],
- export_include_dirs: ["include"],
+ export_include_dirs: ["myinclude"],
stl: "none",
}
@@ -425,14 +435,14 @@
srcs: [
"Test.cpp",
],
- export_include_dirs: ["include"],
+ export_include_dirs: ["myinclude"],
stl: "none",
}
`)
- result.CheckSnapshot("mysdk", "",
+ CheckSnapshot(t, result, "mysdk", "",
checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
.intermediates/mynativelib1/android_arm64_armv8-a_shared/mynativelib1.so -> arm64/lib/mynativelib1.so
.intermediates/mynativelib1/android_arm_armv7-a-neon_shared/mynativelib1.so -> arm/lib/mynativelib1.so
.intermediates/mynativelib2/android_arm64_armv8-a_shared/mynativelib2.so -> arm64/lib/mynativelib2.so
@@ -441,6 +451,82 @@
)
}
+func TestSnapshotWithCcExportGeneratedHeaders(t *testing.T) {
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ native_shared_libs: ["mynativelib"],
+ }
+
+ cc_library_shared {
+ name: "mynativelib",
+ srcs: [
+ "Test.cpp",
+ ],
+ generated_headers: [
+ "generated_foo",
+ ],
+ export_generated_headers: [
+ "generated_foo",
+ ],
+ export_include_dirs: ["myinclude"],
+ stl: "none",
+ }
+
+ genrule {
+ name: "generated_foo",
+ cmd: "generate-foo",
+ out: [
+ "generated_foo/protos/foo/bar.h",
+ ],
+ export_include_dirs: [
+ ".",
+ "protos",
+ ],
+ }
+ `)
+
+ // TODO(b/183322862): Remove this and fix the issue.
+ errorHandler := android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module source path "snapshot/include_gen/generated_foo/gen/protos" does not exist`)
+
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+ name: "mynativelib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ stl: "none",
+ compile_multilib: "both",
+ export_include_dirs: [
+ "include/myinclude",
+ "include_gen/generated_foo/gen",
+ "include_gen/generated_foo/gen/protos",
+ ],
+ arch: {
+ arm64: {
+ srcs: ["arm64/lib/mynativelib.so"],
+ },
+ arm: {
+ srcs: ["arm/lib/mynativelib.so"],
+ },
+ },
+}
+`),
+ checkAllCopyRules(`
+myinclude/Test.h -> include/myinclude/Test.h
+.intermediates/generated_foo/gen/generated_foo/protos/foo/bar.h -> include_gen/generated_foo/gen/generated_foo/protos/foo/bar.h
+.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
+`),
+ snapshotTestErrorHandler(checkSnapshotWithoutSource, errorHandler),
+ snapshotTestErrorHandler(checkSnapshotWithSourcePreferred, errorHandler),
+ snapshotTestErrorHandler(checkSnapshotPreferredWithSource, errorHandler),
+ )
+}
+
// Verify that when the shared library has some common and some arch specific
// properties that the generated snapshot is optimized properly. Substruct
// handling is tested with the sanitize clauses (but note there's a lot of
@@ -458,7 +544,7 @@
"Test.cpp",
"aidl/foo/bar/Test.aidl",
],
- export_include_dirs: ["include"],
+ export_include_dirs: ["myinclude"],
sanitize: {
fuzzer: false,
integer_overflow: true,
@@ -476,50 +562,18 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
- name: "mysdk_mynativelib@current",
- sdk_member_name: "mynativelib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- installable: false,
- stl: "none",
- compile_multilib: "both",
- export_include_dirs: ["include/include"],
- sanitize: {
- fuzzer: false,
- diag: {
- undefined: false,
- },
- },
- arch: {
- arm64: {
- srcs: ["arm64/lib/mynativelib.so"],
- export_system_include_dirs: ["arm64/include/arm64/include"],
- sanitize: {
- integer_overflow: false,
- },
- },
- arm: {
- srcs: ["arm/lib/mynativelib.so"],
- sanitize: {
- integer_overflow: true,
- },
- },
- },
-}
-
-cc_prebuilt_library_shared {
name: "mynativelib",
prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
stl: "none",
compile_multilib: "both",
- export_include_dirs: ["include/include"],
+ export_include_dirs: ["include/myinclude"],
sanitize: {
fuzzer: false,
diag: {
@@ -542,15 +596,9 @@
},
},
}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- native_shared_libs: ["mysdk_mynativelib@current"],
-}
`),
checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h
.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
@@ -573,16 +621,15 @@
}
`)
- result.CheckSnapshot("mymodule_exports", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mymodule_exports", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_binary {
- name: "mymodule_exports_mynativebinary@current",
- sdk_member_name: "mynativebinary",
+ name: "mynativebinary",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
- installable: false,
compile_multilib: "both",
arch: {
arm64: {
@@ -593,12 +640,17 @@
},
},
}
+`),
+ // Make sure that the generated sdk_snapshot uses the native_binaries property.
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
cc_prebuilt_binary {
- name: "mynativebinary",
- prefer: false,
+ name: "mymodule_exports_mynativebinary@current",
+ sdk_member_name: "mynativebinary",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
+ installable: false,
compile_multilib: "both",
arch: {
arm64: {
@@ -654,18 +706,17 @@
}
`)
- result.CheckSnapshot("myexports", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "myexports", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_binary {
- name: "myexports_mynativebinary@current",
- sdk_member_name: "mynativebinary",
+ name: "mynativebinary",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
- installable: false,
stl: "none",
target: {
host: {
@@ -691,14 +742,18 @@
},
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
cc_prebuilt_binary {
- name: "mynativebinary",
- prefer: false,
+ name: "myexports_mynativebinary@current",
+ sdk_member_name: "mynativebinary",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
+ installable: false,
stl: "none",
target: {
host: {
@@ -759,7 +814,16 @@
}
func TestSnapshotWithSingleHostOsType(t *testing.T) {
- ctx, config := testSdkContext(`
+ result := android.GroupFixturePreparers(
+ prepareForSdkTest,
+ ccTestFs.AddToFixture(),
+ cc.PrepareForTestOnLinuxBionic,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.Targets[android.LinuxBionic] = []android.Target{
+ {android.LinuxBionic, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", false},
+ }
+ }),
+ ).RunTestWithBp(t, `
cc_defaults {
name: "mydefaults",
device_supported: false,
@@ -800,12 +864,53 @@
],
stl: "none",
}
- `, ccTestFs, []android.OsType{android.LinuxBionic})
+ `)
- result := runTests(t, ctx, config)
+ CheckSnapshot(t, result, "myexports", "",
+ checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
- result.CheckSnapshot("myexports", "",
- checkAndroidBpContents(`
+cc_prebuilt_binary {
+ name: "mynativebinary",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ device_supported: false,
+ host_supported: true,
+ stl: "none",
+ compile_multilib: "64",
+ target: {
+ host: {
+ enabled: false,
+ },
+ linux_bionic_x86_64: {
+ enabled: true,
+ srcs: ["x86_64/bin/mynativebinary"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "mynativelib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ device_supported: false,
+ host_supported: true,
+ stl: "none",
+ compile_multilib: "64",
+ target: {
+ host: {
+ enabled: false,
+ },
+ linux_bionic_x86_64: {
+ enabled: true,
+ srcs: ["x86_64/lib/mynativelib.so"],
+ },
+ },
+}
+`),
+ checkVersionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_binary {
@@ -829,26 +934,6 @@
},
}
-cc_prebuilt_binary {
- name: "mynativebinary",
- prefer: false,
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- stl: "none",
- compile_multilib: "64",
- target: {
- host: {
- enabled: false,
- },
- linux_bionic_x86_64: {
- enabled: true,
- srcs: ["x86_64/bin/mynativebinary"],
- },
- },
-}
-
cc_prebuilt_library_shared {
name: "myexports_mynativelib@current",
sdk_member_name: "mynativelib",
@@ -870,26 +955,6 @@
},
}
-cc_prebuilt_library_shared {
- name: "mynativelib",
- prefer: false,
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- stl: "none",
- compile_multilib: "64",
- target: {
- host: {
- enabled: false,
- },
- linux_bionic_x86_64: {
- enabled: true,
- srcs: ["x86_64/lib/mynativelib.so"],
- },
- },
-}
-
module_exports_snapshot {
name: "myexports@current",
visibility: ["//visibility:public"],
@@ -939,18 +1004,17 @@
}
`)
- result.CheckSnapshot("mymodule_exports", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mymodule_exports", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_binary {
- name: "mymodule_exports_linker@current",
- sdk_member_name: "linker",
+ name: "linker",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
- installable: false,
stl: "none",
compile_multilib: "both",
static_executable: true,
@@ -969,14 +1033,18 @@
},
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
cc_prebuilt_binary {
- name: "linker",
- prefer: false,
+ name: "mymodule_exports_linker@current",
+ sdk_member_name: "linker",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
+ installable: false,
stl: "none",
compile_multilib: "both",
static_executable: true,
@@ -1036,7 +1104,7 @@
"aidl/foo/bar/Test.aidl",
],
apex_available: ["apex1", "apex2"],
- export_include_dirs: ["include"],
+ export_include_dirs: ["myinclude"],
aidl: {
export_aidl_headers: true,
},
@@ -1044,35 +1112,11 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
- name: "mysdk_mynativelib@current",
- sdk_member_name: "mynativelib",
- visibility: ["//visibility:public"],
- apex_available: [
- "apex1",
- "apex2",
- ],
- installable: false,
- stl: "none",
- compile_multilib: "both",
- export_include_dirs: ["include/include"],
- arch: {
- arm64: {
- srcs: ["arm64/lib/mynativelib.so"],
- export_include_dirs: ["arm64/include_gen/mynativelib"],
- },
- arm: {
- srcs: ["arm/lib/mynativelib.so"],
- export_include_dirs: ["arm/include_gen/mynativelib"],
- },
- },
-}
-
-cc_prebuilt_library_shared {
name: "mynativelib",
prefer: false,
visibility: ["//visibility:public"],
@@ -1082,35 +1126,29 @@
],
stl: "none",
compile_multilib: "both",
- export_include_dirs: ["include/include"],
+ export_include_dirs: ["include/myinclude"],
arch: {
arm64: {
srcs: ["arm64/lib/mynativelib.so"],
- export_include_dirs: ["arm64/include_gen/mynativelib"],
+ export_include_dirs: ["arm64/include_gen/mynativelib/android_arm64_armv8-a_shared/gen/aidl"],
},
arm: {
srcs: ["arm/lib/mynativelib.so"],
- export_include_dirs: ["arm/include_gen/mynativelib"],
+ export_include_dirs: ["arm/include_gen/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl"],
},
},
}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- native_shared_libs: ["mysdk_mynativelib@current"],
-}
`),
checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
-.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BpTest.h
.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
-.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BpTest.h
`),
)
}
@@ -1175,33 +1213,11 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
- name: "mysdk_mynativelib@current",
- sdk_member_name: "mynativelib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- installable: false,
- stl: "none",
- compile_multilib: "both",
- shared_libs: [
- "mysdk_myothernativelib@current",
- "libc",
- ],
- arch: {
- arm64: {
- srcs: ["arm64/lib/mynativelib.so"],
- },
- arm: {
- srcs: ["arm/lib/mynativelib.so"],
- },
- },
-}
-
-cc_prebuilt_library_shared {
name: "mynativelib",
prefer: false,
visibility: ["//visibility:public"],
@@ -1223,25 +1239,6 @@
}
cc_prebuilt_library_shared {
- name: "mysdk_myothernativelib@current",
- sdk_member_name: "myothernativelib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- installable: false,
- stl: "none",
- compile_multilib: "both",
- system_shared_libs: ["libm"],
- arch: {
- arm64: {
- srcs: ["arm64/lib/myothernativelib.so"],
- },
- arm: {
- srcs: ["arm/lib/myothernativelib.so"],
- },
- },
-}
-
-cc_prebuilt_library_shared {
name: "myothernativelib",
prefer: false,
visibility: ["//visibility:public"],
@@ -1260,24 +1257,6 @@
}
cc_prebuilt_library_shared {
- name: "mysdk_mysystemnativelib@current",
- sdk_member_name: "mysystemnativelib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- installable: false,
- stl: "none",
- compile_multilib: "both",
- arch: {
- arm64: {
- srcs: ["arm64/lib/mysystemnativelib.so"],
- },
- arm: {
- srcs: ["arm/lib/mysystemnativelib.so"],
- },
- },
-}
-
-cc_prebuilt_library_shared {
name: "mysystemnativelib",
prefer: false,
visibility: ["//visibility:public"],
@@ -1293,16 +1272,6 @@
},
},
}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- native_shared_libs: [
- "mysdk_mynativelib@current",
- "mysdk_myothernativelib@current",
- "mysdk_mysystemnativelib@current",
- ],
-}
`),
checkAllCopyRules(`
.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
@@ -1332,7 +1301,7 @@
"Test.cpp",
"aidl/foo/bar/Test.aidl",
],
- export_include_dirs: ["include"],
+ export_include_dirs: ["myinclude"],
aidl: {
export_aidl_headers: true,
},
@@ -1341,8 +1310,39 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+ name: "mynativelib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ device_supported: false,
+ host_supported: true,
+ sdk_version: "minimum",
+ stl: "none",
+ compile_multilib: "both",
+ export_include_dirs: ["include/myinclude"],
+ target: {
+ host: {
+ enabled: false,
+ },
+ linux_glibc_x86_64: {
+ enabled: true,
+ srcs: ["x86_64/lib/mynativelib.so"],
+ export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl"],
+ },
+ linux_glibc_x86: {
+ enabled: true,
+ srcs: ["x86/lib/mynativelib.so"],
+ export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl"],
+ },
+ },
+}
+`),
+ checkVersionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
@@ -1356,7 +1356,7 @@
sdk_version: "minimum",
stl: "none",
compile_multilib: "both",
- export_include_dirs: ["include/include"],
+ export_include_dirs: ["include/myinclude"],
target: {
host: {
enabled: false,
@@ -1364,40 +1364,12 @@
linux_glibc_x86_64: {
enabled: true,
srcs: ["x86_64/lib/mynativelib.so"],
- export_include_dirs: ["x86_64/include_gen/mynativelib"],
+ export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl"],
},
linux_glibc_x86: {
enabled: true,
srcs: ["x86/lib/mynativelib.so"],
- export_include_dirs: ["x86/include_gen/mynativelib"],
- },
- },
-}
-
-cc_prebuilt_library_shared {
- name: "mynativelib",
- prefer: false,
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- sdk_version: "minimum",
- stl: "none",
- compile_multilib: "both",
- export_include_dirs: ["include/include"],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- srcs: ["x86_64/lib/mynativelib.so"],
- export_include_dirs: ["x86_64/include_gen/mynativelib"],
- },
- linux_glibc_x86: {
- enabled: true,
- srcs: ["x86/lib/mynativelib.so"],
- export_include_dirs: ["x86/include_gen/mynativelib"],
+ export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl"],
},
},
}
@@ -1422,15 +1394,15 @@
}
`),
checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
.intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> x86_64/lib/mynativelib.so
-.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BpTest.h
.intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> x86/lib/mynativelib.so
-.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/Test.h -> x86/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BnTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BpTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/Test.h -> x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BnTest.h -> x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BpTest.h -> x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BpTest.h
`),
)
}
@@ -1465,18 +1437,17 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
- name: "mysdk_mynativelib@current",
- sdk_member_name: "mynativelib",
+ name: "mynativelib",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
- installable: false,
stl: "none",
target: {
host: {
@@ -1502,14 +1473,18 @@
},
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
- name: "mynativelib",
- prefer: false,
+ name: "mysdk_mynativelib@current",
+ sdk_member_name: "mynativelib",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
+ installable: false,
stl: "none",
target: {
host: {
@@ -1582,7 +1557,7 @@
"Test.cpp",
"aidl/foo/bar/Test.aidl",
],
- export_include_dirs: ["include"],
+ export_include_dirs: ["myinclude"],
aidl: {
export_aidl_headers: true,
},
@@ -1590,67 +1565,40 @@
}
`)
- result.CheckSnapshot("myexports", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "myexports", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_static {
- name: "myexports_mynativelib@current",
- sdk_member_name: "mynativelib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- installable: false,
- stl: "none",
- compile_multilib: "both",
- export_include_dirs: ["include/include"],
- arch: {
- arm64: {
- srcs: ["arm64/lib/mynativelib.a"],
- export_include_dirs: ["arm64/include_gen/mynativelib"],
- },
- arm: {
- srcs: ["arm/lib/mynativelib.a"],
- export_include_dirs: ["arm/include_gen/mynativelib"],
- },
- },
-}
-
-cc_prebuilt_library_static {
name: "mynativelib",
prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
stl: "none",
compile_multilib: "both",
- export_include_dirs: ["include/include"],
+ export_include_dirs: ["include/myinclude"],
arch: {
arm64: {
srcs: ["arm64/lib/mynativelib.a"],
- export_include_dirs: ["arm64/include_gen/mynativelib"],
+ export_include_dirs: ["arm64/include_gen/mynativelib/android_arm64_armv8-a_static/gen/aidl"],
},
arm: {
srcs: ["arm/lib/mynativelib.a"],
- export_include_dirs: ["arm/include_gen/mynativelib"],
+ export_include_dirs: ["arm/include_gen/mynativelib/android_arm_armv7-a-neon_static/gen/aidl"],
},
},
}
-
-module_exports_snapshot {
- name: "myexports@current",
- visibility: ["//visibility:public"],
- native_static_libs: ["myexports_mynativelib@current"],
-}
`),
checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
.intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> arm64/lib/mynativelib.a
-.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BpTest.h
.intermediates/mynativelib/android_arm_armv7-a-neon_static/mynativelib.a -> arm/lib/mynativelib.a
-.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BpTest.h
`),
)
}
@@ -1672,7 +1620,7 @@
"Test.cpp",
"aidl/foo/bar/Test.aidl",
],
- export_include_dirs: ["include"],
+ export_include_dirs: ["myinclude"],
aidl: {
export_aidl_headers: true,
},
@@ -1680,8 +1628,38 @@
}
`)
- result.CheckSnapshot("myexports", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "myexports", "",
+ checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_static {
+ name: "mynativelib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ device_supported: false,
+ host_supported: true,
+ stl: "none",
+ compile_multilib: "both",
+ export_include_dirs: ["include/myinclude"],
+ target: {
+ host: {
+ enabled: false,
+ },
+ linux_glibc_x86_64: {
+ enabled: true,
+ srcs: ["x86_64/lib/mynativelib.a"],
+ export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl"],
+ },
+ linux_glibc_x86: {
+ enabled: true,
+ srcs: ["x86/lib/mynativelib.a"],
+ export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl"],
+ },
+ },
+}
+`),
+ checkVersionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_static {
@@ -1694,7 +1672,7 @@
installable: false,
stl: "none",
compile_multilib: "both",
- export_include_dirs: ["include/include"],
+ export_include_dirs: ["include/myinclude"],
target: {
host: {
enabled: false,
@@ -1702,39 +1680,12 @@
linux_glibc_x86_64: {
enabled: true,
srcs: ["x86_64/lib/mynativelib.a"],
- export_include_dirs: ["x86_64/include_gen/mynativelib"],
+ export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl"],
},
linux_glibc_x86: {
enabled: true,
srcs: ["x86/lib/mynativelib.a"],
- export_include_dirs: ["x86/include_gen/mynativelib"],
- },
- },
-}
-
-cc_prebuilt_library_static {
- name: "mynativelib",
- prefer: false,
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- stl: "none",
- compile_multilib: "both",
- export_include_dirs: ["include/include"],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- srcs: ["x86_64/lib/mynativelib.a"],
- export_include_dirs: ["x86_64/include_gen/mynativelib"],
- },
- linux_glibc_x86: {
- enabled: true,
- srcs: ["x86/lib/mynativelib.a"],
- export_include_dirs: ["x86/include_gen/mynativelib"],
+ export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl"],
},
},
}
@@ -1759,15 +1710,15 @@
}
`),
checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
.intermediates/mynativelib/linux_glibc_x86_64_static/mynativelib.a -> x86_64/lib/mynativelib.a
-.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h
.intermediates/mynativelib/linux_glibc_x86_static/mynativelib.a -> x86/lib/mynativelib.a
-.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/Test.h -> x86/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BpTest.h
+.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/Test.h -> x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BpTest.h
`),
)
}
@@ -1784,28 +1735,27 @@
srcs: [
"Test.cpp",
],
- export_include_dirs: ["include"],
+ export_include_dirs: ["myinclude"],
stl: "none",
recovery_available: true,
vendor_available: true,
}
`)
- result.CheckSnapshot("myexports", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "myexports", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library {
- name: "myexports_mynativelib@current",
- sdk_member_name: "mynativelib",
+ name: "mynativelib",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
- installable: false,
recovery_available: true,
vendor_available: true,
stl: "none",
compile_multilib: "both",
- export_include_dirs: ["include/include"],
+ export_include_dirs: ["include/myinclude"],
arch: {
arm64: {
static: {
@@ -1825,17 +1775,22 @@
},
},
}
+`),
+ // Make sure that the generated sdk_snapshot uses the native_libs property.
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library {
- name: "mynativelib",
- prefer: false,
+ name: "myexports_mynativelib@current",
+ sdk_member_name: "mynativelib",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
+ installable: false,
recovery_available: true,
vendor_available: true,
stl: "none",
compile_multilib: "both",
- export_include_dirs: ["include/include"],
+ export_include_dirs: ["include/myinclude"],
arch: {
arm64: {
static: {
@@ -1863,11 +1818,14 @@
}
`),
checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
.intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> arm64/lib/mynativelib.a
.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
.intermediates/mynativelib/android_arm_armv7-a-neon_static/mynativelib.a -> arm/lib/mynativelib.a
-.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
+`),
+ // TODO(b/183315522): Remove this and fix the issue.
+ snapshotTestErrorHandler(checkSnapshotPreferredWithSource, android.FixtureExpectsAtLeastOneErrorMatchingPattern(`\Qunrecognized property "arch.arm.shared.export_include_dirs"\E`)),
)
}
@@ -1893,7 +1851,7 @@
"Test.cpp",
"aidl/foo/bar/Test.aidl",
],
- export_include_dirs: ["include"],
+ export_include_dirs: ["myinclude"],
aidl: {
export_aidl_headers: true,
},
@@ -1901,8 +1859,35 @@
}
`)
- result.CheckSnapshot("myexports", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "myexports", "",
+ checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_static {
+ name: "mynativelib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ device_supported: false,
+ host_supported: true,
+ stl: "none",
+ compile_multilib: "64",
+ export_include_dirs: [
+ "include/myinclude",
+ "include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl",
+ ],
+ target: {
+ host: {
+ enabled: false,
+ },
+ linux_glibc_x86_64: {
+ enabled: true,
+ srcs: ["x86_64/lib/mynativelib.a"],
+ },
+ },
+}
+`),
+ checkVersionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_static {
@@ -1915,7 +1900,10 @@
installable: false,
stl: "none",
compile_multilib: "64",
- export_include_dirs: ["include/include"],
+ export_include_dirs: [
+ "include/myinclude",
+ "include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl",
+ ],
target: {
host: {
enabled: false,
@@ -1923,29 +1911,6 @@
linux_glibc_x86_64: {
enabled: true,
srcs: ["x86_64/lib/mynativelib.a"],
- export_include_dirs: ["x86_64/include_gen/mynativelib"],
- },
- },
-}
-
-cc_prebuilt_library_static {
- name: "mynativelib",
- prefer: false,
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- device_supported: false,
- host_supported: true,
- stl: "none",
- compile_multilib: "64",
- export_include_dirs: ["include/include"],
- target: {
- host: {
- enabled: false,
- },
- linux_glibc_x86_64: {
- enabled: true,
- srcs: ["x86_64/lib/mynativelib.a"],
- export_include_dirs: ["x86_64/include_gen/mynativelib"],
},
},
}
@@ -1965,13 +1930,14 @@
enabled: true,
},
},
-}`),
+}
+`),
checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h -> include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h
+.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h -> include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h
.intermediates/mynativelib/linux_glibc_x86_64_static/mynativelib.a -> x86_64/lib/mynativelib.a
-.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/Test.h
-.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BnTest.h
-.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BpTest.h
`),
)
}
@@ -1985,43 +1951,27 @@
cc_library_headers {
name: "mynativeheaders",
- export_include_dirs: ["include"],
+ export_include_dirs: ["myinclude"],
stl: "none",
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_headers {
- name: "mysdk_mynativeheaders@current",
- sdk_member_name: "mynativeheaders",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- stl: "none",
- compile_multilib: "both",
- export_include_dirs: ["include/include"],
-}
-
-cc_prebuilt_library_headers {
name: "mynativeheaders",
prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
stl: "none",
compile_multilib: "both",
- export_include_dirs: ["include/include"],
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- native_header_libs: ["mysdk_mynativeheaders@current"],
+ export_include_dirs: ["include/myinclude"],
}
`),
checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
`),
)
}
@@ -2039,25 +1989,25 @@
name: "mynativeheaders",
device_supported: false,
host_supported: true,
- export_include_dirs: ["include"],
+ export_include_dirs: ["myinclude"],
stl: "none",
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_headers {
- name: "mysdk_mynativeheaders@current",
- sdk_member_name: "mynativeheaders",
+ name: "mynativeheaders",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
stl: "none",
compile_multilib: "both",
- export_include_dirs: ["include/include"],
+ export_include_dirs: ["include/myinclude"],
target: {
host: {
enabled: false,
@@ -2070,17 +2020,20 @@
},
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_headers {
- name: "mynativeheaders",
- prefer: false,
+ name: "mysdk_mynativeheaders@current",
+ sdk_member_name: "mynativeheaders",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
stl: "none",
compile_multilib: "both",
- export_include_dirs: ["include/include"],
+ export_include_dirs: ["include/myinclude"],
target: {
host: {
enabled: false,
@@ -2114,7 +2067,7 @@
}
`),
checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
`),
)
}
@@ -2131,20 +2084,51 @@
name: "mynativeheaders",
host_supported: true,
stl: "none",
- export_system_include_dirs: ["include"],
+ export_system_include_dirs: ["myinclude"],
target: {
android: {
- export_include_dirs: ["include-android"],
+ export_include_dirs: ["myinclude-android"],
},
host: {
- export_include_dirs: ["include-host"],
+ export_include_dirs: ["myinclude-host"],
},
},
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_headers {
+ name: "mynativeheaders",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ host_supported: true,
+ stl: "none",
+ compile_multilib: "both",
+ export_system_include_dirs: ["common_os/include/myinclude"],
+ target: {
+ host: {
+ enabled: false,
+ },
+ android: {
+ export_include_dirs: ["android/include/myinclude-android"],
+ },
+ linux_glibc: {
+ export_include_dirs: ["linux_glibc/include/myinclude-host"],
+ },
+ linux_glibc_x86_64: {
+ enabled: true,
+ },
+ linux_glibc_x86: {
+ enabled: true,
+ },
+ },
+}
+`),
+ checkVersionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_headers {
@@ -2155,44 +2139,16 @@
host_supported: true,
stl: "none",
compile_multilib: "both",
- export_system_include_dirs: ["common_os/include/include"],
+ export_system_include_dirs: ["common_os/include/myinclude"],
target: {
host: {
enabled: false,
},
android: {
- export_include_dirs: ["android/include/include-android"],
+ export_include_dirs: ["android/include/myinclude-android"],
},
linux_glibc: {
- export_include_dirs: ["linux_glibc/include/include-host"],
- },
- linux_glibc_x86_64: {
- enabled: true,
- },
- linux_glibc_x86: {
- enabled: true,
- },
- },
-}
-
-cc_prebuilt_library_headers {
- name: "mynativeheaders",
- prefer: false,
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- host_supported: true,
- stl: "none",
- compile_multilib: "both",
- export_system_include_dirs: ["common_os/include/include"],
- target: {
- host: {
- enabled: false,
- },
- android: {
- export_include_dirs: ["android/include/include-android"],
- },
- linux_glibc: {
- export_include_dirs: ["linux_glibc/include/include-host"],
+ export_include_dirs: ["linux_glibc/include/myinclude-host"],
},
linux_glibc_x86_64: {
enabled: true,
@@ -2222,9 +2178,9 @@
}
`),
checkAllCopyRules(`
-include/Test.h -> common_os/include/include/Test.h
-include-android/AndroidTest.h -> android/include/include-android/AndroidTest.h
-include-host/HostTest.h -> linux_glibc/include/include-host/HostTest.h
+myinclude/Test.h -> common_os/include/myinclude/Test.h
+myinclude-android/AndroidTest.h -> android/include/myinclude-android/AndroidTest.h
+myinclude-host/HostTest.h -> linux_glibc/include/myinclude-host/HostTest.h
`),
)
}
@@ -2252,28 +2208,11 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
- name: "mysdk_sslnil@current",
- sdk_member_name: "sslnil",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- installable: false,
- compile_multilib: "both",
- arch: {
- arm64: {
- srcs: ["arm64/lib/sslnil.so"],
- },
- arm: {
- srcs: ["arm/lib/sslnil.so"],
- },
- },
-}
-
-cc_prebuilt_library_shared {
name: "sslnil",
prefer: false,
visibility: ["//visibility:public"],
@@ -2290,24 +2229,6 @@
}
cc_prebuilt_library_shared {
- name: "mysdk_sslempty@current",
- sdk_member_name: "sslempty",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- installable: false,
- compile_multilib: "both",
- system_shared_libs: [],
- arch: {
- arm64: {
- srcs: ["arm64/lib/sslempty.so"],
- },
- arm: {
- srcs: ["arm/lib/sslempty.so"],
- },
- },
-}
-
-cc_prebuilt_library_shared {
name: "sslempty",
prefer: false,
visibility: ["//visibility:public"],
@@ -2325,24 +2246,6 @@
}
cc_prebuilt_library_shared {
- name: "mysdk_sslnonempty@current",
- sdk_member_name: "sslnonempty",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- installable: false,
- compile_multilib: "both",
- system_shared_libs: ["mysdk_sslnil@current"],
- arch: {
- arm64: {
- srcs: ["arm64/lib/sslnonempty.so"],
- },
- arm: {
- srcs: ["arm/lib/sslnonempty.so"],
- },
- },
-}
-
-cc_prebuilt_library_shared {
name: "sslnonempty",
prefer: false,
visibility: ["//visibility:public"],
@@ -2358,16 +2261,6 @@
},
},
}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- native_shared_libs: [
- "mysdk_sslnil@current",
- "mysdk_sslempty@current",
- "mysdk_sslnonempty@current",
- ],
-}
`))
result = testSdkWithCc(t, `
@@ -2388,17 +2281,16 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
- name: "mysdk_sslvariants@current",
- sdk_member_name: "sslvariants",
+ name: "sslvariants",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
host_supported: true,
- installable: false,
compile_multilib: "both",
target: {
host: {
@@ -2423,13 +2315,17 @@
},
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
- name: "sslvariants",
- prefer: false,
+ name: "mysdk_sslvariants@current",
+ sdk_member_name: "sslvariants",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
host_supported: true,
+ installable: false,
compile_multilib: "both",
target: {
host: {
@@ -2496,35 +2392,11 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
- name: "mysdk_stubslib@current",
- sdk_member_name: "stubslib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- installable: false,
- compile_multilib: "both",
- stubs: {
- versions: [
- "1",
- "2",
- "3",
- ],
- },
- arch: {
- arm64: {
- srcs: ["arm64/lib/stubslib.so"],
- },
- arm: {
- srcs: ["arm/lib/stubslib.so"],
- },
- },
-}
-
-cc_prebuilt_library_shared {
name: "stubslib",
prefer: false,
visibility: ["//visibility:public"],
@@ -2535,6 +2407,7 @@
"1",
"2",
"3",
+ "current",
],
},
arch: {
@@ -2546,12 +2419,6 @@
},
},
}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- native_shared_libs: ["mysdk_stubslib@current"],
-}
`))
}
@@ -2579,23 +2446,23 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
- name: "mysdk_stubslib@current",
- sdk_member_name: "stubslib",
+ name: "stubslib",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
host_supported: true,
- installable: false,
compile_multilib: "both",
stubs: {
versions: [
"1",
"2",
"3",
+ "current",
],
},
target: {
@@ -2618,19 +2485,24 @@
},
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
- name: "stubslib",
- prefer: false,
+ name: "mysdk_stubslib@current",
+ sdk_member_name: "stubslib",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
host_supported: true,
+ installable: false,
compile_multilib: "both",
stubs: {
versions: [
"1",
"2",
"3",
+ "current",
],
},
target: {
@@ -2689,17 +2561,16 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
- name: "mysdk_mylib@current",
- sdk_member_name: "mylib",
+ name: "mylib",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
host_supported: true,
- installable: false,
unique_host_soname: true,
compile_multilib: "both",
target: {
@@ -2722,13 +2593,17 @@
},
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
- name: "mylib",
- prefer: false,
+ name: "mysdk_mylib@current",
+ sdk_member_name: "mylib",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
host_supported: true,
+ installable: false,
unique_host_soname: true,
compile_multilib: "both",
target: {
@@ -2789,7 +2664,7 @@
cc_library_shared {
name: "mynativelib",
srcs: ["Test.cpp"],
- export_include_dirs: ["include"],
+ export_include_dirs: ["myinclude"],
arch: {
arm64: {
export_system_include_dirs: ["arm64/include"],
@@ -2801,27 +2676,14 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
+ // Mixing the snapshot with the source (irrespective of which one is preferred) causes a problem
+ // due to missing variants.
+ // TODO(b/183204176): Remove this and fix the cause.
+ snapshotWithSourceErrorHandler := android.FixtureExpectsAtLeastOneErrorMatchingPattern(`\QReplaceDependencies could not find identical variant {os:android,image:,arch:arm64_armv8-a,sdk:,link:shared,version:} for module mynativelib\E`)
-cc_prebuilt_library_shared {
- name: "mysdk_mynativelib@current",
- sdk_member_name: "mynativelib",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- installable: false,
- compile_multilib: "both",
- export_include_dirs: ["include/include"],
- arch: {
- arm64: {
- export_system_include_dirs: ["arm64/include/arm64/include"],
- },
- arm: {
- srcs: ["arm/lib/mynativelib.so"],
- },
- },
-}
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
cc_prebuilt_library_shared {
name: "mynativelib",
@@ -2829,7 +2691,7 @@
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
compile_multilib: "both",
- export_include_dirs: ["include/include"],
+ export_include_dirs: ["include/myinclude"],
arch: {
arm64: {
export_system_include_dirs: ["arm64/include/arm64/include"],
@@ -2839,16 +2701,13 @@
},
},
}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- native_shared_libs: ["mysdk_mynativelib@current"],
-}
`),
checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
+myinclude/Test.h -> include/myinclude/Test.h
arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h
-.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
+`),
+ snapshotTestErrorHandler(checkSnapshotWithSourcePreferred, snapshotWithSourceErrorHandler),
+ snapshotTestErrorHandler(checkSnapshotPreferredWithSource, snapshotWithSourceErrorHandler),
)
}
diff --git a/sdk/compat_config_sdk_test.go b/sdk/compat_config_sdk_test.go
new file mode 100644
index 0000000..00073c2
--- /dev/null
+++ b/sdk/compat_config_sdk_test.go
@@ -0,0 +1,91 @@
+// Copyright 2021 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 sdk
+
+import (
+ "testing"
+
+ "android/soong/android"
+ "android/soong/java"
+)
+
+func TestSnapshotWithCompatConfig(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ java.PrepareForTestWithPlatformCompatConfig,
+ ).RunTestWithBp(t, `
+ sdk {
+ name: "mysdk",
+ compat_configs: ["myconfig"],
+ }
+
+ platform_compat_config {
+ name: "myconfig",
+ }
+ `)
+
+ CheckSnapshot(t, result, "mysdk", "",
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_platform_compat_config {
+ name: "mysdk_myconfig@current",
+ sdk_member_name: "myconfig",
+ visibility: ["//visibility:public"],
+ metadata: "compat_configs/myconfig/myconfig_meta.xml",
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ visibility: ["//visibility:public"],
+ compat_configs: ["mysdk_myconfig@current"],
+}
+`),
+ checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_platform_compat_config {
+ name: "myconfig",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ metadata: "compat_configs/myconfig/myconfig_meta.xml",
+}
+`),
+ checkAllCopyRules(`
+.intermediates/myconfig/android_common/myconfig_meta.xml -> compat_configs/myconfig/myconfig_meta.xml
+`),
+ snapshotTestChecker(checkSnapshotWithoutSource,
+ func(t *testing.T, result *android.TestResult) {
+ // Make sure that the snapshot metadata is collated by the platform compat config singleton.
+ java.CheckMergedCompatConfigInputs(t, result, "snapshot module", "snapshot/compat_configs/myconfig/myconfig_meta.xml")
+ }),
+
+ snapshotTestChecker(checkSnapshotWithSourcePreferred,
+ func(t *testing.T, result *android.TestResult) {
+ // Make sure that the snapshot metadata is collated by the platform compat config singleton.
+ java.CheckMergedCompatConfigInputs(t, result, "snapshot module",
+ "out/soong/.intermediates/myconfig/android_common/myconfig_meta.xml",
+ )
+ }),
+
+ snapshotTestChecker(checkSnapshotPreferredWithSource,
+ func(t *testing.T, result *android.TestResult) {
+ // Make sure that the snapshot metadata is collated by the platform compat config singleton.
+ java.CheckMergedCompatConfigInputs(t, result, "snapshot module",
+ "snapshot/compat_configs/myconfig/myconfig_meta.xml",
+ )
+ }),
+ )
+}
diff --git a/sdk/exports.go b/sdk/exports.go
index d313057..9a0ba4e 100644
--- a/sdk/exports.go
+++ b/sdk/exports.go
@@ -17,8 +17,12 @@
import "android/soong/android"
func init() {
- android.RegisterModuleType("module_exports", ModuleExportsFactory)
- android.RegisterModuleType("module_exports_snapshot", ModuleExportsSnapshotsFactory)
+ registerModuleExportsBuildComponents(android.InitRegistrationContext)
+}
+
+func registerModuleExportsBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("module_exports", ModuleExportsFactory)
+ ctx.RegisterModuleType("module_exports_snapshot", ModuleExportsSnapshotsFactory)
}
// module_exports defines the exports of a mainline module. The exports are Soong modules
diff --git a/sdk/exports_test.go b/sdk/exports_test.go
index 1c59244..fd7741c 100644
--- a/sdk/exports_test.go
+++ b/sdk/exports_test.go
@@ -42,7 +42,7 @@
"package/Android.bp": []byte(packageBp),
})
- result.CheckSnapshot("myexports", "package",
+ CheckSnapshot(t, result, "myexports", "package",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index d989c5b..208cd58 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -17,94 +17,36 @@
import (
"testing"
+ "android/soong/android"
"android/soong/java"
)
-func testSdkWithJava(t *testing.T, bp string) *testSdkResult {
- t.Helper()
+var prepareForSdkTestWithJava = android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaBuildComponents,
+ PrepareForTestWithSdkBuildComponents,
- fs := map[string][]byte{
- "Test.java": nil,
- "resource.test": nil,
- "aidl/foo/bar/Test.aidl": nil,
+ // Ensure that all source paths are provided. This helps ensure that the snapshot generation is
+ // consistent and all files referenced from the snapshot's Android.bp file have actually been
+ // copied into the snapshot.
+ android.PrepareForTestDisallowNonExistentPaths,
- // For java_import
- "prebuilt.jar": nil,
+ // Files needs by most of the tests.
+ android.MockFS{
+ "Test.java": nil,
+ }.AddToFixture(),
+)
- // For java_sdk_library
- "api/current.txt": nil,
- "api/removed.txt": nil,
- "api/system-current.txt": nil,
- "api/system-removed.txt": nil,
- "api/test-current.txt": nil,
- "api/test-removed.txt": nil,
- "api/module-lib-current.txt": nil,
- "api/module-lib-removed.txt": nil,
- "api/system-server-current.txt": nil,
- "api/system-server-removed.txt": nil,
- "build/soong/scripts/gen-java-current-api-files.sh": nil,
- "docs/known_doctags": nil,
- "100/public/api/myjavalib.txt": nil,
- "100/public/api/myjavalib-removed.txt": nil,
- "100/system/api/myjavalib.txt": nil,
- "100/system/api/myjavalib-removed.txt": nil,
- "100/module-lib/api/myjavalib.txt": nil,
- "100/module-lib/api/myjavalib-removed.txt": nil,
- "100/system-server/api/myjavalib.txt": nil,
- "100/system-server/api/myjavalib-removed.txt": nil,
- }
-
- // for java_sdk_library tests
- bp = `
-java_system_modules_import {
- name: "core-current-stubs-system-modules",
-}
-java_system_modules_import {
- name: "stable-core-platform-api-stubs-system-modules",
-}
-java_import {
- name: "stable.core.platform.api.stubs",
-}
-java_import {
- name: "android_stubs_current",
-}
-java_import {
- name: "android_system_stubs_current",
-}
-java_import {
- name: "android_test_stubs_current",
-}
-java_import {
- name: "android_module_lib_stubs_current",
-}
-java_import {
- name: "android_system_server_stubs_current",
-}
-java_import {
- name: "core-lambda-stubs",
- sdk_version: "none",
-}
-java_import {
- name: "ext",
- sdk_version: "none",
-}
-java_import {
- name: "framework",
- sdk_version: "none",
-}
-prebuilt_apis {
- name: "sdk",
- api_dirs: ["100"],
-}
-` + bp
-
- return testSdkWithFs(t, bp, fs)
-}
+var prepareForSdkTestWithJavaSdkLibrary = android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ java.PrepareForTestWithJavaDefaultModules,
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ java.FixtureWithLastReleaseApis("myjavalib"),
+)
// Contains tests for SDK members provided by the java package.
func TestSdkDependsOnSourceEvenWhenPrebuiltPreferred(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_header_libs: ["sdkmember"],
@@ -116,46 +58,24 @@
system_modules: "none",
sdk_version: "none",
}
-
- java_import {
- name: "sdkmember",
- prefer: true,
- jars: ["prebuilt.jar"],
- }
`)
// Make sure that the mysdk module depends on "sdkmember" and not "prebuilt_sdkmember".
- java.CheckModuleDependencies(t, result.ctx, "mysdk", "android_common", []string{"sdkmember"})
+ sdkChecker := func(t *testing.T, result *android.TestResult) {
+ java.CheckModuleDependencies(t, result.TestContext, "mysdk", "android_common", []string{"sdkmember"})
+ }
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "mysdk_sdkmember@current",
- sdk_member_name: "sdkmember",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/sdkmember.jar"],
-}
-
-java_import {
- name: "sdkmember",
- prefer: false,
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/sdkmember.jar"],
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_header_libs: ["mysdk_sdkmember@current"],
-}
-`))
+ CheckSnapshot(t, result, "mysdk", "",
+ snapshotTestChecker(checkSnapshotWithSourcePreferred, sdkChecker),
+ snapshotTestChecker(checkSnapshotPreferredWithSource, sdkChecker),
+ )
}
func TestBasicSdkWithJavaLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ prepareForSdkTestWithApex,
+ ).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_header_libs: ["sdkmember"],
@@ -211,6 +131,7 @@
uses_sdks: ["mysdk@1"],
key: "myapex.key",
certificate: ":myapex.cert",
+ updatable: false,
}
apex {
@@ -219,14 +140,15 @@
uses_sdks: ["mysdk@2"],
key: "myapex.key",
certificate: ":myapex.cert",
+ updatable: false,
}
`)
- sdkMemberV1 := result.ctx.ModuleForTests("sdkmember_mysdk_1", "android_common").Rule("combineJar").Output
- sdkMemberV2 := result.ctx.ModuleForTests("sdkmember_mysdk_2", "android_common").Rule("combineJar").Output
+ sdkMemberV1 := result.ModuleForTests("sdkmember_mysdk_1", "android_common").Rule("combineJar").Output
+ sdkMemberV2 := result.ModuleForTests("sdkmember_mysdk_2", "android_common").Rule("combineJar").Output
- javalibForMyApex := result.ctx.ModuleForTests("myjavalib", "android_common_apex10000_mysdk_1")
- javalibForMyApex2 := result.ctx.ModuleForTests("myjavalib", "android_common_apex10000_mysdk_2")
+ javalibForMyApex := result.ModuleForTests("myjavalib", "android_common_apex10000_mysdk_1")
+ javalibForMyApex2 := result.ModuleForTests("myjavalib", "android_common_apex10000_mysdk_2")
// Depending on the uses_sdks value, different libs are linked
ensureListContains(t, pathsToStrings(javalibForMyApex.Rule("javac").Implicits), sdkMemberV1.String())
@@ -234,7 +156,10 @@
}
func TestSnapshotWithJavaHeaderLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
+ ).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_header_libs: ["myjavalib"],
@@ -253,7 +178,7 @@
}
`)
- result.CheckSnapshot("mysdk", "",
+ CheckSnapshot(t, result, "mysdk", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -288,7 +213,10 @@
}
func TestHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
+ ).RunTestWithBp(t, `
sdk {
name: "mysdk",
device_supported: false,
@@ -310,7 +238,7 @@
}
`)
- result.CheckSnapshot("mysdk", "",
+ CheckSnapshot(t, result, "mysdk", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -350,7 +278,7 @@
}
func TestDeviceAndHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
sdk {
name: "mysdk",
host_supported: true,
@@ -367,7 +295,7 @@
}
`)
- result.CheckSnapshot("mysdk", "",
+ CheckSnapshot(t, result, "mysdk", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -418,7 +346,11 @@
}
func TestSnapshotWithJavaImplLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
+ android.FixtureAddFile("resource.txt", nil),
+ ).RunTestWithBp(t, `
module_exports {
name: "myexports",
java_libs: ["myjavalib"],
@@ -438,7 +370,7 @@
}
`)
- result.CheckSnapshot("myexports", "",
+ CheckSnapshot(t, result, "myexports", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -472,8 +404,69 @@
)
}
+func TestSnapshotWithJavaBootLibrary(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ android.FixtureAddFile("aidl", nil),
+ android.FixtureAddFile("resource.txt", nil),
+ ).RunTestWithBp(t, `
+ module_exports {
+ name: "myexports",
+ java_boot_libs: ["myjavalib"],
+ }
+
+ java_library {
+ name: "myjavalib",
+ srcs: ["Test.java"],
+ java_resources: ["resource.txt"],
+ // The aidl files should not be copied to the snapshot because a java_boot_libs member is not
+ // intended to be used for compiling Java, only for accessing the dex implementation jar.
+ aidl: {
+ export_include_dirs: ["aidl"],
+ },
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ }
+ `)
+
+ CheckSnapshot(t, result, "myexports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "myexports_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+ name: "myjavalib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ jars: ["java/myjavalib.jar"],
+}
+
+module_exports_snapshot {
+ name: "myexports@current",
+ visibility: ["//visibility:public"],
+ java_boot_libs: ["myexports_myjavalib@current"],
+}
+`),
+ checkAllCopyRules(`
+.intermediates/myjavalib/android_common/withres/myjavalib.jar -> java/myjavalib.jar
+`),
+ )
+}
+
func TestHostSnapshotWithJavaImplLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
+ ).RunTestWithBp(t, `
module_exports {
name: "myexports",
device_supported: false,
@@ -495,7 +488,7 @@
}
`)
- result.CheckSnapshot("myexports", "",
+ CheckSnapshot(t, result, "myexports", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -535,7 +528,7 @@
}
func TestSnapshotWithJavaTest(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
module_exports {
name: "myexports",
java_tests: ["myjavatests"],
@@ -551,7 +544,7 @@
}
`)
- result.CheckSnapshot("myexports", "",
+ CheckSnapshot(t, result, "myexports", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -587,7 +580,7 @@
}
func TestHostSnapshotWithJavaTest(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
module_exports {
name: "myexports",
device_supported: false,
@@ -606,7 +599,7 @@
}
`)
- result.CheckSnapshot("myexports", "",
+ CheckSnapshot(t, result, "myexports", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -648,7 +641,7 @@
}
func TestSnapshotWithJavaSystemModules(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_header_libs: ["exported-system-module"],
@@ -675,7 +668,7 @@
}
`)
- result.CheckSnapshot("mysdk", "",
+ CheckSnapshot(t, result, "mysdk", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -746,7 +739,7 @@
}
func TestHostSnapshotWithJavaSystemModules(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
sdk {
name: "mysdk",
device_supported: false,
@@ -771,7 +764,7 @@
}
`)
- result.CheckSnapshot("mysdk", "",
+ CheckSnapshot(t, result, "mysdk", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -826,7 +819,7 @@
}
func TestDeviceAndHostSnapshotWithOsSpecificMembers(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
module_exports {
name: "myexports",
host_supported: true,
@@ -862,7 +855,7 @@
}
`)
- result.CheckSnapshot("myexports", "",
+ CheckSnapshot(t, result, "myexports", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -959,7 +952,7 @@
}
func TestSnapshotWithJavaSdkLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -976,7 +969,7 @@
}
`)
- result.CheckSnapshot("mysdk", "",
+ CheckSnapshot(t, result, "mysdk", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -1046,14 +1039,14 @@
`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
.intermediates/myjavalib.stubs.system/android_common/javac/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
-.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
.intermediates/myjavalib.stubs.test/android_common/javac/myjavalib.stubs.test.jar -> sdk_library/test/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.test/android_common/myjavalib.stubs.source.test_api.txt -> sdk_library/test/myjavalib.txt
-.intermediates/myjavalib.stubs.source.test/android_common/myjavalib.stubs.source.test_removed.txt -> sdk_library/test/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.test/android_common/metalava/myjavalib.stubs.source.test_api.txt -> sdk_library/test/myjavalib.txt
+.intermediates/myjavalib.stubs.source.test/android_common/metalava/myjavalib.stubs.source.test_removed.txt -> sdk_library/test/myjavalib-removed.txt
`),
checkMergeZips(
".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1063,7 +1056,7 @@
}
func TestSnapshotWithJavaSdkLibrary_SdkVersion_None(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1077,7 +1070,7 @@
}
`)
- result.CheckSnapshot("mysdk", "",
+ CheckSnapshot(t, result, "mysdk", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -1119,8 +1112,8 @@
`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
`),
checkMergeZips(
".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1129,7 +1122,7 @@
}
func TestSnapshotWithJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1146,7 +1139,7 @@
}
`)
- result.CheckSnapshot("mysdk", "",
+ CheckSnapshot(t, result, "mysdk", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -1188,8 +1181,8 @@
`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
`),
checkMergeZips(
".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1198,7 +1191,7 @@
}
func TestSnapshotWithJavaSdkLibrary_ApiScopes(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1218,7 +1211,7 @@
}
`)
- result.CheckSnapshot("mysdk", "",
+ CheckSnapshot(t, result, "mysdk", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -1274,11 +1267,11 @@
`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
.intermediates/myjavalib.stubs.system/android_common/javac/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
-.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
`),
checkMergeZips(
".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1288,7 +1281,7 @@
}
func TestSnapshotWithJavaSdkLibrary_ModuleLib(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1311,7 +1304,7 @@
}
`)
- result.CheckSnapshot("mysdk", "",
+ CheckSnapshot(t, result, "mysdk", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -1381,14 +1374,14 @@
`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
.intermediates/myjavalib.stubs.system/android_common/javac/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
-.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
.intermediates/myjavalib.stubs.module_lib/android_common/javac/myjavalib.stubs.module_lib.jar -> sdk_library/module-lib/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.module_lib/android_common/myjavalib.stubs.source.module_lib_api.txt -> sdk_library/module-lib/myjavalib.txt
-.intermediates/myjavalib.stubs.source.module_lib/android_common/myjavalib.stubs.source.module_lib_removed.txt -> sdk_library/module-lib/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.module_lib/android_common/metalava/myjavalib.stubs.source.module_lib_api.txt -> sdk_library/module-lib/myjavalib.txt
+.intermediates/myjavalib.stubs.source.module_lib/android_common/metalava/myjavalib.stubs.source.module_lib_removed.txt -> sdk_library/module-lib/myjavalib-removed.txt
`),
checkMergeZips(
".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1399,7 +1392,7 @@
}
func TestSnapshotWithJavaSdkLibrary_SystemServer(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1419,7 +1412,7 @@
}
`)
- result.CheckSnapshot("mysdk", "",
+ CheckSnapshot(t, result, "mysdk", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -1475,11 +1468,11 @@
`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
.intermediates/myjavalib.stubs.system_server/android_common/javac/myjavalib.stubs.system_server.jar -> sdk_library/system-server/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.system_server/android_common/myjavalib.stubs.source.system_server_api.txt -> sdk_library/system-server/myjavalib.txt
-.intermediates/myjavalib.stubs.source.system_server/android_common/myjavalib.stubs.source.system_server_removed.txt -> sdk_library/system-server/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.system_server/android_common/metalava/myjavalib.stubs.source.system_server_api.txt -> sdk_library/system-server/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system_server/android_common/metalava/myjavalib.stubs.source.system_server_removed.txt -> sdk_library/system-server/myjavalib-removed.txt
`),
checkMergeZips(
".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1489,7 +1482,7 @@
}
func TestSnapshotWithJavaSdkLibrary_NamingScheme(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1507,7 +1500,7 @@
}
`)
- result.CheckSnapshot("mysdk", "",
+ CheckSnapshot(t, result, "mysdk", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -1551,8 +1544,8 @@
`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
`),
checkMergeZips(
".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1561,7 +1554,10 @@
}
func TestSnapshotWithJavaSdkLibrary_DoctagFiles(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJavaSdkLibrary,
+ android.FixtureAddFile("docs/known_doctags", nil),
+ ).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1583,7 +1579,7 @@
}
`)
- result.CheckSnapshot("mysdk", "",
+ CheckSnapshot(t, result, "mysdk", "",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -1627,8 +1623,8 @@
`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
docs/known_doctags -> doctags/docs/known_doctags
`),
)
diff --git a/sdk/sdk.go b/sdk/sdk.go
index f3d0750..b60fb18 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -33,10 +33,14 @@
pctx.Import("android/soong/android")
pctx.Import("android/soong/java/config")
- android.RegisterModuleType("sdk", SdkModuleFactory)
- android.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
- android.PreDepsMutators(RegisterPreDepsMutators)
- android.PostDepsMutators(RegisterPostDepsMutators)
+ registerSdkBuildComponents(android.InitRegistrationContext)
+}
+
+func registerSdkBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("sdk", SdkModuleFactory)
+ ctx.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
+ ctx.PreDepsMutators(RegisterPreDepsMutators)
+ ctx.PostDepsMutators(RegisterPostDepsMutators)
}
type sdk struct {
@@ -354,15 +358,36 @@
// For dependencies from an in-development version of an SDK member to frozen versions of the same member
// e.g. libfoo -> libfoo.mysdk.11 and libfoo.mysdk.12
+//
+// The dependency represented by this tag requires that for every APEX variant created for the
+// `from` module that an equivalent APEX variant is created for the 'to' module. This is because an
+// APEX that requires a specific version of an sdk (via the `uses_sdks` property will replace
+// dependencies on the unversioned sdk member with a dependency on the appropriate versioned sdk
+// member. In order for that to work the versioned sdk member needs to have a variant for that APEX.
+// As it is not known at the time that the APEX variants are created which specific APEX variants of
+// a versioned sdk members will be required it is necessary for the versioned sdk members to have
+// variants for any APEX that it could be used within.
+//
+// If the APEX selects a versioned sdk member then it will not have a dependency on the `from`
+// module at all so any dependencies of that module will not affect the APEX. However, if the APEX
+// selects the unversioned sdk member then it must exclude all the versioned sdk members. In no
+// situation would this dependency cause the `to` module to be added to the APEX hence why this tag
+// also excludes the `to` module from being added to the APEX contents.
type sdkMemberVersionedDepTag struct {
dependencyTag
member string
version string
}
+func (t sdkMemberVersionedDepTag) AlwaysRequireApexVariant() bool {
+ return true
+}
+
// Mark this tag so dependencies that use it are excluded from visibility enforcement.
func (t sdkMemberVersionedDepTag) ExcludeFromVisibilityEnforcement() {}
+var _ android.AlwaysRequireApexVariantTag = sdkMemberVersionedDepTag{}
+
// Step 1: create dependencies from an SDK module to its members.
func memberMutator(mctx android.BottomUpMutatorContext) {
if s, ok := mctx.Module().(*sdk); ok {
@@ -420,20 +445,26 @@
}
}
+// An interface that encapsulates all the functionality needed to manage the sdk dependencies.
+//
+// It is a mixture of apex and sdk module functionality.
+type sdkAndApexModule interface {
+ android.Module
+ android.DepIsInSameApex
+ android.RequiredSdks
+}
+
// Step 4: transitively ripple down the SDK requirements from the root modules like APEX to its
// descendants
func sdkDepsMutator(mctx android.TopDownMutatorContext) {
- if parent, ok := mctx.Module().(interface {
- android.DepIsInSameApex
- android.RequiredSdks
- }); ok {
+ if parent, ok := mctx.Module().(sdkAndApexModule); ok {
// Module types for Mainline modules (e.g. APEX) are expected to implement RequiredSdks()
// by reading its own properties like `uses_sdks`.
requiredSdks := parent.RequiredSdks()
if len(requiredSdks) > 0 {
mctx.VisitDirectDeps(func(m android.Module) {
// Only propagate required sdks from the apex onto its contents.
- if dep, ok := m.(android.SdkAware); ok && parent.DepIsInSameApex(mctx, dep) {
+ if dep, ok := m.(android.SdkAware); ok && android.IsDepInSameApex(mctx, parent, dep) {
dep.BuildWithSdks(requiredSdks)
}
})
@@ -455,6 +486,15 @@
// sdk containing sdkmember.
memberName := versionedSdkMember.MemberName()
+ // Convert a panic into a normal error to allow it to be more easily tested for. This is a
+ // temporary workaround, once http://b/183204176 has been fixed this can be removed.
+ // TODO(b/183204176): Remove this after fixing.
+ defer func() {
+ if r := recover(); r != nil {
+ mctx.ModuleErrorf("%s", r)
+ }
+ }()
+
// Replace dependencies on sdkmember with a dependency on the current module which
// is a versioned prebuilt of the sdkmember if required.
mctx.ReplaceDependenciesIf(memberName, func(from blueprint.Module, tag blueprint.DependencyTag, to blueprint.Module) bool {
@@ -472,10 +512,7 @@
// Step 6: ensure that the dependencies outside of the APEX are all from the required SDKs
func sdkRequirementsMutator(mctx android.TopDownMutatorContext) {
- if m, ok := mctx.Module().(interface {
- android.DepIsInSameApex
- android.RequiredSdks
- }); ok {
+ if m, ok := mctx.Module().(sdkAndApexModule); ok {
requiredSdks := m.RequiredSdks()
if len(requiredSdks) == 0 {
return
@@ -494,9 +531,18 @@
return
}
- // If the dep is outside of the APEX, but is not in any of the
- // required SDKs, we know that the dep is a violation.
+ // If the dep is outside of the APEX, but is not in any of the required SDKs, we know that the
+ // dep is a violation.
if sa, ok := dep.(android.SdkAware); ok {
+ // It is not an error if a dependency that is excluded from the apex due to the tag is not
+ // in one of the required SDKs. That is because all of the existing tags that implement it
+ // do not depend on modules which can or should belong to an sdk_snapshot.
+ if _, ok := tag.(android.ExcludeFromApexContentsTag); ok {
+ // The tag defines a dependency that never requires the child module to be part of the
+ // same apex.
+ return
+ }
+
if !m.DepIsInSameApex(mctx, dep) && !requiredSdks.Contains(sa.ContainingSdk()) {
mctx.ModuleErrorf("depends on %q (in SDK %q) that isn't part of the required SDKs: %v",
sa.Name(), sa.ContainingSdk(), requiredSdks)
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index c4dc41b..b7da95c 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -31,7 +31,7 @@
os.Exit(0)
}
- runTestWithBuildDir(m)
+ os.Exit(m.Run())
}
func TestDepNotInRequiredSdks(t *testing.T) {
@@ -169,7 +169,7 @@
"package/Android.bp": []byte(packageBp),
})
- result.CheckSnapshot("mysdk", "package",
+ CheckSnapshot(t, result, "mysdk", "package",
checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -309,20 +309,16 @@
`)
}
-func TestSDkInstall(t *testing.T) {
+func TestSdkInstall(t *testing.T) {
sdk := `
sdk {
name: "mysdk",
}
`
- result := testSdkWithFs(t, ``,
- map[string][]byte{
- "Android.bp": []byte(sdk),
- })
+ result := testSdkWithFs(t, sdk, nil)
- result.CheckSnapshot("mysdk", "",
- checkAllOtherCopyRules(`.intermediates/mysdk/common_os/mysdk-current.zip -> mysdk-current.zip`),
- )
+ CheckSnapshot(t, result, "mysdk", "",
+ checkAllOtherCopyRules(`.intermediates/mysdk/common_os/mysdk-current.zip -> mysdk-current.zip`))
}
type EmbeddedPropertiesStruct struct {
@@ -390,12 +386,10 @@
extractor := newCommonValueExtractor(common)
- h := TestHelper{t}
-
err := extractor.extractCommonProperties(common, structs)
- h.AssertDeepEquals("unexpected error", nil, err)
+ android.AssertDeepEquals(t, "unexpected error", nil, err)
- h.AssertDeepEquals("common properties not correct",
+ android.AssertDeepEquals(t, "common properties not correct",
&testPropertiesStruct{
name: "common",
private: "",
@@ -413,7 +407,7 @@
},
common)
- h.AssertDeepEquals("updated properties[0] not correct",
+ android.AssertDeepEquals(t, "updated properties[0] not correct",
&testPropertiesStruct{
name: "struct-0",
private: "common",
@@ -431,7 +425,7 @@
},
structs[0])
- h.AssertDeepEquals("updated properties[1] not correct",
+ android.AssertDeepEquals(t, "updated properties[1] not correct",
&testPropertiesStruct{
name: "struct-1",
private: "common",
@@ -465,10 +459,8 @@
extractor := newCommonValueExtractor(common)
- h := TestHelper{t}
-
err := extractor.extractCommonProperties(common, structs)
- h.AssertErrorMessageEquals("unexpected error", `field "S_Common" is not tagged as "arch_variant" but has arch specific properties:
+ android.AssertErrorMessageEquals(t, "unexpected error", `field "S_Common" is not tagged as "arch_variant" but has arch specific properties:
"struct-0" has value "should-be-but-is-not-common0"
"struct-1" has value "should-be-but-is-not-common1"`, err)
}
diff --git a/sdk/testing.go b/sdk/testing.go
index 38755a9..9465e13 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -16,23 +16,21 @@
import (
"fmt"
- "io/ioutil"
- "os"
"path/filepath"
- "reflect"
"strings"
"testing"
"android/soong/android"
"android/soong/apex"
"android/soong/cc"
+ "android/soong/genrule"
"android/soong/java"
)
-func testSdkContext(bp string, fs map[string][]byte, extraOsTypes []android.OsType) (*android.TestContext, android.Config) {
- extraOsTypes = append(extraOsTypes, android.Android, android.Windows)
-
- bp = bp + `
+// Prepare for running an sdk test with an apex.
+var prepareForSdkTestWithApex = android.GroupFixturePreparers(
+ apex.PrepareForTestWithApexBuildComponents,
+ android.FixtureAddTextFile("sdk/tests/Android.bp", `
apex_key {
name: "myapex.key",
public_key: "myapex.avbpubkey",
@@ -43,132 +41,71 @@
name: "myapex.cert",
certificate: "myapex",
}
- ` + cc.GatherRequiredDepsForTest(extraOsTypes...)
+ `),
- mockFS := map[string][]byte{
- "build/make/target/product/security": nil,
+ android.FixtureMergeMockFs(map[string][]byte{
"apex_manifest.json": nil,
"system/sepolicy/apex/myapex-file_contexts": nil,
"system/sepolicy/apex/myapex2-file_contexts": nil,
"system/sepolicy/apex/mysdkapex-file_contexts": nil,
- "myapex.avbpubkey": nil,
- "myapex.pem": nil,
- "myapex.x509.pem": nil,
- "myapex.pk8": nil,
- }
+ "sdk/tests/myapex.avbpubkey": nil,
+ "sdk/tests/myapex.pem": nil,
+ "sdk/tests/myapex.x509.pem": nil,
+ "sdk/tests/myapex.pk8": nil,
+ }),
+)
- cc.GatherRequiredFilesForTest(mockFS)
+// Legacy preparer used for running tests within the sdk package.
+//
+// This includes everything that was needed to run any test in the sdk package prior to the
+// introduction of the test fixtures. Tests that are being converted to use fixtures directly
+// rather than through the testSdkError() and testSdkWithFs() methods should avoid using this and
+// instead should use the various preparers directly using android.GroupFixturePreparers(...) to
+// group them when necessary.
+//
+// deprecated
+var prepareForSdkTest = android.GroupFixturePreparers(
+ cc.PrepareForTestWithCcDefaultModules,
+ genrule.PrepareForTestWithGenRuleBuildComponents,
+ java.PrepareForTestWithJavaBuildComponents,
+ PrepareForTestWithSdkBuildComponents,
- for k, v := range fs {
- mockFS[k] = v
- }
+ prepareForSdkTestWithApex,
- config := android.TestArchConfig(buildDir, nil, bp, mockFS)
-
- // Add windows as a default disable OS to test behavior when some OS variants
- // are disabled.
- config.Targets[android.Windows] = []android.Target{
- {android.Windows, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", true},
- }
-
- for _, extraOsType := range extraOsTypes {
- switch extraOsType {
- case android.LinuxBionic:
- config.Targets[android.LinuxBionic] = []android.Target{
- {android.LinuxBionic, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", false},
- }
+ cc.PrepareForTestOnWindows,
+ android.FixtureModifyConfig(func(config android.Config) {
+ // Add windows as a default disable OS to test behavior when some OS variants
+ // are disabled.
+ config.Targets[android.Windows] = []android.Target{
+ {android.Windows, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", true},
}
- }
+ }),
- ctx := android.NewTestArchContext(config)
+ // Make sure that every test provides all the source files.
+ android.PrepareForTestDisallowNonExistentPaths,
+ android.MockFS{
+ "Test.java": nil,
+ }.AddToFixture(),
+)
- // Enable androidmk support.
- // * Register the singleton
- // * Configure that we are inside make
- // * Add CommonOS to ensure that androidmk processing works.
- android.RegisterAndroidMkBuildComponents(ctx)
- android.SetKatiEnabledForTests(config)
- config.Targets[android.CommonOS] = []android.Target{
- {android.CommonOS, android.Arch{ArchType: android.Common}, android.NativeBridgeDisabled, "", "", true},
- }
+var PrepareForTestWithSdkBuildComponents = android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(registerModuleExportsBuildComponents),
+ android.FixtureRegisterWithContext(registerSdkBuildComponents),
+)
- // from android package
- android.RegisterPackageBuildComponents(ctx)
- ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
- ctx.PreArchMutators(android.RegisterVisibilityRuleChecker)
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- ctx.PreArchMutators(android.RegisterComponentsMutator)
-
- android.RegisterPrebuiltMutators(ctx)
-
- // Register these after the prebuilt mutators have been registered to match what
- // happens at runtime.
- ctx.PreArchMutators(android.RegisterVisibilityRuleGatherer)
- ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
-
- // from java package
- java.RegisterJavaBuildComponents(ctx)
- java.RegisterAppBuildComponents(ctx)
- java.RegisterSdkLibraryBuildComponents(ctx)
- java.RegisterPrebuiltApisBuildComponents(ctx)
- java.RegisterStubsBuildComponents(ctx)
- java.RegisterSystemModulesBuildComponents(ctx)
-
- // from cc package
- cc.RegisterRequiredBuildComponentsForTest(ctx)
-
- // from apex package
- ctx.RegisterModuleType("apex", apex.BundleFactory)
- ctx.RegisterModuleType("apex_key", apex.ApexKeyFactory)
- ctx.PostDepsMutators(apex.RegisterPostDepsMutators)
-
- // from this package
- ctx.RegisterModuleType("sdk", SdkModuleFactory)
- ctx.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
- ctx.RegisterModuleType("module_exports", ModuleExportsFactory)
- ctx.RegisterModuleType("module_exports_snapshot", ModuleExportsSnapshotsFactory)
- ctx.PreDepsMutators(RegisterPreDepsMutators)
- ctx.PostDepsMutators(RegisterPostDepsMutators)
-
- ctx.Register()
-
- return ctx, config
-}
-
-func runTests(t *testing.T, ctx *android.TestContext, config android.Config) *testSdkResult {
+func testSdkWithFs(t *testing.T, bp string, fs android.MockFS) *android.TestResult {
t.Helper()
- _, errs := ctx.ParseBlueprintsFiles(".")
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
- return &testSdkResult{
- TestHelper: TestHelper{t: t},
- ctx: ctx,
- config: config,
- }
-}
-
-func testSdkWithFs(t *testing.T, bp string, fs map[string][]byte) *testSdkResult {
- t.Helper()
- ctx, config := testSdkContext(bp, fs, nil)
- return runTests(t, ctx, config)
+ return android.GroupFixturePreparers(
+ prepareForSdkTest,
+ fs.AddToFixture(),
+ ).RunTestWithBp(t, bp)
}
func testSdkError(t *testing.T, pattern, bp string) {
t.Helper()
- ctx, config := testSdkContext(bp, nil, nil)
- _, 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)
+ prepareForSdkTest.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
+ RunTestWithBp(t, bp)
}
func ensureListContains(t *testing.T, result []string, expected string) {
@@ -186,73 +123,18 @@
return ret
}
-// Provides general test support.
-type TestHelper struct {
- t *testing.T
-}
-
-func (h *TestHelper) AssertStringEquals(message string, expected string, actual string) {
- h.t.Helper()
- if actual != expected {
- h.t.Errorf("%s: expected %s, actual %s", message, expected, actual)
- }
-}
-
-func (h *TestHelper) AssertErrorMessageEquals(message string, expected string, actual error) {
- h.t.Helper()
- if actual == nil {
- h.t.Errorf("Expected error but was nil")
- } else if actual.Error() != expected {
- h.t.Errorf("%s: expected %s, actual %s", message, expected, actual.Error())
- }
-}
-
-func (h *TestHelper) AssertTrimmedStringEquals(message string, expected string, actual string) {
- h.t.Helper()
- h.AssertStringEquals(message, strings.TrimSpace(expected), strings.TrimSpace(actual))
-}
-
-func (h *TestHelper) AssertDeepEquals(message string, expected interface{}, actual interface{}) {
- h.t.Helper()
- if !reflect.DeepEqual(actual, expected) {
- h.t.Errorf("%s: expected %#v, actual %#v", message, expected, actual)
- }
-}
-
-func (h *TestHelper) AssertPanic(message string, funcThatShouldPanic func()) {
- h.t.Helper()
- panicked := false
- func() {
- defer func() {
- if x := recover(); x != nil {
- panicked = true
- }
- }()
- funcThatShouldPanic()
- }()
- if !panicked {
- h.t.Error(message)
- }
-}
-
-// Encapsulates result of processing an SDK definition. Provides support for
-// checking the state of the build structures.
-type testSdkResult struct {
- TestHelper
- ctx *android.TestContext
- config android.Config
-}
-
// Analyse the sdk build rules to extract information about what it is doing.
-
+//
// e.g. find the src/dest pairs from each cp command, the various zip files
// generated, etc.
-func (r *testSdkResult) getSdkSnapshotBuildInfo(sdk *sdk) *snapshotBuildInfo {
- androidBpContents := sdk.GetAndroidBpContentsForTests()
-
+func getSdkSnapshotBuildInfo(t *testing.T, result *android.TestResult, sdk *sdk) *snapshotBuildInfo {
info := &snapshotBuildInfo{
- r: r,
- androidBpContents: androidBpContents,
+ t: t,
+ r: result,
+ androidBpContents: sdk.GetAndroidBpContentsForTests(),
+ androidUnversionedBpContents: sdk.GetUnversionedAndroidBpContentsForTests(),
+ androidVersionedBpContents: sdk.GetVersionedAndroidBpContentsForTests(),
+ snapshotTestCustomizations: map[snapshotTest]*snapshotTestCustomization{},
}
buildParams := sdk.BuildParamsForTests()
@@ -292,7 +174,7 @@
info.intermediateZip = info.outputZip
mergeInput := android.NormalizePathForTesting(bp.Input)
if info.intermediateZip != mergeInput {
- r.t.Errorf("Expected intermediate zip %s to be an input to merge zips but found %s instead",
+ t.Errorf("Expected intermediate zip %s to be an input to merge zips but found %s instead",
info.intermediateZip, mergeInput)
}
@@ -311,28 +193,38 @@
return info
}
-func (r *testSdkResult) Module(name string, variant string) android.Module {
- return r.ctx.ModuleForTests(name, variant).Module()
-}
+// The enum of different sdk snapshot tests performed by CheckSnapshot.
+type snapshotTest int
-func (r *testSdkResult) ModuleForTests(name string, variant string) android.TestingModule {
- return r.ctx.ModuleForTests(name, variant)
-}
+const (
+ // The enumeration of the different test configurations.
+ // A test with the snapshot/Android.bp file but without the original Android.bp file.
+ checkSnapshotWithoutSource snapshotTest = iota
+
+ // A test with both the original source and the snapshot, with the source preferred.
+ checkSnapshotWithSourcePreferred
+
+ // A test with both the original source and the snapshot, with the snapshot preferred.
+ checkSnapshotPreferredWithSource
+
+ // The directory into which the snapshot will be 'unpacked'.
+ snapshotSubDir = "snapshot"
+)
// Check the snapshot build rules.
//
// Takes a list of functions which check different facets of the snapshot build rules.
// Allows each test to customize what is checked without duplicating lots of code
// or proliferating check methods of different flavors.
-func (r *testSdkResult) CheckSnapshot(name string, dir string, checkers ...snapshotBuildInfoChecker) {
- r.t.Helper()
+func CheckSnapshot(t *testing.T, result *android.TestResult, name string, dir string, checkers ...snapshotBuildInfoChecker) {
+ t.Helper()
// The sdk CommonOS variant is always responsible for generating the snapshot.
variant := android.CommonOS.Name
- sdk := r.Module(name, variant).(*sdk)
+ sdk := result.Module(name, variant).(*sdk)
- snapshotBuildInfo := r.getSdkSnapshotBuildInfo(sdk)
+ snapshotBuildInfo := getSdkSnapshotBuildInfo(t, result, sdk)
// Check state of the snapshot build.
for _, checker := range checkers {
@@ -344,18 +236,65 @@
if dir != "" {
dir = filepath.Clean(dir) + "/"
}
- r.AssertStringEquals("Snapshot zip file in wrong place",
+ android.AssertStringEquals(t, "Snapshot zip file in wrong place",
fmt.Sprintf(".intermediates/%s%s/%s/%s-current.zip", dir, name, variant, name), actual)
// Populate a mock filesystem with the files that would have been copied by
// the rules.
- fs := make(map[string][]byte)
+ fs := android.MockFS{}
for _, dest := range snapshotBuildInfo.snapshotContents {
- fs[dest] = nil
+ fs[filepath.Join(snapshotSubDir, dest)] = nil
+ }
+ fs[filepath.Join(snapshotSubDir, "Android.bp")] = []byte(snapshotBuildInfo.androidBpContents)
+
+ // The preparers from the original source fixture.
+ sourcePreparers := result.Preparer()
+
+ // Preparer to combine the snapshot and the source.
+ snapshotPreparer := android.GroupFixturePreparers(sourcePreparers, fs.AddToFixture())
+
+ var runSnapshotTestWithCheckers = func(t *testing.T, testConfig snapshotTest, extraPreparer android.FixturePreparer) {
+ customization := snapshotBuildInfo.snapshotTestCustomization(testConfig)
+
+ // TODO(b/183184375): Set Config.TestAllowNonExistentPaths = false to verify that all the
+ // files the snapshot needs are actually copied into the snapshot.
+
+ // Run the snapshot with the snapshot preparer and the extra preparer, which must come after as
+ // it may need to modify parts of the MockFS populated by the snapshot preparer.
+ result := android.GroupFixturePreparers(snapshotPreparer, extraPreparer).
+ ExtendWithErrorHandler(customization.errorHandler).
+ RunTest(t)
+
+ // Perform any additional checks the test need on the result of processing the snapshot.
+ for _, checker := range customization.checkers {
+ checker(t, result)
+ }
}
- // Process the generated bp file to make sure it is valid.
- testSdkWithFs(r.t, snapshotBuildInfo.androidBpContents, fs)
+ t.Run("snapshot without source", func(t *testing.T) {
+ // Remove the source Android.bp file to make sure it works without.
+ removeSourceAndroidBp := android.FixtureModifyMockFS(func(fs android.MockFS) {
+ delete(fs, "Android.bp")
+ })
+
+ runSnapshotTestWithCheckers(t, checkSnapshotWithoutSource, removeSourceAndroidBp)
+ })
+
+ t.Run("snapshot with source preferred", func(t *testing.T) {
+ runSnapshotTestWithCheckers(t, checkSnapshotWithSourcePreferred, android.NullFixturePreparer)
+ })
+
+ t.Run("snapshot preferred with source", func(t *testing.T) {
+ // Replace the snapshot/Android.bp file with one where "prefer: false," has been replaced with
+ // "prefer: true,"
+ preferPrebuilts := android.FixtureModifyMockFS(func(fs android.MockFS) {
+ snapshotBpFile := filepath.Join(snapshotSubDir, "Android.bp")
+ unpreferred := string(fs[snapshotBpFile])
+ fs[snapshotBpFile] = []byte(strings.ReplaceAll(unpreferred, "prefer: false,", "prefer: true,"))
+ })
+
+ runSnapshotTestWithCheckers(t, checkSnapshotPreferredWithSource, preferPrebuilts)
+ })
}
type snapshotBuildInfoChecker func(info *snapshotBuildInfo)
@@ -365,8 +304,35 @@
// Both the expected and actual string are both trimmed before comparing.
func checkAndroidBpContents(expected string) snapshotBuildInfoChecker {
return func(info *snapshotBuildInfo) {
- info.r.t.Helper()
- info.r.AssertTrimmedStringEquals("Android.bp contents do not match", expected, info.androidBpContents)
+ info.t.Helper()
+ android.AssertTrimmedStringEquals(info.t, "Android.bp contents do not match", expected, info.androidBpContents)
+ }
+}
+
+// Check that the snapshot's unversioned generated Android.bp is correct.
+//
+// This func should be used to check the general snapshot generation code.
+//
+// Both the expected and actual string are both trimmed before comparing.
+func checkUnversionedAndroidBpContents(expected string) snapshotBuildInfoChecker {
+ return func(info *snapshotBuildInfo) {
+ info.t.Helper()
+ android.AssertTrimmedStringEquals(info.t, "unversioned Android.bp contents do not match", expected, info.androidUnversionedBpContents)
+ }
+}
+
+// Check that the snapshot's versioned generated Android.bp is correct.
+//
+// This func should only be used to check the version specific snapshot generation code,
+// i.e. the encoding of version into module names and the generation of the _snapshot module. The
+// general snapshot generation code should be checked using the checkUnversionedAndroidBpContents()
+// func.
+//
+// Both the expected and actual string are both trimmed before comparing.
+func checkVersionedAndroidBpContents(expected string) snapshotBuildInfoChecker {
+ return func(info *snapshotBuildInfo) {
+ info.t.Helper()
+ android.AssertTrimmedStringEquals(info.t, "versioned Android.bp contents do not match", expected, info.androidVersionedBpContents)
}
}
@@ -377,41 +343,90 @@
// before comparing.
func checkAllCopyRules(expected string) snapshotBuildInfoChecker {
return func(info *snapshotBuildInfo) {
- info.r.t.Helper()
- info.r.AssertTrimmedStringEquals("Incorrect copy rules", expected, info.copyRules)
+ info.t.Helper()
+ android.AssertTrimmedStringEquals(info.t, "Incorrect copy rules", expected, info.copyRules)
}
}
func checkAllOtherCopyRules(expected string) snapshotBuildInfoChecker {
return func(info *snapshotBuildInfo) {
- info.r.t.Helper()
- info.r.AssertTrimmedStringEquals("Incorrect copy rules", expected, info.otherCopyRules)
+ info.t.Helper()
+ android.AssertTrimmedStringEquals(info.t, "Incorrect copy rules", expected, info.otherCopyRules)
}
}
// Check that the specified paths match the list of zips to merge with the intermediate zip.
func checkMergeZips(expected ...string) snapshotBuildInfoChecker {
return func(info *snapshotBuildInfo) {
- info.r.t.Helper()
+ info.t.Helper()
if info.intermediateZip == "" {
- info.r.t.Errorf("No intermediate zip file was created")
+ info.t.Errorf("No intermediate zip file was created")
}
- info.r.AssertDeepEquals("mismatching merge zip files", expected, info.mergeZips)
+ android.AssertDeepEquals(info.t, "mismatching merge zip files", expected, info.mergeZips)
}
}
+type resultChecker func(t *testing.T, result *android.TestResult)
+
+// snapshotTestChecker registers a checker that will be run against the result of processing the
+// generated snapshot for the specified snapshotTest.
+func snapshotTestChecker(snapshotTest snapshotTest, checker resultChecker) snapshotBuildInfoChecker {
+ return func(info *snapshotBuildInfo) {
+ customization := info.snapshotTestCustomization(snapshotTest)
+ customization.checkers = append(customization.checkers, checker)
+ }
+}
+
+// snapshotTestErrorHandler registers an error handler to use when processing the snapshot
+// in the specific test case.
+//
+// Generally, the snapshot should work with all the test cases but some do not and just in case
+// there are a lot of issues to resolve, or it will take a lot of time this is a
+// get-out-of-jail-free card that allows progress to be made.
+//
+// deprecated: should only be used as a temporary workaround with an attached to do and bug.
+func snapshotTestErrorHandler(snapshotTest snapshotTest, handler android.FixtureErrorHandler) snapshotBuildInfoChecker {
+ return func(info *snapshotBuildInfo) {
+ customization := info.snapshotTestCustomization(snapshotTest)
+ customization.errorHandler = handler
+ }
+}
+
+// Encapsulates information provided by each test to customize a specific snapshotTest.
+type snapshotTestCustomization struct {
+ // Checkers that are run on the result of processing the preferred snapshot in a specific test
+ // case.
+ checkers []resultChecker
+
+ // Specify an error handler for when processing a specific test case.
+ //
+ // In some cases the generated snapshot cannot be used in a test configuration. Those cases are
+ // invariably bugs that need to be resolved but sometimes that can take a while. This provides a
+ // mechanism to temporarily ignore that error.
+ errorHandler android.FixtureErrorHandler
+}
+
// Encapsulates information about the snapshot build structure in order to insulate tests from
// knowing too much about internal structures.
//
// All source/input paths are relative either the build directory. All dest/output paths are
// relative to the snapshot root directory.
type snapshotBuildInfo struct {
- r *testSdkResult
+ t *testing.T
+
+ // The result from RunTest()
+ r *android.TestResult
// The contents of the generated Android.bp file
androidBpContents string
+ // The contents of the unversioned Android.bp file
+ androidUnversionedBpContents string
+
+ // The contents of the versioned Android.bp file
+ androidVersionedBpContents string
+
// The paths, relative to the snapshot root, of all files and directories copied into the
// snapshot.
snapshotContents []string
@@ -435,29 +450,21 @@
// The final output zip.
outputZip string
+
+ // The test specific customizations for each snapshot test.
+ snapshotTestCustomizations map[snapshotTest]*snapshotTestCustomization
}
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_sdk_test")
- if err != nil {
- panic(err)
+// snapshotTestCustomization gets the test specific customization for the specified snapshotTest.
+//
+// If no customization was created previously then it creates a default customization.
+func (i *snapshotBuildInfo) snapshotTestCustomization(snapshotTest snapshotTest) *snapshotTestCustomization {
+ customization := i.snapshotTestCustomizations[snapshotTest]
+ if customization == nil {
+ customization = &snapshotTestCustomization{
+ errorHandler: android.FixtureExpectsNoErrors,
+ }
+ i.snapshotTestCustomizations[snapshotTest] = customization
}
-}
-
-func tearDown() {
- _ = os.RemoveAll(buildDir)
-}
-
-func runTestWithBuildDir(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ return customization
}
diff --git a/sdk/update.go b/sdk/update.go
index 377aaae..522a888 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -572,12 +572,20 @@
}
func generateBpContents(contents *generatedContents, bpFile *bpFile) {
+ generateFilteredBpContents(contents, bpFile, func(*bpModule) bool {
+ return true
+ })
+}
+
+func generateFilteredBpContents(contents *generatedContents, bpFile *bpFile, moduleFilter func(module *bpModule) bool) {
contents.Printfln("// This is auto-generated. DO NOT EDIT.")
for _, bpModule := range bpFile.order {
- contents.Printfln("")
- contents.Printfln("%s {", bpModule.moduleType)
- outputPropertySet(contents, bpModule.bpPropertySet)
- contents.Printfln("}")
+ if moduleFilter(bpModule) {
+ contents.Printfln("")
+ contents.Printfln("%s {", bpModule.moduleType)
+ outputPropertySet(contents, bpModule.bpPropertySet)
+ contents.Printfln("}")
+ }
}
}
@@ -639,6 +647,22 @@
return contents.content.String()
}
+func (s *sdk) GetUnversionedAndroidBpContentsForTests() string {
+ contents := &generatedContents{}
+ generateFilteredBpContents(contents, s.builderForTests.bpFile, func(module *bpModule) bool {
+ return !strings.Contains(module.properties["name"].(string), "@")
+ })
+ return contents.content.String()
+}
+
+func (s *sdk) GetVersionedAndroidBpContentsForTests() string {
+ contents := &generatedContents{}
+ generateFilteredBpContents(contents, s.builderForTests.bpFile, func(module *bpModule) bool {
+ return strings.Contains(module.properties["name"].(string), "@")
+ })
+ return contents.content.String()
+}
+
type snapshotBuilder struct {
ctx android.ModuleContext
sdk *sdk
@@ -1326,7 +1350,7 @@
// Compute the list of possible os types that this sdk could support.
func (s *sdk) getPossibleOsTypes() []android.OsType {
var osTypes []android.OsType
- for _, osType := range android.OsTypeList {
+ for _, osType := range android.OsTypeList() {
if s.DeviceSupported() {
if osType.Class == android.Device && osType != android.Fuchsia {
osTypes = append(osTypes, osType)
diff --git a/sh/Android.bp b/sh/Android.bp
index e5ffeef..f9198dc 100644
--- a/sh/Android.bp
+++ b/sh/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-sh",
pkgPath: "android/soong/sh",
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 8db33a3..42d5680 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -24,6 +24,7 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/bazel"
"android/soong/cc"
"android/soong/tradefed"
)
@@ -39,12 +40,32 @@
func init() {
pctx.Import("android/soong/android")
- android.RegisterModuleType("sh_binary", ShBinaryFactory)
- android.RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
- android.RegisterModuleType("sh_test", ShTestFactory)
- android.RegisterModuleType("sh_test_host", ShTestHostFactory)
+ registerShBuildComponents(android.InitRegistrationContext)
+
+ android.RegisterBp2BuildMutator("sh_binary", ShBinaryBp2Build)
}
+func registerShBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("sh_binary", ShBinaryFactory)
+ ctx.RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
+ ctx.RegisterModuleType("sh_test", ShTestFactory)
+ ctx.RegisterModuleType("sh_test_host", ShTestHostFactory)
+}
+
+// Test fixture preparer that will register most sh build components.
+//
+// Singletons and mutators should only be added here if they are needed for a majority of sh
+// module types, otherwise they should be added under a separate preparer to allow them to be
+// selected only when needed to reduce test execution time.
+//
+// Module types do not have much of an overhead unless they are used so this should include as many
+// module types as possible. The exceptions are those module types that require mutators and/or
+// singletons in order to function in which case they should be kept together in a separate
+// preparer.
+var PrepareForTestWithShBuildComponents = android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(registerShBuildComponents),
+)
+
type shBinaryProperties struct {
// Source file of this prebuilt.
Src *string `android:"path,arch_variant"`
@@ -126,6 +147,7 @@
type ShBinary struct {
android.ModuleBase
+ android.BazelModuleBase
properties shBinaryProperties
@@ -154,9 +176,6 @@
}
func (s *ShBinary) DepsMutator(ctx android.BottomUpMutatorContext) {
- if s.properties.Src == nil {
- ctx.PropertyErrorf("src", "missing prebuilt source file")
- }
}
func (s *ShBinary) OutputFile() android.OutputPath {
@@ -191,6 +210,10 @@
return proptools.Bool(s.properties.Vendor_ramdisk_available) || s.ModuleBase.InstallInVendorRamdisk()
}
+func (s *ShBinary) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
func (s *ShBinary) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
return proptools.Bool(s.properties.Recovery_available) || s.ModuleBase.InstallInRecovery()
}
@@ -203,6 +226,10 @@
}
func (s *ShBinary) generateAndroidBuildActions(ctx android.ModuleContext) {
+ if s.properties.Src == nil {
+ ctx.PropertyErrorf("src", "missing prebuilt source file")
+ }
+
s.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(s.properties.Src))
filename := proptools.String(s.properties.Filename)
filenameFromSrc := proptools.Bool(s.properties.Filename_from_src)
@@ -239,7 +266,7 @@
OutputFile: android.OptionalPathForPath(s.outputFilePath),
Include: "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
s.customAndroidMkEntries(entries)
entries.SetString("LOCAL_MODULE_RELATIVE_PATH", proptools.String(s.properties.Sub_dir))
},
@@ -275,7 +302,7 @@
ctx.AddFarVariationDependencies(ctx.Target().Variations(), shTestDataBinsTag, s.testProperties.Data_bins...)
ctx.AddFarVariationDependencies(append(ctx.Target().Variations(), sharedLibVariations...),
shTestDataLibsTag, s.testProperties.Data_libs...)
- if ctx.Target().Os.Class == android.Host && len(ctx.Config().Targets[android.Android]) > 0 {
+ if (ctx.Target().Os.Class == android.Host || ctx.BazelConversionMode()) && len(ctx.Config().Targets[android.Android]) > 0 {
deviceVariations := ctx.Config().AndroidFirstDeviceTarget.Variations()
ctx.AddFarVariationDependencies(deviceVariations, shTestDataDeviceBinsTag, s.testProperties.Data_device_bins...)
ctx.AddFarVariationDependencies(append(deviceVariations, sharedLibVariations...),
@@ -345,15 +372,8 @@
depTag := ctx.OtherModuleDependencyTag(dep)
switch depTag {
case shTestDataBinsTag, shTestDataDeviceBinsTag:
- if cc, isCc := dep.(*cc.Module); isCc {
- s.addToDataModules(ctx, cc.OutputFile().Path().Base(), cc.OutputFile().Path())
- return
- }
- property := "data_bins"
- if depTag == shTestDataDeviceBinsTag {
- property = "data_device_bins"
- }
- ctx.PropertyErrorf(property, "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep))
+ path := android.OutputFileForModule(ctx, dep, "")
+ s.addToDataModules(ctx, path.Base(), path)
case shTestDataLibsTag, shTestDataDeviceLibsTag:
if cc, isCc := dep.(*cc.Module); isCc {
// Copy to an intermediate output directory to append "lib[64]" to the path,
@@ -395,7 +415,7 @@
OutputFile: android.OptionalPathForPath(s.outputFilePath),
Include: "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
s.customAndroidMkEntries(entries)
entries.SetPath("LOCAL_MODULE_PATH", s.installDir.ToMakePath())
entries.AddCompatibilityTestSuites(s.testProperties.Test_suites...)
@@ -427,6 +447,7 @@
func InitShBinaryModule(s *ShBinary) {
s.AddProperties(&s.properties)
+ android.InitBazelModule(s)
}
// sh_binary is for a shell script or batch file to be installed as an
@@ -467,4 +488,65 @@
return module
}
+type bazelShBinaryAttributes struct {
+ Srcs bazel.LabelListAttribute
+ // Bazel also supports the attributes below, but (so far) these are not required for Bionic
+ // deps
+ // data
+ // args
+ // compatible_with
+ // deprecation
+ // distribs
+ // env
+ // exec_compatible_with
+ // exec_properties
+ // features
+ // licenses
+ // output_licenses
+ // restricted_to
+ // tags
+ // target_compatible_with
+ // testonly
+ // toolchains
+ // visibility
+}
+
+type bazelShBinary struct {
+ android.BazelTargetModuleBase
+ bazelShBinaryAttributes
+}
+
+func BazelShBinaryFactory() android.Module {
+ module := &bazelShBinary{}
+ module.AddProperties(&module.bazelShBinaryAttributes)
+ android.InitBazelTargetModule(module)
+ return module
+}
+
+func ShBinaryBp2Build(ctx android.TopDownMutatorContext) {
+ m, ok := ctx.Module().(*ShBinary)
+ if !ok || !m.ConvertWithBp2build(ctx) {
+ return
+ }
+
+ srcs := bazel.MakeLabelListAttribute(
+ android.BazelLabelForModuleSrc(ctx, []string{*m.properties.Src}))
+
+ attrs := &bazelShBinaryAttributes{
+ Srcs: srcs,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "sh_binary",
+ }
+
+ ctx.CreateBazelTargetModule(BazelShBinaryFactory, m.Name(), props, attrs)
+}
+
+func (m *bazelShBinary) Name() string {
+ return m.BaseModuleName()
+}
+
+func (m *bazelShBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+
var Bool = proptools.Bool
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index c664461..9e7e594 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -1,63 +1,43 @@
package sh
import (
- "io/ioutil"
"os"
"path/filepath"
- "reflect"
"testing"
"android/soong/android"
"android/soong/cc"
)
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_sh_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ os.Exit(m.Run())
}
-func testShBinary(t *testing.T, bp string) (*android.TestContext, android.Config) {
- fs := map[string][]byte{
+var prepareForShTest = android.GroupFixturePreparers(
+ cc.PrepareForTestWithCcBuildComponents,
+ PrepareForTestWithShBuildComponents,
+ android.FixtureMergeMockFs(android.MockFS{
"test.sh": nil,
"testdata/data1": nil,
"testdata/sub/data2": nil,
- }
+ }),
+)
- config := android.TestArchConfig(buildDir, nil, bp, fs)
+// testShBinary runs tests using the prepareForShTest
+//
+// Do not add any new usages of this, instead use the prepareForShTest directly as it makes it much
+// easier to customize the test behavior.
+//
+// If it is necessary to customize the behavior of an existing test that uses this then please first
+// convert the test to using prepareForShTest first and then in a following change add the
+// appropriate fixture preparers. Keeping the conversion change separate makes it easy to verify
+// that it did not change the test behavior unexpectedly.
+//
+// deprecated
+func testShBinary(t *testing.T, bp string) (*android.TestContext, android.Config) {
+ result := prepareForShTest.RunTestWithBp(t, bp)
- ctx := android.NewTestArchContext(config)
- ctx.RegisterModuleType("sh_test", ShTestFactory)
- ctx.RegisterModuleType("sh_test_host", ShTestHostFactory)
-
- cc.RegisterRequiredBuildComponentsForTest(ctx)
-
- ctx.Register()
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
-
- return ctx, config
+ return result.TestContext, result.Config
}
func TestShTestSubDir(t *testing.T) {
@@ -71,13 +51,11 @@
mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
- entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+ entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
- expectedPath := "/tmp/target/product/test_device/data/nativetest64/foo_test"
+ expectedPath := "out/target/product/test_device/data/nativetest64/foo_test"
actualPath := entries.EntryMap["LOCAL_MODULE_PATH"][0]
- if expectedPath != actualPath {
- t.Errorf("Unexpected LOCAL_MODULE_PATH expected: %q, actual: %q", expectedPath, actualPath)
- }
+ android.AssertStringPathRelativeToTopEquals(t, "LOCAL_MODULE_PATH[0]", config, expectedPath, actualPath)
}
func TestShTest(t *testing.T) {
@@ -95,19 +73,15 @@
mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
- entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+ entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
- expectedPath := "/tmp/target/product/test_device/data/nativetest64/foo"
+ expectedPath := "out/target/product/test_device/data/nativetest64/foo"
actualPath := entries.EntryMap["LOCAL_MODULE_PATH"][0]
- if expectedPath != actualPath {
- t.Errorf("Unexpected LOCAL_MODULE_PATH expected: %q, actual: %q", expectedPath, actualPath)
- }
+ android.AssertStringPathRelativeToTopEquals(t, "LOCAL_MODULE_PATH[0]", config, expectedPath, actualPath)
expectedData := []string{":testdata/data1", ":testdata/sub/data2"}
actualData := entries.EntryMap["LOCAL_TEST_DATA"]
- if !reflect.DeepEqual(expectedData, actualData) {
- t.Errorf("Unexpected test data expected: %q, actual: %q", expectedData, actualData)
- }
+ android.AssertDeepEquals(t, "LOCAL_TEST_DATA", expectedData, actualData)
}
func TestShTest_dataModules(t *testing.T) {
@@ -150,22 +124,17 @@
libExt = ".dylib"
}
relocated := variant.Output("relocated/lib64/libbar" + libExt)
- expectedInput := filepath.Join(buildDir, ".intermediates/libbar/"+arch+"_shared/libbar"+libExt)
- if relocated.Input.String() != expectedInput {
- t.Errorf("Unexpected relocation input, expected: %q, actual: %q",
- expectedInput, relocated.Input.String())
- }
+ expectedInput := "out/soong/.intermediates/libbar/" + arch + "_shared/libbar" + libExt
+ android.AssertPathRelativeToTopEquals(t, "relocation input", expectedInput, relocated.Input)
mod := variant.Module().(*ShTest)
- entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+ entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
expectedData := []string{
- filepath.Join(buildDir, ".intermediates/bar", arch, ":bar"),
- filepath.Join(buildDir, ".intermediates/foo", arch, "relocated/:lib64/libbar"+libExt),
+ filepath.Join("out/soong/.intermediates/bar", arch, ":bar"),
+ filepath.Join("out/soong/.intermediates/foo", arch, "relocated/:lib64/libbar"+libExt),
}
actualData := entries.EntryMap["LOCAL_TEST_DATA"]
- if !reflect.DeepEqual(expectedData, actualData) {
- t.Errorf("Unexpected test data, expected: %q, actual: %q", expectedData, actualData)
- }
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", config, expectedData, actualData)
}
}
@@ -220,21 +189,16 @@
variant := ctx.ModuleForTests("foo", buildOS+"_x86_64")
relocated := variant.Output("relocated/lib64/libbar.so")
- expectedInput := filepath.Join(buildDir, ".intermediates/libbar/android_arm64_armv8-a_shared/libbar.so")
- if relocated.Input.String() != expectedInput {
- t.Errorf("Unexpected relocation input, expected: %q, actual: %q",
- expectedInput, relocated.Input.String())
- }
+ expectedInput := "out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so"
+ android.AssertPathRelativeToTopEquals(t, "relocation input", expectedInput, relocated.Input)
mod := variant.Module().(*ShTest)
- entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+ entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
expectedData := []string{
- filepath.Join(buildDir, ".intermediates/bar/android_arm64_armv8-a/:bar"),
+ "out/soong/.intermediates/bar/android_arm64_armv8-a/:bar",
// libbar has been relocated, and so has a variant that matches the host arch.
- filepath.Join(buildDir, ".intermediates/foo/"+buildOS+"_x86_64/relocated/:lib64/libbar.so"),
+ "out/soong/.intermediates/foo/" + buildOS + "_x86_64/relocated/:lib64/libbar.so",
}
actualData := entries.EntryMap["LOCAL_TEST_DATA"]
- if !reflect.DeepEqual(expectedData, actualData) {
- t.Errorf("Unexpected test data, expected: %q, actual: %q", expectedData, actualData)
- }
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", config, expectedData, actualData)
}
diff --git a/shared/Android.bp b/shared/Android.bp
index 2a4f56f..deb17f8 100644
--- a/shared/Android.bp
+++ b/shared/Android.bp
@@ -1,8 +1,17 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-shared",
pkgPath: "android/soong/shared",
srcs: [
+ "env.go",
"paths.go",
+ "debug.go",
+ ],
+ testSrcs: [
+ "paths_test.go",
],
deps: [
"soong-bazel",
diff --git a/shared/debug.go b/shared/debug.go
new file mode 100644
index 0000000..5392f2b
--- /dev/null
+++ b/shared/debug.go
@@ -0,0 +1,69 @@
+package shared
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "strings"
+ "syscall"
+)
+
+var (
+ isDebugging bool
+)
+
+// Finds the Delve binary to use. Either uses the SOONG_DELVE_PATH environment
+// variable or if that is unset, looks at $PATH.
+func ResolveDelveBinary() string {
+ result := os.Getenv("SOONG_DELVE_PATH")
+ if result == "" {
+ result, _ = exec.LookPath("dlv")
+ }
+
+ return result
+}
+
+// Returns whether the current process is running under Delve due to
+// ReexecWithDelveMaybe().
+func IsDebugging() bool {
+ return isDebugging
+}
+
+// Re-executes the binary in question under the control of Delve when
+// delveListen is not the empty string. delvePath gives the path to the Delve.
+func ReexecWithDelveMaybe(delveListen, delvePath string) {
+ isDebugging = os.Getenv("SOONG_DELVE_REEXECUTED") == "true"
+ if isDebugging || delveListen == "" {
+ return
+ }
+
+ if delvePath == "" {
+ fmt.Fprintln(os.Stderr, "Delve debugging requested but failed to find dlv")
+ os.Exit(1)
+ }
+
+ soongDelveEnv := []string{}
+ for _, env := range os.Environ() {
+ idx := strings.IndexRune(env, '=')
+ if idx != -1 {
+ soongDelveEnv = append(soongDelveEnv, env)
+ }
+ }
+
+ soongDelveEnv = append(soongDelveEnv, "SOONG_DELVE_REEXECUTED=true")
+
+ dlvArgv := []string{
+ delvePath,
+ "--listen=:" + delveListen,
+ "--headless=true",
+ "--api-version=2",
+ "exec",
+ os.Args[0],
+ "--",
+ }
+
+ dlvArgv = append(dlvArgv, os.Args[1:]...)
+ syscall.Exec(delvePath, dlvArgv, soongDelveEnv)
+ fmt.Fprintln(os.Stderr, "exec() failed while trying to reexec with Delve")
+ os.Exit(1)
+}
diff --git a/env/env.go b/shared/env.go
similarity index 76%
rename from env/env.go
rename to shared/env.go
index 735a38a..152729b 100644
--- a/env/env.go
+++ b/shared/env.go
@@ -12,15 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// env implements the environment JSON file handling for the soong_env command line tool run before
-// the builder and for the env writer in the builder.
-package env
+// Implements the environment JSON file handling for serializing the
+// environment variables that were used in soong_build so that soong_ui can
+// check whether they have changed
+package shared
import (
"encoding/json"
"fmt"
"io/ioutil"
- "os"
"sort"
)
@@ -57,7 +57,7 @@
// Reads and deserializes a Soong environment file located at the given file path to determine its
// staleness. If any environment variable values have changed, it prints them out and returns true.
// Failing to read or parse the file also causes it to return true.
-func StaleEnvFile(filepath string) (bool, error) {
+func StaleEnvFile(filepath string, getenv func(string) string) (bool, error) {
data, err := ioutil.ReadFile(filepath)
if err != nil {
return true, err
@@ -74,7 +74,7 @@
for _, entry := range contents {
key := entry.Key
old := entry.Value
- cur := os.Getenv(key)
+ cur := getenv(key)
if old != cur {
changed = append(changed, fmt.Sprintf("%s (%q -> %q)", key, old, cur))
}
@@ -91,6 +91,28 @@
return false, nil
}
+// Deserializes and environment serialized by EnvFileContents() and returns it
+// as a map[string]string.
+func EnvFromFile(envFile string) (map[string]string, error) {
+ result := make(map[string]string)
+ data, err := ioutil.ReadFile(envFile)
+ if err != nil {
+ return result, err
+ }
+
+ var contents envFileData
+ err = json.Unmarshal(data, &contents)
+ if err != nil {
+ return result, err
+ }
+
+ for _, entry := range contents {
+ result[entry.Key] = entry.Value
+ }
+
+ return result, nil
+}
+
// Implements sort.Interface so that we can use sort.Sort on envFileData arrays.
func (e envFileData) Len() int {
return len(e)
diff --git a/shared/paths.go b/shared/paths.go
index 1b9ff60..fca8b4c 100644
--- a/shared/paths.go
+++ b/shared/paths.go
@@ -30,6 +30,21 @@
BazelMetricsDir() string
}
+// Joins the path strings in the argument list, taking absolute paths into
+// account. That is, if one of the strings is an absolute path, the ones before
+// are ignored.
+func JoinPath(base string, rest ...string) string {
+ result := base
+ for _, next := range rest {
+ if filepath.IsAbs(next) {
+ result = next
+ } else {
+ result = filepath.Join(result, next)
+ }
+ }
+ return result
+}
+
// Given the out directory, returns the root of the temp directory (to be cleared at the start of each execution of Soong)
func TempDirForOutDir(outDir string) (tempPath string) {
return filepath.Join(outDir, ".temp")
diff --git a/shared/paths_test.go b/shared/paths_test.go
new file mode 100644
index 0000000..018d55f
--- /dev/null
+++ b/shared/paths_test.go
@@ -0,0 +1,32 @@
+// Copyright 2021 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 shared
+
+import (
+ "testing"
+)
+
+func assertEqual(t *testing.T, expected, actual string) {
+ t.Helper()
+ if expected != actual {
+ t.Errorf("expected %q != got %q", expected, actual)
+ }
+}
+
+func TestJoinPath(t *testing.T) {
+ assertEqual(t, "/a/b", JoinPath("c/d", "/a/b"))
+ assertEqual(t, "a/b", JoinPath("a", "b"))
+ assertEqual(t, "/a/b", JoinPath("x", "/a", "b"))
+}
diff --git a/symbol_inject/Android.bp b/symbol_inject/Android.bp
index 8308043..7180248 100644
--- a/symbol_inject/Android.bp
+++ b/symbol_inject/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-symbol_inject",
pkgPath: "android/soong/symbol_inject",
diff --git a/symbol_inject/cmd/Android.bp b/symbol_inject/cmd/Android.bp
index ee2f259..ac23f00 100644
--- a/symbol_inject/cmd/Android.bp
+++ b/symbol_inject/cmd/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "symbol_inject",
deps: ["soong-symbol_inject"],
diff --git a/sysprop/Android.bp b/sysprop/Android.bp
index 48094f1..1d5eb31 100644
--- a/sysprop/Android.bp
+++ b/sysprop/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-sysprop",
pkgPath: "android/soong/sysprop",
@@ -10,6 +14,7 @@
],
srcs: [
"sysprop_library.go",
+ "testing.go",
],
testSrcs: [
"sysprop_test.go",
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 42caff2..f1c2d0d 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -19,6 +19,7 @@
import (
"fmt"
"io"
+ "os"
"path"
"sync"
@@ -36,9 +37,10 @@
}
type syspropGenProperties struct {
- Srcs []string `android:"path"`
- Scope string
- Name *string
+ Srcs []string `android:"path"`
+ Scope string
+ Name *string
+ Check_api *string
}
type syspropJavaGenRule struct {
@@ -67,10 +69,6 @@
func init() {
pctx.HostBinToolVariable("soongZipCmd", "soong_zip")
pctx.HostBinToolVariable("syspropJavaCmd", "sysprop_java")
-
- android.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.BottomUp("sysprop_deps", syspropDepsMutator).Parallel()
- })
}
// syspropJavaGenRule module generates srcjar containing generated java APIs.
@@ -102,6 +100,12 @@
}
}
+func (g *syspropJavaGenRule) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // Add a dependency from the stubs to sysprop library so that the generator rule can depend on
+ // the check API rule of the sysprop library.
+ ctx.AddFarVariationDependencies(nil, nil, proptools.String(g.properties.Check_api))
+}
+
func (g *syspropJavaGenRule) OutputFiles(tag string) (android.Paths, error) {
switch tag {
case "":
@@ -125,8 +129,8 @@
properties syspropLibraryProperties
checkApiFileTimeStamp android.WritablePath
- latestApiFile android.Path
- currentApiFile android.Path
+ latestApiFile android.OptionalPath
+ currentApiFile android.OptionalPath
dumpedApiFile android.WritablePath
}
@@ -156,14 +160,17 @@
// If set to true, build a variant of the module for the host. Defaults to false.
Host_supported *bool
- // Whether public stub exists or not.
- Public_stub *bool `blueprint:"mutated"`
-
Cpp struct {
// Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
// Forwarded to cc_library.min_sdk_version
Min_sdk_version *string
}
+
+ Java struct {
+ // Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
+ // Forwarded to java_library.min_sdk_version
+ Min_sdk_version *string
+ }
}
var (
@@ -186,7 +193,11 @@
}
func init() {
- android.RegisterModuleType("sysprop_library", syspropLibraryFactory)
+ registerSyspropBuildComponents(android.InitRegistrationContext)
+}
+
+func registerSyspropBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("sysprop_library", syspropLibraryFactory)
}
func (m *syspropLibrary) Name() string {
@@ -201,11 +212,8 @@
return "lib" + m.BaseModuleName()
}
-func (m *syspropLibrary) JavaPublicStubName() string {
- if proptools.Bool(m.properties.Public_stub) {
- return m.BaseModuleName() + "_public"
- }
- return ""
+func (m *syspropLibrary) javaPublicStubName() string {
+ return m.BaseModuleName() + "_public"
}
func (m *syspropLibrary) javaGenModuleName() string {
@@ -220,11 +228,7 @@
return m.ModuleBase.Name()
}
-func (m *syspropLibrary) HasPublicStub() bool {
- return proptools.Bool(m.properties.Public_stub)
-}
-
-func (m *syspropLibrary) CurrentSyspropApiFile() android.Path {
+func (m *syspropLibrary) CurrentSyspropApiFile() android.OptionalPath {
return m.currentApiFile
}
@@ -243,8 +247,11 @@
return
}
- m.currentApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", baseModuleName+"-current.txt")
- m.latestApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", baseModuleName+"-latest.txt")
+ apiDirectoryPath := path.Join(ctx.ModuleDir(), "api")
+ currentApiFilePath := path.Join(apiDirectoryPath, baseModuleName+"-current.txt")
+ latestApiFilePath := path.Join(apiDirectoryPath, baseModuleName+"-latest.txt")
+ m.currentApiFile = android.ExistentPathForSource(ctx, currentApiFilePath)
+ m.latestApiFile = android.ExistentPathForSource(ctx, latestApiFilePath)
// dump API rule
rule := android.NewRuleBuilder(pctx, ctx)
@@ -258,19 +265,37 @@
// check API rule
rule = android.NewRuleBuilder(pctx, ctx)
+ // We allow that the API txt files don't exist, when the sysprop_library only contains internal
+ // properties. But we have to feed current api file and latest api file to the rule builder.
+ // Currently we can't get android.Path representing the null device, so we add any existing API
+ // txt files to implicits, and then directly feed string paths, rather than calling Input(Path)
+ // method.
+ var apiFileList android.Paths
+ currentApiArgument := os.DevNull
+ if m.currentApiFile.Valid() {
+ apiFileList = append(apiFileList, m.currentApiFile.Path())
+ currentApiArgument = m.currentApiFile.String()
+ }
+
+ latestApiArgument := os.DevNull
+ if m.latestApiFile.Valid() {
+ apiFileList = append(apiFileList, m.latestApiFile.Path())
+ latestApiArgument = m.latestApiFile.String()
+ }
+
// 1. compares current.txt to api-dump.txt
// current.txt should be identical to api-dump.txt.
msg := fmt.Sprintf(`\n******************************\n`+
`API of sysprop_library %s doesn't match with current.txt\n`+
`Please update current.txt by:\n`+
- `m %s-dump-api && rm -rf %q && cp -f %q %q\n`+
+ `m %s-dump-api && mkdir -p %q && rm -rf %q && cp -f %q %q\n`+
`******************************\n`, baseModuleName, baseModuleName,
- m.currentApiFile.String(), m.dumpedApiFile.String(), m.currentApiFile.String())
+ apiDirectoryPath, currentApiFilePath, m.dumpedApiFile.String(), currentApiFilePath)
rule.Command().
Text("( cmp").Flag("-s").
Input(m.dumpedApiFile).
- Input(m.currentApiFile).
+ Text(currentApiArgument).
Text("|| ( echo").Flag("-e").
Flag(`"` + msg + `"`).
Text("; exit 38) )")
@@ -285,11 +310,12 @@
rule.Command().
Text("( ").
BuiltTool("sysprop_api_checker").
- Input(m.latestApiFile).
- Input(m.currentApiFile).
+ Text(latestApiArgument).
+ Text(currentApiArgument).
Text(" || ( echo").Flag("-e").
Flag(`"` + msg + `"`).
- Text("; exit 38) )")
+ Text("; exit 38) )").
+ Implicits(apiFileList)
m.checkApiFileTimeStamp = android.PathForModuleOut(ctx, "check_api.timestamp")
@@ -307,6 +333,7 @@
// Actual implementation libraries are created on LoadHookMutator
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
fmt.Fprintf(w, "LOCAL_MODULE := %s\n", m.Name())
+ data.Entries.WriteLicenseVariables(w)
fmt.Fprintf(w, "LOCAL_MODULE_CLASS := FAKE\n")
fmt.Fprintf(w, "LOCAL_MODULE_TAGS := optional\n")
fmt.Fprintf(w, "include $(BUILD_SYSTEM)/base_rules.mk\n\n")
@@ -375,16 +402,19 @@
}
type javaLibraryProperties struct {
- Name *string
- Srcs []string
- Soc_specific *bool
- Device_specific *bool
- Product_specific *bool
- Required []string
- Sdk_version *string
- Installable *bool
- Libs []string
- Stem *string
+ Name *string
+ Srcs []string
+ Soc_specific *bool
+ Device_specific *bool
+ Product_specific *bool
+ Required []string
+ Sdk_version *string
+ Installable *bool
+ Libs []string
+ Stem *string
+ SyspropPublicStub string
+ Apex_available []string
+ Min_sdk_version *string
}
func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) {
@@ -392,31 +422,6 @@
ctx.PropertyErrorf("srcs", "sysprop_library must specify srcs")
}
- missingApi := false
-
- for _, txt := range []string{"-current.txt", "-latest.txt"} {
- path := path.Join(ctx.ModuleDir(), "api", m.BaseModuleName()+txt)
- file := android.ExistentPathForSource(ctx, path)
- if !file.Valid() {
- ctx.ModuleErrorf("API file %#v doesn't exist", path)
- missingApi = true
- }
- }
-
- if missingApi {
- script := "build/soong/scripts/gen-sysprop-api-files.sh"
- p := android.ExistentPathForSource(ctx, script)
-
- if !p.Valid() {
- panic(fmt.Sprintf("script file %s doesn't exist", script))
- }
-
- ctx.ModuleErrorf("One or more api files are missing. "+
- "You can create them by:\n"+
- "%s %q %q", script, ctx.ModuleDir(), m.BaseModuleName())
- return
- }
-
// ctx's Platform or Specific functions represent where this sysprop_library installed.
installedInSystem := ctx.Platform() || ctx.SystemExtSpecific()
installedInVendorOrOdm := ctx.SocSpecific() || ctx.DeviceSpecific()
@@ -491,35 +496,44 @@
// Contrast to C++, syspropJavaGenRule module will generate srcjar and the srcjar will be fed
// to Java implementation library.
ctx.CreateModule(syspropJavaGenFactory, &syspropGenProperties{
- Srcs: m.properties.Srcs,
- Scope: scope,
- Name: proptools.StringPtr(m.javaGenModuleName()),
- })
-
- ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{
- Name: proptools.StringPtr(m.BaseModuleName()),
- Srcs: []string{":" + m.javaGenModuleName()},
- Soc_specific: proptools.BoolPtr(ctx.SocSpecific()),
- Device_specific: proptools.BoolPtr(ctx.DeviceSpecific()),
- Product_specific: proptools.BoolPtr(ctx.ProductSpecific()),
- Installable: m.properties.Installable,
- Sdk_version: proptools.StringPtr("core_current"),
- Libs: []string{javaSyspropStub},
+ Srcs: m.properties.Srcs,
+ Scope: scope,
+ Name: proptools.StringPtr(m.javaGenModuleName()),
+ Check_api: proptools.StringPtr(ctx.ModuleName()),
})
// if platform sysprop_library is installed in /system or /system-ext, we regard it as an API
// and allow any modules (even from different partition) to link against the sysprop_library.
// To do that, we create a public stub and expose it to modules with sdk_version: system_*.
+ var publicStub string
if isOwnerPlatform && installedInSystem {
- m.properties.Public_stub = proptools.BoolPtr(true)
+ publicStub = m.javaPublicStubName()
+ }
+
+ ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{
+ Name: proptools.StringPtr(m.BaseModuleName()),
+ Srcs: []string{":" + m.javaGenModuleName()},
+ Soc_specific: proptools.BoolPtr(ctx.SocSpecific()),
+ Device_specific: proptools.BoolPtr(ctx.DeviceSpecific()),
+ Product_specific: proptools.BoolPtr(ctx.ProductSpecific()),
+ Installable: m.properties.Installable,
+ Sdk_version: proptools.StringPtr("core_current"),
+ Libs: []string{javaSyspropStub},
+ SyspropPublicStub: publicStub,
+ Apex_available: m.ApexProperties.Apex_available,
+ Min_sdk_version: m.properties.Java.Min_sdk_version,
+ })
+
+ if publicStub != "" {
ctx.CreateModule(syspropJavaGenFactory, &syspropGenProperties{
- Srcs: m.properties.Srcs,
- Scope: "public",
- Name: proptools.StringPtr(m.javaGenPublicStubName()),
+ Srcs: m.properties.Srcs,
+ Scope: "public",
+ Name: proptools.StringPtr(m.javaGenPublicStubName()),
+ Check_api: proptools.StringPtr(ctx.ModuleName()),
})
ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{
- Name: proptools.StringPtr(m.JavaPublicStubName()),
+ Name: proptools.StringPtr(publicStub),
Srcs: []string{":" + m.javaGenPublicStubName()},
Installable: proptools.BoolPtr(false),
Sdk_version: proptools.StringPtr("core_current"),
@@ -538,15 +552,3 @@
*libraries = append(*libraries, "//"+ctx.ModuleDir()+":"+ctx.ModuleName())
}
}
-
-// syspropDepsMutator adds dependencies from java implementation library to sysprop library.
-// java implementation library then depends on check API rule of sysprop library.
-func syspropDepsMutator(ctx android.BottomUpMutatorContext) {
- if m, ok := ctx.Module().(*syspropLibrary); ok {
- ctx.AddReverseDependency(m, nil, m.javaGenModuleName())
-
- if proptools.Bool(m.properties.Public_stub) {
- ctx.AddReverseDependency(m, nil, m.javaGenPublicStubName())
- }
- }
-}
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index b8b93f6..6d35f9c 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -15,84 +15,70 @@
package sysprop
import (
- "reflect"
+ "os"
+ "strings"
+ "testing"
"android/soong/android"
"android/soong/cc"
"android/soong/java"
- "io/ioutil"
- "os"
- "strings"
- "testing"
-
- "github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_sysprop_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ os.Exit(m.Run())
}
-func testContext(config android.Config) *android.TestContext {
-
- ctx := android.NewTestArchContext(config)
- java.RegisterJavaBuildComponents(ctx)
- java.RegisterAppBuildComponents(ctx)
- java.RegisterSystemModulesBuildComponents(ctx)
-
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.BottomUp("sysprop_deps", syspropDepsMutator).Parallel()
- })
-
- android.RegisterPrebuiltMutators(ctx)
-
- cc.RegisterRequiredBuildComponentsForTest(ctx)
- ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.BottomUp("sysprop_java", java.SyspropMutator).Parallel()
- })
-
- ctx.RegisterModuleType("sysprop_library", syspropLibraryFactory)
-
- ctx.Register()
-
- return ctx
-}
-
-func run(t *testing.T, ctx *android.TestContext, config android.Config) {
+func test(t *testing.T, bp string) *android.TestResult {
t.Helper()
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
-}
-func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
- bp += cc.GatherRequiredDepsForTest(android.Android)
+ bp += `
+ cc_library {
+ name: "libbase",
+ host_supported: true,
+ }
- mockFS := map[string][]byte{
+ cc_library_headers {
+ name: "libbase_headers",
+ vendor_available: true,
+ recovery_available: true,
+ }
+
+ cc_library {
+ name: "liblog",
+ no_libcrt: true,
+ nocrt: true,
+ system_shared_libs: [],
+ recovery_available: true,
+ host_supported: true,
+ llndk_stubs: "liblog.llndk",
+ }
+
+ llndk_library {
+ name: "liblog.llndk",
+ symbol_file: "",
+ }
+
+ java_library {
+ name: "sysprop-library-stub-platform",
+ sdk_version: "core_current",
+ }
+
+ java_library {
+ name: "sysprop-library-stub-vendor",
+ soc_specific: true,
+ sdk_version: "core_current",
+ }
+
+ java_library {
+ name: "sysprop-library-stub-product",
+ product_specific: true,
+ sdk_version: "core_current",
+ }
+ `
+
+ mockFS := android.MockFS{
"a.java": nil,
"b.java": nil,
"c.java": nil,
@@ -136,31 +122,24 @@
"com/android2/OdmProperties.sysprop": nil,
}
- for k, v := range fs {
- mockFS[k] = v
- }
+ result := android.GroupFixturePreparers(
+ cc.PrepareForTestWithCcDefaultModules,
+ java.PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithSyspropBuildComponents,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.DeviceSystemSdkVersions = []string{"28"}
+ variables.DeviceVndkVersion = proptools.StringPtr("current")
+ variables.Platform_vndk_version = proptools.StringPtr("29")
+ }),
+ mockFS.AddToFixture(),
+ android.FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
- config := java.TestConfig(buildDir, env, bp, mockFS)
-
- config.TestProductVariables.DeviceSystemSdkVersions = []string{"28"}
- config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = proptools.StringPtr("VER")
-
- return config
-
-}
-
-func test(t *testing.T, bp string) *android.TestContext {
- t.Helper()
- config := testConfig(nil, bp, nil)
- ctx := testContext(config)
- run(t, ctx, config)
-
- return ctx
+ return result
}
func TestSyspropLibrary(t *testing.T) {
- ctx := test(t, `
+ result := test(t, `
sysprop_library {
name: "sysprop-platform",
apex_available: ["//apex_available:platform"],
@@ -259,65 +238,22 @@
static_libs: ["sysprop-platform", "sysprop-vendor"],
}
- cc_library {
- name: "libbase",
- host_supported: true,
- }
-
- cc_library_headers {
- name: "libbase_headers",
- vendor_available: true,
- recovery_available: true,
- }
-
- cc_library {
- name: "liblog",
- no_libcrt: true,
- nocrt: true,
- system_shared_libs: [],
- recovery_available: true,
- host_supported: true,
- llndk_stubs: "liblog.llndk",
- }
-
cc_binary_host {
name: "hostbin",
static_libs: ["sysprop-platform"],
}
-
- llndk_library {
- name: "liblog.llndk",
- symbol_file: "",
- }
-
- java_library {
- name: "sysprop-library-stub-platform",
- sdk_version: "core_current",
- }
-
- java_library {
- name: "sysprop-library-stub-vendor",
- soc_specific: true,
- sdk_version: "core_current",
- }
-
- java_library {
- name: "sysprop-library-stub-product",
- product_specific: true,
- sdk_version: "core_current",
- }
- `)
+ `)
// Check for generated cc_library
for _, variant := range []string{
- "android_vendor.VER_arm_armv7-a-neon_shared",
- "android_vendor.VER_arm_armv7-a-neon_static",
- "android_vendor.VER_arm64_armv8-a_shared",
- "android_vendor.VER_arm64_armv8-a_static",
+ "android_vendor.29_arm_armv7-a-neon_shared",
+ "android_vendor.29_arm_armv7-a-neon_static",
+ "android_vendor.29_arm64_armv8-a_shared",
+ "android_vendor.29_arm64_armv8-a_static",
} {
- ctx.ModuleForTests("libsysprop-platform", variant)
- ctx.ModuleForTests("libsysprop-vendor", variant)
- ctx.ModuleForTests("libsysprop-odm", variant)
+ result.ModuleForTests("libsysprop-platform", variant)
+ result.ModuleForTests("libsysprop-vendor", variant)
+ result.ModuleForTests("libsysprop-odm", variant)
}
for _, variant := range []string{
@@ -326,54 +262,45 @@
"android_arm64_armv8-a_shared",
"android_arm64_armv8-a_static",
} {
- library := ctx.ModuleForTests("libsysprop-platform", variant).Module().(*cc.Module)
+ library := result.ModuleForTests("libsysprop-platform", variant).Module().(*cc.Module)
expectedApexAvailableOnLibrary := []string{"//apex_available:platform"}
- if !reflect.DeepEqual(library.ApexProperties.Apex_available, expectedApexAvailableOnLibrary) {
- t.Errorf("apex available property on libsysprop-platform must be %#v, but was %#v.",
- expectedApexAvailableOnLibrary, library.ApexProperties.Apex_available)
- }
+ android.AssertDeepEquals(t, "apex available property on libsysprop-platform", expectedApexAvailableOnLibrary, library.ApexProperties.Apex_available)
// product variant of vendor-owned sysprop_library
- ctx.ModuleForTests("libsysprop-vendor-on-product", variant)
+ result.ModuleForTests("libsysprop-vendor-on-product", variant)
}
- ctx.ModuleForTests("sysprop-platform", "android_common")
- ctx.ModuleForTests("sysprop-platform_public", "android_common")
- ctx.ModuleForTests("sysprop-vendor", "android_common")
- ctx.ModuleForTests("sysprop-vendor-on-product", "android_common")
+ result.ModuleForTests("sysprop-platform", "android_common")
+ result.ModuleForTests("sysprop-platform_public", "android_common")
+ result.ModuleForTests("sysprop-vendor", "android_common")
+ result.ModuleForTests("sysprop-vendor-on-product", "android_common")
// Check for exported includes
coreVariant := "android_arm64_armv8-a_static"
- vendorVariant := "android_vendor.VER_arm64_armv8-a_static"
+ vendorVariant := "android_vendor.29_arm64_armv8-a_static"
platformInternalPath := "libsysprop-platform/android_arm64_armv8-a_static/gen/sysprop/include"
platformPublicCorePath := "libsysprop-platform/android_arm64_armv8-a_static/gen/sysprop/public/include"
- platformPublicVendorPath := "libsysprop-platform/android_vendor.VER_arm64_armv8-a_static/gen/sysprop/public/include"
+ platformPublicVendorPath := "libsysprop-platform/android_vendor.29_arm64_armv8-a_static/gen/sysprop/public/include"
platformOnProductPath := "libsysprop-platform-on-product/android_arm64_armv8-a_static/gen/sysprop/public/include"
- vendorInternalPath := "libsysprop-vendor/android_vendor.VER_arm64_armv8-a_static/gen/sysprop/include"
+ vendorInternalPath := "libsysprop-vendor/android_vendor.29_arm64_armv8-a_static/gen/sysprop/include"
vendorPublicPath := "libsysprop-vendor-on-product/android_arm64_armv8-a_static/gen/sysprop/public/include"
- platformClient := ctx.ModuleForTests("cc-client-platform", coreVariant)
+ platformClient := result.ModuleForTests("cc-client-platform", coreVariant)
platformFlags := platformClient.Rule("cc").Args["cFlags"]
// platform should use platform's internal header
- if !strings.Contains(platformFlags, platformInternalPath) {
- t.Errorf("flags for platform must contain %#v, but was %#v.",
- platformInternalPath, platformFlags)
- }
+ android.AssertStringDoesContain(t, "flags for platform", platformFlags, platformInternalPath)
- platformStaticClient := ctx.ModuleForTests("cc-client-platform-static", coreVariant)
+ platformStaticClient := result.ModuleForTests("cc-client-platform-static", coreVariant)
platformStaticFlags := platformStaticClient.Rule("cc").Args["cFlags"]
// platform-static should use platform's internal header
- if !strings.Contains(platformStaticFlags, platformInternalPath) {
- t.Errorf("flags for platform-static must contain %#v, but was %#v.",
- platformInternalPath, platformStaticFlags)
- }
+ android.AssertStringDoesContain(t, "flags for platform-static", platformStaticFlags, platformInternalPath)
- productClient := ctx.ModuleForTests("cc-client-product", coreVariant)
+ productClient := result.ModuleForTests("cc-client-product", coreVariant)
productFlags := productClient.Rule("cc").Args["cFlags"]
// Product should use platform's and vendor's public headers
@@ -383,7 +310,7 @@
platformPublicCorePath, vendorPublicPath, productFlags)
}
- vendorClient := ctx.ModuleForTests("cc-client-vendor", vendorVariant)
+ vendorClient := result.ModuleForTests("cc-client-vendor", vendorVariant)
vendorFlags := vendorClient.Rule("cc").Args["cFlags"]
// Vendor should use platform's public header and vendor's internal header
@@ -394,15 +321,56 @@
}
// Java modules linking against system API should use public stub
- javaSystemApiClient := ctx.ModuleForTests("java-platform", "android_common")
- publicStubFound := false
- ctx.VisitDirectDeps(javaSystemApiClient.Module(), func(dep blueprint.Module) {
- if dep.Name() == "sysprop-platform_public" {
- publicStubFound = true
- }
- })
- if !publicStubFound {
- t.Errorf("system api client should use public stub")
+ javaSystemApiClient := result.ModuleForTests("java-platform", "android_common").Rule("javac")
+ syspropPlatformPublic := result.ModuleForTests("sysprop-platform_public", "android_common").Description("for turbine")
+ if g, w := javaSystemApiClient.Implicits.Strings(), syspropPlatformPublic.Output.String(); !android.InList(w, g) {
+ t.Errorf("system api client should use public stub %q, got %q", w, g)
}
+}
+func TestApexAvailabilityIsForwarded(t *testing.T) {
+ result := test(t, `
+ sysprop_library {
+ name: "sysprop-platform",
+ apex_available: ["//apex_available:platform"],
+ srcs: ["android/sysprop/PlatformProperties.sysprop"],
+ api_packages: ["android.sysprop"],
+ property_owner: "Platform",
+ }
+ `)
+
+ expected := []string{"//apex_available:platform"}
+
+ ccModule := result.ModuleForTests("libsysprop-platform", "android_arm64_armv8-a_shared").Module().(*cc.Module)
+ propFromCc := ccModule.ApexProperties.Apex_available
+ android.AssertDeepEquals(t, "apex_available forwarding to cc module", expected, propFromCc)
+
+ javaModule := result.ModuleForTests("sysprop-platform", "android_common").Module().(*java.Library)
+ propFromJava := javaModule.ApexProperties.Apex_available
+ android.AssertDeepEquals(t, "apex_available forwarding to java module", expected, propFromJava)
+}
+
+func TestMinSdkVersionIsForwarded(t *testing.T) {
+ result := test(t, `
+ sysprop_library {
+ name: "sysprop-platform",
+ srcs: ["android/sysprop/PlatformProperties.sysprop"],
+ api_packages: ["android.sysprop"],
+ property_owner: "Platform",
+ cpp: {
+ min_sdk_version: "29",
+ },
+ java: {
+ min_sdk_version: "30",
+ },
+ }
+ `)
+
+ ccModule := result.ModuleForTests("libsysprop-platform", "android_arm64_armv8-a_shared").Module().(*cc.Module)
+ propFromCc := proptools.String(ccModule.Properties.Min_sdk_version)
+ android.AssertStringEquals(t, "min_sdk_version forwarding to cc module", "29", propFromCc)
+
+ javaModule := result.ModuleForTests("sysprop-platform", "android_common").Module().(*java.Library)
+ propFromJava := javaModule.MinSdkVersionString()
+ android.AssertStringEquals(t, "min_sdk_version forwarding to java module", "30", propFromJava)
}
diff --git a/cmd/soong_env/Android.bp b/sysprop/testing.go
similarity index 71%
copy from cmd/soong_env/Android.bp
copy to sysprop/testing.go
index 4db0da3..3e14be1 100644
--- a/cmd/soong_env/Android.bp
+++ b/sysprop/testing.go
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright (C) 2021 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,13 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-bootstrap_go_binary {
- name: "soong_env",
- deps: [
- "soong-env",
- ],
- srcs: [
- "soong_env.go",
- ],
- default: true,
-}
+package sysprop
+
+import "android/soong/android"
+
+var PrepareForTestWithSyspropBuildComponents = android.FixtureRegisterWithContext(registerSyspropBuildComponents)
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
new file mode 100755
index 0000000..a483370
--- /dev/null
+++ b/tests/bootstrap_test.sh
@@ -0,0 +1,580 @@
+#!/bin/bash -eu
+
+# This test exercises the bootstrapping process of the build system
+# in a source tree that only contains enough files for Bazel and Soong to work.
+
+source "$(dirname "$0")/lib.sh"
+
+function test_smoke {
+ setup
+ run_soong
+}
+
+function test_null_build() {
+ setup
+ run_soong
+ local bootstrap_mtime1=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
+ local output_mtime1=$(stat -c "%y" out/soong/build.ninja)
+ run_soong
+ local bootstrap_mtime2=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
+ local output_mtime2=$(stat -c "%y" out/soong/build.ninja)
+
+ if [[ "$bootstrap_mtime1" == "$bootstrap_mtime2" ]]; then
+ # Bootstrapping is always done. It doesn't take a measurable amount of time.
+ fail "Bootstrap Ninja file did not change on null build"
+ fi
+
+ if [[ "$output_mtime1" != "$output_mtime2" ]]; then
+ fail "Output Ninja file changed on null build"
+ fi
+}
+
+function test_soong_build_rebuilt_if_blueprint_changes() {
+ setup
+ run_soong
+ local mtime1=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
+
+ sed -i 's/pluginGenSrcCmd/pluginGenSrcCmd2/g' build/blueprint/bootstrap/bootstrap.go
+
+ run_soong
+ local mtime2=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
+
+ if [[ "$mtime1" == "$mtime2" ]]; then
+ fail "Bootstrap Ninja file did not change"
+ fi
+}
+
+function test_change_android_bp() {
+ setup
+ mkdir -p a
+ cat > a/Android.bp <<'EOF'
+python_binary_host {
+ name: "my_little_binary_host",
+ srcs: ["my_little_binary_host.py"]
+}
+EOF
+ touch a/my_little_binary_host.py
+ run_soong
+
+ grep -q "^# Module:.*my_little_binary_host" out/soong/build.ninja || fail "module not found"
+
+ cat > a/Android.bp <<'EOF'
+python_binary_host {
+ name: "my_great_binary_host",
+ srcs: ["my_great_binary_host.py"]
+}
+EOF
+ touch a/my_great_binary_host.py
+ run_soong
+
+ grep -q "^# Module:.*my_little_binary_host" out/soong/build.ninja && fail "old module found"
+ grep -q "^# Module:.*my_great_binary_host" out/soong/build.ninja || fail "new module not found"
+}
+
+
+function test_add_android_bp() {
+ setup
+ run_soong
+ local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+ mkdir -p a
+ cat > a/Android.bp <<'EOF'
+python_binary_host {
+ name: "my_little_binary_host",
+ srcs: ["my_little_binary_host.py"]
+}
+EOF
+ touch a/my_little_binary_host.py
+ run_soong
+
+ local mtime2=$(stat -c "%y" out/soong/build.ninja)
+ if [[ "$mtime1" == "$mtime2" ]]; then
+ fail "Output Ninja file did not change"
+ fi
+
+ grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja || fail "New module not in output"
+
+ run_soong
+}
+
+function test_delete_android_bp() {
+ setup
+ mkdir -p a
+ cat > a/Android.bp <<'EOF'
+python_binary_host {
+ name: "my_little_binary_host",
+ srcs: ["my_little_binary_host.py"]
+}
+EOF
+ touch a/my_little_binary_host.py
+ run_soong
+
+ grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja || fail "Module not in output"
+
+ rm a/Android.bp
+ run_soong
+
+ if grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja; then
+ fail "Old module in output"
+ fi
+}
+
+# Test that an incremental build with a glob doesn't rerun soong_build, and
+# only regenerates the globs on the first but not the second incremental build.
+function test_glob_noop_incremental() {
+ setup
+
+ mkdir -p a
+ cat > a/Android.bp <<'EOF'
+python_binary_host {
+ name: "my_little_binary_host",
+ srcs: ["*.py"],
+}
+EOF
+ touch a/my_little_binary_host.py
+ run_soong
+ local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+ local glob_deps_file=out/soong/.glob/a/__py.glob.d
+
+ if [ -e "$glob_deps_file" ]; then
+ fail "Glob deps file unexpectedly written on first build"
+ fi
+
+ run_soong
+ local ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
+
+ # There is an ineffiencency in glob that requires bpglob to rerun once for each glob to update
+ # the entry in the .ninja_log. It doesn't update the output file, but we can detect the rerun
+ # by checking if the deps file was created.
+ if [ ! -e "$glob_deps_file" ]; then
+ fail "Glob deps file missing after second build"
+ fi
+
+ local glob_deps_mtime2=$(stat -c "%y" "$glob_deps_file")
+
+ if [[ "$ninja_mtime1" != "$ninja_mtime2" ]]; then
+ fail "Ninja file rewritten on null incremental build"
+ fi
+
+ run_soong
+ local ninja_mtime3=$(stat -c "%y" out/soong/build.ninja)
+ local glob_deps_mtime3=$(stat -c "%y" "$glob_deps_file")
+
+ if [[ "$ninja_mtime2" != "$ninja_mtime3" ]]; then
+ fail "Ninja file rewritten on null incremental build"
+ fi
+
+ # The bpglob commands should not rerun after the first incremental build.
+ if [[ "$glob_deps_mtime2" != "$glob_deps_mtime3" ]]; then
+ fail "Glob deps file rewritten on second null incremental build"
+ fi
+}
+
+function test_add_file_to_glob() {
+ setup
+
+ mkdir -p a
+ cat > a/Android.bp <<'EOF'
+python_binary_host {
+ name: "my_little_binary_host",
+ srcs: ["*.py"],
+}
+EOF
+ touch a/my_little_binary_host.py
+ run_soong
+ local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+ touch a/my_little_library.py
+ run_soong
+
+ local mtime2=$(stat -c "%y" out/soong/build.ninja)
+ if [[ "$mtime1" == "$mtime2" ]]; then
+ fail "Output Ninja file did not change"
+ fi
+
+ grep -q my_little_library.py out/soong/build.ninja || fail "new file is not in output"
+}
+
+function test_soong_build_rerun_iff_environment_changes() {
+ setup
+
+ mkdir -p cherry
+ cat > cherry/Android.bp <<'EOF'
+bootstrap_go_package {
+ name: "cherry",
+ pkgPath: "android/soong/cherry",
+ deps: [
+ "blueprint",
+ "soong",
+ "soong-android",
+ ],
+ srcs: [
+ "cherry.go",
+ ],
+ pluginFor: ["soong_build"],
+}
+EOF
+
+ cat > cherry/cherry.go <<'EOF'
+package cherry
+
+import (
+ "android/soong/android"
+ "github.com/google/blueprint"
+)
+
+var (
+ pctx = android.NewPackageContext("cherry")
+)
+
+func init() {
+ android.RegisterSingletonType("cherry", CherrySingleton)
+}
+
+func CherrySingleton() android.Singleton {
+ return &cherrySingleton{}
+}
+
+type cherrySingleton struct{}
+
+func (p *cherrySingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ cherryRule := ctx.Rule(pctx, "cherry",
+ blueprint.RuleParams{
+ Command: "echo CHERRY IS " + ctx.Config().Getenv("CHERRY") + " > ${out}",
+ CommandDeps: []string{},
+ Description: "Cherry",
+ })
+
+ outputFile := android.PathForOutput(ctx, "cherry", "cherry.txt")
+ var deps android.Paths
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: cherryRule,
+ Output: outputFile,
+ Inputs: deps,
+ })
+}
+EOF
+
+ export CHERRY=TASTY
+ run_soong
+ grep -q "CHERRY IS TASTY" out/soong/build.ninja \
+ || fail "first value of environment variable is not used"
+
+ export CHERRY=RED
+ run_soong
+ grep -q "CHERRY IS RED" out/soong/build.ninja \
+ || fail "second value of environment variable not used"
+ local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+ run_soong
+ local mtime2=$(stat -c "%y" out/soong/build.ninja)
+ if [[ "$mtime1" != "$mtime2" ]]; then
+ fail "Output Ninja file changed when environment variable did not"
+ fi
+
+}
+
+function test_add_file_to_soong_build() {
+ setup
+ run_soong
+ local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+ mkdir -p a
+ cat > a/Android.bp <<'EOF'
+bootstrap_go_package {
+ name: "picard-soong-rules",
+ pkgPath: "android/soong/picard",
+ deps: [
+ "blueprint",
+ "soong",
+ "soong-android",
+ ],
+ srcs: [
+ "picard.go",
+ ],
+ pluginFor: ["soong_build"],
+}
+EOF
+
+ cat > a/picard.go <<'EOF'
+package picard
+
+import (
+ "android/soong/android"
+ "github.com/google/blueprint"
+)
+
+var (
+ pctx = android.NewPackageContext("picard")
+)
+
+func init() {
+ android.RegisterSingletonType("picard", PicardSingleton)
+}
+
+func PicardSingleton() android.Singleton {
+ return &picardSingleton{}
+}
+
+type picardSingleton struct{}
+
+func (p *picardSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ picardRule := ctx.Rule(pctx, "picard",
+ blueprint.RuleParams{
+ Command: "echo Make it so. > ${out}",
+ CommandDeps: []string{},
+ Description: "Something quotable",
+ })
+
+ outputFile := android.PathForOutput(ctx, "picard", "picard.txt")
+ var deps android.Paths
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: picardRule,
+ Output: outputFile,
+ Inputs: deps,
+ })
+}
+
+EOF
+
+ run_soong
+ local mtime2=$(stat -c "%y" out/soong/build.ninja)
+ if [[ "$mtime1" == "$mtime2" ]]; then
+ fail "Output Ninja file did not change"
+ fi
+
+ grep -q "Make it so" out/soong/build.ninja || fail "New action not present"
+}
+
+# Tests a glob in a build= statement in an Android.bp file, which is interpreted
+# during bootstrapping.
+function test_glob_during_bootstrapping() {
+ setup
+
+ mkdir -p a
+ cat > a/Android.bp <<'EOF'
+build=["foo*.bp"]
+EOF
+ cat > a/fooa.bp <<'EOF'
+bootstrap_go_package {
+ name: "picard-soong-rules",
+ pkgPath: "android/soong/picard",
+ deps: [
+ "blueprint",
+ "soong",
+ "soong-android",
+ ],
+ srcs: [
+ "picard.go",
+ ],
+ pluginFor: ["soong_build"],
+}
+EOF
+
+ cat > a/picard.go <<'EOF'
+package picard
+
+import (
+ "android/soong/android"
+ "github.com/google/blueprint"
+)
+
+var (
+ pctx = android.NewPackageContext("picard")
+)
+
+func init() {
+ android.RegisterSingletonType("picard", PicardSingleton)
+}
+
+func PicardSingleton() android.Singleton {
+ return &picardSingleton{}
+}
+
+type picardSingleton struct{}
+
+var Message = "Make it so."
+
+func (p *picardSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ picardRule := ctx.Rule(pctx, "picard",
+ blueprint.RuleParams{
+ Command: "echo " + Message + " > ${out}",
+ CommandDeps: []string{},
+ Description: "Something quotable",
+ })
+
+ outputFile := android.PathForOutput(ctx, "picard", "picard.txt")
+ var deps android.Paths
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: picardRule,
+ Output: outputFile,
+ Inputs: deps,
+ })
+}
+
+EOF
+
+ run_soong
+ local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+ grep -q "Make it so" out/soong/build.ninja || fail "Original action not present"
+
+ cat > a/foob.bp <<'EOF'
+bootstrap_go_package {
+ name: "worf-soong-rules",
+ pkgPath: "android/soong/worf",
+ deps: [
+ "blueprint",
+ "soong",
+ "soong-android",
+ "picard-soong-rules",
+ ],
+ srcs: [
+ "worf.go",
+ ],
+ pluginFor: ["soong_build"],
+}
+EOF
+
+ cat > a/worf.go <<'EOF'
+package worf
+
+import "android/soong/picard"
+
+func init() {
+ picard.Message = "Engage."
+}
+EOF
+
+ run_soong
+ local mtime2=$(stat -c "%y" out/soong/build.ninja)
+ if [[ "$mtime1" == "$mtime2" ]]; then
+ fail "Output Ninja file did not change"
+ fi
+
+ grep -q "Engage" out/soong/build.ninja || fail "New action not present"
+
+ if grep -q "Make it so" out/soong/build.ninja; then
+ fail "Original action still present"
+ fi
+}
+
+function test_null_build_after_docs {
+ setup
+ run_soong
+ local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+ prebuilts/build-tools/linux-x86/bin/ninja -f out/soong/build.ninja soong_docs
+ run_soong
+ local mtime2=$(stat -c "%y" out/soong/build.ninja)
+
+ if [[ "$mtime1" != "$mtime2" ]]; then
+ fail "Output Ninja file changed on null build"
+ fi
+}
+
+function test_integrated_bp2build_smoke {
+ setup
+ INTEGRATED_BP2BUILD=1 run_soong
+ if [[ ! -e out/soong/.bootstrap/bp2build_workspace_marker ]]; then
+ fail "bp2build marker file not created"
+ fi
+}
+
+function test_integrated_bp2build_add_android_bp {
+ setup
+
+ mkdir -p a
+ touch a/a.txt
+ cat > a/Android.bp <<'EOF'
+filegroup {
+ name: "a",
+ srcs: ["a.txt"],
+ bazel_module: { bp2build_available: true },
+}
+EOF
+
+ INTEGRATED_BP2BUILD=1 run_soong
+ if [[ ! -e out/soong/bp2build/a/BUILD ]]; then
+ fail "a/BUILD not created";
+ fi
+
+ mkdir -p b
+ touch b/b.txt
+ cat > b/Android.bp <<'EOF'
+filegroup {
+ name: "b",
+ srcs: ["b.txt"],
+ bazel_module: { bp2build_available: true },
+}
+EOF
+
+ INTEGRATED_BP2BUILD=1 run_soong
+ if [[ ! -e out/soong/bp2build/b/BUILD ]]; then
+ fail "b/BUILD not created";
+ fi
+}
+
+function test_integrated_bp2build_null_build {
+ setup
+
+ INTEGRATED_BP2BUILD=1 run_soong
+ local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+ INTEGRATED_BP2BUILD=1 run_soong
+ local mtime2=$(stat -c "%y" out/soong/build.ninja)
+
+ if [[ "$mtime1" != "$mtime2" ]]; then
+ fail "Output Ninja file changed on null build"
+ fi
+}
+
+function test_integrated_bp2build_add_to_glob {
+ setup
+
+ mkdir -p a
+ touch a/a1.txt
+ cat > a/Android.bp <<'EOF'
+filegroup {
+ name: "a",
+ srcs: ["*.txt"],
+ bazel_module: { bp2build_available: true },
+}
+EOF
+
+ INTEGRATED_BP2BUILD=1 run_soong
+ grep -q a1.txt out/soong/bp2build/a/BUILD || fail "a1.txt not in BUILD file"
+
+ touch a/a2.txt
+ INTEGRATED_BP2BUILD=1 run_soong
+ grep -q a2.txt out/soong/bp2build/a/BUILD || fail "a2.txt not in BUILD file"
+}
+
+function test_dump_json_module_graph() {
+ setup
+ SOONG_DUMP_JSON_MODULE_GRAPH="$MOCK_TOP/modules.json" run_soong
+ if [[ ! -r "$MOCK_TOP/modules.json" ]]; then
+ fail "JSON file was not created"
+ fi
+}
+
+test_smoke
+test_null_build
+test_null_build_after_docs
+test_soong_build_rebuilt_if_blueprint_changes
+test_glob_noop_incremental
+test_add_file_to_glob
+test_add_android_bp
+test_change_android_bp
+test_delete_android_bp
+test_add_file_to_soong_build
+test_glob_during_bootstrapping
+test_soong_build_rerun_iff_environment_changes
+test_dump_json_module_graph
+test_integrated_bp2build_smoke
+test_integrated_bp2build_null_build
+test_integrated_bp2build_add_to_glob
diff --git a/tests/lib.sh b/tests/lib.sh
new file mode 100644
index 0000000..3795dfc
--- /dev/null
+++ b/tests/lib.sh
@@ -0,0 +1,113 @@
+#!/bin/bash -eu
+
+HARDWIRED_MOCK_TOP=
+# Uncomment this to be able to view the source tree after a test is run
+# HARDWIRED_MOCK_TOP=/tmp/td
+
+REAL_TOP="$(readlink -f "$(dirname "$0")"/../../..)"
+
+if [[ ! -z "$HARDWIRED_MOCK_TOP" ]]; then
+ MOCK_TOP="$HARDWIRED_MOCK_TOP"
+else
+ MOCK_TOP=$(mktemp -t -d st.XXXXX)
+ trap cleanup_mock_top EXIT
+fi
+
+WARMED_UP_MOCK_TOP=$(mktemp -t soong_integration_tests_warmup.XXXXXX.tar.gz)
+trap 'rm -f "$WARMED_UP_MOCK_TOP"' EXIT
+
+function warmup_mock_top {
+ info "Warming up mock top ..."
+ info "Mock top warmup archive: $WARMED_UP_MOCK_TOP"
+ cleanup_mock_top
+ mkdir -p "$MOCK_TOP"
+ cd "$MOCK_TOP"
+
+ create_mock_soong
+ run_soong
+ tar czf "$WARMED_UP_MOCK_TOP" *
+}
+
+function cleanup_mock_top {
+ cd /
+ rm -fr "$MOCK_TOP"
+}
+
+function info {
+ echo -e "\e[92;1m[TEST HARNESS INFO]\e[0m" $*
+}
+
+function fail {
+ echo -e "\e[91;1mFAILED:\e[0m" $*
+ exit 1
+}
+
+function copy_directory() {
+ local dir="$1"
+ local parent="$(dirname "$dir")"
+
+ mkdir -p "$MOCK_TOP/$parent"
+ cp -R "$REAL_TOP/$dir" "$MOCK_TOP/$parent"
+}
+
+function symlink_file() {
+ local file="$1"
+
+ mkdir -p "$MOCK_TOP/$(dirname "$file")"
+ ln -s "$REAL_TOP/$file" "$MOCK_TOP/$file"
+}
+
+function symlink_directory() {
+ local dir="$1"
+
+ mkdir -p "$MOCK_TOP/$dir"
+ # We need to symlink the contents of the directory individually instead of
+ # using one symlink for the whole directory because finder.go doesn't follow
+ # symlinks when looking for Android.bp files
+ for i in $(ls "$REAL_TOP/$dir"); do
+ local target="$MOCK_TOP/$dir/$i"
+ local source="$REAL_TOP/$dir/$i"
+
+ if [[ -e "$target" ]]; then
+ if [[ ! -d "$source" || ! -d "$target" ]]; then
+ fail "Trying to symlink $dir twice"
+ fi
+ else
+ ln -s "$REAL_TOP/$dir/$i" "$MOCK_TOP/$dir/$i";
+ fi
+ done
+}
+
+function create_mock_soong {
+ copy_directory build/blueprint
+ copy_directory build/soong
+
+ symlink_directory prebuilts/go
+ symlink_directory prebuilts/build-tools
+ symlink_directory external/golang-protobuf
+
+ touch "$MOCK_TOP/Android.bp"
+}
+
+function setup() {
+ cleanup_mock_top
+ mkdir -p "$MOCK_TOP"
+
+ echo
+ echo ----------------------------------------------------------------------------
+ info "Running test case \e[96;1m${FUNCNAME[1]}\e[0m"
+ cd "$MOCK_TOP"
+
+ tar xzf "$WARMED_UP_MOCK_TOP"
+}
+
+function run_soong() {
+ build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests
+}
+
+info "Starting Soong integration test suite $(basename $0)"
+info "Mock top: $MOCK_TOP"
+
+
+export ALLOW_MISSING_DEPENDENCIES=true
+warmup_mock_top
diff --git a/tests/mixed_mode_test.sh b/tests/mixed_mode_test.sh
new file mode 100755
index 0000000..7dbafea
--- /dev/null
+++ b/tests/mixed_mode_test.sh
@@ -0,0 +1,28 @@
+#!/bin/bash -eu
+
+# This test exercises mixed builds where Soong and Bazel cooperate in building
+# Android.
+#
+# When the execroot is deleted, the Bazel server process will automatically
+# terminate itself.
+
+source "$(dirname "$0")/lib.sh"
+
+function create_mock_bazel() {
+ copy_directory build/bazel
+
+ symlink_directory prebuilts/bazel
+ symlink_directory prebuilts/jdk
+
+ symlink_file WORKSPACE
+ symlink_file tools/bazel
+}
+
+function test_bazel_smoke {
+ setup
+ create_mock_bazel
+
+ tools/bazel info
+}
+
+test_bazel_smoke
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
new file mode 100755
index 0000000..db24037
--- /dev/null
+++ b/tests/run_integration_tests.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+TOP="$(readlink -f "$(dirname "$0")"/../../..)"
+"$TOP/build/soong/tests/bootstrap_test.sh"
+"$TOP/build/soong/tests/mixed_mode_test.sh"
+
diff --git a/third_party/zip/Android.bp b/third_party/zip/Android.bp
index ec89c0c..f279d12 100644
--- a/third_party/zip/Android.bp
+++ b/third_party/zip/Android.bp
@@ -12,6 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ "build_soong_third_party_zip_license",
+ ],
+}
+
+license {
+ name: "build_soong_third_party_zip_license",
+ license_kinds: [
+ "SPDX-license-identifier-BSD",
+ ],
+ license_text: ["LICENSE"],
+}
+
bootstrap_go_package {
name: "android-archive-zip",
pkgPath: "android/soong/third_party/zip",
diff --git a/third_party/zip/LICENSE b/third_party/zip/LICENSE
new file mode 100644
index 0000000..e5c5baf
--- /dev/null
+++ b/third_party/zip/LICENSE
@@ -0,0 +1,28 @@
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of the Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/tradefed/Android.bp b/tradefed/Android.bp
index 4e4e6a7..f0336a3 100644
--- a/tradefed/Android.bp
+++ b/tradefed/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-tradefed",
pkgPath: "android/soong/tradefed",
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index 27d71e8..3d96c84 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -245,6 +245,25 @@
return path
}
+func AutoGenRustBenchmarkConfig(ctx android.ModuleContext, testConfigProp *string,
+ testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool) android.Path {
+ path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
+ if autogenPath != nil {
+ templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
+ if templatePath.Valid() {
+ autogenTemplate(ctx, autogenPath, templatePath.String(), config, "")
+ } else {
+ if ctx.Device() {
+ autogenTemplate(ctx, autogenPath, "${RustDeviceBenchmarkConfigTemplate}", config, "")
+ } else {
+ autogenTemplate(ctx, autogenPath, "${RustHostBenchmarkConfigTemplate}", config, "")
+ }
+ }
+ return autogenPath
+ }
+ return path
+}
+
func AutoGenRobolectricTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string,
testSuites []string, autoGenConfig *bool) android.Path {
path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
diff --git a/tradefed/config.go b/tradefed/config.go
index f3566a8..999424c 100644
--- a/tradefed/config.go
+++ b/tradefed/config.go
@@ -34,6 +34,8 @@
pctx.SourcePathVariable("PythonBinaryHostTestConfigTemplate", "build/make/core/python_binary_host_test_config_template.xml")
pctx.SourcePathVariable("RustDeviceTestConfigTemplate", "build/make/core/rust_device_test_config_template.xml")
pctx.SourcePathVariable("RustHostTestConfigTemplate", "build/make/core/rust_host_test_config_template.xml")
+ pctx.SourcePathVariable("RustDeviceBenchmarkConfigTemplate", "build/make/core/rust_device_benchmark_config_template.xml")
+ pctx.SourcePathVariable("RustHostBenchmarkConfigTemplate", "build/make/core/rust_host_benchmark_config_template.xml")
pctx.SourcePathVariable("RobolectricTestConfigTemplate", "build/make/core/robolectric_test_config_template.xml")
pctx.SourcePathVariable("ShellTestConfigTemplate", "build/make/core/shell_test_config_template.xml")
diff --git a/tradefed/makevars.go b/tradefed/makevars.go
index f9682e4..9b5a20f 100644
--- a/tradefed/makevars.go
+++ b/tradefed/makevars.go
@@ -33,6 +33,8 @@
ctx.Strict("PYTHON_BINARY_HOST_TEST_CONFIG_TEMPLATE", "${PythonBinaryHostTestConfigTemplate}")
ctx.Strict("RUST_DEVICE_TEST_CONFIG_TEMPLATE", "${RustDeviceTestConfigTemplate}")
ctx.Strict("RUST_HOST_TEST_CONFIG_TEMPLATE", "${RustHostTestConfigTemplate}")
+ ctx.Strict("RUST_DEVICE_BENCHMARK_CONFIG_TEMPLATE", "${RustDeviceBenchmarkConfigTemplate}")
+ ctx.Strict("RUST_HOST_BENCHMARK_CONFIG_TEMPLATE", "${RustHostBenchmarkConfigTemplate}")
ctx.Strict("SHELL_TEST_CONFIG_TEMPLATE", "${ShellTestConfigTemplate}")
ctx.Strict("EMPTY_TEST_CONFIG", "${EmptyTestConfig}")
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index c314b7b..d17b464 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-ui-build-paths",
pkgPath: "android/soong/ui/build/paths",
@@ -28,6 +32,8 @@
name: "soong-ui-build",
pkgPath: "android/soong/ui/build",
deps: [
+ "blueprint",
+ "blueprint-bootstrap",
"soong-ui-build-paths",
"soong-ui-logger",
"soong-ui-metrics",
diff --git a/ui/build/bazel.go b/ui/build/bazel.go
index 81ce939..0ebfcd8 100644
--- a/ui/build/bazel.go
+++ b/ui/build/bazel.go
@@ -116,6 +116,7 @@
"RBE_exec_strategy",
"RBE_invocation_id",
"RBE_log_dir",
+ "RBE_num_retries_if_mismatched",
"RBE_platform",
"RBE_remote_accept_cache",
"RBE_remote_update_cache",
@@ -148,6 +149,9 @@
cmd.Args = append(cmd.Args, "--action_env=PATH="+pathEnvValue)
}
+ // Allow Bazel actions to see the SHELL variable (passed to Bazel above)
+ cmd.Args = append(cmd.Args, "--action_env=SHELL")
+
// Append custom build flags to the Bazel command. Changes to these flags
// may invalidate Bazel's analysis cache.
// These should be appended as the final args, so that they take precedence.
diff --git a/ui/build/build.go b/ui/build/build.go
index 926da31..3692f4f 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -218,6 +218,11 @@
what = what &^ BuildKati
}
+ if config.SkipNinja() {
+ ctx.Verboseln("Skipping Ninja as requested")
+ what = what &^ BuildNinja
+ }
+
if config.StartGoma() {
// Ensure start Goma compiler_proxy
startGoma(ctx, config)
@@ -252,6 +257,11 @@
if what&BuildSoong != 0 {
// Run Soong
runSoong(ctx, config)
+
+ if config.Environment().IsEnvTrue("GENERATE_BAZEL_FILES") {
+ // Return early, if we're using Soong as the bp2build converter.
+ return
+ }
}
if what&BuildKati != 0 {
@@ -285,7 +295,7 @@
}
// Run ninja
- runNinja(ctx, config)
+ runNinjaForBuild(ctx, config)
}
// Currently, using Bazel requires Kati and Soong to run first, so check whether to run Bazel last.
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 06f6c63..6ba497c 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -117,14 +117,14 @@
productOut("misc_info.txt"),
productOut("apex"),
productOut("kernel"),
+ productOut("kernel-*"),
productOut("data"),
productOut("skin"),
productOut("obj/NOTICE_FILES"),
productOut("obj/PACKAGING"),
productOut("ramdisk"),
productOut("debug_ramdisk"),
- productOut("vendor-ramdisk"),
- productOut("vendor-ramdisk-debug.cpio.gz"),
+ productOut("vendor_ramdisk"),
productOut("vendor_debug_ramdisk"),
productOut("test_harness_ramdisk"),
productOut("recovery"),
diff --git a/ui/build/config.go b/ui/build/config.go
index 1152cd7..4816d1f 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -48,6 +48,7 @@
dist bool
skipConfig bool
skipKati bool
+ skipNinja bool
skipSoongTests bool
// From the product config
@@ -552,6 +553,8 @@
if arg == "--make-mode" {
} else if arg == "showcommands" {
c.verbose = true
+ } else if arg == "--skip-ninja" {
+ c.skipNinja = true
} else if arg == "--skip-make" {
c.skipConfig = true
c.skipKati = true
@@ -772,6 +775,10 @@
return c.skipKati
}
+func (c *configImpl) SkipNinja() bool {
+ return c.skipNinja
+}
+
func (c *configImpl) SkipConfig() bool {
return c.skipConfig
}
diff --git a/ui/build/environment.go b/ui/build/environment.go
index 6d8a28f..50d059f 100644
--- a/ui/build/environment.go
+++ b/ui/build/environment.go
@@ -33,6 +33,19 @@
return &env
}
+// Returns a copy of the environment as a map[string]string.
+func (e *Environment) AsMap() map[string]string {
+ result := make(map[string]string)
+
+ for _, envVar := range *e {
+ if k, v, ok := decodeKeyValue(envVar); ok {
+ result[k] = v
+ }
+ }
+
+ return result
+}
+
// Get returns the value associated with the key, and whether it exists.
// It's equivalent to the os.LookupEnv function, but with this copy of the
// Environment.
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 7799766..5961c45 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -30,7 +30,7 @@
// Constructs and runs the Ninja command line with a restricted set of
// environment variables. It's important to restrict the environment Ninja runs
// for hermeticity reasons, and to avoid spurious rebuilds.
-func runNinja(ctx Context, config Config) {
+func runNinjaForBuild(ctx Context, config Config) {
ctx.BeginTrace(metrics.PrimaryNinja, "ninja")
defer ctx.EndTrace()
@@ -145,6 +145,7 @@
"RBE_exec_strategy",
"RBE_invocation_id",
"RBE_log_dir",
+ "RBE_num_retries_if_mismatched",
"RBE_platform",
"RBE_remote_accept_cache",
"RBE_remote_update_cache",
diff --git a/ui/build/paths/logs_test.go b/ui/build/paths/logs_test.go
index 3b1005f..067f3f3 100644
--- a/ui/build/paths/logs_test.go
+++ b/ui/build/paths/logs_test.go
@@ -26,6 +26,9 @@
)
func TestSendLog(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in short mode, sometimes hangs")
+ }
t.Run("Short name", func(t *testing.T) {
d, err := ioutil.TempDir("", "s")
if err != nil {
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index d9c33f6..45ccd04 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -106,15 +106,21 @@
cmd := Command(ctx, config, "startRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd))
if output, err := cmd.CombinedOutput(); err != nil {
- ctx.Fatalf("rbe bootstrap failed with: %v\n%s\n", err, output)
+ ctx.Fatalf("Unable to start RBE reproxy\nFAILED: RBE bootstrap failed with: %v\n%s\n", err, output)
}
}
func stopRBE(ctx Context, config Config) {
cmd := Command(ctx, config, "stopRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd), "-shutdown")
- if output, err := cmd.CombinedOutput(); err != nil {
+ output, err := cmd.CombinedOutput()
+ if err != nil {
ctx.Fatalf("rbe bootstrap with shutdown failed with: %v\n%s\n", err, output)
}
+
+ if len(output) > 0 {
+ fmt.Fprintln(ctx.Writer, "")
+ fmt.Fprintln(ctx.Writer, fmt.Sprintf("%s", output))
+ }
}
// DumpRBEMetrics creates a metrics protobuf file containing RBE related metrics.
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 6a93672..7e94b25 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -19,9 +19,13 @@
"os"
"path/filepath"
"strconv"
- "strings"
+
+ "android/soong/shared"
+ "github.com/google/blueprint/deptools"
soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/bootstrap"
"github.com/golang/protobuf/proto"
"github.com/google/blueprint/microfactory"
@@ -30,78 +34,212 @@
"android/soong/ui/status"
)
+const (
+ availableEnvFile = "soong.environment.available"
+ usedEnvFile = "soong.environment.used"
+)
+
+func writeEnvironmentFile(ctx Context, envFile string, envDeps map[string]string) error {
+ data, err := shared.EnvFileContents(envDeps)
+ if err != nil {
+ return err
+ }
+
+ return ioutil.WriteFile(envFile, data, 0644)
+}
+
// This uses Android.bp files and various tools to generate <builddir>/build.ninja.
//
-// However, the execution of <builddir>/build.ninja happens later in build/soong/ui/build/build.go#Build()
+// However, the execution of <builddir>/build.ninja happens later in
+// build/soong/ui/build/build.go#Build()
//
-// We want to rely on as few prebuilts as possible, so there is some bootstrapping here.
+// We want to rely on as few prebuilts as possible, so we need to bootstrap
+// Soong. The process is as follows:
//
-// "Microfactory" is a tool for compiling Go code. We use it to build two other tools:
-// - minibp, used to generate build.ninja files. This is really build/blueprint/bootstrap/command.go#Main()
-// - bpglob, used during incremental builds to identify files in a glob that have changed
+// 1. We use "Microfactory", a simple tool to compile Go code, to build
+// first itself, then soong_ui from soong_ui.bash. This binary contains
+// parts of soong_build that are needed to build itself.
+// 2. This simplified version of soong_build then reads the Blueprint files
+// that describe itself and emits .bootstrap/build.ninja that describes
+// how to build its full version and use that to produce the final Ninja
+// file Soong emits.
+// 3. soong_ui executes .bootstrap/build.ninja
//
-// In reality, several build.ninja files are generated and/or used during the bootstrapping and build process.
-// See build/blueprint/bootstrap/doc.go for more information.
-//
+// (After this, Kati is executed to parse the Makefiles, but that's not part of
+// bootstrapping Soong)
+
+// A tiny struct used to tell Blueprint that it's in bootstrap mode. It would
+// probably be nicer to use a flag in bootstrap.Args instead.
+type BlueprintConfig struct {
+ srcDir string
+ buildDir string
+ ninjaBuildDir string
+ debugCompilation bool
+}
+
+func (c BlueprintConfig) SrcDir() string {
+ return "."
+}
+
+func (c BlueprintConfig) BuildDir() string {
+ return c.buildDir
+}
+
+func (c BlueprintConfig) NinjaBuildDir() string {
+ return c.ninjaBuildDir
+}
+
+func (c BlueprintConfig) DebugCompilation() bool {
+ return c.debugCompilation
+}
+
+func environmentArgs(config Config, suffix string) []string {
+ return []string{
+ "--available_env", shared.JoinPath(config.SoongOutDir(), availableEnvFile),
+ "--used_env", shared.JoinPath(config.SoongOutDir(), usedEnvFile+suffix),
+ }
+}
+func bootstrapBlueprint(ctx Context, config Config, integratedBp2Build bool) {
+ ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
+ defer ctx.EndTrace()
+
+ var args bootstrap.Args
+
+ mainNinjaFile := shared.JoinPath(config.SoongOutDir(), "build.ninja")
+ globFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/soong-build-globs.ninja")
+ bootstrapGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.ninja")
+ bootstrapDepFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja.d")
+
+ args.RunGoTests = !config.skipSoongTests
+ args.UseValidations = true // Use validations to depend on tests
+ args.BuildDir = config.SoongOutDir()
+ args.NinjaBuildDir = config.OutDir()
+ args.TopFile = "Android.bp"
+ args.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list")
+ args.OutFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja")
+ args.GlobFile = globFile
+ args.GeneratingPrimaryBuilder = true
+
+ args.DelveListen = os.Getenv("SOONG_DELVE")
+ if args.DelveListen != "" {
+ args.DelvePath = shared.ResolveDelveBinary()
+ }
+
+ commonArgs := bootstrap.PrimaryBuilderExtraFlags(args, bootstrapGlobFile, mainNinjaFile)
+ bp2BuildMarkerFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/bp2build_workspace_marker")
+ mainSoongBuildInputs := []string{"Android.bp"}
+
+ if integratedBp2Build {
+ mainSoongBuildInputs = append(mainSoongBuildInputs, bp2BuildMarkerFile)
+ }
+
+ soongBuildArgs := make([]string, 0)
+ soongBuildArgs = append(soongBuildArgs, commonArgs...)
+ soongBuildArgs = append(soongBuildArgs, environmentArgs(config, "")...)
+ soongBuildArgs = append(soongBuildArgs, "Android.bp")
+
+ mainSoongBuildInvocation := bootstrap.PrimaryBuilderInvocation{
+ Inputs: mainSoongBuildInputs,
+ Outputs: []string{mainNinjaFile},
+ Args: soongBuildArgs,
+ }
+
+ if integratedBp2Build {
+ bp2buildArgs := []string{"--bp2build_marker", bp2BuildMarkerFile}
+ bp2buildArgs = append(bp2buildArgs, commonArgs...)
+ bp2buildArgs = append(bp2buildArgs, environmentArgs(config, ".bp2build")...)
+ bp2buildArgs = append(bp2buildArgs, "Android.bp")
+
+ bp2buildInvocation := bootstrap.PrimaryBuilderInvocation{
+ Inputs: []string{"Android.bp"},
+ Outputs: []string{bp2BuildMarkerFile},
+ Args: bp2buildArgs,
+ }
+ args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{
+ bp2buildInvocation,
+ mainSoongBuildInvocation,
+ }
+ } else {
+ args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{mainSoongBuildInvocation}
+ }
+
+ blueprintCtx := blueprint.NewContext()
+ blueprintCtx.SetIgnoreUnknownModuleTypes(true)
+ blueprintConfig := BlueprintConfig{
+ srcDir: os.Getenv("TOP"),
+ buildDir: config.SoongOutDir(),
+ ninjaBuildDir: config.OutDir(),
+ debugCompilation: os.Getenv("SOONG_DELVE") != "",
+ }
+
+ bootstrapDeps := bootstrap.RunBlueprint(args, blueprintCtx, blueprintConfig)
+ err := deptools.WriteDepFile(bootstrapDepFile, args.OutFile, bootstrapDeps)
+ if err != nil {
+ ctx.Fatalf("Error writing depfile '%s': %s", bootstrapDepFile, err)
+ }
+}
+
+func checkEnvironmentFile(currentEnv *Environment, envFile string) {
+ getenv := func(k string) string {
+ v, _ := currentEnv.Get(k)
+ return v
+ }
+ if stale, _ := shared.StaleEnvFile(envFile, getenv); stale {
+ os.Remove(envFile)
+ }
+}
+
func runSoong(ctx Context, config Config) {
ctx.BeginTrace(metrics.RunSoong, "soong")
defer ctx.EndTrace()
- // Use an anonymous inline function for tracing purposes (this pattern is used several times below).
- func() {
- ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
- defer ctx.EndTrace()
+ // We have two environment files: .available is the one with every variable,
+ // .used with the ones that were actually used. The latter is used to
+ // determine whether Soong needs to be re-run since why re-run it if only
+ // unused variables were changed?
+ envFile := filepath.Join(config.SoongOutDir(), availableEnvFile)
- // Use validations to depend on tests.
- args := []string{"-n"}
-
- if !config.skipSoongTests {
- // Run tests.
- args = append(args, "-t")
+ for _, n := range []string{".bootstrap", ".minibootstrap"} {
+ dir := filepath.Join(config.SoongOutDir(), n)
+ if err := os.MkdirAll(dir, 0755); err != nil {
+ ctx.Fatalf("Cannot mkdir " + dir)
}
+ }
- cmd := Command(ctx, config, "blueprint bootstrap", "build/blueprint/bootstrap.bash", args...)
- cmd.Environment.Set("BLUEPRINTDIR", "./build/blueprint")
- cmd.Environment.Set("BOOTSTRAP", "./build/blueprint/bootstrap.bash")
- cmd.Environment.Set("BUILDDIR", config.SoongOutDir())
- cmd.Environment.Set("GOROOT", "./"+filepath.Join("prebuilts/go", config.HostPrebuiltTag()))
- cmd.Environment.Set("BLUEPRINT_LIST_FILE", filepath.Join(config.FileListDir(), "Android.bp.list"))
- cmd.Environment.Set("NINJA_BUILDDIR", config.OutDir())
- cmd.Environment.Set("SRCDIR", ".")
- cmd.Environment.Set("TOPNAME", "Android.bp")
- cmd.Sandbox = soongSandbox
+ integratedBp2Build := config.Environment().IsEnvTrue("INTEGRATED_BP2BUILD")
- cmd.RunAndPrintOrFatal()
- }()
+ // This is done unconditionally, but does not take a measurable amount of time
+ bootstrapBlueprint(ctx, config, integratedBp2Build)
+
+ soongBuildEnv := config.Environment().Copy()
+ soongBuildEnv.Set("TOP", os.Getenv("TOP"))
+ // For Bazel mixed builds.
+ soongBuildEnv.Set("BAZEL_PATH", "./tools/bazel")
+ soongBuildEnv.Set("BAZEL_HOME", filepath.Join(config.BazelOutDir(), "bazelhome"))
+ soongBuildEnv.Set("BAZEL_OUTPUT_BASE", filepath.Join(config.BazelOutDir(), "output"))
+ soongBuildEnv.Set("BAZEL_WORKSPACE", absPath(ctx, "."))
+ soongBuildEnv.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir())
+
+ // For Soong bootstrapping tests
+ if os.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
+ soongBuildEnv.Set("ALLOW_MISSING_DEPENDENCIES", "true")
+ }
+
+ err := writeEnvironmentFile(ctx, envFile, soongBuildEnv.AsMap())
+ if err != nil {
+ ctx.Fatalf("failed to write environment file %s: %s", envFile, err)
+ }
func() {
ctx.BeginTrace(metrics.RunSoong, "environment check")
defer ctx.EndTrace()
- envFile := filepath.Join(config.SoongOutDir(), ".soong.environment")
- envTool := filepath.Join(config.SoongOutDir(), ".bootstrap/bin/soong_env")
- if _, err := os.Stat(envFile); err == nil {
- if _, err := os.Stat(envTool); err == nil {
- cmd := Command(ctx, config, "soong_env", envTool, envFile)
- cmd.Sandbox = soongSandbox
+ soongBuildEnvFile := filepath.Join(config.SoongOutDir(), usedEnvFile)
+ checkEnvironmentFile(soongBuildEnv, soongBuildEnvFile)
- var buf strings.Builder
- cmd.Stdout = &buf
- cmd.Stderr = &buf
- if err := cmd.Run(); err != nil {
- ctx.Verboseln("soong_env failed, forcing manifest regeneration")
- os.Remove(envFile)
- }
-
- if buf.Len() > 0 {
- ctx.Verboseln(buf.String())
- }
- } else {
- ctx.Verboseln("Missing soong_env tool, forcing manifest regeneration")
- os.Remove(envFile)
- }
- } else if !os.IsNotExist(err) {
- ctx.Fatalf("Failed to stat %f: %v", envFile, err)
+ if integratedBp2Build {
+ bp2buildEnvFile := filepath.Join(config.SoongOutDir(), usedEnvFile+".bp2build")
+ checkEnvironmentFile(soongBuildEnv, bp2buildEnvFile)
}
}()
@@ -111,16 +249,6 @@
cfg.TrimPath = absPath(ctx, ".")
func() {
- ctx.BeginTrace(metrics.RunSoong, "minibp")
- defer ctx.EndTrace()
-
- minibp := filepath.Join(config.SoongOutDir(), ".minibootstrap/minibp")
- if _, err := microfactory.Build(&cfg, minibp, "github.com/google/blueprint/bootstrap/minibp"); err != nil {
- ctx.Fatalln("Failed to build minibp:", err)
- }
- }()
-
- func() {
ctx.BeginTrace(metrics.RunSoong, "bpglob")
defer ctx.EndTrace()
@@ -151,26 +279,24 @@
"--frontend_file", fifo,
"-f", filepath.Join(config.SoongOutDir(), file))
- // For Bazel mixed builds.
- cmd.Environment.Set("BAZEL_PATH", "./tools/bazel")
- cmd.Environment.Set("BAZEL_HOME", filepath.Join(config.BazelOutDir(), "bazelhome"))
- cmd.Environment.Set("BAZEL_OUTPUT_BASE", filepath.Join(config.BazelOutDir(), "output"))
- cmd.Environment.Set("BAZEL_WORKSPACE", absPath(ctx, "."))
- cmd.Environment.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir())
+ var ninjaEnv Environment
- cmd.Environment.Set("SOONG_SANDBOX_SOONG_BUILD", "true")
+ // This is currently how the command line to invoke soong_build finds the
+ // root of the source tree and the output root
+ ninjaEnv.Set("TOP", os.Getenv("TOP"))
+
+ cmd.Environment = &ninjaEnv
cmd.Sandbox = soongSandbox
cmd.RunAndStreamOrFatal()
}
-
- // This build generates .bootstrap/build.ninja, which is used in the next step.
- ninja("minibootstrap", ".minibootstrap/build.ninja")
-
// This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
ninja("bootstrap", ".bootstrap/build.ninja")
- soongBuildMetrics := loadSoongBuildMetrics(ctx, config)
- logSoongBuildMetrics(ctx, soongBuildMetrics)
+ var soongBuildMetrics *soong_metrics_proto.SoongBuildMetrics
+ if shouldCollectBuildSoongMetrics(config) {
+ soongBuildMetrics := loadSoongBuildMetrics(ctx, config)
+ logSoongBuildMetrics(ctx, soongBuildMetrics)
+ }
distGzipFile(ctx, config, config.SoongNinjaFile(), "soong")
@@ -179,11 +305,16 @@
distGzipFile(ctx, config, config.SoongMakeVarsMk(), "soong")
}
- if ctx.Metrics != nil {
+ if shouldCollectBuildSoongMetrics(config) && ctx.Metrics != nil {
ctx.Metrics.SetSoongBuildMetrics(soongBuildMetrics)
}
}
+func shouldCollectBuildSoongMetrics(config Config) bool {
+ // Do not collect metrics protobuf if the soong_build binary ran as the bp2build converter.
+ return config.Environment().IsFalse("GENERATE_BAZEL_FILES")
+}
+
func loadSoongBuildMetrics(ctx Context, config Config) *soong_metrics_proto.SoongBuildMetrics {
soongBuildMetricsFile := filepath.Join(config.OutDir(), "soong", "soong_build_metrics.pb")
buf, err := ioutil.ReadFile(soongBuildMetricsFile)
diff --git a/ui/logger/Android.bp b/ui/logger/Android.bp
index 8091ef9..269a5a0 100644
--- a/ui/logger/Android.bp
+++ b/ui/logger/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-ui-logger",
pkgPath: "android/soong/ui/logger",
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 95c8f5c..c428ec4 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-ui-metrics",
pkgPath: "android/soong/ui/metrics",
diff --git a/ui/metrics/proc/Android.bp b/ui/metrics/proc/Android.bp
index 32d8217..4501fed 100644
--- a/ui/metrics/proc/Android.bp
+++ b/ui/metrics/proc/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-ui-metrics-proc",
pkgPath: "android/soong/ui/metrics/proc",
diff --git a/ui/status/Android.bp b/ui/status/Android.bp
index 19e5a2a..ac31390 100644
--- a/ui/status/Android.bp
+++ b/ui/status/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-ui-status",
pkgPath: "android/soong/ui/status",
diff --git a/ui/terminal/Android.bp b/ui/terminal/Android.bp
index aa6e35d..fdf300f 100644
--- a/ui/terminal/Android.bp
+++ b/ui/terminal/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-ui-terminal",
pkgPath: "android/soong/ui/terminal",
diff --git a/ui/tracer/Android.bp b/ui/tracer/Android.bp
index af588f1..d8942fd 100644
--- a/ui/tracer/Android.bp
+++ b/ui/tracer/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-ui-tracer",
pkgPath: "android/soong/ui/tracer",
diff --git a/vnames.go.json b/vnames.go.json
deleted file mode 100644
index 7ce2d4b..0000000
--- a/vnames.go.json
+++ /dev/null
@@ -1,9 +0,0 @@
-[
- {
- "pattern": "(.*)",
- "vname": {
- "corpus": "android.googlesource.com/platform/superproject",
- "path": "@1@"
- }
- }
-]
diff --git a/vnames.json b/vnames.json
index f9d3adc..096260f 100644
--- a/vnames.json
+++ b/vnames.json
@@ -2,7 +2,6 @@
{
"pattern": "out/(.*)",
"vname": {
- "corpus": "android.googlesource.com/platform/superproject",
"root": "out",
"path": "@1@"
}
@@ -10,9 +9,7 @@
{
"pattern": "(.*)",
"vname": {
- "corpus": "android.googlesource.com/platform/superproject",
"path": "@1@"
}
}
]
-
diff --git a/xml/Android.bp b/xml/Android.bp
index cd25cff..1542930 100644
--- a/xml/Android.bp
+++ b/xml/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-xml",
pkgPath: "android/soong/xml",
@@ -9,6 +13,7 @@
"soong-etc",
],
srcs: [
+ "testing.go",
"xml.go",
],
testSrcs: [
diff --git a/cmd/soong_env/Android.bp b/xml/testing.go
similarity index 72%
rename from cmd/soong_env/Android.bp
rename to xml/testing.go
index 4db0da3..1d09f10 100644
--- a/cmd/soong_env/Android.bp
+++ b/xml/testing.go
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,13 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-bootstrap_go_binary {
- name: "soong_env",
- deps: [
- "soong-env",
- ],
- srcs: [
- "soong_env.go",
- ],
- default: true,
-}
+package xml
+
+import "android/soong/android"
+
+var PreparerForTestWithXmlBuildComponents = android.FixtureRegisterWithContext(registerXmlBuildComponents)
diff --git a/xml/xml.go b/xml/xml.go
index 8810ae4..c281078 100644
--- a/xml/xml.go
+++ b/xml/xml.go
@@ -53,10 +53,14 @@
)
func init() {
- android.RegisterModuleType("prebuilt_etc_xml", PrebuiltEtcXmlFactory)
+ registerXmlBuildComponents(android.InitRegistrationContext)
pctx.HostBinToolVariable("XmlLintCmd", "xmllint")
}
+func registerXmlBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("prebuilt_etc_xml", PrebuiltEtcXmlFactory)
+}
+
type prebuiltEtcXmlProperties struct {
// Optional DTD that will be used to validate the xml file.
Schema *string `android:"path"`
diff --git a/xml/xml_test.go b/xml/xml_test.go
index 138503c..a59a293 100644
--- a/xml/xml_test.go
+++ b/xml/xml_test.go
@@ -15,7 +15,6 @@
package xml
import (
- "io/ioutil"
"os"
"testing"
@@ -23,62 +22,31 @@
"android/soong/etc"
)
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_xml_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ os.Exit(m.Run())
}
-func testXml(t *testing.T, bp string) *android.TestContext {
- fs := map[string][]byte{
+func testXml(t *testing.T, bp string) *android.TestResult {
+ fs := android.MockFS{
"foo.xml": nil,
"foo.dtd": nil,
"bar.xml": nil,
"bar.xsd": nil,
"baz.xml": nil,
}
- config := android.TestArchConfig(buildDir, nil, bp, fs)
- ctx := android.NewTestArchContext(config)
- ctx.RegisterModuleType("prebuilt_etc", etc.PrebuiltEtcFactory)
- ctx.RegisterModuleType("prebuilt_etc_xml", PrebuiltEtcXmlFactory)
- ctx.Register()
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
- return ctx
-}
-
-func assertEqual(t *testing.T, name, expected, actual string) {
- t.Helper()
- if expected != actual {
- t.Errorf(name+" expected %q != got %q", expected, actual)
- }
+ return android.GroupFixturePreparers(
+ android.PrepareForTestWithArchMutator,
+ etc.PrepareForTestWithPrebuiltEtc,
+ PreparerForTestWithXmlBuildComponents,
+ fs.AddToFixture(),
+ android.FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
}
// Minimal test
func TestPrebuiltEtcXml(t *testing.T) {
- ctx := testXml(t, `
+ result := testXml(t, `
prebuilt_etc_xml {
name: "foo.xml",
src: "foo.xml",
@@ -103,14 +71,14 @@
{rule: "xmllint-minimal", input: "baz.xml"},
} {
t.Run(tc.schemaType, func(t *testing.T) {
- rule := ctx.ModuleForTests(tc.input, "android_arm64_armv8-a").Rule(tc.rule)
- assertEqual(t, "input", tc.input, rule.Input.String())
+ rule := result.ModuleForTests(tc.input, "android_arm64_armv8-a").Rule(tc.rule)
+ android.AssertStringEquals(t, "input", tc.input, rule.Input.String())
if tc.schemaType != "" {
- assertEqual(t, "schema", tc.schema, rule.Args[tc.schemaType])
+ android.AssertStringEquals(t, "schema", tc.schema, rule.Args[tc.schemaType])
}
})
}
- m := ctx.ModuleForTests("foo.xml", "android_arm64_armv8-a").Module().(*prebuiltEtcXml)
- assertEqual(t, "installDir", buildDir+"/target/product/test_device/system/etc", m.InstallDirPath().String())
+ m := result.ModuleForTests("foo.xml", "android_arm64_armv8-a").Module().(*prebuiltEtcXml)
+ android.AssertPathRelativeToTopEquals(t, "installDir", "out/soong/target/product/test_device/system/etc", m.InstallDirPath())
}
diff --git a/zip/Android.bp b/zip/Android.bp
index 5081e91..14541eb 100644
--- a/zip/Android.bp
+++ b/zip/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
subdirs = ["cmd"]
bootstrap_go_package {
@@ -21,6 +25,7 @@
"android-archive-zip",
"blueprint-pathtools",
"soong-jar",
+ "soong-response",
],
srcs: [
"zip.go",
diff --git a/zip/cmd/Android.bp b/zip/cmd/Android.bp
index 6029a69..43bf232 100644
--- a/zip/cmd/Android.bp
+++ b/zip/cmd/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
blueprint_go_binary {
name: "soong_zip",
deps: [
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index fc976f6..cbc73ed 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -24,7 +24,6 @@
import (
"flag"
"fmt"
- "io/ioutil"
"os"
"runtime"
"runtime/pprof"
@@ -32,6 +31,7 @@
"strconv"
"strings"
+ "android/soong/response"
"android/soong/zip"
)
@@ -125,12 +125,18 @@
var expandedArgs []string
for _, arg := range os.Args {
if strings.HasPrefix(arg, "@") {
- bytes, err := ioutil.ReadFile(strings.TrimPrefix(arg, "@"))
+ f, err := os.Open(strings.TrimPrefix(arg, "@"))
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
- respArgs := zip.ReadRespFile(bytes)
+
+ respArgs, err := response.ReadRspFile(f)
+ f.Close()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
expandedArgs = append(expandedArgs, respArgs...)
} else {
expandedArgs = append(expandedArgs, arg)
diff --git a/zip/zip.go b/zip/zip.go
index cb85f5c..84e974b 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -29,7 +29,8 @@
"sync"
"syscall"
"time"
- "unicode"
+
+ "android/soong/response"
"github.com/google/blueprint/pathtools"
@@ -164,14 +165,15 @@
}
defer f.Close()
- list, err := ioutil.ReadAll(f)
+ arg := b.state
+ arg.SourceFiles, err = response.ReadRspFile(f)
if err != nil {
b.err = err
return b
}
-
- arg := b.state
- arg.SourceFiles = ReadRespFile(list)
+ for i := range arg.SourceFiles {
+ arg.SourceFiles[i] = pathtools.MatchEscape(arg.SourceFiles[i])
+ }
b.fileArgs = append(b.fileArgs, arg)
return b
}
@@ -250,49 +252,6 @@
Filesystem pathtools.FileSystem
}
-const NOQUOTE = '\x00'
-
-func ReadRespFile(bytes []byte) []string {
- var args []string
- var arg []rune
-
- isEscaping := false
- quotingStart := NOQUOTE
- for _, c := range string(bytes) {
- switch {
- case isEscaping:
- if quotingStart == '"' {
- if !(c == '"' || c == '\\') {
- // '\"' or '\\' will be escaped under double quoting.
- arg = append(arg, '\\')
- }
- }
- arg = append(arg, c)
- isEscaping = false
- case c == '\\' && quotingStart != '\'':
- isEscaping = true
- case quotingStart == NOQUOTE && (c == '\'' || c == '"'):
- quotingStart = c
- case quotingStart != NOQUOTE && c == quotingStart:
- quotingStart = NOQUOTE
- case quotingStart == NOQUOTE && unicode.IsSpace(c):
- // Current character is a space outside quotes
- if len(arg) != 0 {
- args = append(args, string(arg))
- }
- arg = arg[:0]
- default:
- arg = append(arg, c)
- }
- }
-
- if len(arg) != 0 {
- args = append(args, string(arg))
- }
-
- return args
-}
-
func zipTo(args ZipArgs, w io.Writer) error {
if args.EmulateJar {
args.AddDirectoryEntriesToZip = true
@@ -333,11 +292,11 @@
continue
}
- globbed, _, err := z.fs.Glob(s, nil, followSymlinks)
+ result, err := z.fs.Glob(s, nil, followSymlinks)
if err != nil {
return err
}
- if len(globbed) == 0 {
+ if len(result.Matches) == 0 {
err := &os.PathError{
Op: "lstat",
Path: s,
@@ -349,7 +308,7 @@
return err
}
}
- srcs = append(srcs, globbed...)
+ srcs = append(srcs, result.Matches...)
}
if fa.GlobDir != "" {
if exists, isDir, err := z.fs.Exists(fa.GlobDir); err != nil {
@@ -377,11 +336,11 @@
return err
}
}
- globbed, _, err := z.fs.Glob(filepath.Join(fa.GlobDir, "**/*"), nil, followSymlinks)
+ result, err := z.fs.Glob(filepath.Join(fa.GlobDir, "**/*"), nil, followSymlinks)
if err != nil {
return err
}
- srcs = append(srcs, globbed...)
+ srcs = append(srcs, result.Matches...)
}
for _, src := range srcs {
err := fillPathPairs(fa, src, &pathMappings, args.NonDeflatedFiles, noCompression)
@@ -403,6 +362,8 @@
buf := &bytes.Buffer{}
var out io.Writer = buf
+ var zipErr error
+
if !args.WriteIfChanged {
f, err := os.Create(args.OutputFilePath)
if err != nil {
@@ -411,7 +372,7 @@
defer f.Close()
defer func() {
- if err != nil {
+ if zipErr != nil {
os.Remove(args.OutputFilePath)
}
}()
@@ -419,9 +380,9 @@
out = f
}
- err := zipTo(args, out)
- if err != nil {
- return err
+ zipErr = zipTo(args, out)
+ if zipErr != nil {
+ return zipErr
}
if args.WriteIfChanged {
diff --git a/zip/zip_test.go b/zip/zip_test.go
index a16e092..a37ae41 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -46,13 +46,14 @@
"dangling -> missing": nil,
"a/a/d -> b": nil,
"c": fileC,
- "l_nl": []byte("a/a/a\na/a/b\nc\n"),
- "l_sp": []byte("a/a/a a/a/b c"),
+ "l_nl": []byte("a/a/a\na/a/b\nc\n\\[\n"),
+ "l_sp": []byte("a/a/a a/a/b c \\["),
"l2": []byte("missing\n"),
- "rsp": []byte("'a/a/a'\na/a/b\n'@'\n'foo'\\''bar'"),
+ "rsp": []byte("'a/a/a'\na/a/b\n'@'\n'foo'\\''bar'\n'['"),
"@ -> c": nil,
"foo'bar -> c": nil,
"manifest.txt": fileCustomManifest,
+ "[": fileEmpty,
})
func fh(name string, contents []byte, method uint16) zip.FileHeader {
@@ -127,13 +128,15 @@
args: fileArgsBuilder().
File("a/a/a").
File("a/a/b").
- File("c"),
+ File("c").
+ File(`\[`),
compressionLevel: 9,
files: []zip.FileHeader{
fh("a/a/a", fileA, zip.Deflate),
fh("a/a/b", fileB, zip.Deflate),
fh("c", fileC, zip.Deflate),
+ fh("[", fileEmpty, zip.Store),
},
},
{
@@ -235,6 +238,7 @@
fh("a/a/a", fileA, zip.Deflate),
fh("a/a/b", fileB, zip.Deflate),
fh("c", fileC, zip.Deflate),
+ fh("[", fileEmpty, zip.Store),
},
},
{
@@ -247,6 +251,7 @@
fh("a/a/a", fileA, zip.Deflate),
fh("a/a/b", fileB, zip.Deflate),
fh("c", fileC, zip.Deflate),
+ fh("[", fileEmpty, zip.Store),
},
},
{
@@ -260,6 +265,7 @@
fh("a/a/b", fileB, zip.Deflate),
fh("@", fileC, zip.Deflate),
fh("foo'bar", fileC, zip.Deflate),
+ fh("[", fileEmpty, zip.Store),
},
},
{
@@ -529,78 +535,6 @@
}
}
-func TestReadRespFile(t *testing.T) {
- testCases := []struct {
- name, in string
- out []string
- }{
- {
- name: "single quoting test case 1",
- in: `./cmd '"'-C`,
- out: []string{"./cmd", `"-C`},
- },
- {
- name: "single quoting test case 2",
- in: `./cmd '-C`,
- out: []string{"./cmd", `-C`},
- },
- {
- name: "single quoting test case 3",
- in: `./cmd '\"'-C`,
- out: []string{"./cmd", `\"-C`},
- },
- {
- name: "single quoting test case 4",
- in: `./cmd '\\'-C`,
- out: []string{"./cmd", `\\-C`},
- },
- {
- name: "none quoting test case 1",
- in: `./cmd \'-C`,
- out: []string{"./cmd", `'-C`},
- },
- {
- name: "none quoting test case 2",
- in: `./cmd \\-C`,
- out: []string{"./cmd", `\-C`},
- },
- {
- name: "none quoting test case 3",
- in: `./cmd \"-C`,
- out: []string{"./cmd", `"-C`},
- },
- {
- name: "double quoting test case 1",
- in: `./cmd "'"-C`,
- out: []string{"./cmd", `'-C`},
- },
- {
- name: "double quoting test case 2",
- in: `./cmd "\\"-C`,
- out: []string{"./cmd", `\-C`},
- },
- {
- name: "double quoting test case 3",
- in: `./cmd "\""-C`,
- out: []string{"./cmd", `"-C`},
- },
- {
- name: "ninja rsp file",
- in: "'a'\nb\n'@'\n'foo'\\''bar'\n'foo\"bar'",
- out: []string{"a", "b", "@", "foo'bar", `foo"bar`},
- },
- }
-
- for _, testCase := range testCases {
- t.Run(testCase.name, func(t *testing.T) {
- got := ReadRespFile([]byte(testCase.in))
- if !reflect.DeepEqual(got, testCase.out) {
- t.Errorf("expected %q got %q", testCase.out, got)
- }
- })
- }
-}
-
func TestSrcJar(t *testing.T) {
mockFs := pathtools.MockFs(map[string][]byte{
"wrong_package.java": []byte("package foo;"),