Merge changes from topic "tvts-test-host-jni"
* changes:
Add jni_libs property to java tests
Add Target to cc.SharedLibraryInfo
diff --git a/Android.bp b/Android.bp
index 1012dba..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,
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/android/Android.bp b/android/Android.bp
index eabb137..62d5e20 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,7 +24,9 @@
"api_levels.go",
"arch.go",
"arch_list.go",
+ "bazel.go",
"bazel_handler.go",
+ "bazel_paths.go",
"config.go",
"csuite_config.go",
"deapexer.go",
@@ -29,6 +37,7 @@
"deptag.go",
"expand.go",
"filegroup.go",
+ "fixture.go",
"hooks.go",
"image.go",
"license.go",
@@ -59,29 +68,30 @@
"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",
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 5856851..618e4be 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -44,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 {
@@ -141,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.
@@ -448,7 +469,13 @@
// 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()
@@ -470,7 +497,7 @@
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...)
@@ -478,6 +505,8 @@
// 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)
+ } else if len(amod.commonProperties.Effective_licenses) > 0 {
+ a.SetString("LOCAL_LICENSE_PACKAGE_NAME", strings.Join(amod.commonProperties.Effective_licenses, " "))
}
a.SetString("LOCAL_MODULE_CLASS", a.Class)
a.SetString("LOCAL_PREBUILT_MODULE_FILE", a.OutputFile.String())
@@ -519,7 +548,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))
@@ -561,17 +590,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)
}
@@ -732,7 +767,7 @@
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,
@@ -745,7 +780,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
@@ -768,7 +803,7 @@
data.Include = "$(BUILD_PREBUILT)"
}
- data.fillInData(ctx.Config(), ctx.BlueprintFile(mod), mod)
+ data.fillInData(ctx, mod)
prefix := ""
if amod.ArchSpecific() {
@@ -795,7 +830,7 @@
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
+ 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
@@ -850,7 +885,7 @@
// 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 31c62e9..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
@@ -82,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 {
@@ -111,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 {
@@ -127,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).
@@ -244,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
@@ -251,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.
@@ -407,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) }
@@ -706,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
@@ -726,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
@@ -736,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.
@@ -830,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
}
@@ -849,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 1f786e6..109b1c8 100644
--- a/android/apex_test.go
+++ b/android/apex_test.go
@@ -33,10 +33,10 @@
{
name: "single",
in: []ApexInfo{
- {"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"foo", "apex10000"},
@@ -45,11 +45,11 @@
{
name: "merge",
in: []ApexInfo{
- {"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
- {"bar", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"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, false}},
+ {"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, nil, false}},
wantAliases: [][2]string{
{"bar", "apex10000_baz_1"},
{"foo", "apex10000_baz_1"},
@@ -58,12 +58,12 @@
{
name: "don't merge version",
in: []ApexInfo{
- {"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
- {"bar", "30", false, nil, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"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, NotForPrebuiltApex},
- {"apex10000", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"apex30", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"bar", "apex30"},
@@ -73,11 +73,11 @@
{
name: "merge updatable",
in: []ApexInfo{
- {"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
- {"bar", "current", true, nil, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"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, NotForPrebuiltApex},
+ {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"bar", "apex10000"},
@@ -87,12 +87,12 @@
{
name: "don't merge sdks",
in: []ApexInfo{
- {"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
- {"bar", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"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, NotForPrebuiltApex},
- {"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"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"},
@@ -102,15 +102,15 @@
{
name: "don't merge when for prebuilt_apex",
in: []ApexInfo{
- {"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
- {"bar", "current", true, nil, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"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", "current", true, nil, []string{"baz"}, nil, ForPrebuiltApex},
+ {"baz", FutureApiLevel, true, nil, []string{"baz"}, nil, ForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000", "current", true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
- {"baz", "current", true, nil, []string{"baz"}, nil, ForPrebuiltApex},
+ {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+ {"baz", FutureApiLevel, true, nil, []string{"baz"}, nil, ForPrebuiltApex},
},
wantAliases: [][2]string{
{"bar", "apex10000"},
@@ -121,7 +121,7 @@
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 baee9be..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,20 +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 HOST_PREFER_32_BIT=true support for Art modules.
- prefer32 := false
- if base.prefer32 != nil {
- prefer32 = base.prefer32(mctx, base, os)
- }
- if os == Windows {
- // Windows builds always prefer 32-bit
- prefer32 = true
- }
+ // Windows builds always prefer 32-bit
+ prefer32 := os == Windows
// Determine the multilib selection for this module.
multilib, extraMultilib := decodeMultilib(base, os.Class)
@@ -647,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.
@@ -719,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--
}
@@ -838,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)
@@ -1434,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"}},
}
@@ -1601,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..260f1e5
--- /dev/null
+++ b/android/bazel.go
@@ -0,0 +1,289 @@
+// 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
+ "libc_static_dispatch", // eakammer@, cc_library_static, 'private/bionic_asm.h' file not found
+ "libc_dynamic_dispatch", // eakammer@, cc_library_static, 'private/bionic_ifuncs.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 4a25119..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,9 +605,10 @@
context.results = make(map[cqueryKey]string)
var cqueryOutput string
+ var cqueryErr string
var err error
- intermediatesDirPath := absolutePath(context.intermediatesDir())
+ intermediatesDirPath := absolutePath(context.paths.intermediatesDir())
if _, err := os.Stat(intermediatesDirPath); os.IsNotExist(err) {
err = os.Mkdir(intermediatesDirPath, 0777)
}
@@ -380,36 +617,42 @@
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
@@ -424,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)
}
}
@@ -435,11 +679,13 @@
//
// 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
@@ -453,14 +699,15 @@
// 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
@@ -471,7 +718,7 @@
}
func (context *bazelContext) OutputBase() string {
- return context.outputBase
+ return context.paths.outputBase
}
// Singleton used for registering BUILD file ninja dependencies (needed
@@ -490,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)
@@ -504,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",
@@ -516,6 +766,10 @@
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
@@ -525,3 +779,16 @@
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_paths.go b/android/bazel_paths.go
new file mode 100644
index 0000000..13f4949
--- /dev/null
+++ b/android/bazel_paths.go
@@ -0,0 +1,353 @@
+// 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 (
+ "android/soong/bazel"
+ "fmt"
+ "path/filepath"
+ "strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/pathtools"
+)
+
+// bazel_paths contains methods to:
+// * resolve Soong path and module references into bazel.LabelList
+// * resolve Bazel path references into Soong-compatible paths
+//
+// There is often a similar method for Bazel as there is for Soong path handling and should be used
+// in similar circumstances
+//
+// Bazel Soong
+//
+// BazelLabelForModuleSrc PathForModuleSrc
+// BazelLabelForModuleSrcExcludes PathForModuleSrcExcludes
+// BazelLabelForModuleDeps n/a
+// tbd PathForSource
+// tbd ExistentPathsForSources
+// PathForBazelOut PathForModuleOut
+//
+// Use cases:
+// * Module contains a property (often tagged `android:"path"`) that expects paths *relative to the
+// module directory*:
+// * BazelLabelForModuleSrcExcludes, if the module also contains an excludes_<propname> property
+// * BazelLabelForModuleSrc, otherwise
+// * Converting references to other modules to Bazel Labels:
+// BazelLabelForModuleDeps
+// * Converting a path obtained from bazel_handler cquery results:
+// PathForBazelOut
+//
+// NOTE: all Soong globs are expanded within Soong rather than being converted to a Bazel glob
+// syntax. This occurs because Soong does not have a concept of crossing package boundaries,
+// so the glob as computed by Soong may contain paths that cross package-boundaries. These
+// would be unknowingly omitted if the glob were handled by Bazel. By expanding globs within
+// Soong, we support identification and detection (within Bazel) use of paths that cross
+// package boundaries.
+//
+// Path resolution:
+// * filepath/globs: resolves as itself or is converted to an absolute Bazel label (e.g.
+// //path/to/dir:<filepath>) if path exists in a separate package or subpackage.
+// * references to other modules (using the ":name{.tag}" syntax). These resolve as a Bazel label
+// for a target. If the Bazel target is in the local module directory, it will be returned
+// relative to the current package (e.g. ":<target>"). Otherwise, it will be returned as an
+// absolute Bazel label (e.g. "//path/to/dir:<target>"). If the reference to another module
+// cannot be resolved,the function will panic. This is often due to the dependency not being added
+// via an AddDependency* method.
+
+// 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 expects a list of reference to other modules, ("<module>"
+// or ":<module>") and returns a Bazel-compatible label which corresponds 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
+}
+
+// BazelLabelForModuleSrc expects a list of path (relative to local module directory) and module
+// references (":<module>") and returns a bazel.LabelList{} containing the resolved references in
+// paths, relative to the local module, or Bazel-labels (absolute if in a different package or
+// relative if within the same package).
+// Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
+// will have already been handled by the path_deps mutator.
+func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList {
+ return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil))
+}
+
+// BazelLabelForModuleSrc expects lists of path and excludes (relative to local module directory)
+// and module references (":<module>") and returns a bazel.LabelList{} containing the resolved
+// references in paths, minus those in excludes, relative to the local module, or Bazel-labels
+// (absolute if in a different package or relative if within the same package).
+// Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
+// will have already been handled by the path_deps mutator.
+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
+}
+
+// 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
+}
+
+// expandSrcsForBazel returns bazel.LabelList with paths rooted from the module's local source
+// directory and Bazel target labels, excluding those included in the excludes argument (which
+// should already be expanded to resolve references to Soong-modules). Valid elements of paths
+// include:
+// * filepath, relative to local module directory, resolves as a filepath relative to the local
+// source directory
+// * glob, relative to the local module directory, resolves as filepath(s), relative to the local
+// module directory. Because Soong does not have a concept of crossing package boundaries, the
+// glob as computed by Soong may contain paths that cross package-boundaries that would be
+// unknowingly omitted if the glob were handled by Bazel. To allow identification and detect
+// (within Bazel) use of paths that cross package boundaries, we expand globs within Soong rather
+// than converting Soong glob syntax to Bazel glob syntax. **Invalid for excludes.**
+// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
+// or OutputFileProducer. These resolve as a Bazel label for a target. If the Bazel target is in
+// the local module directory, it will be returned relative to the current package (e.g.
+// ":<target>"). Otherwise, it will be returned as an absolute Bazel label (e.g.
+// "//path/to/dir:<target>"). If the reference to another module cannot be resolved,the function
+// will panic.
+// 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_deps 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 %q of %q.
+ This is likely because it was not added via AddDependency().
+ This may be due a mutator skipped during bp2build.`, 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)
+}
+
+// BazelOutPath is a Bazel output path compatible to be used for mixed builds within Soong/Ninja.
+type BazelOutPath struct {
+ OutputPath
+}
+
+var _ Path = BazelOutPath{}
+var _ objPathProvider = BazelOutPath{}
+
+func (p BazelOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
+ return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
+}
+
+// PathForBazelOut returns a Path representing the paths... under an output directory dedicated to
+// bazel-owned outputs.
+func PathForBazelOut(ctx PathContext, paths ...string) BazelOutPath {
+ execRootPathComponents := append([]string{"execroot", "__main__"}, paths...)
+ execRootPath := filepath.Join(execRootPathComponents...)
+ validatedExecRootPath, err := validatePath(execRootPath)
+ if err != nil {
+ reportPathError(ctx, err)
+ }
+
+ outputPath := OutputPath{basePath{"", ""},
+ ctx.Config().buildDir,
+ ctx.Config().BazelContext.OutputBase()}
+
+ return BazelOutPath{
+ OutputPath: outputPath.withRel(validatedExecRootPath),
+ }
+}
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 8090889..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 {
@@ -1257,7 +1259,7 @@
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 {
@@ -1271,14 +1273,14 @@
if len(c.productVariables.MemtagHeapAsyncIncludePaths) == 0 {
return false
}
- return HasAnyPrefix(path, c.productVariables.MemtagHeapAsyncIncludePaths)
+ 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)
+ return HasAnyPrefix(path, c.productVariables.MemtagHeapSyncIncludePaths) && !c.MemtagHeapDisabledForPath(path)
}
func (c *config) VendorConfig(name string) VendorConfig {
@@ -1293,10 +1295,6 @@
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)
}
@@ -1398,7 +1396,10 @@
}
func (c *deviceConfig) BoardSepolicyVers() string {
- return String(c.config.productVariables.BoardSepolicyVers)
+ if ver := String(c.config.productVariables.BoardSepolicyVers); ver != "" {
+ return ver
+ }
+ return c.PlatformSepolicyVersion()
}
func (c *deviceConfig) BoardReqdMaskPolicy() []string {
@@ -1413,6 +1414,106 @@
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
@@ -1557,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
@@ -1624,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.
@@ -1662,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/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
index b140b55..3bc6199 100644
--- a/android/license.go
+++ b/android/license.go
@@ -19,7 +19,7 @@
)
type licenseKindDependencyTag struct {
- blueprint.BaseDependencyTag
+ blueprint.BaseDependencyTag
}
var (
diff --git a/android/license_kind_test.go b/android/license_kind_test.go
index 767b64e..1f09568 100644
--- a/android/license_kind_test.go
+++ b/android/license_kind_test.go
@@ -8,7 +8,7 @@
var licenseKindTests = []struct {
name string
- fs map[string][]byte
+ fs MockFS
expectedErrors []string
}{
{
@@ -97,48 +97,19 @@
func TestLicenseKind(t *testing.T) {
for _, test := range licenseKindTests {
t.Run(test.name, func(t *testing.T) {
- _, errs := testLicenseKind(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(
+ prepareForLicenseTest,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("mock_license", newMockLicenseModule)
+ }),
+ test.fs.AddToFixture(),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+ RunTest(t)
})
}
}
-func testLicenseKind(fs map[string][]byte) (*TestContext, []error) {
-
- // Create a new config per test as license_kind information is stored in the config.
- config := TestArchConfig(buildDir, nil, "", fs)
-
- ctx := NewTestArchContext(config)
- RegisterLicenseKindBuildComponents(ctx)
- ctx.RegisterModuleType("mock_license", newMockLicenseModule)
- ctx.Register()
-
- _, errs := ctx.ParseBlueprintsFiles(".")
- if len(errs) > 0 {
- return ctx, errs
- }
-
- _, errs = ctx.PrepareBuildActions(config)
- return ctx, errs
-}
-
type mockLicenseProperties struct {
License_kinds []string
}
diff --git a/android/license_test.go b/android/license_test.go
index 552bbae..2b09a4f 100644
--- a/android/license_test.go
+++ b/android/license_test.go
@@ -4,9 +4,24 @@
"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 map[string][]byte
+ fs MockFS
expectedErrors []string
}{
{
@@ -49,9 +64,9 @@
}`),
},
expectedErrors: []string{
- `other/Blueprints:2:5: module "arule": depends on //top:top_allowed_as_notice `+
+ `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 `+
+ `yetmore/Blueprints:2:5: module "//yetmore": depends on //top:top_allowed_as_notice ` +
`which is not visible to this module`,
},
},
@@ -70,7 +85,7 @@
}`),
},
expectedErrors: []string{
- `top/Blueprints:6:5: module "top_proprietary": license_kinds property `+
+ `top/Blueprints:6:5: module "top_proprietary": license_kinds property ` +
`"top_by_exception_only" is not a license_kind module`,
},
},
@@ -163,58 +178,21 @@
func TestLicense(t *testing.T) {
for _, test := range licenseTests {
t.Run(test.name, func(t *testing.T) {
- _, errs := testLicense(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)
- }
- }
- }
+ // 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(fs map[string][]byte) (*TestContext, []error) {
-
- // Create a new config per test as visibility information is stored in the config.
- env := make(map[string]string)
- env["ANDROID_REQUIRE_LICENSES"] = "1"
- config := TestArchConfig(buildDir, env, "", fs)
-
- ctx := NewTestArchContext(config)
- RegisterPackageBuildComponents(ctx)
- registerTestPrebuiltBuildComponents(ctx)
- RegisterLicenseKindBuildComponents(ctx)
- RegisterLicenseBuildComponents(ctx)
- ctx.RegisterModuleType("rule", newMockRuleModule)
- ctx.PreArchMutators(RegisterVisibilityRuleChecker)
- ctx.PreArchMutators(RegisterLicensesPackageMapper)
- ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
- ctx.PreArchMutators(RegisterLicensesPropertyGatherer)
- ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
- ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
- ctx.PostDepsMutators(RegisterLicensesDependencyChecker)
- ctx.Register()
-
- _, errs := ctx.ParseBlueprintsFiles(".")
- if len(errs) > 0 {
- return ctx, errs
- }
-
- _, errs = ctx.PrepareBuildActions(config)
- return ctx, errs
+func testLicense(t *testing.T, fs MockFS, expectedErrors []string) {
}
type mockRuleModule struct {
diff --git a/android/licenses.go b/android/licenses.go
index 1000429..2838f5d 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -51,7 +51,7 @@
func newApplicableLicensesProperty(name string, licensesProperty *[]string) applicableLicensesProperty {
return applicableLicensesPropertyImpl{
- name: name,
+ name: name,
licensesProperty: licensesProperty,
}
}
diff --git a/android/licenses_test.go b/android/licenses_test.go
index b94add7..913dc88 100644
--- a/android/licenses_test.go
+++ b/android/licenses_test.go
@@ -6,16 +6,28 @@
"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 map[string][]byte
- 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 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",
@@ -71,28 +83,28 @@
},
effectiveLicenses: map[string][]string{
"libexample1": []string{"top_Apache2"},
- "libnested": []string{"top_Apache2"},
- "libother": []string{"top_Apache2"},
+ "libnested": []string{"top_Apache2"},
+ "libother": []string{"top_Apache2"},
},
effectiveKinds: map[string][]string{
"libexample1": []string{"notice"},
- "libnested": []string{"notice"},
- "libother": []string{"notice"},
+ "libnested": []string{"notice"},
+ "libother": []string{"notice"},
},
effectivePackage: map[string]string{
"libexample1": "topDog",
- "libnested": "topDog",
- "libother": "topDog",
+ "libnested": "topDog",
+ "libother": "topDog",
},
effectiveConditions: map[string][]string{
"libexample1": []string{"shownotice"},
- "libnested": []string{"shownotice"},
- "libother": []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"},
+ "libnested": []string{"top/LICENSE", "top/NOTICE"},
+ "libother": []string{"top/LICENSE", "top/NOTICE"},
},
},
@@ -147,28 +159,28 @@
}`),
},
effectiveLicenses: map[string][]string{
- "libexample": []string{"nested_other", "top_other"},
+ "libexample": []string{"nested_other", "top_other"},
"libsamepackage": []string{},
- "libnested": []string{},
- "libother": []string{},
+ "libnested": []string{},
+ "libother": []string{},
},
effectiveInheritedLicenses: map[string][]string{
- "libexample": []string{"nested_other", "top_other"},
+ "libexample": []string{"nested_other", "top_other"},
"libsamepackage": []string{"nested_other", "top_other"},
- "libnested": []string{"nested_other", "top_other"},
- "libother": []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"},
+ "libexample": []string{"nested_notice", "top_notice"},
"libsamepackage": []string{},
- "libnested": []string{},
- "libother": []string{},
+ "libnested": []string{},
+ "libother": []string{},
},
effectiveConditions: map[string][]string{
- "libexample": []string{"notice"},
+ "libexample": []string{"notice"},
"libsamepackage": []string{},
- "libnested": []string{},
- "libother": []string{},
+ "libnested": []string{},
+ "libother": []string{},
},
},
{
@@ -218,32 +230,32 @@
}`),
},
effectiveLicenses: map[string][]string{
- "libexample": []string{"other", "top_nested"},
+ "libexample": []string{"other", "top_nested"},
"libsamepackage": []string{},
- "libnested": []string{},
- "libother": []string{},
- "liboutsider": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ "liboutsider": []string{},
},
effectiveInheritedLicenses: map[string][]string{
- "libexample": []string{"other", "top_nested"},
+ "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"},
+ "libnested": []string{"other", "top_nested"},
+ "libother": []string{"other", "top_nested"},
+ "liboutsider": []string{"other", "top_nested"},
},
effectiveKinds: map[string][]string{
- "libexample": []string{},
+ "libexample": []string{},
"libsamepackage": []string{},
- "libnested": []string{},
- "libother": []string{},
- "liboutsider": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ "liboutsider": []string{},
},
effectiveNotices: map[string][]string{
- "libexample": []string{"top/nested/LICENSE.txt"},
+ "libexample": []string{"top/nested/LICENSE.txt"},
"libsamepackage": []string{},
- "libnested": []string{},
- "libother": []string{},
- "liboutsider": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ "liboutsider": []string{},
},
},
@@ -285,11 +297,11 @@
}`),
},
effectiveLicenses: map[string][]string{
- "libexample": []string{"by_exception_only"},
+ "libexample": []string{"by_exception_only"},
"libdefaults": []string{"notice"},
},
effectiveInheritedLicenses: map[string][]string{
- "libexample": []string{"by_exception_only"},
+ "libexample": []string{"by_exception_only"},
"libdefaults": []string{"notice"},
},
},
@@ -327,11 +339,11 @@
}`),
},
effectiveLicenses: map[string][]string{
- "libexample": []string{"top_notice"},
+ "libexample": []string{"top_notice"},
"liboutsider": []string{},
},
effectiveInheritedLicenses: map[string][]string{
- "libexample": []string{"top_notice"},
+ "libexample": []string{"top_notice"},
"liboutsider": []string{"top_notice"},
},
},
@@ -370,15 +382,15 @@
}`),
},
effectiveLicenses: map[string][]string{
- "libexample": []string{"top_notice"},
- "libnested": []string{"outsider"},
- "libother": []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{},
+ "libexample": []string{"top_notice"},
+ "libnested": []string{"outsider"},
+ "libother": []string{},
"liboutsider": []string{"top_notice", "outsider"},
},
},
@@ -449,7 +461,7 @@
},
effectiveInheritedLicenses: map[string][]string{
"module": []string{"prebuilt", "top_sources"},
- "other": []string{"prebuilt", "top_sources"},
+ "other": []string{"prebuilt", "top_sources"},
},
},
}
@@ -457,40 +469,49 @@
func TestLicenses(t *testing.T) {
for _, test := range licensesTests {
t.Run(test.name, func(t *testing.T) {
- ctx, errs := testLicenses(buildDir, test.fs)
-
- CheckErrorsAgainstExpectations(t, errs, test.expectedErrors)
+ // 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, ctx, test.effectiveLicenses)
+ checkEffectiveLicenses(t, result, test.effectiveLicenses)
}
if test.effectivePackage != nil {
- checkEffectivePackage(t, ctx, test.effectivePackage)
+ checkEffectivePackage(t, result, test.effectivePackage)
}
if test.effectiveNotices != nil {
- checkEffectiveNotices(t, ctx, test.effectiveNotices)
+ checkEffectiveNotices(t, result, test.effectiveNotices)
}
if test.effectiveKinds != nil {
- checkEffectiveKinds(t, ctx, test.effectiveKinds)
+ checkEffectiveKinds(t, result, test.effectiveKinds)
}
if test.effectiveConditions != nil {
- checkEffectiveConditions(t, ctx, test.effectiveConditions)
+ checkEffectiveConditions(t, result, test.effectiveConditions)
}
if test.effectiveInheritedLicenses != nil {
- checkEffectiveInheritedLicenses(t, ctx, test.effectiveInheritedLicenses)
+ checkEffectiveInheritedLicenses(t, result, test.effectiveInheritedLicenses)
}
})
}
}
-func checkEffectiveLicenses(t *testing.T, ctx *TestContext, effectiveLicenses map[string][]string) {
+func checkEffectiveLicenses(t *testing.T, result *TestResult, effectiveLicenses map[string][]string) {
actualLicenses := make(map[string][]string)
- ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ result.Context.Context.VisitAllModules(func(m blueprint.Module) {
if _, ok := m.(*licenseModule); ok {
return
}
@@ -523,9 +544,9 @@
}
}
-func checkEffectiveInheritedLicenses(t *testing.T, ctx *TestContext, effectiveInheritedLicenses map[string][]string) {
+func checkEffectiveInheritedLicenses(t *testing.T, result *TestResult, effectiveInheritedLicenses map[string][]string) {
actualLicenses := make(map[string][]string)
- ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ result.Context.Context.VisitAllModules(func(m blueprint.Module) {
if _, ok := m.(*licenseModule); ok {
return
}
@@ -548,7 +569,7 @@
for _, l := range base.commonProperties.Effective_licenses {
inherited[l] = true
}
- ctx.Context.Context.VisitDepsDepthFirst(m, func(c blueprint.Module) {
+ result.Context.Context.VisitDepsDepthFirst(m, func(c blueprint.Module) {
if _, ok := c.(*licenseModule); ok {
return
}
@@ -588,9 +609,9 @@
}
}
-func checkEffectivePackage(t *testing.T, ctx *TestContext, effectivePackage map[string]string) {
+func checkEffectivePackage(t *testing.T, result *TestResult, effectivePackage map[string]string) {
actualPackage := make(map[string]string)
- ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ result.Context.Context.VisitAllModules(func(m blueprint.Module) {
if _, ok := m.(*licenseModule); ok {
return
}
@@ -628,9 +649,9 @@
}
}
-func checkEffectiveNotices(t *testing.T, ctx *TestContext, effectiveNotices map[string][]string) {
+func checkEffectiveNotices(t *testing.T, result *TestResult, effectiveNotices map[string][]string) {
actualNotices := make(map[string][]string)
- ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ result.Context.Context.VisitAllModules(func(m blueprint.Module) {
if _, ok := m.(*licenseModule); ok {
return
}
@@ -663,9 +684,9 @@
}
}
-func checkEffectiveKinds(t *testing.T, ctx *TestContext, effectiveKinds map[string][]string) {
+func checkEffectiveKinds(t *testing.T, result *TestResult, effectiveKinds map[string][]string) {
actualKinds := make(map[string][]string)
- ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ result.Context.Context.VisitAllModules(func(m blueprint.Module) {
if _, ok := m.(*licenseModule); ok {
return
}
@@ -698,9 +719,9 @@
}
}
-func checkEffectiveConditions(t *testing.T, ctx *TestContext, effectiveConditions map[string][]string) {
+func checkEffectiveConditions(t *testing.T, result *TestResult, effectiveConditions map[string][]string) {
actualConditions := make(map[string][]string)
- ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ result.Context.Context.VisitAllModules(func(m blueprint.Module) {
if _, ok := m.(*licenseModule); ok {
return
}
@@ -754,41 +775,6 @@
return true
}
-func testLicenses(buildDir string, fs map[string][]byte) (*TestContext, []error) {
-
- // Create a new config per test as licenses information is stored in the config.
- env := make(map[string]string)
- env["ANDROID_REQUIRE_LICENSES"] = "1"
- config := TestArchConfig(buildDir, env, "", fs)
-
- ctx := NewTestArchContext(config)
- ctx.RegisterModuleType("mock_bad_module", newMockLicensesBadModule)
- ctx.RegisterModuleType("mock_library", newMockLicensesLibraryModule)
- ctx.RegisterModuleType("mock_defaults", defaultsLicensesFactory)
-
- // Order of the following method calls is significant.
- RegisterPackageBuildComponents(ctx)
- registerTestPrebuiltBuildComponents(ctx)
- RegisterLicenseKindBuildComponents(ctx)
- RegisterLicenseBuildComponents(ctx)
- ctx.PreArchMutators(RegisterVisibilityRuleChecker)
- ctx.PreArchMutators(RegisterLicensesPackageMapper)
- ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
- ctx.PreArchMutators(RegisterLicensesPropertyGatherer)
- ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
- ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
- ctx.PostDepsMutators(RegisterLicensesDependencyChecker)
- ctx.Register()
-
- _, errs := ctx.ParseBlueprintsFiles(".")
- if len(errs) > 0 {
- return ctx, errs
- }
-
- _, errs = ctx.PrepareBuildActions(config)
- return ctx, errs
-}
-
type mockLicensesBadProperties struct {
Visibility []string
}
diff --git a/android/module.go b/android/module.go
index d189d1a..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,12 +444,14 @@
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
@@ -491,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 /
@@ -695,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
@@ -1066,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.
@@ -1108,8 +1181,6 @@
initRcPaths Paths
vintfFragmentsPaths Paths
-
- prefer32 func(ctx BaseModuleContext, base *ModuleBase, os OsType) bool
}
func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {}
@@ -1136,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 {
@@ -1478,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)
}
@@ -1531,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
}
@@ -1770,6 +1845,18 @@
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
@@ -1782,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...)
}
@@ -1844,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 {
@@ -2187,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})
}
}
})
@@ -2334,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(, )?`)
@@ -2467,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 72c68b2..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 {
@@ -174,7 +202,7 @@
RegisterPrebuiltsPostDepsMutators,
RegisterVisibilityRuleEnforcer,
RegisterLicensesDependencyChecker,
- RegisterNeverallowMutator,
+ registerNeverallowMutator,
RegisterOverridePostDepsMutators,
}
@@ -196,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
@@ -215,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 {
@@ -340,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
}
@@ -376,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 {
@@ -386,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
}
@@ -419,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,
@@ -483,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()
}
@@ -546,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...)
}
@@ -579,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 7455e6a..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()
}
@@ -661,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 b761065..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
@@ -285,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 7012fc7..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)
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 99be13f..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
@@ -61,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 592b9e1..026cb87 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -51,6 +51,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 +106,7 @@
InstallInSanitizerDir() bool
InstallInRamdisk() bool
InstallInVendorRamdisk() bool
+ InstallInDebugRamdisk() bool
InstallInRecovery() bool
InstallInRoot() bool
InstallBypassMake() bool
@@ -126,14 +174,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 +218,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 +239,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 +250,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 +286,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 +308,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 +331,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 +341,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 {
@@ -255,24 +355,43 @@
return ret
}
-// PathsForModuleSrc returns Paths rooted from the module's local source directory. It expands globs, references to
-// SourceFileProducer modules using the ":name" syntax, and references to OutputFileProducer modules using the
-// ":name{.tag}" syntax. Properties passed as the paths argument must have been annotated with struct tag
+// PathsForModuleSrc returns a Paths{} containing the resolved references in paths:
+// * filepath, relative to local module directory, resolves as a filepath relative to the local
+// source directory
+// * glob, relative to the local module directory, resolves as filepath(s), relative to the local
+// source directory.
+// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
+// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
+// filepath.
+// Properties passed as the paths argument must have been annotated with struct tag
// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
-// path_properties mutator. If ctx.Config().AllowMissingDependencies() is 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 {
+// path_deps mutator.
+// If a requested module is not found as a dependency:
+// * if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having
+// missing dependencies
+// * otherwise, a ModuleError is thrown.
+func PathsForModuleSrc(ctx ModuleMissingDepsPathContext, paths []string) Paths {
return PathsForModuleSrcExcludes(ctx, paths, nil)
}
-// PathsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding paths listed in
-// the excludes arguments. It expands globs, references to SourceFileProducer modules using the ":name" syntax, and
-// references to OutputFileProducer modules using the ":name{.tag}" syntax. Properties passed as the paths or excludes
-// argument must have been annotated with struct tag `android:"path"` so that dependencies on SourceFileProducer modules
-// will have already been handled by the path_properties mutator. If ctx.Config().AllowMissingDependencies() is
-// 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 {
+// PathsForModuleSrcExcludes returns a Paths{} containing the resolved references in paths, minus
+// those listed in excludes. Elements of paths and excludes are resolved as:
+// * filepath, relative to local module directory, resolves as a filepath relative to the local
+// source directory
+// * glob, relative to the local module directory, resolves as filepath(s), relative to the local
+// source directory. Not valid in excludes.
+// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
+// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
+// filepath.
+// excluding the items (similarly resolved
+// Properties passed as the paths argument must have been annotated with struct tag
+// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
+// path_deps mutator.
+// If a requested module is not found as a dependency:
+// * if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having
+// missing dependencies
+// * otherwise, a ModuleError is thrown.
+func PathsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) Paths {
ret, missingDeps := PathsAndMissingDepsForModuleSrcExcludes(ctx, paths, excludes)
if ctx.Config().AllowMissingDependencies() {
ctx.AddMissingDependencies(missingDeps)
@@ -311,15 +430,46 @@
return ret
}
-// 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
-// ":name{.tag}" syntax. Properties passed as the paths or excludes argument must have been annotated with struct tag
+// 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 a Paths{} containing the resolved references in
+// paths, minus those listed in excludes. Elements of paths and excludes are resolved as:
+// * filepath, relative to local module directory, resolves as a filepath relative to the local
+// source directory
+// * glob, relative to the local module directory, resolves as filepath(s), relative to the local
+// source directory. Not valid in excludes.
+// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
+// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
+// filepath.
+// and a list of the module names of missing module dependencies are returned as the second return.
+// Properties passed as the paths argument must have been annotated with struct tag
// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
-// path_properties mutator. If ctx.Config().AllowMissingDependencies() is 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) {
+// path_deps mutator.
+func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleWithDepsPathContext, paths, excludes []string) (Paths, []string) {
prefix := pathForModuleSrc(ctx).String()
var expandedExcludes []string
@@ -331,23 +481,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 +522,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 +538,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 +568,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 +597,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 +841,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 +880,8 @@
}
type basePath struct {
- path string
- config Config
- rel string
+ path string
+ rel string
}
func (p basePath) Ext() string {
@@ -767,6 +912,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 +933,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 +949,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 +972,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 +1000,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 +1010,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 +1044,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 +1068,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 +1076,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 +1089,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 +1114,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 +1154,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 +1167,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 +1233,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 +1259,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 +1278,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 +1290,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 +1298,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,30 +1323,34 @@
OutputPath
}
+func (p ModuleOutPath) RelativeToTop() Path {
+ p.OutputPath = p.outputPathRelativeToTop()
+ return p
+}
+
var _ Path = ModuleOutPath{}
+var _ WritablePath = ModuleOutPath{}
-func (p ModuleOutPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
+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())
}
-type BazelOutPath struct {
- OutputPath
-}
-
-var _ Path = BazelOutPath{}
-var _ objPathProvider = BazelOutPath{}
-
-func (p BazelOutPath) objPathWithExt(ctx ModuleContext, 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()
@@ -1203,33 +1386,15 @@
fileName+ext)
}
-// PathForBazelOut returns a Path representing the paths... under an output directory dedicated to
-// bazel-owned outputs.
-func PathForBazelOut(ctx PathContext, paths ...string) BazelOutPath {
- execRootPathComponents := append([]string{"execroot", "__main__"}, paths...)
- execRootPath := filepath.Join(execRootPathComponents...)
- validatedExecRootPath, err := validatePath(execRootPath)
- if err != nil {
- reportPathError(ctx, err)
- }
-
- outputPath := OutputPath{basePath{"", ctx.Config(), ""},
- ctx.Config().BazelContext.OutputBase()}
-
- return BazelOutPath{
- OutputPath: outputPath.withRel(validatedExecRootPath),
- }
-}
-
// 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 +1404,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 +1443,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 +1467,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 +1490,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 +1501,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 +1531,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 +1542,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 +1627,8 @@
}
base := InstallPath{
- basePath: basePath{partionPath, ctx.Config(), ""},
+ basePath: basePath{partionPath, ""},
+ buildDir: ctx.Config().buildDir,
partitionDir: partionPath,
makePath: false,
}
@@ -1437,7 +1638,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 +1683,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 +1784,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 +1793,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 +1816,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 +1870,7 @@
inSanitizerDir bool
inRamdisk bool
inVendorRamdisk bool
+ inDebugRamdisk bool
inRecovery bool
inRoot bool
forceOS *OsType
@@ -1675,6 +1903,10 @@
return m.inVendorRamdisk
}
+func (m testModuleInstallPathContext) InstallInDebugRamdisk() bool {
+ return m.inDebugRamdisk
+}
+
func (m testModuleInstallPathContext) InstallInRecovery() bool {
return m.inRecovery
}
@@ -1773,3 +2005,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 9e3e45a..224652e 100644
--- a/android/queryview.go
+++ b/android/queryview.go
@@ -66,12 +66,20 @@
bazelQueryView := ctx.Rule(pctx, "bazelQueryView",
blueprint.RuleParams{
Command: fmt.Sprintf(
- "rm -rf ${outDir}/* && "+
- "%s --bazel_queryview_dir ${outDir} %s && "+
- "echo WORKSPACE: `cat %s` > ${outDir}/.queryview-depfile.d",
- 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 f84acad..4c8088d 100644
--- a/android/register.go
+++ b/android/register.go
@@ -21,21 +21,78 @@
"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
@@ -44,6 +101,8 @@
parallel bool
}
+var _ sortableComponent = &mutator{}
+
type ModuleFactory func() Module
// ModuleFactoryAdaptor wraps a ModuleFactory into a blueprint.ModuleFactory by converting a Module
@@ -84,11 +143,11 @@
}
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 {
@@ -107,46 +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)
}
// Required for SingletonModule types, even though we are not using them.
for _, t := range singletons {
- ctx.RegisterSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
+ 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 {
@@ -168,6 +246,7 @@
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)
@@ -198,8 +277,9 @@
// 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.
@@ -208,6 +288,7 @@
type initRegistrationContext struct {
moduleTypes map[string]ModuleFactory
singletonTypes map[string]SingletonFactory
+ preSingletonTypes map[string]SingletonFactory
moduleTypesForDocs map[string]reflect.Value
}
@@ -238,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.go b/android/sdk.go
index bab0ed8..f2cdc88 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -177,12 +177,6 @@
// to the zip
CopyToSnapshot(src Path, dest string)
- // Return the path to an empty file.
- //
- // This can be used by sdk member types that need to create an empty file in the snapshot, simply
- // pass the value returned from this to the CopyToSnapshot() method.
- EmptyFile() Path
-
// Unzip the supplied zip into the snapshot relative directory destDir.
UnzipToSnapshot(zipPath Path, destDir string)
diff --git a/android/sdk_version.go b/android/sdk_version.go
new file mode 100644
index 0000000..c6c75a3
--- /dev/null
+++ b/android/sdk_version.go
@@ -0,0 +1,283 @@
+// 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 {
+ switch s {
+ case SdkSpecNone, SdkSpecCorePlatform, SdkSpecPrivate:
+ return false
+ }
+
+ 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
+ }
+ 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)"}
+ SdkSpecPrivate = SdkSpec{SdkPrivate, FutureApiLevel, ""}
+ SdkSpecCorePlatform = SdkSpec{SdkCorePlatform, FutureApiLevel, "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_test.go b/android/singleton_module_test.go
index 9232eb4..eb5554c 100644
--- a/android/singleton_module_test.go
+++ b/android/singleton_module_test.go
@@ -15,8 +15,6 @@
package android
import (
- "reflect"
- "strings"
"testing"
)
@@ -43,23 +41,14 @@
return tsm
}
-func runSingletonModuleTest(bp string) (*TestContext, []error) {
- config := TestConfig(buildDir, nil, bp, nil)
+var prepareForSingletonModuleTest = GroupFixturePreparers(
// Enable Kati output to test SingletonModules with MakeVars.
- config.katiEnabled = true
- ctx := NewTestContext(config)
- ctx.RegisterSingletonModuleType("test_singleton_module", testSingletonModuleFactory)
- ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
- ctx.Register()
-
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
- if len(errs) > 0 {
- return ctx, errs
- }
-
- _, errs = ctx.PrepareBuildActions(config)
- return ctx, errs
-}
+ PrepareForTestWithAndroidMk,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterSingletonModuleType("test_singleton_module", testSingletonModuleFactory)
+ ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
+ }),
+)
func TestSingletonModule(t *testing.T) {
bp := `
@@ -67,16 +56,14 @@
name: "test_singleton_module",
}
`
- ctx, errs := runSingletonModuleTest(bp)
- if len(errs) > 0 {
- t.Fatal(errs)
- }
+ result := GroupFixturePreparers(
+ prepareForSingletonModuleTest,
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
- ops := ctx.ModuleForTests("test_singleton_module", "").Module().(*testSingletonModule).ops
+ ops := result.ModuleForTests("test_singleton_module", "").Module().(*testSingletonModule).ops
wantOps := []string{"GenerateAndroidBuildActions", "GenerateSingletonBuildActions", "MakeVars"}
- if !reflect.DeepEqual(ops, wantOps) {
- t.Errorf("Expected operations %q, got %q", wantOps, ops)
- }
+ AssertDeepEquals(t, "operations", wantOps, ops)
}
func TestDuplicateSingletonModule(t *testing.T) {
@@ -89,23 +76,19 @@
name: "test_singleton_module2",
}
`
- _, errs := runSingletonModuleTest(bp)
- if len(errs) == 0 {
- t.Fatal("expected duplicate SingletonModule error")
- }
- if len(errs) != 1 || !strings.Contains(errs[0].Error(), `Duplicate SingletonModule "test_singleton_module", previously used in`) {
- t.Fatalf("expected duplicate SingletonModule error, got %q", errs)
- }
+
+ prepareForSingletonModuleTest.
+ ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
+ `\QDuplicate SingletonModule "test_singleton_module", previously used in\E`,
+ })).RunTestWithBp(t, bp)
}
func TestUnusedSingletonModule(t *testing.T) {
- bp := ``
- ctx, errs := runSingletonModuleTest(bp)
- if len(errs) > 0 {
- t.Fatal(errs)
- }
+ result := GroupFixturePreparers(
+ prepareForSingletonModuleTest,
+ ).RunTest(t)
- singleton := ctx.SingletonForTests("test_singleton_module").Singleton()
+ singleton := result.SingletonForTests("test_singleton_module").Singleton()
sm := singleton.(*singletonModuleSingletonAdaptor).sm
ops := sm.(*testSingletonModule).ops
if ops != nil {
@@ -126,24 +109,16 @@
}
`
- config := TestConfig(buildDir, nil, bp, nil)
- ctx := NewTestContext(config)
- ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
- ctx.BottomUp("test_singleton_module_mutator", testVariantSingletonModuleMutator)
- })
- ctx.RegisterSingletonModuleType("test_singleton_module", testSingletonModuleFactory)
- ctx.Register()
-
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
-
- if len(errs) == 0 {
- _, errs = ctx.PrepareBuildActions(config)
- }
-
- if len(errs) == 0 {
- t.Fatal("expected duplicate SingletonModule error")
- }
- if len(errs) != 1 || !strings.Contains(errs[0].Error(), `GenerateAndroidBuildActions already called for variant`) {
- t.Fatalf("expected duplicate SingletonModule error, got %q", errs)
- }
+ 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_test.go b/android/soong_config_modules_test.go
index 45463fd..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"
)
@@ -181,17 +180,23 @@
}
`
- run := func(t *testing.T, bp string, fs map[string][]byte) {
+ fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
+ return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+ variables.VendorVars = vars
+ })
+ }
+
+ run := func(t *testing.T, bp string, fs MockFS) {
testCases := []struct {
name string
- config Config
+ preparer FixturePreparer
fooExpectedFlags []string
fooDefaultsExpectedFlags []string
}{
{
name: "withValues",
- config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{
- "acme": map[string]string{
+ preparer: fixtureForVendorVars(map[string]map[string]string{
+ "acme": {
"board": "soc_a",
"size": "42",
"feature1": "true",
@@ -221,8 +226,8 @@
},
{
name: "empty_prop_for_string_var",
- config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{
- "acme": map[string]string{"board": "soc_c"}}),
+ preparer: fixtureForVendorVars(map[string]map[string]string{
+ "acme": {"board": "soc_c"}}),
fooExpectedFlags: []string{
"DEFAULT",
"-DGENERIC",
@@ -237,8 +242,8 @@
},
{
name: "unused_string_var",
- config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{
- "acme": map[string]string{"board": "soc_d"}}),
+ preparer: fixtureForVendorVars(map[string]map[string]string{
+ "acme": {"board": "soc_d"}}),
fooExpectedFlags: []string{
"DEFAULT",
"-DGENERIC",
@@ -254,8 +259,8 @@
},
{
- name: "conditions_default",
- config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{}),
+ name: "conditions_default",
+ preparer: fixtureForVendorVars(map[string]map[string]string{}),
fooExpectedFlags: []string{
"DEFAULT",
"-DGENERIC",
@@ -272,32 +277,29 @@
}
for _, tc := range testCases {
- ctx := NewTestContext(tc.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()
+ 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(tc.config)
- FailIfErrored(t, errs)
+ foo := result.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
+ AssertDeepEquals(t, "foo cflags", tc.fooExpectedFlags, foo.props.Cflags)
- foo := ctx.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
- if g, w := foo.props.Cflags, tc.fooExpectedFlags; !reflect.DeepEqual(g, w) {
- t.Errorf("%s: wanted foo cflags %q, got %q", tc.name, w, g)
- }
-
- fooDefaults := ctx.ModuleForTests("foo_with_defaults", "").Module().(*soongConfigTestModule)
- if g, w := fooDefaults.props.Cflags, tc.fooDefaultsExpectedFlags; !reflect.DeepEqual(g, w) {
- t.Errorf("%s: wanted foo_with_defaults cflags %q, got %q", tc.name, w, g)
- }
+ fooDefaults := result.ModuleForTests("foo_with_defaults", "").Module().(*soongConfigTestModule)
+ AssertDeepEquals(t, "foo_with_defaults cflags", tc.fooDefaultsExpectedFlags, fooDefaults.props.Cflags)
+ })
}
-
}
t.Run("single file", func(t *testing.T) {
diff --git a/android/soongconfig/Android.bp b/android/soongconfig/Android.bp
index 6bb68eb..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",
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index c62e76d..34b180d 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -295,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 {
@@ -325,6 +332,8 @@
Type: typ,
})
}
+ // The first element in the list has been handled, remove it from the list.
+ affectableProperties = affectableProperties[1:]
}
}
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index b824c78..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) {
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 470cfd6..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) {
@@ -110,7 +449,11 @@
}
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 {
@@ -143,7 +486,7 @@
}
}
- return TestingModule{module}
+ return newTestingModule(ctx.config, module)
}
func (ctx *TestContext) ModuleVariantsForTests(name string) []string {
@@ -163,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)
@@ -174,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
@@ -182,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())
}
@@ -245,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 {
@@ -267,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.
@@ -332,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 {
@@ -384,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
@@ -405,6 +880,8 @@
t.Errorf("errs[%d] = %q", i, err)
}
}
+
+ return found
}
func CheckErrorsAgainstExpectations(t *testing.T, errs []error, expectedErrorPatterns []string) {
@@ -425,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 {
@@ -443,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
}
@@ -468,17 +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()
- // Allow absolute paths to /dev/
- if strings.HasPrefix(p, "/dev/") {
- return p
- }
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)
}
@@ -487,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 {
@@ -495,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 9b3ed17..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"`
@@ -203,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"`
@@ -240,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"`
@@ -312,6 +311,14 @@
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"`
@@ -324,8 +331,7 @@
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"`
ForceApexSymlinkOptimization *bool `json:",omitempty"`
@@ -334,6 +340,8 @@
DexpreoptGlobalConfig *string `json:",omitempty"`
+ WithDexpreopt bool `json:",omitempty"`
+
ManifestPackageNameOverrides []string `json:",omitempty"`
CertificateOverrides []string `json:",omitempty"`
PackageNameOverrides []string `json:",omitempty"`
@@ -369,6 +377,20 @@
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 {
@@ -415,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" {
@@ -424,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
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 77dde72..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",
@@ -21,10 +25,13 @@
"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 c4ea381..0000000
--- a/apex/allowed_deps.txt
+++ /dev/null
@@ -1,608 +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.dynamicanimation_dynamicanimation(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)
-bpf_syscall_wrappers(minSdkVersion:30)
-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:30)
-framework-permission(minSdkVersion:current)
-framework-permission-s(minSdkVersion:30)
-framework-permission-s-shared(minSdkVersion:30)
-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)
-libderive_sdk(minSdkVersion:30)
-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)
-libsdk_proto(minSdkVersion:30)
-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)
-modules-utils-os(minSdkVersion:30)
-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-client(minSdkVersion:29)
-netd_aidl_interface-java(minSdkVersion:29)
-netd_aidl_interface-unstable-java(minSdkVersion:29)
-netd_event_listener_interface-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)
-note_memtag_heap_async(minSdkVersion:16)
-note_memtag_heap_sync(minSdkVersion:16)
-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.dynamicanimation_dynamicanimation-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_libunwind(minSdkVersion:(no version))
-prebuilt_test_framework-sdkextensions(minSdkVersion:(no version))
-server_configurable_flags(minSdkVersion:29)
-service-media-s(minSdkVersion:29)
-service-permission(minSdkVersion:30)
-service-permission(minSdkVersion:current)
-service-permission-shared(minSdkVersion:30)
-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 99cd75e..9fc701d 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -284,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 {
@@ -394,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",
}
diff --git a/apex/apex.go b/apex/apex.go
index 384d6c7..f5e6fa9 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -29,7 +29,6 @@
"android/soong/android"
"android/soong/bpf"
"android/soong/cc"
- "android/soong/dexpreopt"
prebuilt_etc "android/soong/etc"
"android/soong/filesystem"
"android/soong/java"
@@ -39,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) {
@@ -89,12 +92,24 @@
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
@@ -118,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.
@@ -203,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 {
@@ -536,22 +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}
- fsTag = dependencyTag{name: "filesystem", 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
@@ -574,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) {
@@ -721,19 +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...)
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")
}
- // The ART boot image depends on dex2oat to compile it.
- if !java.SkipDexpreoptBootJars(ctx) {
- dexpreopt.RegisterToolDeps(ctx)
- }
}
// Dependencies for signing
@@ -821,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 {
@@ -854,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()},
@@ -959,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
}
@@ -1004,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 == "" {
@@ -1011,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)
+ }
}
}
@@ -1159,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
+ })
})
}
@@ -1221,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
@@ -1532,9 +1593,6 @@
if dt, ok := depTag.(dependencyTag); ok && !dt.payload {
return false
}
- if depTag == dexpreopt.Dex2oatDepTag {
- return false
- }
ai := ctx.OtherModuleProvider(child, android.ApexInfoProvider).(android.ApexInfo)
externalDep := !android.InList(ctx.ModuleName(), ai.InApexes)
@@ -1642,6 +1700,26 @@
} 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, *java.Import:
@@ -1699,10 +1777,14 @@
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 {
@@ -1801,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
}
@@ -1842,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() {
@@ -1856,25 +1960,6 @@
return
}
- if a.artApex {
- // 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.
- 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)
- }
- }
- // Call GetGlobalSoongConfig to initialize it, which may be necessary if dexpreopt is
- // disabled for libraries/apps, but boot images are still needed.
- if !java.SkipDexpreoptBootJars(ctx) {
- dexpreopt.GetGlobalSoongConfig(ctx)
- }
- }
-
// Remove duplicates in filesInfo
removeDup := func(filesInfo []apexFile) []apexFile {
encountered := make(map[string]apexFile)
@@ -2116,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
}
@@ -2147,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
@@ -2189,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)
}
}
@@ -2230,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.
@@ -2239,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
})
@@ -2345,7 +2434,6 @@
"libFraunhoferAAC",
"libaudio-a2dp-hw-utils",
"libaudio-hearing-aid-hw-utils",
- "libbinder_headers",
"libbluetooth",
"libbluetooth-types",
"libbluetooth-types-header",
@@ -2476,7 +2564,6 @@
"libaudiopolicy",
"libaudioutils",
"libaudioutils_fixedfft",
- "libbinder_headers",
"libbluetooth-types-header",
"libbufferhub",
"libbufferhub_headers",
@@ -2592,7 +2679,6 @@
"libavcenc",
"libavservices_minijail",
"libavservices_minijail",
- "libbinder_headers",
"libbinderthreadstateutils",
"libbluetooth-types-header",
"libbufferhub_headers",
@@ -2843,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",
}
@@ -2867,9 +2945,7 @@
"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",
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 b1e8480..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,
@@ -191,94 +205,24 @@
"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)
- java.RegisterRequiredBuildComponentsForTest(ctx)
-
- ctx.RegisterModuleType("cc_test", cc.TestFactory)
- ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory)
- cc.RegisterVndkLibraryTxtTypes(ctx)
- prebuilt_etc.RegisterPrebuiltEtcBuildComponents(ctx)
- ctx.RegisterModuleType("platform_compat_config", java.PlatformCompatConfigFactory)
- ctx.RegisterModuleType("sh_binary", sh.ShBinaryFactory)
- ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
- 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) {
@@ -354,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",
@@ -375,6 +319,7 @@
"myjar",
"myjar_dex",
],
+ updatable: false,
}
apex {
@@ -435,7 +380,7 @@
}
rust_binary {
- name: "foo.rust",
+ name: "foo.rust",
srcs: ["foo.rs"],
rlibs: ["libfoo.rlib.rust"],
dylibs: ["libfoo.dylib.rust"],
@@ -443,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"],
@@ -475,6 +429,7 @@
binaries: ["foo"],
key: "myapex.key",
file_contexts: ":myapex-file_contexts",
+ updatable: false,
}
cc_library_shared {
@@ -562,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)
@@ -573,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"]
@@ -593,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")
@@ -602,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")
@@ -644,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",
@@ -668,6 +623,7 @@
apps: ["AppFoo"],
rros: ["rro"],
bpfs: ["bpf"],
+ updatable: false,
}
prebuilt_etc {
@@ -732,10 +688,11 @@
}
func TestApexManifest(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
+ updatable: false,
}
apex_key {
@@ -753,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 {
@@ -803,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 {
@@ -872,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")
@@ -897,7 +856,7 @@
func TestApexWithStubsWithMinSdkVersion(t *testing.T) {
t.Parallel()
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -970,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")
@@ -994,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 {
@@ -1071,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)")
}
@@ -1090,11 +1122,12 @@
|
`------> libbar
*/
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -1150,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 {
@@ -1164,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,
@@ -1191,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",
@@ -1214,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,
@@ -1251,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",
@@ -1283,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+`
}
@@ -1338,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{
@@ -1352,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 {
@@ -1420,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
@@ -1456,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",
@@ -1531,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",
@@ -1573,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"]
@@ -1585,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 {
@@ -1630,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 {
@@ -1677,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",
@@ -1710,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,41 +1796,6 @@
ensureListNotContains(t, cm.Properties.AndroidMkStaticLibs, "libunwind")
}
-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"],
- },
- }
- `)
-}
-
func TestApexMinSdkVersion_ErrorIfIncompatibleVersion(t *testing.T) {
testApexError(t, `module "mylib".*: should support min_sdk_version\(29\)`, `
apex {
@@ -1837,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) {
@@ -1874,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",
}
@@ -1886,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",
+ }
`)
}
@@ -1902,6 +1946,7 @@
name: "myapex",
java_libs: ["myjar"],
key: "myapex.key",
+ updatable: false,
}
apex_key {
name: "myapex.key",
@@ -1911,7 +1956,7 @@
java_library {
name: "myjar",
srcs: ["foo/bar/MyClass.java"],
- sdk_version: "core_platform",
+ sdk_version: "test_current",
apex_available: ["myapex"],
}
`,
@@ -1958,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",
@@ -2083,7 +2131,7 @@
}
func TestApexMinSdkVersion_OkayEvenWhenDepIsNewer_IfItSatisfiesApexMinSdkVersion(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -2097,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",
@@ -2131,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",
@@ -2144,6 +2262,7 @@
binaries: ["mybin"],
prebuilts: ["myetc"],
compile_multilib: "both",
+ updatable: false,
}
apex_key {
@@ -2198,7 +2317,7 @@
}
func TestFilesInSubDirWhenNativeBridgeEnabled(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -2210,6 +2329,7 @@
},
compile_multilib: "both",
native_bridge_supported: true,
+ updatable: false,
}
apex_key {
@@ -2256,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 {
@@ -2288,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() {
@@ -2301,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")
@@ -2321,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) {
@@ -2348,6 +2470,7 @@
key: "myapex.key",
native_shared_libs: ["mylib"],
use_vendor: true,
+ updatable: false,
}
apex_key {
@@ -2366,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",
@@ -2397,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"])
@@ -2411,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",
@@ -2443,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{
@@ -2464,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
@@ -2474,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 {
@@ -2501,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 {
@@ -2520,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
@@ -2535,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 {
@@ -2556,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
@@ -2566,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",
@@ -2584,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 {
@@ -2639,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 {
@@ -2696,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",
@@ -2713,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",
@@ -2735,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",
@@ -2757,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",
@@ -2780,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",
@@ -2798,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",
@@ -2823,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 {
@@ -2950,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 {
@@ -3093,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",
}
@@ -3115,7 +3291,7 @@
},
system_shared_libs: [],
stl: "none",
- apex_available: [ "myapex" ],
+ apex_available: [ "com.android.vndk.current" ],
}
cc_library {
@@ -3129,34 +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/vndkproduct.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",
}
@@ -3171,7 +3348,7 @@
},
system_shared_libs: [],
stl: "none",
- apex_available: [ "myapex" ],
+ apex_available: [ "com.android.vndk.current" ],
}
cc_prebuilt_library_shared {
@@ -3190,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",
@@ -3233,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 {
@@ -3264,7 +3441,7 @@
srcs: ["libvndk27_arm64.so"],
},
},
- apex_available: [ "myapex_v27" ],
+ apex_available: [ "com.android.vndk.v27" ],
}
vndk_prebuilt_shared {
@@ -3293,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",
@@ -3375,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",
}
@@ -3405,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",
@@ -3419,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",
}
@@ -3450,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 {
@@ -3495,7 +3629,7 @@
srcs: ["libvndk27binder32.so"],
}
},
- apex_available: [ "myapex_v27" ],
+ apex_available: [ "com.android.vndk.v27" ],
}
`+vndkLibrariesTxtFiles("27"),
withFiles(map[string][]byte{
@@ -3511,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",
}
@@ -3547,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 {
@@ -3568,6 +3704,7 @@
native_shared_libs: ["lib_dep"],
compile_multilib: "both",
file_contexts: ":myapex-file_contexts",
+ updatable: false,
}
apex {
@@ -3576,6 +3713,7 @@
native_shared_libs: ["libfoo"],
compile_multilib: "both",
file_contexts: ":myapex-file_contexts",
+ updatable: false,
}
apex {
@@ -3584,6 +3722,7 @@
native_shared_libs: ["lib_dep", "libfoo"],
compile_multilib: "both",
file_contexts: ":myapex-file_contexts",
+ updatable: false,
}
apex_key {
@@ -3657,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 {
@@ -3690,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
@@ -3701,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 {
@@ -3753,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 {
@@ -3801,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"],
@@ -3891,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"],
@@ -3949,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 {
@@ -3989,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+`
}
@@ -4004,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)
}
@@ -4021,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 {
@@ -4044,6 +4191,7 @@
name: "myapex",
key: "myapex.key",
file_contexts: "my_own_file_contexts",
+ updatable: false,
}
apex_key {
@@ -4063,6 +4211,7 @@
key: "myapex.key",
product_specific: true,
file_contexts: "product_specific_file_contexts",
+ updatable: false,
}
apex_key {
@@ -4072,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 {
@@ -4094,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 {
@@ -4121,7 +4272,7 @@
}
func TestApexKeyFromOtherModule(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex_key {
name: "myapex.key",
public_key: ":my.avbpubkey",
@@ -4154,7 +4305,7 @@
}
func TestPrebuilt(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
prebuilt_apex {
name: "myapex",
arch: {
@@ -4176,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",
@@ -4194,7 +4353,7 @@
}
func TestPrebuiltOverrides(t *testing.T) {
- ctx, config := testApex(t, `
+ ctx := testApex(t, `
prebuilt_apex {
name: "myapex.prebuilt",
src: "myapex-arm.apex",
@@ -4207,29 +4366,30 @@
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 := func(config *dexpreopt.GlobalConfig) {
- config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"})
- }
+ transform := android.NullFixturePreparer
- checkDexJarBuildPath := func(ctx *android.TestContext, name string) {
+ 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.Dependency)
+ p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency)
dexJarBuildPath := p.DexJarBuildPath()
- if expected, actual := ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar", android.NormalizePathForTesting(dexJarBuildPath); actual != expected {
+ 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(ctx *android.TestContext) {
+ 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("libfoo"); !reflect.DeepEqual(expected, actual) {
+ 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)
}
}
@@ -4246,19 +4406,42 @@
src: "myapex-arm.apex",
},
},
- exported_java_libs: ["libfoo"],
+ 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)
- checkDexJarBuildPath(ctx, "libfoo")
+ // 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) {
@@ -4274,7 +4457,7 @@
src: "myapex-arm.apex",
},
},
- exported_java_libs: ["libfoo"],
+ exported_java_libs: ["libfoo", "libbar"],
}
java_import {
@@ -4285,20 +4468,35 @@
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(ctx, "prebuilt_libfoo")
- ensureNoSourceVariant(ctx)
+ 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",
- prefer: true,
arch: {
arm64: {
src: "myapex-arm64.apex",
@@ -4307,32 +4505,397 @@
src: "myapex-arm.apex",
},
},
- exported_java_libs: ["libfoo"],
+ 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(ctx, "prebuilt_libfoo")
- ensureNoSourceVariant(ctx)
+ 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",
@@ -4417,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
@@ -4432,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")
@@ -4440,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()
@@ -4509,11 +5075,12 @@
}
func TestApexWithJavaImport(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
java_libs: ["myjavaimport"],
+ updatable: false,
}
apex_key {
@@ -4537,7 +5104,7 @@
}
func TestApexWithApps(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -4545,6 +5112,7 @@
"AppFoo",
"AppFooPriv",
],
+ updatable: false,
}
apex_key {
@@ -4606,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
@@ -4615,7 +5183,7 @@
}
func TestApexWithAppImports(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -4623,6 +5191,7 @@
"AppFooPrebuilt",
"AppFooPrivPrebuilt",
],
+ updatable: false,
}
apex_key {
@@ -4663,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 {
@@ -4704,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 {
@@ -4741,6 +5312,7 @@
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libfoo"],
+ updatable: false,
}
apex_key {
@@ -4753,6 +5325,7 @@
name: "otherapex",
key: "myapex.key",
native_shared_libs: ["libfoo"],
+ updatable: false,
}
cc_defaults {
@@ -4775,6 +5348,7 @@
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libfoo"],
+ updatable: false,
}
apex_key {
@@ -4787,6 +5361,7 @@
name: "otherapex",
key: "otherapex.key",
native_shared_libs: ["libfoo"],
+ updatable: false,
}
apex_key {
@@ -4805,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.*
@@ -4816,6 +5391,7 @@
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libfoo"],
+ updatable: false,
}
apex_key {
@@ -4853,6 +5429,7 @@
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libfoo"],
+ updatable: false,
}
apex_key {
@@ -4873,6 +5450,7 @@
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libfoo", "libbar"],
+ updatable: false,
}
apex_key {
@@ -4907,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 {
@@ -4969,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 {
@@ -5003,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 {
@@ -5071,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()
@@ -5086,7 +5667,7 @@
}
func TestLegacyAndroid10Support(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
@@ -5128,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,
@@ -5146,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 {
@@ -5183,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 {
@@ -5229,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 {
@@ -5279,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"],
@@ -5302,6 +5886,7 @@
name: "myapex",
key: "myapex.key",
java_libs: ["foo", "bar"],
+ updatable: false,
}
apex_key {
@@ -5368,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)
}
}
@@ -5379,6 +5964,7 @@
name: "myapex",
key: "myapex.key",
java_libs: ["foo"],
+ updatable: false,
}
apex_key {
@@ -5400,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 {
@@ -5426,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",
@@ -5439,6 +6037,7 @@
name: "myapex",
key: "myapex.key",
java_libs: ["myjar"],
+ updatable: false,
}
apex_key {
@@ -5459,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 {
@@ -5485,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
@@ -5503,6 +6103,7 @@
key: "myapex.key",
native_shared_libs: ["mylib"],
java_libs: ["myjar"],
+ updatable: false,
}
apex {
@@ -5601,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")
@@ -5613,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")
@@ -5626,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 {
@@ -5664,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()
@@ -5677,11 +6279,12 @@
}
func TestApexWithJniLibs(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
jni_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -5718,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)
@@ -5739,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 {
@@ -5769,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 {
@@ -5801,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",
@@ -5810,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 := `
@@ -5863,7 +6469,7 @@
srcs: ["a.java"],
sdk_version: "current",
apex_available: [
- "com.android.art.something",
+ "com.android.art.debug",
],
hostdex: true,
}
@@ -5880,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 {
@@ -5891,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 {
@@ -5917,65 +6524,47 @@
}
`
- testDexpreoptWithApexes(t, bp, errmsg, transformDexpreoptConfig)
+ testDexpreoptWithApexes(t, bp, errmsg, preparer)
}
-func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) *android.TestContext {
+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,
- }
- 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("prebuilt_apex", PrebuiltFactory)
- ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- android.RegisterPrebuiltMutators(ctx)
- cc.RegisterRequiredBuildComponentsForTest(ctx)
- java.RegisterRequiredBuildComponentsForTest(ctx)
- ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
- ctx.PreDepsMutators(RegisterPreDepsMutators)
- ctx.PostDepsMutators(RegisterPostDepsMutators)
-
- ctx.Register()
-
- 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)
- } else {
- t.Fatalf("missing expected error %q (0 errors are returned)", errmsg)
+ 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,
}
- return ctx
+ 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) {
@@ -5994,93 +6583,111 @@
`)
}
+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"})
- }
- testNoUpdatableJarsInBootImage(t, "", transform)
+ preparer := java.FixtureConfigureBootJars("platform:some-platform-lib")
+ testNoUpdatableJarsInBootImage(t, "", preparer)
})
-
}
func TestDexpreoptAccessDexFilesFromPrebuiltApex(t *testing.T) {
- transform := func(config *dexpreopt.GlobalConfig) {
- config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"})
- }
+ preparer := java.FixtureConfigureBootJars("myapex:libfoo")
t.Run("prebuilt no source", func(t *testing.T) {
testDexpreoptWithApexes(t, `
prebuilt_apex {
@@ -6100,7 +6707,7 @@
name: "libfoo",
jars: ["libfoo.jar"],
}
-`, "", transform)
+`, "", preparer)
})
t.Run("prebuilt no source", func(t *testing.T) {
@@ -6122,7 +6729,7 @@
name: "libfoo",
jars: ["libfoo.jar"],
}
-`, "", transform)
+`, "", preparer)
})
}
@@ -6134,45 +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.RegisterRequiredBuildComponentsForTest(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) {
@@ -6208,6 +6803,7 @@
name: "myapex",
key: "myapex.key",
java_libs: ["bcp_lib1", "nonbcp_lib2"],
+ updatable: false,
}`,
bootJars: []string{"bcp_lib1"},
modulesPackages: map[string][]string{
@@ -6240,6 +6836,7 @@
name: "myapex",
key: "myapex.key",
java_libs: ["bcp_lib1", "bcp_lib2"],
+ updatable: false,
}
`,
bootJars: []string{"bcp_lib1", "bcp_lib2"},
@@ -6259,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 {
@@ -6321,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
@@ -6344,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 {
@@ -6374,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)
}
@@ -6388,6 +7097,7 @@
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
@@ -6419,10 +7129,11 @@
}
func TestApexKeysTxt(t *testing.T) {
- ctx, _ := testApex(t, `
+ ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
+ updatable: false,
}
apex_key {
@@ -6459,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 {
@@ -6514,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 {
@@ -6549,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")
@@ -6575,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()
@@ -6583,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 {
@@ -6623,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()
@@ -6634,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 {
@@ -6688,6 +7406,7 @@
name: "myapex",
key: "myapex.key",
native_shared_libs: ["mylib"],
+ updatable: false,
}
apex_key {
name: "myapex.key",
@@ -6706,6 +7425,7 @@
enabled: %s,
key: "myapex.key",
native_shared_libs: ["stublib"],
+ updatable: false,
}
`
@@ -6775,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
@@ -6793,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
}
@@ -6830,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)
}
@@ -6842,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 67314d8..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.
@@ -700,15 +703,20 @@
})
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")).
- Text(imageDir.String()).
- Implicits(implicitInputs).
Output(apisBackedbyOutputFile).
- Input(ndkLibraryList)
+ Input(ndkLibraryList).
+ Flags(libNames)
rule.Build("ndk_backedby_list", "Generate API libraries backed by Apex")
a.apisBackedByModuleFile = apisBackedbyOutputFile
@@ -775,12 +783,11 @@
})
a.outputFile = signedOutputFile
- // Process APEX compression if enabled or forced
if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && a.testOnlyShouldForceCompression() {
ctx.PropertyErrorf("test_only_force_compression", "not available")
return
}
- compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.properties.Compressible, true)
+
if apexType == imageApex && (compressionEnabled || a.testOnlyShouldForceCompression()) {
a.isCompressed = true
unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+".capex.unsigned")
@@ -921,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
}
@@ -935,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
index 651cadf..1db13f9 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -49,41 +49,32 @@
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
- prebuilt android.Prebuilt
- properties DeapexerProperties
- apexFileProperties ApexFileProperties
+ properties DeapexerProperties
+ selectedApexProperties SelectedApexProperties
inputApex android.Path
}
func privateDeapexerFactory() android.Module {
module := &Deapexer{}
- module.AddProperties(
- &module.properties,
- &module.apexFileProperties,
- )
- android.InitSingleSourcePrebuiltModule(module, &module.apexFileProperties, "Source")
+ module.AddProperties(&module.properties, &module.selectedApexProperties)
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
return module
}
-func (p *Deapexer) Prebuilt() *android.Prebuilt {
- return &p.prebuilt
-}
-
-func (p *Deapexer) Name() string {
- return p.prebuilt.Name(p.ModuleBase.Name())
-}
-
func (p *Deapexer) DepsMutator(ctx android.BottomUpMutatorContext) {
- if err := p.apexFileProperties.selectSource(ctx); err != nil {
- ctx.ModuleErrorf("%s", err)
- return
- }
-
// 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 {
@@ -93,7 +84,7 @@
}
func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- p.inputApex = p.Prebuilt().SingleSourcePath(ctx)
+ 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")
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 c72a9eb..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
@@ -109,34 +231,41 @@
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"`
}
}
}
-func (p *ApexFileProperties) selectSource(ctx android.BottomUpMutatorContext) error {
- // 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 {
- return fmt.Errorf("compile_multilib shouldn't be \"both\" for prebuilt_apex")
+// 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 ctx.MultiTargets()[0].Arch.ArchType {
+ switch multiTargets[0].Arch.ArchType {
case android.Arm:
src = String(p.Arch.Arm.Src)
case android.Arm64:
@@ -145,20 +274,22 @@
src = String(p.Arch.X86.Src)
case android.X86_64:
src = String(p.Arch.X86_64.Src)
- default:
- return fmt.Errorf("prebuilt_apex does not support %q", ctx.MultiTargets()[0].Arch.String())
}
if src == "" {
src = String(p.Src)
}
- p.Source = src
- return nil
+ 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
- DeapexerProperties
Installable *bool
// Optional name for the installed apex. If unspecified, name of the
@@ -214,28 +345,77 @@
// 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) {
- props := struct {
- Name *string
- }{
- Name: proptools.StringPtr(module.BaseModuleName() + ".deapexer"),
+ 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)
}
- ctx.CreateModule(privateDeapexerFactory,
- &props,
- &module.properties.ApexFileProperties,
- &module.properties.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,
@@ -243,113 +423,50 @@
// 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 := "prebuilt_" + name
+ prebuiltName := android.PrebuiltNameFromSource(name)
if ctx.OtherModuleExists(prebuiltName) {
name = prebuiltName
}
return name
}
-func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
- if err := p.properties.selectSource(ctx); err != nil {
- ctx.ModuleErrorf("%s", err)
- return
- }
+type exportedDependencyTag struct {
+ blueprint.BaseDependencyTag
+ name string
+}
- // 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.properties.Exported_java_libs {
- dep := prebuiltApexExportedModuleName(ctx, lib)
- ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), javaLibTag, dep)
- }
+// 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) {
+ p.deapexerDeps(ctx)
}
var _ ApexInfoMutator = (*Prebuilt)(nil)
-// 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 *Prebuilt) 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 == javaLibTag {
- 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: 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)
- }
+ 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) {
@@ -385,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())
@@ -398,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
@@ -416,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
@@ -432,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
@@ -445,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 {
@@ -504,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) {
@@ -521,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()
@@ -580,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 ccf4e57..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,30 +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/vndkproduct.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",
@@ -94,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") {
@@ -106,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 d222d98..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",
@@ -8,6 +12,7 @@
],
testSrcs: [
"aquery_test.go",
+ "properties_test.go",
],
pluginFor: [
"soong_build",
diff --git a/bazel/aquery.go b/bazel/aquery.go
index a196e8b..555f1dc 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -46,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 int
- // TODO(cparsons): Handle non-flat depsets.
- DirectArtifactIds []int
+ Id int
+ DirectArtifactIds []int
+ TransitiveDepSetIds []int
}
// action contains relevant portions of Bazel's aquery proto, Action.
@@ -74,6 +74,7 @@
// with a Bazel action from Bazel's action graph.
type BuildStatement struct {
Command string
+ Depfile *string
OutputPaths []string
InputPaths []string
Env []KeyValuePair
@@ -105,27 +106,63 @@
}
artifactIdToPath[artifact.Id] = artifactPath
}
- depsetIdToArtifactIds := map[int][]int{}
+
+ 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 {
outputPath, exists := artifactIdToPath[outputId]
if !exists {
return nil, fmt.Errorf("undefined outputId %d", outputId)
}
- outputPaths = append(outputPaths, outputPath)
+ 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 {
- inputArtifacts, exists := depsetIdToArtifactIds[inputDepSetId]
- if !exists {
- return nil, fmt.Errorf("undefined input depsetId %d", inputDepSetId)
+ 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)
@@ -135,16 +172,65 @@
}
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, 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
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 1bd6e67..fa8810f 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -393,9 +393,336 @@
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 || err.Error() != expected {
- t.Errorf("expected error '%s', but got: %s", expected, err)
+ 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())
}
}
@@ -403,7 +730,7 @@
// 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: %s,\n actual: %s",
+ t.Errorf("expected %d build statements, but got %d,\n expected: %v,\n actual: %v",
len(expected), len(actual), expected, actual)
return
}
@@ -414,7 +741,7 @@
continue ACTUAL_LOOP
}
}
- t.Errorf("unexpected build statement %s.\n expected: %s",
+ t.Errorf("unexpected build statement %v.\n expected: %v",
actualStatement, expected)
return
}
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
index 49587f4..d2a8729 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-bp2build",
pkgPath: "android/soong/bp2build",
@@ -6,15 +10,29 @@
"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: [
diff --git a/bp2build/androidbp_to_build_templates.go b/bp2build/androidbp_to_build_templates.go
index 75c3ccb..5fed4fa 100644
--- a/bp2build/androidbp_to_build_templates.go
+++ b/bp2build/androidbp_to_build_templates.go
@@ -15,7 +15,7 @@
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
index 49729e0..007d6d8 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -18,42 +18,48 @@
"android/soong/android"
"fmt"
"os"
+ "strings"
)
-// The Bazel bp2build 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) {
+// 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)
- ruleShims := CreateRuleShims(android.ModuleTypeFactories())
+ buildToTargets, metrics := GenerateBazelTargets(ctx)
- buildToTargets := GenerateSoongModuleTargets(ctx.Context())
+ filesToWrite := CreateBazelFiles(nil, buildToTargets, ctx.mode)
- filesToWrite := CreateBazelFiles(ruleShims, buildToTargets)
+ generatedBuildFiles := []string{}
for _, f := range filesToWrite {
- if err := writeFile(outputDir, ctx, f); err != nil {
+ 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
}
-func writeFile(outputDir android.OutputPath, ctx android.PathContext, f BazelFile) error {
- return writeReadOnlyFile(ctx, getOutputPath(outputDir, ctx, f.Dir), f.Basename, f.Contents)
+// 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 getOutputPath(outputDir android.OutputPath, ctx android.PathContext, dir string) android.OutputPath {
- return outputDir.Join(ctx, dir)
-}
-
-// 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(ctx android.PathContext, dir android.OutputPath, baseName, content string) error {
- android.CreateOutputDirIfNonexistent(dir, os.ModePerm)
- pathToFile := dir.Join(ctx, baseName)
-
- // 0444 is read-only
- err := android.WriteFileToOutputDir(pathToFile, []byte(content), 0444)
-
- return err
+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
index bece8f6..b7a2810 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -16,6 +16,7 @@
import (
"android/soong/android"
+ "android/soong/bazel"
"fmt"
"reflect"
"strings"
@@ -29,8 +30,62 @@
}
type BazelTarget struct {
- name string
- content string
+ 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 {
@@ -44,20 +99,68 @@
}
type CodegenContext struct {
- config android.Config
- context android.Context
+ config android.Config
+ context android.Context
+ mode CodegenMode
+ additionalDeps []string
}
-func (ctx CodegenContext) AddNinjaFileDeps(...string) {}
-func (ctx CodegenContext) Config() android.Config { return ctx.config }
-func (ctx CodegenContext) Context() android.Context { return ctx.context }
+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) CodegenContext {
- return CodegenContext{
+func NewCodegenContext(config android.Config, context android.Context, mode CodegenMode) *CodegenContext {
+ return &CodegenContext{
context: context,
config: config,
+ mode: mode,
}
}
@@ -73,14 +176,110 @@
return attributes
}
-func GenerateSoongModuleTargets(ctx bpToBuildContext) map[string][]BazelTarget {
- buildFileToTargets := make(map[string][]BazelTarget)
- ctx.VisitAllModules(func(m blueprint.Module) {
- dir := ctx.ModuleDir(m)
- t := generateSoongModuleTarget(ctx, m)
- buildFileToTargets[ctx.ModuleDir(m)] = append(buildFileToTargets[dir], t)
+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
+
+ 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
@@ -171,8 +370,12 @@
// 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 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
}
@@ -187,22 +390,47 @@
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 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"
+ if indexedValue != "" {
+ ret += makeIndent(indent + 1)
+ ret += indexedValue
+ ret += ",\n"
+ }
}
+ ret += makeIndent(indent)
+ ret += "]"
}
- 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)
@@ -296,6 +524,13 @@
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, "\"", "\\\"")
}
@@ -306,6 +541,10 @@
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) != "" {
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 4e31aa7..1ede442 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -16,6 +16,8 @@
import (
"android/soong/android"
+ "android/soong/genrule"
+ "strings"
"testing"
)
@@ -25,16 +27,14 @@
expectedBazelTarget string
}{
{
- bp: `custom {
- name: "foo",
-}
+ bp: `custom { name: "foo" }
`,
expectedBazelTarget: `soong_module(
name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
+ soong_module_name = "foo",
+ soong_module_type = "custom",
+ soong_module_variant = "",
+ soong_module_deps = [
],
)`,
},
@@ -46,10 +46,10 @@
`,
expectedBazelTarget: `soong_module(
name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
+ soong_module_name = "foo",
+ soong_module_type = "custom",
+ soong_module_variant = "",
+ soong_module_deps = [
],
ramdisk = True,
)`,
@@ -62,10 +62,10 @@
`,
expectedBazelTarget: `soong_module(
name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
+ soong_module_name = "foo",
+ soong_module_type = "custom",
+ soong_module_variant = "",
+ soong_module_deps = [
],
owner = "a_string_with\"quotes\"_and_\\backslashes\\\\",
)`,
@@ -78,14 +78,12 @@
`,
expectedBazelTarget: `soong_module(
name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
+ soong_module_name = "foo",
+ soong_module_type = "custom",
+ soong_module_variant = "",
+ soong_module_deps = [
],
- required = [
- "bar",
- ],
+ required = ["bar"],
)`,
},
{
@@ -96,10 +94,10 @@
`,
expectedBazelTarget: `soong_module(
name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
+ soong_module_name = "foo",
+ soong_module_type = "custom",
+ soong_module_variant = "",
+ soong_module_deps = [
],
target_required = [
"qux",
@@ -114,35 +112,27 @@
targets: ["goal_foo"],
tag: ".foo",
},
- dists: [
- {
- targets: ["goal_bar"],
- tag: ".bar",
- },
- ],
+ dists: [{
+ targets: ["goal_bar"],
+ tag: ".bar",
+ }],
}
`,
expectedBazelTarget: `soong_module(
name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
+ soong_module_name = "foo",
+ soong_module_type = "custom",
+ soong_module_variant = "",
+ soong_module_deps = [
],
dist = {
"tag": ".foo",
- "targets": [
- "goal_foo",
- ],
+ "targets": ["goal_foo"],
},
- dists = [
- {
- "tag": ".bar",
- "targets": [
- "goal_bar",
- ],
- },
- ],
+ dists = [{
+ "tag": ".bar",
+ "targets": ["goal_bar"],
+ }],
)`,
},
{
@@ -162,24 +152,18 @@
`,
expectedBazelTarget: `soong_module(
name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
+ soong_module_name = "foo",
+ soong_module_type = "custom",
+ soong_module_variant = "",
+ soong_module_deps = [
],
- dists = [
- {
- "tag": ".tag",
- "targets": [
- "my_goal",
- ],
- },
- ],
+ dists = [{
+ "tag": ".tag",
+ "targets": ["my_goal"],
+ }],
owner = "custom_owner",
ramdisk = True,
- required = [
- "bar",
- ],
+ required = ["bar"],
target_required = [
"qux",
"bazqux",
@@ -192,6 +176,7 @@
for _, testCase := range testCases {
config := android.TestConfig(buildDir, nil, testCase.bp, nil)
ctx := android.NewTestContext(config)
+
ctx.RegisterModuleType("custom", customModuleFactory)
ctx.Register()
@@ -200,9 +185,10 @@
_, errs = ctx.PrepareBuildActions(config)
android.FailIfErrored(t, errs)
- bazelTargets := GenerateSoongModuleTargets(ctx.Context.Context)[dir]
- if g, w := len(bazelTargets), 1; g != w {
- t.Fatalf("Expected %d bazel target, got %d", w, g)
+ 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]
@@ -210,8 +196,1244 @@
t.Errorf(
"Expected generated Bazel target to be '%s', got '%s'",
testCase.expectedBazelTarget,
- actualBazelTarget,
+ 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
index 04c4542..f2f6b01 100644
--- a/bp2build/bzl_conversion.go
+++ b/bp2build/bzl_conversion.go
@@ -109,9 +109,9 @@
factoryName := runtime.FuncForPC(reflect.ValueOf(factory).Pointer()).Name()
pkg := strings.Split(factoryName, ".")[0]
attrs := `{
- "module_name": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ "soong_module_name": attr.string(mandatory = True),
+ "soong_module_variant": attr.string(),
+ "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
`
attrs += getAttributes(factory)
attrs += " },"
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index 8bea3f6..30c1a5b 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -83,9 +83,13 @@
custom = rule(
implementation = _custom_impl,
attrs = {
- "module_name": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ "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(),
@@ -107,9 +111,9 @@
custom_defaults = rule(
implementation = _custom_defaults_impl,
attrs = {
- "module_name": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ "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(),
@@ -131,9 +135,9 @@
custom_test_ = rule(
implementation = _custom_test__impl,
attrs = {
- "module_name": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ "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(),
@@ -172,7 +176,7 @@
content: "irrelevant",
},
}
- files := CreateBazelFiles(ruleShims, make(map[string][]BazelTarget))
+ files := CreateBazelFiles(ruleShims, make(map[string]BazelTargets), QueryView)
var actualSoongModuleBzl BazelFile
for _, f := range files {
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
index cdfb38b..6b47cd1 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -17,38 +17,61 @@
func CreateBazelFiles(
ruleShims map[string]RuleShim,
- buildToTargets map[string][]BazelTarget) []BazelFile {
- files := make([]BazelFile, 0, len(ruleShims)+len(buildToTargets)+numAdditionalFiles)
+ buildToTargets map[string]BazelTargets,
+ mode CodegenMode) []BazelFile {
- // Write top level files: WORKSPACE and BUILD. These files are empty.
- files = append(files, newFile("", "WORKSPACE", ""))
- // Used to denote that the top level directory is a package.
- files = append(files, newFile("", "BUILD", ""))
+ var files []BazelFile
- files = append(files, newFile(bazelRulesSubDir, "BUILD", ""))
- files = append(files, newFile(bazelRulesSubDir, "providers.bzl", providersBzl))
+ if mode == QueryView {
+ // Write top level WORKSPACE.
+ files = append(files, newFile("", "WORKSPACE", ""))
- for bzlFileName, ruleShim := range ruleShims {
- files = append(files, newFile(bazelRulesSubDir, bzlFileName+".bzl", ruleShim.content))
+ // 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, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
- files = append(files, createBuildFiles(buildToTargets)...)
+ files = append(files, createBuildFiles(buildToTargets, mode)...)
return files
}
-func createBuildFiles(buildToTargets map[string][]BazelTarget) []BazelFile {
+func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
files := make([]BazelFile, 0, len(buildToTargets))
for _, dir := range android.SortedStringKeys(buildToTargets) {
- content := soongModuleLoad
targets := buildToTargets[dir]
- sort.Slice(targets, func(i, j int) bool { return targets[i].name < targets[j].name })
- for _, t := range targets {
+ 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 += t.content
+ content += "package(default_visibility = [\"//visibility:public\"])"
+ content += "\n\n"
+ content += targets.LoadStatements()
}
- files = append(files, newFile(dir, "BUILD.bazel", content))
+ 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
}
@@ -79,6 +102,7 @@
"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.
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index a38fa6a..9fd6817 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -19,12 +19,14 @@
"testing"
)
-func TestCreateBazelFiles_AddsTopLevelFiles(t *testing.T) {
- files := CreateBazelFiles(map[string]RuleShim{}, map[string][]BazelTarget{})
- expectedFilePaths := []struct {
- dir string
- basename string
- }{
+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",
@@ -47,10 +49,12 @@
},
}
- if g, w := len(files), len(expectedFilePaths); g != w {
- t.Errorf("Expected %d files, got %d", w, g)
+ // 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
@@ -59,15 +63,25 @@
}
})
+ // Compare the file contents
for i := range files {
- if g, w := files[i], expectedFilePaths[i]; g.Dir != w.dir || g.Basename != w.basename {
- t.Errorf("Did not find expected file %s/%s", g.Dir, g.Basename)
- } else if g.Basename == "BUILD" || g.Basename == "WORKSPACE" {
- if g.Contents != "" {
- t.Errorf("Expected %s to have no content.", g)
+ 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 g.Contents == "" {
- t.Errorf("Contents of %s unexpected empty.", g)
+ } 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
index 2da32c6..ef3a78f 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -2,6 +2,14 @@
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 {
@@ -24,6 +32,7 @@
type customModule struct {
android.ModuleBase
+ android.BazelModuleBase
props customProps
}
@@ -41,6 +50,7 @@
func customModuleFactoryBase() android.Module {
module := &customModule{}
module.AddProperties(&module.props)
+ android.InitBazelModule(module)
return module
}
@@ -100,3 +110,79 @@
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 8142f10..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
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 040aa0b..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,9 +520,9 @@
entries.SubName += ".cfi"
}
- entries.SubName += c.androidMkSuffix
+ 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() {
@@ -546,16 +547,16 @@
func (c *snapshotBinaryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
entries.Class = "EXECUTABLES"
- entries.SubName = c.androidMkSuffix
+ 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) {
entries.AddStrings("LOCAL_MODULE_SYMLINKS", c.Properties.Symlinks...)
})
}
func (c *snapshotObjectLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
entries.Class = "STATIC_LIBRARIES"
- entries.SubName = c.androidMkSuffix
+ entries.SubName = c.baseProperties.Androidmk_suffix
entries.ExtraFooters = append(entries.ExtraFooters,
func(w io.Writer, name, prefix, moduleDir string) {
@@ -575,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())
@@ -586,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 {
@@ -616,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 8755efe..7f69d56 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -50,18 +50,14 @@
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.BottomUp("recovery_snapshot", RecoverySnapshotMutator).Parallel()
- ctx.BottomUp("recovery_snapshot_source", RecoverySnapshotSourceMutator).Parallel()
})
ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
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()
@@ -367,7 +363,7 @@
// 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 {
@@ -390,6 +386,17 @@
// 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
@@ -570,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
}
@@ -755,6 +773,7 @@
android.DefaultableModuleBase
android.ApexModuleBase
android.SdkBase
+ android.BazelModuleBase
Properties BaseProperties
VendorProperties VendorProperties
@@ -771,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
@@ -1044,6 +1064,7 @@
}
android.InitAndroidArchModule(c, c.hod, c.multilib)
+ android.InitBazelModule(c)
android.InitApexModule(c)
android.InitSdkAwareModule(c)
android.InitDefaultableModule(c)
@@ -1092,16 +1113,33 @@
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.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
// and does not set llndk.vendor_available: false.
func (c *Module) isImplementationForLLNDKPublic() bool {
@@ -1165,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
}
@@ -1225,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
@@ -1342,6 +1384,19 @@
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)
@@ -1528,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()
@@ -1545,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 {
@@ -1579,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()) {
@@ -1592,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,
@@ -1734,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
}
@@ -1787,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)
}
@@ -1826,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)
@@ -1833,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)
}
}
@@ -1881,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{
@@ -1926,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 {
@@ -1945,21 +2064,6 @@
// nonvariantLibs
vendorPublicLibraries := vendorPublicLibraries(actx.Config())
- vendorSnapshotSharedLibs := vendorSnapshotSharedLibs(actx.Config())
- recoverySnapshotSharedLibs := recoverySnapshotSharedLibs(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{}
@@ -1968,21 +2072,11 @@
// strip #version suffix out
name, _ := StubsLibNameAndVersion(entry)
if c.InRecovery() {
- recoverySnapshotVersion :=
- actx.DeviceConfig().RecoverySnapshotVersion()
- if recoverySnapshotVersion == "current" ||
- recoverySnapshotVersion == "" {
- nonvariantLibs = append(nonvariantLibs, name)
- } else if snapshot, ok := recoverySnapshotSharedLibs.get(
- name, actx.Arch().ArchType); ok {
- nonvariantLibs = append(nonvariantLibs, snapshot)
- } else {
- nonvariantLibs = append(nonvariantLibs, name)
- }
+ 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) {
@@ -2004,56 +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
- }
-
- snapshotHeaderLibs := vendorSnapshotHeaderLibs(actx.Config())
- snapshotStaticLibs := vendorSnapshotStaticLibs(actx.Config())
- snapshotObjects := vendorSnapshotObjects(actx.Config())
-
- if c.InRecovery() {
- rewriteSnapshotLibs = func(lib string, snapshotMap *snapshotMap) string {
- recoverySnapshotVersion :=
- actx.DeviceConfig().RecoverySnapshotVersion()
- if recoverySnapshotVersion == "current" ||
- recoverySnapshotVersion == "" {
- return lib
- } else if snapshot, ok := snapshotMap.get(lib, actx.Arch().ArchType); ok {
- return snapshot
- }
-
- return lib
- }
-
- snapshotHeaderLibs = recoverySnapshotHeaderLibs(actx.Config())
- snapshotStaticLibs = recoverySnapshotStaticLibs(actx.Config())
- snapshotObjects = recoverySnapshotObjects(actx.Config())
- }
-
for _, lib := range deps.HeaderLibs {
depTag := libraryDependencyTag{Kind: headerLibraryDependency}
if inList(lib, deps.ReexportHeaderLibHeaders) {
depTag.reexportFlags = true
}
- lib = rewriteSnapshotLibs(lib, snapshotHeaderLibs)
+ lib = rewriteSnapshotLib(lib, getSnapshot().HeaderLibs)
if c.IsStubs() {
actx.AddFarVariationDependencies(append(ctx.Target().Variations(), c.ImageVariation()),
@@ -2076,7 +2133,7 @@
lib = impl
}
- lib = rewriteSnapshotLibs(lib, snapshotStaticLibs)
+ lib = rewriteSnapshotLib(lib, getSnapshot().StaticLibs)
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
@@ -2096,7 +2153,7 @@
lib = impl
}
- lib = rewriteSnapshotLibs(lib, snapshotStaticLibs)
+ lib = rewriteSnapshotLib(lib, getSnapshot().StaticLibs)
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
@@ -2110,14 +2167,14 @@
depTag := libraryDependencyTag{Kind: staticLibraryDependency, staticUnwinder: true}
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
- }, depTag, rewriteSnapshotLibs(staticUnwinder(actx), snapshotStaticLibs))
+ }, 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, snapshotStaticLibs))
+ }, depTag, rewriteSnapshotLib(lib, getSnapshot().StaticLibs))
}
// shared lib names without the #version suffix
@@ -2181,11 +2238,11 @@
actx.AddVariationDependencies(crtVariations, objDepTag, deps.ObjFiles...)
if deps.CrtBegin != "" {
actx.AddVariationDependencies(crtVariations, CrtBeginDepTag,
- rewriteSnapshotLibs(deps.CrtBegin, snapshotObjects))
+ rewriteSnapshotLib(deps.CrtBegin, getSnapshot().Objects))
}
if deps.CrtEnd != "" {
actx.AddVariationDependencies(crtVariations, CrtEndDepTag,
- rewriteSnapshotLibs(deps.CrtEnd, snapshotObjects))
+ rewriteSnapshotLib(deps.CrtEnd, getSnapshot().Objects))
}
if deps.LinkerFlagsFile != "" {
actx.AddDependency(c, linkerFlagsDepTag, deps.LinkerFlagsFile)
@@ -2253,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
@@ -2428,36 +2480,6 @@
}
}
-// 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
@@ -2477,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()) {
@@ -2625,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 {
@@ -2641,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
}
}
@@ -2788,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(
@@ -2827,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:
@@ -2905,33 +2940,23 @@
return libName
}
-func (c *Module) makeLibName(ctx android.ModuleContext, ccDep LinkableInterface, depName string) string {
- vendorSuffixModules := vendorSuffixModules(ctx.Config())
- recoverySuffixModules := recoverySuffixModules(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 c.InVendor() && vendorSuffixModules[baseName] {
- return baseName + ".vendor"
- } else if c.InRecovery() && recoverySuffixModules[baseName] {
- return baseName + ".recovery"
- } else {
- return baseName
- }
+ return baseName + snapshotPrebuilt.snapshotAndroidMkSuffix()
}
}
@@ -2940,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 {
@@ -3277,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
}
@@ -3323,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 d0d8759..07dcc95 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -16,128 +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.ProductVndkVersion = 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(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")
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()
@@ -152,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") {
@@ -189,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) {
@@ -228,6 +244,120 @@
}
}
+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) {
@@ -266,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
}
@@ -282,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)
@@ -294,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) {
@@ -428,10 +567,10 @@
}
`
- 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)
@@ -449,7 +588,7 @@
// 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"))
@@ -461,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")
@@ -550,14 +689,14 @@
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) {
@@ -600,9 +739,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)
setVndkMustUseVendorVariantListForTest(config, []string{"libvndk"})
@@ -627,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)
@@ -678,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)
@@ -702,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])
@@ -1110,934 +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)
- }
- }
-
- // 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(buildDir, android.Android, nil, bp, nil)
- config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
- 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(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, "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
- checkSnapshotExclude(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
- excludeJsonFiles = append(excludeJsonFiles, 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)
- }
- }
-
- // 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 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, 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(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, "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(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"))
- 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(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"`,
- `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(buildDir, android.Android, nil, bp, nil)
- config.TestProductVariables.RecoverySnapshotVersion = 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 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(buildDir, android.Android, nil, "", mockFS)
- config.TestProductVariables.RecoverySnapshotVersion = 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.
- 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(buildDir, 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 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", `
@@ -2231,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)
@@ -2676,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)
}
@@ -2959,6 +2170,7 @@
vendor_available: true,
product_available: true,
nocrt: true,
+ srcs: ["foo.c"],
target: {
vendor: {
suffix: "-vendor",
@@ -3002,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)
@@ -3017,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,
@@ -3035,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,
@@ -3049,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,
@@ -3084,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,
@@ -3208,9 +2442,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")
// native:vndk
ctx := testCcWithConfig(t, config)
@@ -3414,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
@@ -3445,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) {
@@ -3479,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) {
@@ -3542,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"])
}
@@ -3590,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)
@@ -3640,6 +2878,14 @@
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: ["liball_available"],
no_libcrt : true,
@@ -3656,7 +2902,7 @@
cc_library {
name: "libvendor2",
vendor: true,
- runtime_libs: ["liball_available", "libvendor1"],
+ runtime_libs: ["liball_available", "libvendor1", "libproduct_vendor"],
no_libcrt : true,
nocrt : true,
system_shared_libs : [],
@@ -3679,7 +2925,7 @@
cc_library {
name: "libproduct2",
product_specific: true,
- runtime_libs: ["liball_available", "libproduct1"],
+ runtime_libs: ["liball_available", "libproduct1", "libproduct_vendor"],
no_libcrt : true,
nocrt : true,
system_shared_libs : [],
@@ -3703,23 +2949,23 @@
// 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_available1", variant).Module().(*Module)
checkRuntimeLibs(t, []string{"liball_available.vendor"}, module)
module = ctx.ModuleForTests("libvendor2", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"liball_available.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.VER_arm64_armv8-a_shared"
+ 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"}, module)
+ checkRuntimeLibs(t, []string{"liball_available.product", "libproduct1", "libproduct_vendor"}, module)
}
func TestExcludeRuntimeLibs(t *testing.T) {
@@ -3729,7 +2975,7 @@
module := ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
checkRuntimeLibs(t, []string{"liball_available"}, module)
- 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, nil, module)
}
@@ -3745,10 +2991,10 @@
checkRuntimeLibs(t, []string{"liball_available"}, module)
module = ctx.ModuleForTests("libvendor2", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"liball_available", "libvendor1"}, module)
+ checkRuntimeLibs(t, []string{"liball_available", "libvendor1", "libproduct_vendor"}, module)
module = ctx.ModuleForTests("libproduct2", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"liball_available", "libproduct1"}, module)
+ checkRuntimeLibs(t, []string{"liball_available", "libproduct1", "libproduct_vendor"}, module)
}
func checkStaticLibs(t *testing.T, expected []string, module *Module) {
@@ -3780,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 {
@@ -3912,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
@@ -3990,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)
@@ -4014,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])
@@ -4044,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) {
@@ -4234,6 +3482,9 @@
shared: {
srcs: ["baz.c"],
},
+ bazel_module: {
+ bp2build_available: true,
+ },
}
cc_library_static {
@@ -4306,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) {
@@ -4341,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) {
@@ -4408,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")
@@ -4514,6 +3744,71 @@
}
}
+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 (
@@ -4556,8 +3851,9 @@
}
}
-func makeMemtagTestConfig(t *testing.T) android.Config {
- templateBp := `
+var prepareForTestWithMemtagHeap = android.GroupFixturePreparers(
+ android.FixtureModifyMockFS(func(fs android.MockFS) {
+ templateBp := `
cc_test {
name: "%[1]s_test",
gtest: false,
@@ -4611,35 +3907,34 @@
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")
+ subdirDefaultBp := fmt.Sprintf(templateBp, "default")
+ subdirExcludeBp := fmt.Sprintf(templateBp, "exclude")
+ subdirSyncBp := fmt.Sprintf(templateBp, "sync")
+ subdirAsyncBp := fmt.Sprintf(templateBp, "async")
- mockFS := map[string][]byte{
- "subdir_default/Android.bp": []byte(subdirDefaultBp),
- "subdir_exclude/Android.bp": []byte(subdirExcludeBp),
- "subdir_sync/Android.bp": []byte(subdirSyncBp),
- "subdir_async/Android.bp": []byte(subdirAsyncBp),
- }
-
- return TestConfig(buildDir, android.Android, nil, "", mockFS)
-}
+ 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"
- config := makeMemtagTestConfig(t)
- config.TestProductVariables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
- config.TestProductVariables.MemtagHeapSyncIncludePaths = []string{"subdir_sync"}
- config.TestProductVariables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
- ctx := CreateTestContext(config)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp", "subdir_default/Android.bp", "subdir_exclude/Android.bp", "subdir_sync/Android.bp", "subdir_async/Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
+ 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)
@@ -4693,18 +3988,14 @@
func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
variant := "android_arm64_armv8-a"
- config := makeMemtagTestConfig(t)
- config.TestProductVariables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
- config.TestProductVariables.MemtagHeapSyncIncludePaths = []string{"subdir_sync"}
- config.TestProductVariables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
- config.TestProductVariables.SanitizeDevice = []string{"memtag_heap"}
- ctx := CreateTestContext(config)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp", "subdir_default/Android.bp", "subdir_exclude/Android.bp", "subdir_sync/Android.bp", "subdir_async/Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
+ 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)
@@ -4758,19 +4049,15 @@
func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
variant := "android_arm64_armv8-a"
- config := makeMemtagTestConfig(t)
- config.TestProductVariables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
- config.TestProductVariables.MemtagHeapSyncIncludePaths = []string{"subdir_sync"}
- config.TestProductVariables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
- config.TestProductVariables.SanitizeDevice = []string{"memtag_heap"}
- config.TestProductVariables.SanitizeDeviceDiag = []string{"memtag_heap"}
- ctx := CreateTestContext(config)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp", "subdir_default/Android.bp", "subdir_exclude/Android.bp", "subdir_sync/Android.bp", "subdir_async/Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
+ 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)
@@ -4820,3 +4107,274 @@
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 5f30d3d..78a5a5d 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -230,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)
@@ -254,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 {
@@ -330,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)
}
@@ -355,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() {
@@ -404,12 +414,7 @@
target := "-target " + tc.ClangTriple()
if ctx.Os().Class == android.Device {
- // When built for the non-updateble part of platform, minSdkVersion doesn't matter.
- // It matters only when building we are building for modules that can be unbundled.
- version := "current"
- if !ctx.isForPlatform() || ctx.isSdkVariant() {
- version = ctx.minSdkVersion()
- }
+ version := ctx.minSdkVersion()
if version == "" || version == "current" {
target += strconv.Itoa(android.FutureApiLevelInt)
} else {
@@ -440,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() {
@@ -634,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 35dd10f..5e46d5a 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -142,12 +142,15 @@
// 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_UNGUARDED_AVAILABILITY__",
+ "-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__",
}, " "))
pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
diff --git a/cc/config/global.go b/cc/config/global.go
index c62f784..23106ec 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -97,6 +97,7 @@
"-Wl,--exclude-libs,libgcc_stripped.a",
"-Wl,--exclude-libs,libunwind_llvm.a",
"-Wl,--exclude-libs,libunwind.a",
+ "-Wl,--icf=safe",
}
deviceGlobalLldflags = append(ClangFilterUnknownLldflags(deviceGlobalLdflags),
@@ -144,8 +145,8 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r407598"
- ClangDefaultShortVersion = "12.0.1"
+ ClangDefaultVersion = "clang-r416183b"
+ ClangDefaultShortVersion = "12.0.5"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
@@ -270,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 d5d01b4..c4563e2 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -31,7 +31,6 @@
}
checks := strings.Join([]string{
"-*",
- "abseil-*",
"android-*",
"bugprone-*",
"cert-*",
@@ -62,10 +61,8 @@
}, ",")
// 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.
- // Some test code have clang-diagnostic-padded warnings that cannot be
- // suppressed, but only by disabling clang-analyzer-optin.performance.*.
if ctx.Config().IsEnvTrue("CLANG_ANALYZER_CHECKS") {
- checks += ",clang-analyzer-*,-clang-analyzer-optin.performance.*"
+ checks += ",clang-analyzer-*"
}
return checks
})
@@ -89,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 107ae7d..425e349 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -17,25 +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-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 1035df3..b0344af 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/x86_darwin_host.go
@@ -136,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/fuzz.go b/cc/fuzz.go
index d7da5ab..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"
)
@@ -172,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.
@@ -209,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
@@ -433,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 1ce2169..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,6 +75,10 @@
return Bool(g.Vendor_ramdisk_available)
}
+func (g *GenruleExtraProperties) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
func (g *GenruleExtraProperties) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
// If the build is using a snapshot, the recovery variant under AOSP directories
// is not needed.
@@ -92,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 f89194f..bf662c6 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -49,15 +49,15 @@
)
func (ctx *moduleContext) ProductSpecific() 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 ctx.ModuleContext.ProductSpecific() || ctx.mod.InProduct()
+ return ctx.ModuleContext.ProductSpecific() || ctx.mod.productSpecificModuleContext()
}
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.
- return ctx.ModuleContext.SocSpecific() || 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 {
@@ -80,9 +80,33 @@
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 {
- return 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.
@@ -175,29 +199,73 @@
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 Bool(m.VendorProperties.Vendor_available) {
- if vendorSpecific {
- mctx.PropertyErrorf("vendor_available",
- "doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
- }
- }
-
- if Bool(m.VendorProperties.Product_available) {
- 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 {
@@ -242,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
@@ -264,18 +437,13 @@
productVndkVersion = platformVndkVersion
}
- _, isLLNDKLibrary := m.linker.(*llndkStubDecorator)
- _, isLLNDKHeaders := m.linker.(*llndkHeadersDecorator)
- lib := moduleLibraryInterface(m)
- hasLLNDKStubs := lib != nil && lib.hasLLNDKStubs()
-
- if isLLNDKLibrary || isLLNDKHeaders || hasLLNDKStubs {
+ 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 hasLLNDKStubs {
+ if m.HasLlndkStubs() {
coreVariantNeeded = true
}
if platformVndkVersion != "" {
@@ -292,19 +460,13 @@
// If the device isn't compiling against the VNDK, we always
// use the core mode.
coreVariantNeeded = true
- } 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 {
- if m.InstallInRecovery() {
- recoveryVariantNeeded = true
- } else {
- 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
@@ -330,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
@@ -339,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,
@@ -357,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)
@@ -369,60 +531,60 @@
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 _, ok := m.linker.(*kernelHeadersDecorator); !ok &&
- !m.isSnapshotPrebuilt() &&
+ 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.Properties.ExtraVariants) == 0 {
+ len(m.ExtraVariants()) == 0 {
m.Disable()
}
}
@@ -439,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 260ff75..2b0ee46 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"
)
@@ -58,12 +60,14 @@
Static_ndk_lib *bool
+ // Generate stubs to make this library accessible to APEXes.
Stubs struct {
// Relative path to the symbol map. The symbol map provides the list of
// 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
}
@@ -169,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
@@ -199,6 +205,9 @@
func init() {
RegisterLibraryBuildComponents(android.InitRegistrationContext)
+
+ android.RegisterBp2BuildMutator("cc_library_static", CcLibraryStaticBp2Build)
+ android.RegisterBp2BuildMutator("cc_library", CcLibraryBp2Build)
}
func RegisterLibraryBuildComponents(ctx android.RegistrationContext) {
@@ -209,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
@@ -229,6 +299,7 @@
module, library := NewLibrary(android.HostAndDeviceSupported)
library.BuildOnlyStatic()
module.sdkMemberTypes = []android.SdkMemberType{staticLibrarySdkMemberType}
+ module.bazelHandler = &staticLibraryBazelHandler{module: module}
return module.Init()
}
@@ -405,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.
@@ -449,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))
}
}
@@ -632,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
@@ -711,7 +858,7 @@
type versionedInterface interface {
buildStubs() bool
- setBuildStubs()
+ setBuildStubs(isLatest bool)
hasStubsVariants() bool
setStubsVersion(string)
stubsVersion() string
@@ -1293,9 +1440,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 +1455,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...)
}
}
@@ -1330,10 +1475,16 @@
}
}
+ // 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.
@@ -1348,8 +1499,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()))
}
}
@@ -1423,7 +1577,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.
@@ -1539,11 +1693,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) {
@@ -1554,8 +1726,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) {
@@ -1567,8 +1740,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 {
@@ -1811,7 +1983,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
@@ -1842,6 +2015,7 @@
for i, module := range modules {
module.(*Module).Properties.Sdk_version = StringPtr(versionStrs[i])
+ module.(*Module).Properties.Min_sdk_version = StringPtr(versionStrs[i])
}
}
@@ -1952,3 +2126,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 d15812e..0fb9c09 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -98,17 +98,37 @@
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
@@ -118,6 +138,10 @@
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 (
@@ -149,14 +173,31 @@
}
}
+// 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/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 10de889..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
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 411a369..bea1782 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -247,7 +247,7 @@
module.AddProperties(&prebuilt.properties)
- srcsSupplier := func(ctx android.BaseModuleContext) []string {
+ srcsSupplier := func(ctx android.BaseModuleContext, _ android.Module) []string {
return prebuilt.prebuiltSrcs(ctx)
}
@@ -306,6 +306,7 @@
func NewPrebuiltStaticLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
module, library := NewPrebuiltLibrary(hod)
library.BuildOnlyStatic()
+ module.bazelHandler = &prebuiltStaticLibraryBazelHandler{module: module, library: library}
return module, library
}
@@ -320,6 +321,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 8eeb355..397121e 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -83,7 +83,7 @@
const (
Asan SanitizerType = iota + 1
- hwasan
+ Hwasan
tsan
intOverflow
cfi
@@ -97,7 +97,7 @@
switch t {
case Asan:
return "asan"
- case hwasan:
+ case Hwasan:
return "hwasan"
case tsan:
return "tsan"
@@ -121,7 +121,7 @@
switch t {
case Asan:
return "address"
- case hwasan:
+ case Hwasan:
return "hwaddress"
case memtag_heap:
return "memtag_heap"
@@ -144,7 +144,7 @@
switch t {
case Asan:
return true
- case hwasan:
+ case Hwasan:
return true
case tsan:
return true
@@ -163,7 +163,7 @@
// incompatibleWithCfi returns true if a sanitizer is incompatible with CFI.
func (t SanitizerType) incompatibleWithCfi() bool {
- return t == Asan || t == Fuzzer || t == hwasan
+ return t == Asan || t == Fuzzer || t == Hwasan
}
type SanitizeUserProps struct {
@@ -404,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
@@ -751,7 +745,7 @@
switch t {
case Asan:
return sanitize.Properties.Sanitize.Address
- case hwasan:
+ case Hwasan:
return sanitize.Properties.Sanitize.Hwaddress
case tsan:
return sanitize.Properties.Sanitize.Thread
@@ -773,7 +767,7 @@
// isUnsanitizedVariant returns true if no sanitizers are enabled.
func (sanitize *sanitize) isUnsanitizedVariant() bool {
return !sanitize.isSanitizerEnabled(Asan) &&
- !sanitize.isSanitizerEnabled(hwasan) &&
+ !sanitize.isSanitizerEnabled(Hwasan) &&
!sanitize.isSanitizerEnabled(tsan) &&
!sanitize.isSanitizerEnabled(cfi) &&
!sanitize.isSanitizerEnabled(scs) &&
@@ -784,7 +778,7 @@
// 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) &&
+ !sanitize.isSanitizerEnabled(Hwasan) &&
!sanitize.isSanitizerEnabled(tsan) &&
!sanitize.isSanitizerEnabled(Fuzzer)
}
@@ -793,7 +787,7 @@
switch t {
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)
@@ -908,7 +902,7 @@
if d, ok := child.(PlatformSanitizeable); ok && d.SanitizePropDefined() &&
!d.SanitizeNever() &&
!d.IsSanitizerExplicitlyDisabled(t) {
- if t == cfi || t == hwasan || t == scs {
+ 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.
@@ -1086,6 +1080,12 @@
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"})
@@ -1135,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) {
@@ -1149,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
}
}
@@ -1168,13 +1169,12 @@
}
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
- }
+ // 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
@@ -1286,7 +1286,7 @@
// 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].(PlatformSanitizeable).SetPreventInstall()
modules[0].(PlatformSanitizeable).SetHideFromMake()
@@ -1300,7 +1300,7 @@
if c.StaticallyLinked() && c.ExportedToMake() {
if t == cfi {
cfiStaticLibs(mctx.Config()).add(c, c.Module().Name())
- } else if t == hwasan {
+ } else if t == Hwasan {
hwasanStaticLibs(mctx.Config()).add(c, c.Module().Name())
}
}
@@ -1417,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 2c3fec3..aec950b 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -75,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 020dcd7..885a0ce 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -18,20 +18,17 @@
// 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/proptools"
+ "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
@@ -49,9 +46,9 @@
// 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
@@ -61,46 +58,66 @@
// exclude_from_recovery_snapshot properties.
excludeFromSnapshot(m *Module) bool
- // Returns the snapshotMap to be used for a given module and config, or nil if the
- // module is not included in this image.
- getSnapshotMap(m *Module, cfg android.Config) *snapshotMap
-
- // Returns mutex used for mutual exclusion when updating the snapshot maps.
- getMutex() *sync.Mutex
-
- // For a given arch, a maps of which modules are included in this image.
- suffixModules(config android.Config) map[string]bool
-
- // Whether to add a given module to the suffix map.
- shouldBeAddedToSuffixModules(m *Module) bool
-
// Returns true if the build is using a snapshot for this image.
isUsingSnapshot(cfg android.DeviceConfig) bool
- // Whether to skip the module mutator for a module in a given context.
- skipModuleMutator(ctx android.BottomUpMutatorContext) bool
-
- // Whether to skip the source mutator for a given module.
- skipSourceMutator(ctx android.BottomUpMutatorContext) 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
- android.RegisterSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
+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 {
@@ -116,8 +133,25 @@
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}.
@@ -129,73 +163,13 @@
return m.ExcludeFromVendorSnapshot()
}
-func (vendorSnapshotImage) getSnapshotMap(m *Module, cfg android.Config) *snapshotMap {
- if lib, ok := m.linker.(libraryInterface); ok {
- if lib.static() {
- return vendorSnapshotStaticLibs(cfg)
- } else if lib.shared() {
- return vendorSnapshotSharedLibs(cfg)
- } else {
- // header
- return vendorSnapshotHeaderLibs(cfg)
- }
- } else if m.binary() {
- return vendorSnapshotBinaries(cfg)
- } else if m.object() {
- return vendorSnapshotObjects(cfg)
- } else {
- return nil
- }
-}
-
-func (vendorSnapshotImage) getMutex() *sync.Mutex {
- return &vendorSnapshotsLock
-}
-
-func (vendorSnapshotImage) suffixModules(config android.Config) map[string]bool {
- return vendorSuffixModules(config)
-}
-
-func (vendorSnapshotImage) shouldBeAddedToSuffixModules(module *Module) bool {
- // vendor suffix should be added to snapshots if the source module isn't vendor: true.
- if module.SocSpecific() {
- return false
- }
-
- // 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()
-
- return !ramdiskAvailable && !recoveryAvailable && !vendorRamdiskAvailable
-}
-
func (vendorSnapshotImage) isUsingSnapshot(cfg android.DeviceConfig) bool {
vndkVersion := cfg.VndkVersion()
return vndkVersion != "current" && vndkVersion != ""
}
-func (vendorSnapshotImage) skipModuleMutator(ctx android.BottomUpMutatorContext) bool {
- vndkVersion := ctx.DeviceConfig().VndkVersion()
- module, ok := ctx.Module().(*Module)
- return !ok || module.VndkVersion() != vndkVersion
-}
-
-func (vendorSnapshotImage) skipSourceMutator(ctx android.BottomUpMutatorContext) bool {
- vndkVersion := ctx.DeviceConfig().VndkVersion()
- module, ok := ctx.Module().(*Module)
- if !ok {
- return true
- }
- if module.VndkVersion() != vndkVersion {
- return true
- }
- // .. and also filter out llndk library
- if module.IsLlndk() {
- return true
- }
- return false
+func (vendorSnapshotImage) targetSnapshotVersion(cfg android.DeviceConfig) string {
+ return cfg.VndkVersion()
}
// returns true iff a given module SHOULD BE EXCLUDED, false if included
@@ -208,13 +182,22 @@
return !cfg.VendorSnapshotModules()[name]
}
-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) 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 {
@@ -232,8 +215,8 @@
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.
@@ -245,181 +228,169 @@
return m.ExcludeFromRecoverySnapshot()
}
-func (recoverySnapshotImage) getSnapshotMap(m *Module, cfg android.Config) *snapshotMap {
- if lib, ok := m.linker.(libraryInterface); ok {
- if lib.static() {
- return recoverySnapshotStaticLibs(cfg)
- } else if lib.shared() {
- return recoverySnapshotSharedLibs(cfg)
- } else {
- // header
- return recoverySnapshotHeaderLibs(cfg)
- }
- } else if m.binary() {
- return recoverySnapshotBinaries(cfg)
- } else if m.object() {
- return recoverySnapshotObjects(cfg)
- } else {
- return nil
- }
-}
-
-func (recoverySnapshotImage) getMutex() *sync.Mutex {
- return &recoverySnapshotsLock
-}
-
-func (recoverySnapshotImage) suffixModules(config android.Config) map[string]bool {
- return recoverySuffixModules(config)
-}
-
-func (recoverySnapshotImage) shouldBeAddedToSuffixModules(module *Module) bool {
- return proptools.BoolDefault(module.Properties.Recovery_available, false)
-}
-
func (recoverySnapshotImage) isUsingSnapshot(cfg android.DeviceConfig) bool {
recoverySnapshotVersion := cfg.RecoverySnapshotVersion()
return recoverySnapshotVersion != "current" && recoverySnapshotVersion != ""
}
-func (recoverySnapshotImage) skipModuleMutator(ctx android.BottomUpMutatorContext) bool {
- module, ok := ctx.Module().(*Module)
- return !ok || !module.InRecovery()
-}
-
-func (recoverySnapshotImage) skipSourceMutator(ctx android.BottomUpMutatorContext) bool {
- module, ok := ctx.Module().(*Module)
- return !ok || !module.InRecovery()
+func (recoverySnapshotImage) targetSnapshotVersion(cfg android.DeviceConfig) string {
+ return cfg.RecoverySnapshotVersion()
}
func (recoverySnapshotImage) excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
- // directed recovery snapshot is not implemented yet
- return false
+ // 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")
-)
-
-var (
- recoverySnapshotsLock sync.Mutex
- recoverySuffixModulesKey = android.NewOnceKey("recoverySuffixModules")
- recoverySnapshotHeaderLibsKey = android.NewOnceKey("recoverySnapshotHeaderLibs")
- recoverySnapshotStaticLibsKey = android.NewOnceKey("recoverySnapshotStaticLibs")
- recoverySnapshotSharedLibsKey = android.NewOnceKey("recoverySnapshotSharedLibs")
- recoverySnapshotBinariesKey = android.NewOnceKey("recoverySnapshotBinaries")
- recoverySnapshotObjectsKey = android.NewOnceKey("recoverySnapshotObjects")
-)
-
-// 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 recoverySuffixModules(config android.Config) map[string]bool {
- return config.Once(recoverySuffixModulesKey, func() interface{} {
- return make(map[string]bool)
- }).(map[string]bool)
+func (s *snapshot) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
}
-func recoverySnapshotHeaderLibs(config android.Config) *snapshotMap {
- return config.Once(recoverySnapshotHeaderLibsKey, func() interface{} {
- return newSnapshotMap()
- }).(*snapshotMap)
+func (s *snapshot) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
}
-func recoverySnapshotSharedLibs(config android.Config) *snapshotMap {
- return config.Once(recoverySnapshotSharedLibsKey, func() interface{} {
- return newSnapshotMap()
- }).(*snapshotMap)
+func (s *snapshot) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+ return []string{s.image.imageVariantName(ctx.DeviceConfig())}
}
-func recoverySnapshotStaticLibs(config android.Config) *snapshotMap {
- return config.Once(recoverySnapshotStaticLibsKey, func() interface{} {
- return newSnapshotMap()
- }).(*snapshotMap)
+func (s *snapshot) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
}
-func recoverySnapshotBinaries(config android.Config) *snapshotMap {
- return config.Once(recoverySnapshotBinariesKey, func() interface{} {
- return newSnapshotMap()
- }).(*snapshotMap)
+func (s *snapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // Nothing, the snapshot module is only used to forward dependency information in DepsMutator.
}
-func recoverySnapshotObjects(config android.Config) *snapshotMap {
- return config.Once(recoverySnapshotObjectsKey, func() interface{} {
- return newSnapshotMap()
- }).(*snapshotMap)
+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 {
@@ -429,9 +400,12 @@
// 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.
- Module_suffix string
+ ModuleSuffix string `blueprint:"mutated"`
}
// baseSnapshotDecorator provides common basic functions for all snapshot modules, such as snapshot
@@ -448,6 +422,7 @@
// will be seen as "libbase.vendor_static.30.arm64" by Soong.
type baseSnapshotDecorator struct {
baseProperties baseSnapshotDecoratorProperties
+ image snapshotImage
}
func (p *baseSnapshotDecorator) Name(name string) string {
@@ -455,12 +430,7 @@
}
func (p *baseSnapshotDecorator) NameSuffix() string {
- versionSuffix := p.version()
- if p.arch() != "" {
- versionSuffix += "." + p.arch()
- }
-
- return p.baseProperties.Module_suffix + versionSuffix
+ return getSnapshotNameSuffix(p.moduleSuffix(), p.version(), p.arch())
}
func (p *baseSnapshotDecorator) version() string {
@@ -471,18 +441,33 @@
return p.baseProperties.Target_arch
}
-func (p *baseSnapshotDecorator) module_suffix() string {
- return p.baseProperties.Module_suffix
+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.baseProperties.Module_suffix = 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)
@@ -542,7 +527,6 @@
// Library flags for cfi variant.
Cfi snapshotLibraryProperties `android:"arch_variant"`
}
- androidMkSuffix string
}
func (p *snapshotLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
@@ -565,13 +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)
-
- if m.InVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
- p.androidMkSuffix = vendorSuffix
- } else if m.InRecovery() && recoverySuffixModules(ctx.Config())[m.BaseModuleName()] {
- p.androidMkSuffix = recoverySuffix
- }
+ p.setSnapshotAndroidMkSuffix(ctx)
if p.header() {
return p.libraryDecorator.link(ctx, flags, deps, objs)
@@ -585,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
@@ -656,7 +642,7 @@
}
}
-func snapshotLibraryFactory(suffix string) (*Module, *snapshotLibraryDecorator) {
+func snapshotLibraryFactory(image snapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
module, library := NewLibrary(android.DeviceSupported)
module.stl = nil
@@ -679,7 +665,7 @@
module.linker = prebuilt
module.installer = prebuilt
- prebuilt.init(module, suffix)
+ prebuilt.init(module, image, moduleSuffix)
module.AddProperties(
&prebuilt.properties,
&prebuilt.sanitizerProperties,
@@ -693,7 +679,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()
}
@@ -703,7 +689,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()
}
@@ -713,7 +699,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()
}
@@ -723,7 +709,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()
}
@@ -733,7 +719,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()
}
@@ -743,7 +729,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()
}
@@ -765,8 +751,7 @@
type snapshotBinaryDecorator struct {
baseSnapshotDecorator
*binaryDecorator
- properties snapshotBinaryProperties
- androidMkSuffix string
+ properties snapshotBinaryProperties
}
func (p *snapshotBinaryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
@@ -782,6 +767,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
}
@@ -790,14 +777,6 @@
p.unstrippedOutputFile = in
binName := in.Base()
- m := ctx.Module().(*Module)
- if m.InVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
- p.androidMkSuffix = vendorSuffix
- } else if m.InRecovery() && recoverySuffixModules(ctx.Config())[m.BaseModuleName()] {
- p.androidMkSuffix = recoverySuffix
-
- }
-
// use cpExecutable to make it executable
outputFile := android.PathForModuleOut(ctx, binName)
ctx.Build(pctx, android.BuildParams{
@@ -818,17 +797,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)
@@ -847,7 +826,7 @@
module.stl = nil
module.linker = prebuilt
- prebuilt.init(module, suffix)
+ prebuilt.init(module, image, moduleSuffix)
module.AddProperties(&prebuilt.properties)
return module.Init()
}
@@ -867,8 +846,7 @@
type snapshotObjectLinker struct {
baseSnapshotDecorator
objectLinker
- properties vendorSnapshotObjectProperties
- androidMkSuffix string
+ properties vendorSnapshotObjectProperties
}
func (p *snapshotObjectLinker) matchesWithDevice(config android.DeviceConfig) bool {
@@ -884,18 +862,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)
-
- if m.InVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
- p.androidMkSuffix = vendorSuffix
- } else if m.InRecovery() && recoverySuffixModules(ctx.Config())[m.BaseModuleName()] {
- p.androidMkSuffix = recoverySuffix
- }
-
return android.PathForModuleSrc(ctx, *p.properties.Src)
}
@@ -916,7 +888,7 @@
}
module.linker = prebuilt
- prebuilt.init(module, vendorSnapshotObjectSuffix)
+ prebuilt.init(module, vendorSnapshotImageSingleton, snapshotObjectSuffix)
module.AddProperties(&prebuilt.properties)
return module.Init()
}
@@ -934,130 +906,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) {
- snapshotMutator(ctx, vendorSnapshotImageSingleton)
-}
-
-func RecoverySnapshotMutator(ctx android.BottomUpMutatorContext) {
- snapshotMutator(ctx, recoverySnapshotImageSingleton)
-}
-
-func snapshotMutator(ctx android.BottomUpMutatorContext, image snapshotImage) {
- if !image.isUsingSnapshot(ctx.DeviceConfig()) {
- return
- }
- module, ok := ctx.Module().(*Module)
- if !ok || !module.Enabled() {
- return
- }
- if image.skipModuleMutator(ctx) {
- 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 = image.getSnapshotMap(module, ctx.Config())
- if snapshotMap == nil {
- return
- }
-
- mutex := image.getMutex()
- mutex.Lock()
- defer mutex.Unlock()
- snapshotMap.add(module.BaseModuleName(), ctx.Arch().ArchType, ctx.ModuleName())
-}
-
-// VendorSnapshotSourceMutator disables source modules which have corresponding snapshots.
-func VendorSnapshotSourceMutator(ctx android.BottomUpMutatorContext) {
- snapshotSourceMutator(ctx, vendorSnapshotImageSingleton)
-}
-
-func RecoverySnapshotSourceMutator(ctx android.BottomUpMutatorContext) {
- snapshotSourceMutator(ctx, recoverySnapshotImageSingleton)
-}
-
-func snapshotSourceMutator(ctx android.BottomUpMutatorContext, image snapshotImage) {
- if !ctx.Device() {
- return
- }
- if !image.isUsingSnapshot(ctx.DeviceConfig()) {
- return
- }
-
- module, ok := ctx.Module().(*Module)
- if !ok {
- return
- }
-
- if image.shouldBeAddedToSuffixModules(module) {
- mutex := image.getMutex()
- mutex.Lock()
- defer mutex.Unlock()
-
- image.suffixModules(ctx.Config())[ctx.ModuleName()] = true
- }
-
- if module.isSnapshotPrebuilt() {
- return
- }
- if image.skipSourceMutator(ctx) {
- return
- }
-
- var snapshotMap *snapshotMap = image.getSnapshotMap(module, ctx.Config())
- if snapshotMap == nil {
- 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 c50ef45..c32fa36 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -80,7 +80,7 @@
}
for _, image := range []snapshotImage{vendorSnapshotImageSingleton, recoverySnapshotImageSingleton} {
- if isSnapshotAware(ctx.DeviceConfig(), m, image.isProprietaryPath(ctx.ModuleDir()), apexInfo, image) {
+ if isSnapshotAware(ctx.DeviceConfig(), m, image.isProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()), apexInfo, image) {
return true
}
}
diff --git a/cc/stl.go b/cc/stl.go
index 75fab17..4f8865f 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -140,6 +140,17 @@
}
func staticUnwinder(ctx android.BaseModuleContext) string {
+ 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"
}
@@ -188,12 +199,7 @@
if needsLibAndroidSupport(ctx) {
deps.StaticLibs = append(deps.StaticLibs, "ndk_libandroid_support")
}
- // TODO: Switch the NDK over to the LLVM unwinder for non-arm32 architectures.
- 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))
}
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 f715a8d..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.
@@ -373,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})
}
@@ -399,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,
@@ -429,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 dc9a59d..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,7 @@
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,
@@ -113,6 +131,7 @@
name: "libunwind",
defaults: ["linux_bionic_supported"],
vendor_available: true,
+ vendor_ramdisk_available: true,
product_available: true,
recovery_available: true,
native_bridge_supported: true,
@@ -170,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"],
@@ -238,6 +234,7 @@
cc_library {
name: "libprofile-extras",
vendor_available: true,
+ vendor_ramdisk_available: true,
product_available: true,
recovery_available: true,
native_coverage: false,
@@ -248,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,
@@ -319,6 +317,7 @@
system_shared_libs: [],
stl: "none",
vendor_available: true,
+ vendor_ramdisk_available: true,
product_available: true,
recovery_available: true,
host_supported: true,
@@ -356,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",
@@ -364,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",
@@ -464,23 +455,10 @@
name: "note_memtag_heap_sync",
}
`
+}
- supportLinuxBionic := false
- for _, os := range oses {
- if os == android.Fuchsia {
- ret += `
- cc_library {
- name: "libbioniccompat",
- stl: "none",
- }
- cc_library {
- name: "libcompiler_rt",
- stl: "none",
- }
- `
- }
- if os == android.Windows {
- ret += `
+func withWindowsModules() string {
+ return `
toolchain_library {
name: "libwinpthread",
host_supported: true,
@@ -493,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"],
@@ -536,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 {
@@ -569,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)
}
@@ -577,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)
@@ -586,17 +690,15 @@
ctx.RegisterModuleType("vendor_public_library", vendorPublicLibraryFactory)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
- 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("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
- ctx.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
return ctx
}
diff --git a/cc/tidy.go b/cc/tidy.go
index 972ad7b..616cf8a 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -20,6 +20,7 @@
"github.com/google/blueprint/proptools"
+ "android/soong/android"
"android/soong/cc/config"
)
@@ -41,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}
}
@@ -73,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)
}
@@ -110,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() {
@@ -156,7 +180,7 @@
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(tidy.Properties.Tidy_checks_as_errors), ",")
+ 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 7346aac..2f68cca 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -90,73 +90,24 @@
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{
- "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
}
@@ -177,7 +128,7 @@
// Any module in a recovery proprietary path is a recovery proprietary
// module.
- if isRecoveryProprietaryPath(ctx.ModuleDir()) {
+ if isRecoveryProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) {
return true
}
@@ -222,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
@@ -239,17 +190,13 @@
if _, ok := m.linker.(*llndkHeadersDecorator); ok {
return false
}
- // If we are using directed snapshot AND we have to exclude this module, skip this
- if image.excludeFromDirectedSnapshot(cfg, m.BaseModuleName()) {
- return false
- }
// Libraries
if l, ok := m.linker.(snapshotLibraryInterface); ok {
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
}
@@ -370,18 +317,19 @@
var headers android.Paths
- copyFile := copyFileRule
- if c.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.
- copyFile = func(ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
+ 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
@@ -417,7 +365,7 @@
out := filepath.Join(configsDir, path.Base())
if !installedConfigs[out] {
installedConfigs[out] = true
- ret = append(ret, copyFile(ctx, path, out))
+ ret = append(ret, copyFile(ctx, path, out, fake))
}
}
@@ -468,7 +416,7 @@
prop.ModuleName += ".cfi"
}
snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, stem)
- ret = append(ret, copyFile(ctx, libPath, snapshotLibOut))
+ ret = append(ret, copyFile(ctx, libPath, snapshotLibOut, fake))
} else {
stem = ctx.ModuleName(m)
}
@@ -482,7 +430,7 @@
// install bin
binPath := m.outputFile.Path()
snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base())
- ret = append(ret, copyFile(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.
@@ -490,7 +438,7 @@
objPath := m.outputFile.Path()
snapshotObjOut := filepath.Join(snapshotArchDir, targetArch, "object",
ctx.ModuleName(m)+filepath.Ext(objPath.Base()))
- ret = append(ret, copyFile(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())
@@ -514,7 +462,7 @@
}
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 c.image.excludeFromSnapshot(m) {
@@ -530,9 +478,17 @@
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()...)
@@ -551,7 +507,7 @@
// install all headers after removing duplicates
for _, header := range android.FirstUniquePaths(headers) {
- snapshotOutputs = append(snapshotOutputs, copyFile(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.
@@ -570,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 ff38db5..1a8a454 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -561,7 +561,7 @@
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())
},
},
@@ -609,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)
@@ -827,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)
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index cf7c8b6..fc4412a 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 6714978..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: [
@@ -21,7 +25,6 @@
"soong",
"soong-android",
"soong-bp2build",
- "soong-env",
"soong-ui-metrics_proto",
],
srcs: [
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 1d5e7b3..1e796ec 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -17,23 +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"
- "android/soong/bp2build"
)
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 {
@@ -52,16 +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("GENERATE_BAZEL_FILES")
-}
-
-func newContext(configuration android.Config) *android.Context {
+func newContext(configuration android.Config, prepareBuildActions bool) *android.Context {
ctx := android.NewContext(configuration)
ctx.Register()
- if !shouldPrepareBuildActions(configuration) {
+ if !prepareBuildActions {
configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
}
ctx.SetNameInterface(newNameResolver(configuration))
@@ -69,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)
@@ -78,83 +98,259 @@
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 bazelConversionRequested(configuration) {
- // Run the alternate pipeline of bp2build mutators and singleton to convert Blueprint to BUILD files
- // before everything else.
- runBp2Build(configuration, extraNinjaDeps)
- // Short-circuit and return.
+ if docFile != "" {
+ // 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
}
- 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(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(secondPassConfig)
- bootstrap.Main(ctx.Context, secondPassConfig, extraNinjaDeps...)
- } else {
- ctx = newContext(configuration)
- bootstrap.Main(ctx.Context, configuration, extraNinjaDeps...)
+ finalOutputFile := doChosenActivity(configuration, extraNinjaDeps)
+ writeUsedEnvironmentFile(configuration, finalOutputFile)
+}
+
+func writeUsedEnvironmentFile(configuration android.Config, finalOutputFile string) {
+ if usedEnvFile == "" {
+ return
}
- // 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)
- }
+ 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)
}
- if docFile != "" {
- if err := writeDocs(ctx, docFile); err != nil {
- fmt.Fprintf(os.Stderr, "%s", 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)
}
- // 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)
- }
+ // 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)
}
}
@@ -165,28 +361,59 @@
// Register an alternate set of singletons and mutators for bazel
// conversion for Bazel conversion.
bp2buildCtx := android.NewContext(configuration)
- bp2buildCtx.RegisterForBazelConversion()
- configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
+
+ // 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))
- bootstrap.Main(bp2buildCtx.Context, configuration, extraNinjaDeps...)
+ bp2buildCtx.RegisterForBazelConversion()
- codegenContext := bp2build.NewCodegenContext(configuration, *bp2buildCtx)
- bp2build.Codegen(codegenContext)
-}
-
-// shouldPrepareBuildActions reads configuration and flags if build actions
-// should be generated.
-func shouldPrepareBuildActions(configuration android.Config) bool {
- // Generating Soong docs
- if docFile != "" {
- return false
+ // 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 79ea94a..edc8a42 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -22,11 +22,14 @@
"path/filepath"
)
-func createBazelQueryView(ctx *android.Context, bazelQueryViewDir string) error {
+func createBazelQueryView(ctx *bp2build.CodegenContext, bazelQueryViewDir string) error {
ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
- buildToTargets := bp2build.GenerateSoongModuleTargets(*ctx)
- filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets)
+ // 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)
+
+ filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets, bp2build.QueryView)
for _, f := range filesToWrite {
if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil {
return err
diff --git a/cmd/soong_build/writedocs.go b/cmd/soong_build/writedocs.go
index f2c2c9b..a69de6a 100644
--- a/cmd/soong_build/writedocs.go
+++ b/cmd/soong_build/writedocs.go
@@ -95,13 +95,13 @@
return result
}
-func getPackages(ctx *android.Context) ([]*bpdoc.Package, error) {
+func getPackages(ctx *android.Context, config interface{}) ([]*bpdoc.Package, error) {
moduleTypeFactories := android.ModuleTypeFactoriesForDocs()
- return bootstrap.ModuleTypeDocs(ctx.Context, moduleTypeFactories)
+ 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 532d8fc..ad52b00 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -488,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" {
@@ -515,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/config.go b/dexpreopt/config.go
index 867ece6..26ff5ba 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -33,6 +33,8 @@
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
@@ -84,6 +86,15 @@
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,
@@ -104,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
@@ -113,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
@@ -257,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{}
@@ -277,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
@@ -290,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.
@@ -303,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
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 af73d0c..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},
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 5ef4a90..cf98717 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -16,6 +16,8 @@
import (
"fmt"
+ "path/filepath"
+ "strings"
"android/soong/android"
@@ -24,7 +26,11 @@
)
func init() {
- android.RegisterModuleType("android_filesystem", filesystemFactory)
+ registerBuildComponents(android.InitRegistrationContext)
+}
+
+func registerBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("android_filesystem", filesystemFactory)
}
type filesystem struct {
@@ -37,6 +43,11 @@
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
@@ -47,6 +58,26 @@
// 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
@@ -64,13 +95,37 @@
var dependencyTag = struct {
blueprint.BaseDependencyTag
- android.InstallAlwaysNeededDependencyTag
+ 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"
}
@@ -78,30 +133,114 @@
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)
propFile, toolDeps := f.buildPropFile(ctx)
- f.output = android.PathForModuleOut(ctx, f.installFileName()).OutputPath
+ output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
builder.Command().BuiltTool("build_image").
Text(rootDir.String()). // input directory
Input(propFile).
Implicits(toolDeps).
- Output(f.output).
+ 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) {
@@ -120,9 +259,18 @@
deps = append(deps, path)
}
- // TODO(jiyong): support more filesystem types other than ext4
- addStr("fs_type", "ext4")
- addStr("mount_point", "system")
+ // 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.
@@ -138,7 +286,12 @@
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")
- addStr("partition_name", f.Name())
+ 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
@@ -146,7 +299,7 @@
builder.Command().Text("rm").Flag("-rf").Output(propFile)
for _, p := range props {
builder.Command().
- Text("echo").Flag("-e").
+ Text("echo").
Flag(`"` + p.name + "=" + p.value + `"`).
Text(">>").Output(propFile)
}
@@ -154,6 +307,57 @@
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)
// Implements android.AndroidMkEntriesProvider
@@ -162,7 +366,7 @@
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())
},
@@ -170,11 +374,25 @@
}}
}
+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)
@@ -182,3 +400,10 @@
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 cc454b0..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,14 +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())
},
},
@@ -216,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)
}
@@ -243,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)
@@ -251,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())
},
},
}}
@@ -265,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 {
@@ -283,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)
},
},
@@ -313,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)
@@ -428,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)
@@ -444,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)
})
@@ -460,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)
}
@@ -488,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)
}
@@ -506,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)
}
@@ -535,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)
}
@@ -634,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...)
@@ -642,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)
+ }
},
},
}}
@@ -650,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)
})
@@ -671,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...)
@@ -687,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 e2647cf..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,8 +162,8 @@
{"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)
@@ -168,7 +172,7 @@
}
func TestImportSoongDexJar(t *testing.T) {
- ctx, config := testJava(t, `
+ result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
java_import {
name: "my-java-import",
jars: ["a.jar"],
@@ -177,14 +181,10 @@
}
`)
- mod := ctx.ModuleForTests("my-java-import", "android_common").Module()
- entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
- expectedSoongDexJar := buildDir + "/.intermediates/my-java-import/android_common/dex/my-java-import.jar"
+ 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"]
- if len(actualSoongDexJar) != 1 {
- t.Errorf("LOCAL_SOONG_DEX_JAR incorrect len %d", len(actualSoongDexJar))
- } else if actualSoongDexJar[0] != expectedSoongDexJar {
- t.Errorf("LOCAL_SOONG_DEX_JAR mismatch, actual: %s, expected: %s", actualSoongDexJar[0], expectedSoongDexJar)
- }
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_SOONG_DEX_JAR", result.Config, []string{expectedSoongDexJar}, actualSoongDexJar)
}
diff --git a/java/app.go b/java/app.go
index e6c9a2d..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 {
@@ -1216,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 {
@@ -1226,7 +1255,16 @@
if tag, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok {
dep := ctx.OtherModuleName(m)
if lib, ok := m.(UsesLibraryDependency); ok {
- clcMap.AddContext(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})
@@ -1255,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)
@@ -1275,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 df940f1..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)
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 344d23b..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 {
@@ -212,14 +252,15 @@
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 {
@@ -232,7 +273,7 @@
}
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)
@@ -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 004cbbb..e57c3e9 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -15,6 +15,7 @@
package java
import (
+ "fmt"
"path/filepath"
"sort"
"strings"
@@ -35,6 +36,13 @@
//
// 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:
@@ -203,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)
}
@@ -385,22 +402,6 @@
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().
@@ -438,58 +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) {
+//
+// 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)
// 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 := image.modules.IndexOfJar(name)
+ 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.
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
@@ -513,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)
@@ -561,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)
@@ -595,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 {
@@ -860,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 {
@@ -959,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 48bc244..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,25 +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)
- 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()
@@ -73,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) {
@@ -115,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 c74009e..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,80 +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
-
- 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
//
@@ -315,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
}
@@ -357,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)
@@ -453,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)
@@ -470,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.
@@ -480,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:
@@ -568,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)
@@ -596,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 {
@@ -614,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) {
@@ -641,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)
@@ -651,6 +561,8 @@
Flag("-XDignore.symbol.file").
Flag("-Xdoclint:none")
+ j.expandArgs(ctx, cmd)
+
rule.Command().
BuiltTool("soong_zip").
Flag("-write_if_changed").
@@ -811,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
@@ -909,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))
@@ -946,657 +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 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.
@@ -1649,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)
@@ -1683,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 4bd255c..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
@@ -77,30 +137,25 @@
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
@@ -125,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()...)
@@ -160,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 {
@@ -172,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)
}
}
@@ -225,6 +261,47 @@
rule.Build("hiddenAPIStubFlagsFile", "hiddenapi stub flags")
}
+// 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)
+
+ // Strip a prebuilt_ prefix so that this can match a prebuilt module that has not been renamed.
+ name = android.RemoveOptionalPrebuiltPrefix(name)
+
+ // Ignore any module that is not listed in the boot image configuration.
+ index := configuredBootJars.IndexOfJar(name)
+ if index == -1 {
+ return false
+ }
+
+ // 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
inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-flags.csv")
@@ -238,63 +315,21 @@
return outputPath
}
-// 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
+func prebuiltIndexRule(ctx android.SingletonContext) {
+ outputPath := hiddenAPISingletonPaths(ctx).index
+ inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-index.csv")
- 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]
- }
- }
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Output: outputPath,
+ Input: inputPath,
})
+}
- if combinedRemovedApis == nil {
- ctx.Errorf("Failed to find combined-removed-dex.")
- }
-
- rule := android.NewRuleBuilder(pctx, ctx)
-
+// 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
- tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp")
-
- stubFlags := hiddenAPISingletonPaths(ctx).stubFlags
-
- 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-r ",
- android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-r-loprio.txt")).FlagWithArg("--tag ", "lo-prio").
- 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("--unsupported ", android.PathForSource(
- ctx, "frameworks/base/config/hiddenapi-unsupported-packages.txt")).Flag("--packages ").
- FlagWithOutput("--output ", tempPath)
-
- commitChangeForRestat(rule, tempPath, outputPath)
-
- rule.Build("hiddenAPIFlagsFile", "hiddenapi flags")
-
return outputPath
}
@@ -313,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.
@@ -357,104 +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
- }
-
- if ctx.Config().PrebuiltHiddenApiDir(ctx) != "" {
- outputPath := hiddenAPISingletonPaths(ctx).index
- inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-index.csv")
-
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cp,
- Output: outputPath,
- Input: inputPath,
- })
-
- h.index = outputPath
- 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 0f9ef58..5ea9a5b 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -15,139 +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, prebuiltHiddenApiDir *string) android.Config {
- config := testConfig(nil, bp, nil)
- config.TestProductVariables.BootJars = android.CreateTestConfiguredJarList(bootJars)
- config.TestProductVariables.PrebuiltHiddenApiDir = prebuiltHiddenApiDir
- 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, prebuiltHiddenApiDir *string) (*android.TestContext, android.Config) {
- config := testConfigWithBootJars(bp, bootJars, prebuiltHiddenApiDir)
-
- 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"}, nil)
+ }
+ `)
- 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"}, nil)
+ `)
- 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"}, nil)
+ }
+ `)
- 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"}, nil)
+ }
+ `)
- 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) {
@@ -158,6 +157,9 @@
systemStub string
testStub string
corePlatformStub string
+
+ // Additional test preparer
+ preparer android.FixturePreparer
}{
{
name: "testBundled",
@@ -166,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,
@@ -173,50 +176,49 @@
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) {
@@ -230,36 +232,34 @@
// Where to find the prebuilt hiddenapi files:
prebuiltHiddenApiDir := "path/to/prebuilt/hiddenapi"
- ctx, _ := testHiddenAPIBootJars(t, `
+ result := android.GroupFixturePreparers(
+ hiddenApiFixtureFactory,
+ FixtureConfigureBootJars("platform:foo"),
+ fixtureSetPrebuiltHiddenApiDirProductVariable(&prebuiltHiddenApiDir),
+ ).RunTestWithBp(t, `
java_import {
name: "foo",
jars: ["a.jar"],
compile_dex: true,
}
- `, []string{":foo"}, &prebuiltHiddenApiDir)
+ `)
expectedCpInput := prebuiltHiddenApiDir + "/hiddenapi-flags.csv"
- expectedCpOutput := buildDir + "/hiddenapi/hiddenapi-flags.csv"
- expectedFlagsCsv := buildDir + "/hiddenapi/hiddenapi-flags.csv"
+ expectedCpOutput := "out/soong/hiddenapi/hiddenapi-flags.csv"
+ expectedFlagsCsv := "out/soong/hiddenapi/hiddenapi-flags.csv"
- foo := ctx.ModuleForTests("foo", "android_common")
+ foo := result.ModuleForTests("foo", "android_common")
- hiddenAPI := ctx.SingletonForTests("hiddenapi")
+ 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"]
- if actualCpInput.String() != expectedCpInput {
- t.Errorf("Prebuilt hiddenapi cp rule input mismatch, actual: %s, expected: %s", actualCpInput, expectedCpInput)
- }
+ android.AssertPathRelativeToTopEquals(t, "hiddenapi cp rule input", expectedCpInput, actualCpInput)
- if actualCpOutput.String() != expectedCpOutput {
- t.Errorf("Prebuilt hiddenapi cp rule output mismatch, actual: %s, expected: %s", actualCpOutput, expectedCpOutput)
- }
+ android.AssertPathRelativeToTopEquals(t, "hiddenapi cp rule output", expectedCpOutput, actualCpOutput)
- if actualFlagsCsv != expectedFlagsCsv {
- t.Errorf("Prebuilt hiddenapi encode dex rule flags csv mismatch, actual: %s, expected: %s", actualFlagsCsv, expectedFlagsCsv)
- }
+ android.AssertStringEquals(t, "hiddenapi encode dex rule flags csv", expectedFlagsCsv, actualFlagsCsv)
}
diff --git a/java/java.go b/java/java.go
index bc5bdb1..adb0c56 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"
@@ -36,68 +34,12 @@
)
func init() {
- RegisterJavaBuildComponents(android.InitRegistrationContext)
+ registerJavaBuildComponents(android.InitRegistrationContext)
- // Register sdk member types.
- android.RegisterSdkMemberType(javaHeaderLibsSdkMemberType)
-
- // Register java implementation libraries for use only in module_exports (not sdk).
- android.RegisterSdkMemberType(&librarySdkMemberType{
- android.SdkMemberTypeBase{
- PropertyName: "java_libs",
- },
- 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]
- },
- sdkSnapshotFilePathForJar,
- copyEverythingToSnapshot,
- })
-
- // Register java boot libraries for use in sdk.
- //
- // 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,
- },
- func(ctx android.SdkMemberContext, j *Library) android.Path {
- // Java boot libs are only provided in the SDK to provide access to their dex implementation
- // jar for use by dexpreopting and boot jars package check. They do not need to provide an
- // actual implementation jar but the java_import will need a file that exists so just copy an
- // empty file. Any attempt to use that file as a jar will cause a build error.
- return ctx.SnapshotBuilder().EmptyFile()
- },
- func(osPrefix, name string) string {
- // Create a special name for the implementation jar to try and provide some useful information
- // to a developer that attempts to compile against this.
- // TODO(b/175714559): Provide a proper error message in Soong not ninja.
- return filepath.Join(osPrefix, "java_boot_libs", "snapshot", "jars", "are", "invalid", name+jarFileSuffix)
- },
- onlyCopyJarToSnapshot,
- })
-
- // Register java test libraries for use only in module_exports (not sdk).
- 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)
@@ -123,421 +65,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
-
- // 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.
+ // 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 {
@@ -552,18 +191,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
}
@@ -572,24 +200,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
@@ -620,29 +230,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 {
@@ -694,71 +305,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...)
@@ -776,174 +323,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
-
- 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
-}
-
type deps struct {
classpath classpath
java9Classpath classpath
@@ -974,265 +353,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
}
@@ -1287,818 +412,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)
//
@@ -2146,6 +459,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
@@ -2462,7 +783,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)
@@ -2643,12 +973,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)
//
@@ -2712,9 +1053,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...)
@@ -2769,8 +1111,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
@@ -2819,30 +1167,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 {
@@ -2873,11 +1219,17 @@
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
}
@@ -2902,18 +1254,18 @@
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))...)
}
}
@@ -2946,14 +1298,16 @@
// Get the path of the dex implementation jar from the `deapexer` module.
di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
- j.dexJarFile = di.PrebuiltExportPath(j.BaseModuleName(), ".dexjar")
- if j.dexJarFile == nil {
+ 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, sdkContext(j))
+ sdkDep := decodeSdkDep(ctx, android.SdkContext(j))
if sdkDep.invalidVersion {
ctx.AddMissingDependencies(sdkDep.bootclasspath)
ctx.AddMissingDependencies(sdkDep.java9Classpath)
@@ -2971,22 +1325,26 @@
}
j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
- var dexOutputFile android.ModuleOutPath
- dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
+ var dexOutputFile android.OutputPath
+ dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), 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,
+ dexOutputFile = j.hiddenAPIExtractAndEncode(ctx, 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) {
@@ -3000,8 +1358,6 @@
var _ android.OutputFileProducer = (*Import)(nil)
-var _ Dependency = (*Import)(nil)
-
func (j *Import) HeaderJars() android.Paths {
if j.combinedClasspathFile == nil {
return nil
@@ -3009,17 +1365,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
@@ -3035,22 +1380,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
@@ -3061,7 +1394,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
}
@@ -3156,8 +1502,7 @@
properties DexImportProperties
- dexJarFile android.Path
- maybeStrippedDexJarFile android.Path
+ dexJarFile android.Path
dexpreopter
@@ -3244,8 +1589,6 @@
j.dexpreopt(ctx, dexOutputFile)
- j.maybeStrippedDexJarFile = dexOutputFile
-
if apexInfo.IsForPlatform() {
ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
j.Stem()+".jar", dexOutputFile)
@@ -3320,16 +1663,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{},
@@ -3347,6 +1686,7 @@
&android.ApexProperties{},
&RuntimeResourceOverlayProperties{},
&LintProperties{},
+ &appTestHelperAppProperties{},
)
android.InitDefaultsModule(module)
@@ -3378,16 +1718,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) {
diff --git a/java/java_test.go b/java/java_test.go
index 73734fe..4fc3526 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -16,7 +16,6 @@
import (
"fmt"
- "io/ioutil"
"os"
"path/filepath"
"reflect"
@@ -30,140 +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()
-
- return TestConfig(buildDir, env, bp, fs)
-}
-
-func testContext(config android.Config) *android.TestContext {
-
- ctx := android.NewTestArchContext(config)
- RegisterRequiredBuildComponentsForTest(ctx)
- ctx.RegisterModuleType("java_plugin", PluginFactory)
- ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
- ctx.RegisterModuleType("python_binary_host", python.PythonBinaryHostFactory)
- 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 {
@@ -280,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)
@@ -427,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)
}
}
@@ -537,13 +500,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 "+
@@ -552,7 +518,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 "+
@@ -640,11 +606,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")
}
@@ -673,7 +637,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) {
@@ -685,7 +649,7 @@
}
func TestJavaSdkLibraryImport(t *testing.T) {
- ctx, _ := testJava(t, `
+ result := prepareForJavaTest.RunTestWithBp(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -723,16 +687,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`,
@@ -741,7 +703,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"],
@@ -760,7 +726,7 @@
}
`)
- CheckModuleDependencies(t, ctx, "sdklib", "android_common", []string{
+ CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
`dex2oatd`,
`prebuilt_sdklib`,
`sdklib.impl`,
@@ -769,7 +735,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
@@ -780,7 +746,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"],
@@ -800,7 +770,7 @@
}
`)
- CheckModuleDependencies(t, ctx, "sdklib", "android_common", []string{
+ CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
`dex2oatd`,
`prebuilt_sdklib`,
`sdklib.impl`,
@@ -809,7 +779,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`,
@@ -840,7 +810,7 @@
allowList []string
}
- createTestConfig := func(info testConfigInfo) android.Config {
+ createPreparer := func(info testConfigInfo) android.FixturePreparer {
bpFileTemplate := `
java_library {
name: "foo",
@@ -863,58 +833,75 @@
info.libraryType,
partitionToBpOption(info.toPartition))
- config := testConfig(nil, bpFile, nil)
- configVariables := config.TestProductVariables
+ 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
+ }),
+ )
+ }
- 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
+ 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)
+ }
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
+ createPreparer(info),
+ ).
+ ExtendWithErrorHandler(errorHandler).
+ RunTest(t)
+ })
}
errorMessage := "is not allowed across the partitions"
- testJavaWithConfig(t, createTestConfig(testConfigInfo{
+ runTest(t, testConfigInfo{
libraryType: "java_library",
fromPartition: "product",
toPartition: "system",
enforceVendorInterface: true,
enforceProductInterface: true,
enforceJavaSdkLibraryCheck: false,
- }))
+ }, "")
- testJavaWithConfig(t, createTestConfig(testConfigInfo{
+ runTest(t, testConfigInfo{
libraryType: "java_library",
fromPartition: "product",
toPartition: "system",
enforceVendorInterface: true,
enforceProductInterface: false,
enforceJavaSdkLibraryCheck: true,
- }))
+ }, "")
- testJavaErrorWithConfig(t, errorMessage, createTestConfig(testConfigInfo{
+ runTest(t, testConfigInfo{
libraryType: "java_library",
fromPartition: "product",
toPartition: "system",
enforceVendorInterface: true,
enforceProductInterface: true,
enforceJavaSdkLibraryCheck: true,
- }))
+ }, errorMessage)
- testJavaErrorWithConfig(t, errorMessage, createTestConfig(testConfigInfo{
+ runTest(t, testConfigInfo{
libraryType: "java_library",
fromPartition: "vendor",
toPartition: "system",
enforceVendorInterface: true,
enforceProductInterface: true,
enforceJavaSdkLibraryCheck: true,
- }))
+ }, errorMessage)
- testJavaWithConfig(t, createTestConfig(testConfigInfo{
+ runTest(t, testConfigInfo{
libraryType: "java_library",
fromPartition: "vendor",
toPartition: "system",
@@ -922,43 +909,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)
- testJavaWithConfig(t, createTestConfig(testConfigInfo{
+ runTest(t, testConfigInfo{
libraryType: "java_sdk_library",
fromPartition: "product",
toPartition: "system",
enforceVendorInterface: true,
enforceProductInterface: true,
enforceJavaSdkLibraryCheck: true,
- }))
+ }, "")
- testJavaWithConfig(t, createTestConfig(testConfigInfo{
+ runTest(t, testConfigInfo{
libraryType: "java_sdk_library",
fromPartition: "vendor",
toPartition: "system",
enforceVendorInterface: true,
enforceProductInterface: true,
enforceJavaSdkLibraryCheck: true,
- }))
+ }, "")
- testJavaWithConfig(t, createTestConfig(testConfigInfo{
+ runTest(t, testConfigInfo{
libraryType: "java_sdk_library",
fromPartition: "vendor",
toPartition: "product",
enforceVendorInterface: true,
enforceProductInterface: true,
enforceJavaSdkLibraryCheck: true,
- }))
+ }, "")
}
func TestDefaults(t *testing.T) {
@@ -1010,7 +997,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)
}
@@ -1209,6 +1196,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 {
@@ -1246,7 +1334,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"],
@@ -1268,30 +1358,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) {
@@ -1303,7 +1383,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) {
@@ -1312,246 +1392,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 {
@@ -1633,7 +1473,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",
@@ -1645,14 +1485,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",
@@ -1663,14 +1501,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: ".",
@@ -1747,68 +1591,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"],
@@ -1828,20 +1656,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"],
@@ -1859,14 +1691,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"],
@@ -1884,7 +1720,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"],
@@ -1902,7 +1744,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"],
@@ -1914,7 +1760,7 @@
}
`)
- CheckModuleDependencies(t, ctx, "sdklib", "android_common", []string{
+ CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
`dex2oatd`,
`sdklib.impl`,
`sdklib.stubs`,
@@ -1924,7 +1770,7 @@
}
func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) {
- testJava(t, `
+ prepareForJavaTest.RunTestWithBp(t, `
java_sdk_library_import {
name: "foo",
public: {
@@ -1957,63 +1803,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"],
@@ -2027,7 +1885,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"],
@@ -2043,7 +1905,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"],
@@ -2059,26 +1925,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"],
@@ -2098,7 +1970,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"],
@@ -2118,8 +1994,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)
}
}
@@ -2187,7 +2063,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 {
@@ -2197,7 +2073,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)
}
}
@@ -2267,78 +2143,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 {
@@ -2436,7 +2248,7 @@
}
func TestDataNativeBinaries(t *testing.T) {
- ctx, config := testJava(t, `
+ ctx, _ := testJava(t, `
java_test_host {
name: "foo",
srcs: ["a.java"],
@@ -2452,10 +2264,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 638740f..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 {
@@ -1059,6 +1064,9 @@
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"
@@ -1143,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
@@ -1165,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.
@@ -1389,6 +1401,8 @@
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.
@@ -1455,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())
@@ -1475,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
}
@@ -1492,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.
@@ -1508,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 {
@@ -1521,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*/)
}
@@ -1558,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
@@ -1671,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
@@ -1772,6 +1790,8 @@
android.ApexModuleBase
android.SdkBase
+ hiddenAPI
+
properties sdkLibraryImportProperties
// Map from api scope to the scope specific property structure.
@@ -1786,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)
@@ -1930,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)))
}
}
}
@@ -1982,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)
@@ -2007,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.
@@ -2019,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.
@@ -2038,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 {
@@ -2238,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 5cc546d..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...)
}
@@ -225,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 0b1e2eb..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,19 +178,60 @@
return fs
}
-// Register build components provided by this package that are needed by tests.
+// 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.
//
-// In particular this must register all the components that are used in the `Android.bp` snippet
-// returned by GatherRequiredDepsForTest()
-func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
+// 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)
+ registerJavaBuildComponents(ctx)
+ registerPlatformBootclasspathBuildComponents(ctx)
RegisterPrebuiltApisBuildComponents(ctx)
RegisterRuntimeResourceOverlayBuildComponents(ctx)
RegisterSdkLibraryBuildComponents(ctx)
@@ -115,10 +239,13 @@
RegisterSystemModulesBuildComponents(ctx)
}
-// Gather the module definitions needed by tests that depend upon code from this package.
+// gatherRequiredDepsForTest gathers the module definitions used by
+// PrepareForTestWithJavaDefaultModules.
//
-// Returns an `Android.bp` snippet that defines the modules that are needed by this package.
-func GatherRequiredDepsForTest() string {
+// 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{
@@ -150,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",
@@ -227,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
index 8032dc5..a983b5b 100644
--- a/licenses/Android.bp
+++ b/licenses/Android.bp
@@ -14,1074 +14,1244 @@
// limitations under the License.
package {
- default_visibility: ["//visibility:public"],
- default_applicable_licenses: ["Android-Apache-2.0"],
+ 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"],
+ name: "Android-Apache-2.0",
+ package_name: "Android",
+ 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-AFL-1.1",
- conditions: ["by_exception_only"],
- url: "https://spdx.org/licenses/AFL-1.1.html",
+ name: "SPDX-license-identifier-0BSD",
+ conditions: ["unencumbered"],
+ url: "https://spdx.org/licenses/0BSD",
}
license_kind {
- name: "SPDX-license-identifier-AFL-1.2",
- conditions: ["by_exception_only"],
- url: "https://spdx.org/licenses/AFL-1.2.html",
+ 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-2.0",
- conditions: ["by_exception_only"],
- url: "https://spdx.org/licenses/AFL-2.0.html",
+ 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.1",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/AFL-2.1.html",
+ 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-3.0",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/AFL-3.0.html",
+ name: "SPDX-license-identifier-AFL-2.1",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/AFL-2.1.html",
}
license_kind {
- name: "SPDX-license-identifier-AGPL",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/AGPL.html",
+ 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-1.0",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/AGPL-1.0.html",
+ 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-only",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/AGPL-1.0-only.html",
+ 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-or-later",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/AGPL-1.0-or-later.html",
+ 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-3.0",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/AGPL-3.0.html",
+ 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-only",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/AGPL-3.0-only.html",
+ 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-or-later",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/AGPL-3.0-or-later.html",
+ 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-Apache",
- conditions: ["notice"],
+ 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-Apache-1.0",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/Apache-1.0.html",
+ name: "SPDX-license-identifier-APSL-1.1",
+ conditions: [
+ "reciprocal",
+ ],
+ url: "https://spdx.org/licenses/APSL-1.1.html",
}
license_kind {
- name: "SPDX-license-identifier-Apache-1.1",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/Apache-1.1.html",
+ 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-2.0",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/Apache-2.0.html",
+ name: "SPDX-license-identifier-Apache",
+ conditions: ["notice"],
}
license_kind {
- name: "SPDX-license-identifier-Artistic",
- conditions: ["notice"],
+ name: "SPDX-license-identifier-Apache-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Apache-1.0.html",
}
license_kind {
- name: "SPDX-license-identifier-Artistic-1.0",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/Artistic-1.0.html",
+ name: "SPDX-license-identifier-Apache-1.1",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Apache-1.1.html",
}
license_kind {
- name: "SPDX-license-identifier-Artistic-1.0-Perl",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/Artistic-1.0-Perl.html",
+ 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-1.0-cl8",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/Artistic-1.0-cl8.html",
+ name: "SPDX-license-identifier-Artistic",
+ conditions: ["notice"],
}
license_kind {
- name: "SPDX-license-identifier-Artistic-2.0",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/Artistic-2.0.html",
+ name: "SPDX-license-identifier-Artistic-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Artistic-1.0.html",
}
license_kind {
- name: "SPDX-license-identifier-BSD",
- conditions: ["notice"],
+ 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-BSD-1-Clause",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-1-Clause.html",
+ 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-BSD-2-Clause",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-2-Clause.html",
+ 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-2-Clause-FreeBSD",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-2-Clause-FreeBSD.html",
+ name: "SPDX-license-identifier-BSD",
+ conditions: ["notice"],
}
license_kind {
- name: "SPDX-license-identifier-BSD-2-Clause-NetBSD",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-2-Clause-NetBSD.html",
+ 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-Patent",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-2-Clause-Patent.html",
+ 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-3-Clause",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-3-Clause.html",
+ 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-3-Clause-Attribution",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-3-Clause-Attribution.html",
+ 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-3-Clause-Clear",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-3-Clause-Clear.html",
+ 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-LBNL",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-3-Clause-LBNL.html",
+ 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-No-Nuclear-License",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.html",
+ 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-No-Nuclear-License-2014",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.html",
+ 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-No-Nuclear-Warranty",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.html",
+ 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-Open-MPI",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-3-Clause-Open-MPI.html",
+ 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-4-Clause",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-4-Clause.html",
+ 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-4-Clause-UC",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-4-Clause-UC.html",
+ 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-Protection",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-Protection.html",
+ 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-Source-Code",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSD-Source-Code.html",
+ name: "SPDX-license-identifier-BSD-4-Clause",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-4-Clause.html",
}
license_kind {
- name: "SPDX-license-identifier-BSL-1.0",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/BSL-1.0.html",
+ 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-Beerware",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/Beerware.html",
+ name: "SPDX-license-identifier-BSD-Protection",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-Protection.html",
}
license_kind {
- name: "SPDX-license-identifier-CC-BY",
- conditions: ["notice"],
+ name: "SPDX-license-identifier-BSD-Source-Code",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-Source-Code.html",
}
license_kind {
- name: "SPDX-license-identifier-CC-BY-1.0",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/CC-BY-1.0.html",
+ name: "SPDX-license-identifier-BSL-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSL-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",
+ name: "SPDX-license-identifier-Beerware",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Beerware.html",
}
license_kind {
- name: "SPDX-license-identifier-CC-BY-2.5",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/CC-BY-2.5.html",
+ name: "SPDX-license-identifier-CC-BY",
+ conditions: ["notice"],
}
license_kind {
- name: "SPDX-license-identifier-CC-BY-3.0",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/CC-BY-3.0.html",
+ 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-4.0",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/CC-BY-4.0.html",
+ 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-NC",
- conditions: ["by_exception_only", "not_allowed"],
+ 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-NC-1.0",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/CC-BY-NC-1.0.html",
+ 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-NC-2.0",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/CC-BY-NC-2.0.html",
+ 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-2.5",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/CC-BY-NC-2.5.html",
+ name: "SPDX-license-identifier-CC-BY-NC",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
}
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",
+ 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-4.0",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/CC-BY-NC-4.0.html",
+ 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-ND-1.0",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/CC-BY-NC-ND-1.0.html",
+ 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-ND-2.0",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/CC-BY-NC-ND-2.0.html",
+ 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-ND-2.5",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/CC-BY-NC-ND-2.5.html",
+ 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-3.0",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/CC-BY-NC-ND-3.0.html",
+ 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-4.0",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/CC-BY-NC-ND-4.0.html",
+ 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-SA-1.0",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/CC-BY-NC-SA-1.0.html",
+ 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-SA-2.0",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/CC-BY-NC-SA-2.0.html",
+ 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-SA-2.5",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/CC-BY-NC-SA-2.5.html",
+ 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-3.0",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/CC-BY-NC-SA-3.0.html",
+ 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-4.0",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/CC-BY-NC-SA-4.0.html",
+ 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-ND",
- conditions: ["restricted"],
+ 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-ND-1.0",
- conditions: ["restricted"],
- url: "https://spdx.org/licenses/CC-BY-ND-1.0.html",
+ 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-ND-2.0",
- conditions: ["restricted"],
- url: "https://spdx.org/licenses/CC-BY-ND-2.0.html",
+ 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-2.5",
- conditions: ["restricted"],
- url: "https://spdx.org/licenses/CC-BY-ND-2.5.html",
+ name: "SPDX-license-identifier-CC-BY-ND",
+ conditions: ["restricted"],
}
license_kind {
- name: "SPDX-license-identifier-CC-BY-ND-3.0",
- conditions: ["restricted"],
- url: "https://spdx.org/licenses/CC-BY-ND-3.0.html",
+ 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-4.0",
- conditions: ["restricted"],
- url: "https://spdx.org/licenses/CC-BY-ND-4.0.html",
+ 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-SA",
- conditions: ["restricted"],
+ 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-SA-1.0",
- conditions: ["restricted"],
- url: "https://spdx.org/licenses/CC-BY-SA-1.0.html",
+ 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-SA-2.0",
- conditions: ["restricted"],
- url: "https://spdx.org/licenses/CC-BY-SA-2.0.html",
+ 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-2.5",
- conditions: ["restricted"],
- url: "https://spdx.org/licenses/CC-BY-SA-2.5.html",
+ name: "SPDX-license-identifier-CC-BY-SA",
+ conditions: ["restricted"],
}
license_kind {
- name: "SPDX-license-identifier-CC-BY-SA-3.0",
- conditions: ["restricted"],
- url: "https://spdx.org/licenses/CC-BY-SA-3.0.html",
+ 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-4.0",
- conditions: ["restricted"],
- url: "https://spdx.org/licenses/CC-BY-SA-4.0.html",
+ 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-ND",
- conditions: ["restricted"],
+ 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-CC0-1.0",
- conditions: ["unencumbered"],
- url: "https://spdx.org/licenses/CC0-1.0.html",
+ 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-CPAL-1.0",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/CPAL-1.0.html",
+ 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-CPL-1.0",
- conditions: ["reciprocal"],
- url: "https://spdx.org/licenses/CPL-1.0.html",
+ 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",
- conditions: ["reciprocal"],
+ name: "SPDX-license-identifier-EPL-2.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/EPL-2.0.html",
}
license_kind {
- name: "SPDX-license-identifier-EPL-1.0",
- conditions: ["reciprocal"],
- url: "https://spdx.org/licenses/EPL-1.0.html",
+ name: "SPDX-license-identifier-EUPL",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
}
license_kind {
- name: "SPDX-license-identifier-EPL-2.0",
- conditions: ["reciprocal"],
- url: "https://spdx.org/licenses/EPL-2.0.html",
+ 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",
- conditions: ["by_exception_only", "not_allowed"],
+ 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.0",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/EUPL-1.0.html",
+ 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-EUPL-1.1",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/EUPL-1.0.html",
+ name: "SPDX-license-identifier-FSFAP",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/FSFAP",
}
license_kind {
- name: "SPDX-license-identifier-EUPL-1.2",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/EUPL-1.0.html",
+ name: "SPDX-license-identifier-FTL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/FTL.html",
}
license_kind {
- name: "SPDX-license-identifier-FTL",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/FTL.html",
+ name: "SPDX-license-identifier-GFDL",
+ conditions: ["by_exception_only"],
}
license_kind {
- name: "SPDX-license-identifier-GPL",
- conditions: ["restricted"],
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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"],
+ 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",
+ 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",
+ 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",
+ 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",
+ name: "SPDX-license-identifier-JSON",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/JSON.html",
}
license_kind {
- name: "SPDX-license-identifier-LGPL",
- conditions: ["restricted"],
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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"],
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ name: "SPDX-license-identifier-MITNFA",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MITNFA.html",
}
license_kind {
- name: "SPDX-license-identifier-MPL",
- conditions: ["reciprocal"],
+ 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",
+ 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",
+ 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",
+ 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",
+ 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",
+ name: "SPDX-license-identifier-MS-PL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MS-PL.html",
}
license_kind {
- name: "SPDX-license-identifier-NCSA",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/NCSA.html",
+ name: "SPDX-license-identifier-MS-RL",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/MS-RL.html",
}
license_kind {
- name: "SPDX-license-identifier-OFL",
- conditions: ["by_exception_only"],
+ name: "SPDX-license-identifier-NCSA",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/NCSA.html",
}
license_kind {
- name: "SPDX-license-identifier-OFL-1.0",
- conditions: ["by_exception_only"],
- url: "https://spdx.org/licenses/OFL-1.0.html",
+ name: "SPDX-license-identifier-OFL",
+ conditions: ["by_exception_only"],
}
license_kind {
- name: "SPDX-license-identifier-OFL-1.0-RFN",
- conditions: ["by_exception_only"],
- url: "https://spdx.org/licenses/OFL-1.0-RFN.html",
+ 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-no-RFN",
- conditions: ["by_exception_only"],
- url: "https://spdx.org/licenses/OFL-1.0-no-RFN.html",
+ 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.1",
- conditions: ["by_exception_only"],
- url: "https://spdx.org/licenses/OFL-1.1.html",
+ 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-RFN",
- conditions: ["by_exception_only"],
- url: "https://spdx.org/licenses/OFL-1.1-RFN.html",
+ 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-no-RFN",
- conditions: ["by_exception_only"],
- url: "https://spdx.org/licenses/OFL-1.1-no-RFN.html",
+ 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-OpenSSL",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/OpenSSL.html",
+ 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-PSF-2.0",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/PSF-2.0.html",
+ name: "SPDX-license-identifier-OpenSSL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/OpenSSL.html",
}
license_kind {
- name: "SPDX-license-identifier-SISSL",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/SISSL.html",
+ 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-1.2",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/SISSL-1.2.html",
+ name: "SPDX-license-identifier-SISSL",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/SISSL.html",
}
license_kind {
- name: "SPDX-license-identifier-SPL-1.0",
- conditions: ["by_exception_only", "reciprocal"],
- url: "https://spdx.org/licenses/SPL-1.0.html",
+ 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-SSPL",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/SSPL.html",
+ 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-UPL-1.0",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/UPL-1.-.html",
+ name: "SPDX-license-identifier-SSPL",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
+ url: "https://spdx.org/licenses/SSPL.html",
}
license_kind {
- name: "SPDX-license-identifier-Unicode-DFS",
- conditions: ["notice"],
+ 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-2015",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/Unicode-DFS-2015.html",
+ name: "SPDX-license-identifier-Unicode-DFS",
+ conditions: ["notice"],
}
license_kind {
- name: "SPDX-license-identifier-Unicode-DFS-2016",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/Unicode-DFS-2016.html",
+ name: "SPDX-license-identifier-Unicode-DFS-2015",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Unicode-DFS-2015.html",
}
license_kind {
- name: "SPDX-license-identifier-Unlicense",
- conditions: ["unencumbered"],
- url: "https://spdx.org/licenses/Unlicense.html",
+ name: "SPDX-license-identifier-Unicode-DFS-2016",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Unicode-DFS-2016.html",
}
license_kind {
- name: "SPDX-license-identifier-W3C",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/W3C.html",
+ name: "SPDX-license-identifier-Unlicense",
+ conditions: ["unencumbered"],
+ url: "https://spdx.org/licenses/Unlicense.html",
}
license_kind {
- name: "SPDX-license-identifier-W3C-19980720",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/W3C-19980720.html",
+ name: "SPDX-license-identifier-W3C",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/W3C.html",
}
license_kind {
- name: "SPDX-license-identifier-W3C-20150513",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/W3C-20150513.html",
+ name: "SPDX-license-identifier-W3C-19980720",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/W3C-19980720.html",
}
license_kind {
- name: "SPDX-license-identifier-WTFPL",
- conditions: ["by_exception_only", "not_allowed"],
- url: "https://spdx.org/licenses/WTFPL.html",
+ name: "SPDX-license-identifier-W3C-20150513",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/W3C-20150513.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",
+ name: "SPDX-license-identifier-WTFPL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/WTFPL.html",
}
license_kind {
- name: "SPDX-license-identifier-Xnet",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/Xnet.html",
+ 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-ZPL",
- conditions: ["notice"],
+ name: "SPDX-license-identifier-Xnet",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Xnet.html",
}
license_kind {
- name: "SPDX-license-identifier-ZPL-1.1",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/ZPL-1.1.html",
+ name: "SPDX-license-identifier-ZPL",
+ conditions: ["notice"],
}
license_kind {
- name: "SPDX-license-identifier-ZPL-2.0",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/ZPL-2.0.html",
+ 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.1",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/ZPL-2.1.html",
+ name: "SPDX-license-identifier-ZPL-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ZPL-2.0.html",
}
license_kind {
- name: "SPDX-license-identifier-Zend-2.0",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/Zend-2.0.html",
+ name: "SPDX-license-identifier-ZPL-2.1",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ZPL-2.1.html",
}
license_kind {
- name: "SPDX-license-identifier-Zlib",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/Zlib.html",
+ name: "SPDX-license-identifier-Zend-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Zend-2.0.html",
}
license_kind {
- name: "SPDX-license-identifier-libtiff",
- conditions: ["notice"],
- url: "https://spdx.org/licenses/libtiff.html",
+ 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"],
+ name: "legacy_unknown",
+ conditions: ["by_exception_only"],
}
license_kind {
- name: "legacy_unencumbered",
- conditions: ["unencumbered"],
+ name: "legacy_unencumbered",
+ conditions: ["unencumbered"],
}
license_kind {
- name: "legacy_permissive",
- conditions: ["permissive"],
+ name: "legacy_permissive",
+ conditions: ["permissive"],
}
license_kind {
- name: "legacy_notice",
- conditions: ["notice"],
+ name: "legacy_notice",
+ conditions: ["notice"],
}
license_kind {
- name: "legacy_reciprocal",
- conditions: ["reciprocal"],
+ name: "legacy_reciprocal",
+ conditions: ["reciprocal"],
}
license_kind {
- name: "legacy_restricted",
- conditions: ["restricted"],
+ name: "legacy_restricted",
+ conditions: ["restricted"],
}
license_kind {
- name: "legacy_by_exception_only",
- conditions: ["by_exception_only"],
+ name: "legacy_by_exception_only",
+ conditions: ["by_exception_only"],
}
license_kind {
- name: "legacy_not_allowed",
- conditions: ["by_exception_only", "not_allowed"],
+ name: "legacy_not_a_contribution",
+ conditions: [
+ "by_exception_only",
+ "not_allowed",
+ ],
}
license_kind {
- name: "legacy_proprietary",
- conditions: ["by_exception_only", "not_allowed", "proprietary"],
+ 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/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/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 ca07d07..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
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 56d660e..bcc26b8 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -29,7 +29,7 @@
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 != "" {
@@ -260,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 baeab69..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)
}
@@ -195,30 +195,8 @@
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 bcea6cc..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,32 +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")
@@ -274,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
}
@@ -295,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)
}
@@ -304,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 08ec877..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.49.0"
+ RustDefaultVersion = "1.51.0"
RustDefaultBase = "prebuilts/rust/"
DefaultEdition = "2018"
Stdlibs = []string{
@@ -47,9 +47,14 @@
"-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
@@ -58,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 5e55e22..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,9 +201,6 @@
}
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 Bool(mod.VendorProperties.Product_available) {
mctx.PropertyErrorf("product_available",
@@ -110,44 +212,19 @@
mctx.PropertyErrorf("double_loadable",
"Rust modules do not yet support double loading")
}
-
- coreVariantNeeded := true
- var vendorVariants []string
-
- if Bool(mod.VendorProperties.Vendor_available) {
- 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 8f0a50c..052fb3a 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...)
}
@@ -606,9 +597,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":
@@ -647,14 +638,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 83add87..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,11 +115,15 @@
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
}
@@ -115,17 +137,14 @@
mod.Properties.PreventInstall = true
}
-// 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) SetHideFromMake() {
mod.Properties.HideFromMake = true
}
func (mod *Module) SanitizePropDefined() bool {
- return false
+ // 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 {
@@ -148,8 +167,8 @@
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
}
@@ -213,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 {
@@ -237,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
}
@@ -254,14 +297,15 @@
}
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
}
@@ -273,10 +317,15 @@
SharedLibDeps android.Paths
StaticLibs android.Paths
ProcMacros RustLibraries
- linkDirs []string
- depFlags []string
- linkObjects []string
- //ReexportedDeps android.Paths
+
+ // 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
+ linkObjects []string
// Used by bindgen modules which call clang
depClangFlags []string
@@ -284,8 +333,6 @@
depGeneratedHeaders android.Paths
depSystemIncludePaths android.Paths
- coverageFiles android.Paths
-
CrtBegin android.OptionalPath
CrtEnd android.OptionalPath
@@ -302,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
@@ -319,16 +370,16 @@
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
}
@@ -337,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,
})
@@ -409,6 +455,7 @@
module.AddProperties(
&BaseProperties{},
&cc.VendorProperties{},
+ &BenchmarkProperties{},
&BindgenProperties{},
&BaseCompilerProperties{},
&BinaryCompilerProperties{},
@@ -420,6 +467,7 @@
&cc.CoverageProperties{},
&cc.RustBindgenClangProperties{},
&ClippyProperties{},
+ &SanitizeProperties{},
)
android.InitDefaultsModule(module)
@@ -502,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()))
}
@@ -527,7 +570,7 @@
return false
}
- return mod.outputFile.Valid() && !mod.Properties.PreventInstall
+ return mod.OutputFile().Valid() && !mod.Properties.PreventInstall
}
var _ cc.LinkableInterface = (*Module)(nil)
@@ -548,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)
@@ -566,6 +612,7 @@
module := newBaseModule(hod, multilib)
module.coverage = &coverage{}
module.clippy = &clippy{}
+ module.sanitize = &sanitize{}
return module
}
@@ -658,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() {
@@ -680,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
@@ -698,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) {
@@ -723,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
}
@@ -763,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
@@ -776,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 {
@@ -799,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:
@@ -810,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.
@@ -853,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())
@@ -867,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)
@@ -887,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())
@@ -906,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)
@@ -937,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
@@ -1060,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...)
@@ -1203,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 ea62af4..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,14 +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.
@@ -61,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
old mode 100644
new mode 100755
index e0da602..4abaaba
--- a/scripts/gen_ndk_backedby_apex.sh
+++ b/scripts/gen_ndk_backedby_apex.sh
@@ -23,33 +23,50 @@
echo "**************************** Usage Instructions ****************************"
echo "This script is used to generate the Mainline modules backed-by NDK symbols."
echo ""
- echo "To run this script use: ./ndk_backedby_module.sh \$BINARY_IMAGE_DIRECTORY \$OUTPUT_FILE_PATH \$NDK_LIB_NAME_LIST"
- echo "For example: If all the module image files that you would like to run is under directory '/myModule' and output write to /backedby.txt then the command would be:"
- echo "./ndk_usedby_module.sh /myModule /backedby.txt /ndkLibList.txt"
+ 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() {
- dir="$1"
- [[ ! -e "$2" ]] && echo "" >> "$2"
+ 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
- find "$dir" -type f -name "$soFileName" -exec echo "$soFileName" >> "$2" \;
+ if contains "$soFileName" "$@"; then
+ echo "$soFileName" >> "$out"
+ fi
fi
- done < "$3"
+ done < "$ndk_list"
}
if [[ "$1" == "help" ]]
then
printHelp
-elif [[ "$#" -ne 3 ]]
+elif [[ "$#" -lt 2 ]]
then
- echo "Wrong argument length. Expecting 3 argument representing image file directory, output path, path to ndk library list."
+ 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
- [[ -e "$2" ]] && rm "$2"
- genBackedByList "$1" "$2" "$3"
+ 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/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/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..17ddf17 100644
--- a/sdk/exports_test.go
+++ b/sdk/exports_test.go
@@ -42,8 +42,19 @@
"package/Android.bp": []byte(packageBp),
})
- result.CheckSnapshot("myexports", "package",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "myexports", "package",
+ checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "myjavalib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ jars: ["java/myjavalib.jar"],
+}
+`),
+ checkVersionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
@@ -54,18 +65,11 @@
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_libs: ["myexports_myjavalib@current"],
}
-`))
+`),
+ )
}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 488afd8..54916d8 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,8 +178,19 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "myjavalib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ jars: ["java/myjavalib.jar"],
+}
+`),
+ checkVersionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
@@ -265,20 +201,11 @@
jars: ["java/myjavalib.jar"],
}
-java_import {
- name: "myjavalib",
- prefer: false,
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/myjavalib.jar"],
-}
-
sdk_snapshot {
name: "mysdk@current",
visibility: ["//visibility:public"],
java_header_libs: ["mysdk_myjavalib@current"],
}
-
`),
checkAllCopyRules(`
.intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/myjavalib.jar
@@ -288,7 +215,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,23 +240,26 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
+ name: "myjavalib",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
jars: ["java/myjavalib.jar"],
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
java_import {
- name: "myjavalib",
- prefer: false,
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
device_supported: false,
@@ -350,7 +283,7 @@
}
func TestDeviceAndHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
sdk {
name: "mysdk",
host_supported: true,
@@ -367,13 +300,13 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
+ name: "myjavalib",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
host_supported: true,
@@ -386,10 +319,13 @@
},
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
java_import {
- name: "myjavalib",
- prefer: false,
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
host_supported: true,
@@ -418,7 +354,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,8 +378,19 @@
}
`)
- result.CheckSnapshot("myexports", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "myexports", "",
+ checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "myjavalib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ jars: ["java/myjavalib.jar"],
+}
+`),
+ checkVersionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
@@ -450,20 +401,11 @@
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_libs: ["myexports_myjavalib@current"],
}
-
`),
checkAllCopyRules(`
.intermediates/myjavalib/android_common/withres/myjavalib.jar -> java/myjavalib.jar
@@ -473,7 +415,11 @@
}
func TestSnapshotWithJavaBootLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ android.FixtureAddFile("aidl", nil),
+ android.FixtureAddFile("resource.txt", nil),
+ ).RunTestWithBp(t, `
module_exports {
name: "myexports",
java_boot_libs: ["myjavalib"],
@@ -494,8 +440,19 @@
}
`)
- result.CheckSnapshot("myexports", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "myexports", "",
+ checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "myjavalib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ jars: ["java/myjavalib.jar"],
+}
+`),
+ checkVersionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
@@ -503,15 +460,7 @@
sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
- jars: ["java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar"],
-}
-
-java_import {
- name: "myjavalib",
- prefer: false,
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar"],
+ jars: ["java/myjavalib.jar"],
}
module_exports_snapshot {
@@ -519,16 +468,18 @@
visibility: ["//visibility:public"],
java_boot_libs: ["myexports_myjavalib@current"],
}
-
`),
checkAllCopyRules(`
-.intermediates/myexports/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar
+.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,
@@ -550,23 +501,26 @@
}
`)
- result.CheckSnapshot("myexports", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "myexports", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
- name: "myexports_myjavalib@current",
- sdk_member_name: "myjavalib",
+ name: "myjavalib",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
jars: ["java/myjavalib.jar"],
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
java_import {
- name: "myjavalib",
- prefer: false,
+ name: "myexports_myjavalib@current",
+ sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
device_supported: false,
@@ -590,7 +544,7 @@
}
func TestSnapshotWithJavaTest(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
module_exports {
name: "myexports",
java_tests: ["myjavatests"],
@@ -606,22 +560,25 @@
}
`)
- result.CheckSnapshot("myexports", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "myexports", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_test_import {
- name: "myexports_myjavatests@current",
- sdk_member_name: "myjavatests",
+ name: "myjavatests",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
jars: ["java/myjavatests.jar"],
test_config: "java/myjavatests-AndroidTest.xml",
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
java_test_import {
- name: "myjavatests",
- prefer: false,
+ name: "myexports_myjavatests@current",
+ sdk_member_name: "myjavatests",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
jars: ["java/myjavatests.jar"],
@@ -642,7 +599,7 @@
}
func TestHostSnapshotWithJavaTest(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
module_exports {
name: "myexports",
device_supported: false,
@@ -661,13 +618,13 @@
}
`)
- result.CheckSnapshot("myexports", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "myexports", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_test_import {
- name: "myexports_myjavatests@current",
- sdk_member_name: "myjavatests",
+ name: "myjavatests",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
device_supported: false,
@@ -675,10 +632,13 @@
jars: ["java/myjavatests.jar"],
test_config: "java/myjavatests-AndroidTest.xml",
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
java_test_import {
- name: "myjavatests",
- prefer: false,
+ name: "myexports_myjavatests@current",
+ sdk_member_name: "myjavatests",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
device_supported: false,
@@ -703,7 +663,7 @@
}
func TestSnapshotWithJavaSystemModules(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_header_libs: ["exported-system-module"],
@@ -730,21 +690,42 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
- name: "mysdk_exported-system-module@current",
- sdk_member_name: "exported-system-module",
+ name: "exported-system-module",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
jars: ["java/exported-system-module.jar"],
}
java_import {
- name: "exported-system-module",
+ name: "mysdk_system-module",
prefer: false,
+ visibility: ["//visibility:private"],
+ apex_available: ["//apex_available:platform"],
+ jars: ["java/system-module.jar"],
+}
+
+java_system_modules_import {
+ name: "my-system-modules",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ libs: [
+ "mysdk_system-module",
+ "exported-system-module",
+ ],
+}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "mysdk_exported-system-module@current",
+ sdk_member_name: "exported-system-module",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
jars: ["java/exported-system-module.jar"],
@@ -758,14 +739,6 @@
jars: ["java/system-module.jar"],
}
-java_import {
- name: "mysdk_system-module",
- prefer: false,
- visibility: ["//visibility:private"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/system-module.jar"],
-}
-
java_system_modules_import {
name: "mysdk_my-system-modules@current",
sdk_member_name: "my-system-modules",
@@ -776,16 +749,6 @@
],
}
-java_system_modules_import {
- name: "my-system-modules",
- prefer: false,
- visibility: ["//visibility:public"],
- libs: [
- "mysdk_system-module",
- "exported-system-module",
- ],
-}
-
sdk_snapshot {
name: "mysdk@current",
visibility: ["//visibility:public"],
@@ -801,7 +764,7 @@
}
func TestHostSnapshotWithJavaSystemModules(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
sdk {
name: "mysdk",
device_supported: false,
@@ -826,13 +789,13 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
- name: "mysdk_system-module@current",
- sdk_member_name: "system-module",
+ name: "mysdk_system-module",
+ prefer: false,
visibility: ["//visibility:private"],
apex_available: ["//apex_available:platform"],
device_supported: false,
@@ -840,9 +803,21 @@
jars: ["java/system-module.jar"],
}
-java_import {
- name: "mysdk_system-module",
+java_system_modules_import {
+ name: "my-system-modules",
prefer: false,
+ visibility: ["//visibility:public"],
+ device_supported: false,
+ host_supported: true,
+ libs: ["mysdk_system-module"],
+}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "mysdk_system-module@current",
+ sdk_member_name: "system-module",
visibility: ["//visibility:private"],
apex_available: ["//apex_available:platform"],
device_supported: false,
@@ -859,15 +834,6 @@
libs: ["mysdk_system-module@current"],
}
-java_system_modules_import {
- name: "my-system-modules",
- prefer: false,
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- libs: ["mysdk_system-module"],
-}
-
sdk_snapshot {
name: "mysdk@current",
visibility: ["//visibility:public"],
@@ -881,7 +847,7 @@
}
func TestDeviceAndHostSnapshotWithOsSpecificMembers(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
module_exports {
name: "myexports",
host_supported: true,
@@ -917,13 +883,13 @@
}
`)
- result.CheckSnapshot("myexports", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "myexports", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_import {
- name: "myexports_hostjavalib@current",
- sdk_member_name: "hostjavalib",
+ name: "hostjavalib",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
device_supported: false,
@@ -932,10 +898,37 @@
}
java_import {
- name: "hostjavalib",
+ name: "androidjavalib",
prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
+ jars: ["java/androidjavalib.jar"],
+}
+
+java_import {
+ name: "myjavalib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ host_supported: true,
+ target: {
+ android: {
+ jars: ["java/android/myjavalib.jar"],
+ },
+ linux_glibc: {
+ jars: ["java/linux_glibc/myjavalib.jar"],
+ },
+ },
+}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "myexports_hostjavalib@current",
+ sdk_member_name: "hostjavalib",
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
jars: ["java/hostjavalib.jar"],
@@ -950,14 +943,6 @@
}
java_import {
- name: "androidjavalib",
- prefer: false,
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/androidjavalib.jar"],
-}
-
-java_import {
name: "myexports_myjavalib@current",
sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
@@ -973,22 +958,6 @@
},
}
-java_import {
- name: "myjavalib",
- prefer: false,
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- host_supported: true,
- target: {
- android: {
- jars: ["java/android/myjavalib.jar"],
- },
- linux_glibc: {
- jars: ["java/linux_glibc/myjavalib.jar"],
- },
- },
-}
-
module_exports_snapshot {
name: "myexports@current",
visibility: ["//visibility:public"],
@@ -1014,7 +983,7 @@
}
func TestSnapshotWithJavaSdkLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1031,13 +1000,13 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
+ name: "myjavalib",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:anyapex"],
shared_library: false,
@@ -1063,10 +1032,13 @@
sdk_version: "test_current",
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
- name: "myjavalib",
- prefer: false,
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:anyapex"],
shared_library: false,
@@ -1101,14 +1073,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",
@@ -1118,7 +1090,7 @@
}
func TestSnapshotWithJavaSdkLibrary_SdkVersion_None(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1132,13 +1104,13 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
+ name: "myjavalib",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
shared_library: true,
@@ -1150,10 +1122,13 @@
sdk_version: "none",
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
- name: "myjavalib",
- prefer: false,
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
shared_library: true,
@@ -1174,8 +1149,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",
@@ -1184,7 +1159,7 @@
}
func TestSnapshotWithJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1201,13 +1176,13 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
+ name: "myjavalib",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
shared_library: true,
@@ -1219,10 +1194,13 @@
sdk_version: "module_current",
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
- name: "myjavalib",
- prefer: false,
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
shared_library: true,
@@ -1243,8 +1221,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",
@@ -1253,7 +1231,7 @@
}
func TestSnapshotWithJavaSdkLibrary_ApiScopes(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1273,13 +1251,13 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
+ name: "myjavalib",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:anyapex"],
shared_library: true,
@@ -1298,10 +1276,13 @@
sdk_version: "system_current",
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
- name: "myjavalib",
- prefer: false,
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:anyapex"],
shared_library: true,
@@ -1329,11 +1310,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",
@@ -1343,7 +1324,7 @@
}
func TestSnapshotWithJavaSdkLibrary_ModuleLib(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1366,13 +1347,13 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
+ name: "myjavalib",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:anyapex"],
shared_library: true,
@@ -1398,10 +1379,13 @@
sdk_version: "module_current",
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
- name: "myjavalib",
- prefer: false,
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:anyapex"],
shared_library: true,
@@ -1436,14 +1420,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",
@@ -1454,7 +1438,7 @@
}
func TestSnapshotWithJavaSdkLibrary_SystemServer(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1474,13 +1458,13 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
+ name: "myjavalib",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:anyapex"],
shared_library: true,
@@ -1499,10 +1483,13 @@
sdk_version: "system_server_current",
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
- name: "myjavalib",
- prefer: false,
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:anyapex"],
shared_library: true,
@@ -1530,11 +1517,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",
@@ -1544,7 +1531,7 @@
}
func TestSnapshotWithJavaSdkLibrary_NamingScheme(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1562,13 +1549,13 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
+ name: "myjavalib",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:anyapex"],
naming_scheme: "default",
@@ -1581,10 +1568,13 @@
sdk_version: "current",
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
- name: "myjavalib",
- prefer: false,
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:anyapex"],
naming_scheme: "default",
@@ -1606,8 +1596,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",
@@ -1616,7 +1606,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"],
@@ -1638,13 +1631,13 @@
}
`)
- result.CheckSnapshot("mysdk", "",
- checkAndroidBpContents(`
+ CheckSnapshot(t, result, "mysdk", "",
+ checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
- name: "mysdk_myjavalib@current",
- sdk_member_name: "myjavalib",
+ name: "myjavalib",
+ prefer: false,
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
shared_library: true,
@@ -1657,10 +1650,13 @@
sdk_version: "current",
},
}
+`),
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
java_sdk_library_import {
- name: "myjavalib",
- prefer: false,
+ name: "mysdk_myjavalib@current",
+ sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
apex_available: ["//apex_available:platform"],
shared_library: true,
@@ -1682,8 +1678,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 1ac873b..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,127 +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.RegisterRequiredBuildComponentsForTest(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) {
@@ -181,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()
@@ -287,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)
}
@@ -306,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 {
@@ -339,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)
@@ -360,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)
}
}
@@ -372,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
@@ -430,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 b5bc9f4..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
@@ -653,9 +677,6 @@
filesToZip android.Paths
zipsToMerge android.Paths
- // The path to an empty file.
- emptyFile android.WritablePath
-
prebuiltModules map[string]*bpModule
prebuiltOrder []*bpModule
@@ -706,19 +727,6 @@
s.zipsToMerge = append(s.zipsToMerge, tmpZipPath)
}
-func (s *snapshotBuilder) EmptyFile() android.Path {
- if s.emptyFile == nil {
- ctx := s.ctx
- s.emptyFile = android.PathForModuleOut(ctx, "empty")
- s.ctx.Build(pctx, android.BuildParams{
- Rule: android.Touch,
- Output: s.emptyFile,
- })
- }
-
- return s.emptyFile
-}
-
func (s *snapshotBuilder) AddPrebuiltModule(member android.SdkMember, moduleType string) android.BpModule {
name := member.Name()
if s.prebuiltModules[name] != nil {
@@ -1342,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 18749b5..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...),
@@ -388,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...)
@@ -420,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
@@ -460,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 2ccce15..f1c2d0d 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -37,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 {
@@ -68,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.
@@ -103,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 "":
@@ -157,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 (
@@ -187,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 {
@@ -202,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 {
@@ -221,10 +228,6 @@
return m.ModuleBase.Name()
}
-func (m *syspropLibrary) HasPublicStub() bool {
- return proptools.Bool(m.properties.Public_stub)
-}
-
func (m *syspropLibrary) CurrentSyspropApiFile() android.OptionalPath {
return m.currentApiFile
}
@@ -399,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) {
@@ -490,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"),
@@ -537,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 5cb9e64..6d35f9c 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -15,82 +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.RegisterRequiredBuildComponentsForTest(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,
@@ -134,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"],
@@ -257,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{
@@ -324,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
@@ -381,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
@@ -392,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..98e3b66
--- /dev/null
+++ b/tests/bootstrap_test.sh
@@ -0,0 +1,585 @@
+#!/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
+
+ # This test needs to start from a clean build, but setup creates an
+ # initialized tree that has already been built once. Clear the out
+ # directory to start from scratch.
+ rm -rf out
+
+ 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 a5986b2..6ba497c 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -124,8 +124,7 @@
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 15da1bc..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
@@ -56,7 +57,6 @@
katiSuffix string
targetDevice string
targetDeviceDir string
- fullBuild bool
// Autodetected
totalRAM uint64
@@ -553,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
@@ -773,6 +775,10 @@
return c.skipKati
}
+func (c *configImpl) SkipNinja() bool {
+ return c.skipNinja
+}
+
func (c *configImpl) SkipConfig() bool {
return c.skipConfig
}
@@ -792,14 +798,6 @@
c.targetDevice = device
}
-func (c *configImpl) FullBuild() bool {
- return c.fullBuild
-}
-
-func (c *configImpl) SetFullBuild(fullBuild bool) {
- c.fullBuild = fullBuild
-}
-
func (c *configImpl) TargetBuildVariant() string {
if v, ok := c.environ.Get("TARGET_BUILD_VARIANT"); ok {
return v
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index be6f00a..fe0aca9 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -214,9 +214,6 @@
// So that later Kati runs can find BoardConfig.mk faster
"TARGET_DEVICE_DIR",
- // To decide whether to skip the old installed cleanup step.
- "FULL_BUILD",
-
// Whether --werror_overriding_commands will work
"BUILD_BROKEN_DUP_RULES",
@@ -281,7 +278,6 @@
config.SetNinjaArgs(strings.Fields(makeVars["NINJA_GOALS"]))
config.SetTargetDevice(makeVars["TARGET_DEVICE"])
config.SetTargetDeviceDir(makeVars["TARGET_DEVICE_DIR"])
- config.SetFullBuild(makeVars["FULL_BUILD"] == "true")
config.SetBuildBrokenDupRules(makeVars["BUILD_BROKEN_DUP_RULES"] == "true")
config.SetBuildBrokenUsesNetwork(makeVars["BUILD_BROKEN_USES_NETWORK"] == "true")
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/kati.go b/ui/build/kati.go
index 668b20e..06ec646 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -229,11 +229,7 @@
// Cleanup steps.
cleanCopyHeaders(ctx, config)
- // Skip the old installed file cleanup step for few non-full build goals as we don't create
- // an installed file list for them.
- if config.FullBuild() {
- cleanOldInstalledFiles(ctx, config)
- }
+ cleanOldInstalledFiles(ctx, config)
}
// Clean out obsolete header files on the disk that were *not copied* during the
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;"),