diff options
| -rw-r--r-- | android/apex.go | 18 | ||||
| -rw-r--r-- | android/api_levels.go | 4 | ||||
| -rw-r--r-- | apex/Android.bp | 1 | ||||
| -rw-r--r-- | apex/apex.go | 14 | ||||
| -rw-r--r-- | apex/apex_test.go | 280 | ||||
| -rw-r--r-- | apex/boot_image_test.go | 128 | ||||
| -rw-r--r-- | apex/builder.go | 4 | ||||
| -rw-r--r-- | cc/androidmk.go | 6 | ||||
| -rw-r--r-- | java/Android.bp | 2 | ||||
| -rw-r--r-- | java/boot_image.go | 85 | ||||
| -rw-r--r-- | java/boot_image_test.go | 31 | ||||
| -rw-r--r-- | java/boot_jars.go | 34 | ||||
| -rw-r--r-- | java/dexpreopt_bootjars.go | 28 | ||||
| -rw-r--r-- | java/prebuilt_apis.go | 101 | ||||
| -rw-r--r-- | java/testing.go | 11 | ||||
| -rw-r--r-- | rust/androidmk.go | 7 | ||||
| -rw-r--r-- | rust/rust_test.go | 1 | ||||
| -rw-r--r-- | rust/test.go | 17 | ||||
| -rw-r--r-- | rust/test_test.go | 7 |
19 files changed, 726 insertions, 53 deletions
diff --git a/android/apex.go b/android/apex.go index 31c62e994..b87ff09e2 100644 --- a/android/apex.go +++ b/android/apex.go @@ -111,6 +111,19 @@ func (i ApexInfo) InApex(apex string) bool { 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 { @@ -830,9 +843,8 @@ func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiL 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 } diff --git a/android/api_levels.go b/android/api_levels.go index 08328e16d..1b53f3f2a 100644 --- a/android/api_levels.go +++ b/android/api_levels.go @@ -81,6 +81,10 @@ func (this ApiLevel) IsCurrent() bool { 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 { diff --git a/apex/Android.bp b/apex/Android.bp index 77dde72ac..b6fdcf415 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -25,6 +25,7 @@ bootstrap_go_package { ], testSrcs: [ "apex_test.go", + "boot_image_test.go", "vndk_test.go", ], pluginFor: ["soong_build"], diff --git a/apex/apex.go b/apex/apex.go index 384d6c7a6..ade8fa909 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -854,11 +854,17 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { 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(), + MinSdkVersionStr: minSdkVersion.String(), RequiredSdks: a.RequiredSdks(), Updatable: a.Updatable(), InApexes: []string{mctx.ModuleName()}, @@ -2116,17 +2122,13 @@ func (a *apexBundle) checkMinSdkVersion(ctx android.ModuleContext) { 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 } diff --git a/apex/apex_test.go b/apex/apex_test.go index ae0cbe969..7f5be7ec5 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -2135,6 +2135,74 @@ func TestApexMinSdkVersion_OkayEvenWhenDepIsNewer_IfItSatisfiesApexMinSdkVersion expectLink("mylib", "shared_apex30", "mylib2", "shared_apex30") } +func TestApexMinSdkVersion_WorksWithSdkCodename(t *testing.T) { + withSAsActiveCodeNames := func(fs map[string][]byte, config android.Config) { + config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("S") + config.TestProductVariables.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 := func(fs map[string][]byte, config android.Config) { + config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("S") + config.TestProductVariables.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 "S" 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_S/libbar.so") +} + func TestFilesInSubDir(t *testing.T) { ctx, _ := testApex(t, ` apex { @@ -4330,6 +4398,215 @@ func TestPrebuiltExportDexImplementationJars(t *testing.T) { }) } +func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { + transform := func(config *dexpreopt.GlobalConfig) { + config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"}) + } + + checkBootDexJarPath := func(ctx *android.TestContext, bootDexJarPath string) { + s := ctx.SingletonForTests("dex_bootjars") + foundLibfooJar := false + for _, output := range s.AllOutputs() { + if strings.HasSuffix(output, "/libfoo.jar") { + foundLibfooJar = true + buildRule := s.Output(output) + actual := android.NormalizePathForTesting(buildRule.Input) + if actual != bootDexJarPath { + t.Errorf("Incorrect boot dex jar path '%s', expected '%s'", actual, bootDexJarPath) + } + } + } + if !foundLibfooJar { + t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs") + } + } + + 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"], + } + + java_import { + name: "libfoo", + jars: ["libfoo.jar"], + apex_available: ["myapex"], + } + ` + + ctx := testDexpreoptWithApexes(t, bp, "", transform) + checkBootDexJarPath(ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") + }) + + 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"], + } + + java_import { + name: "libfoo", + jars: ["libfoo.jar"], + apex_available: ["myapex"], + } + + java_library { + name: "libfoo", + srcs: ["foo/bar/MyClass.java"], + 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'", transform) + }) + + 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"], + } + + java_import { + name: "libfoo", + prefer: true, + jars: ["libfoo.jar"], + apex_available: ["myapex"], + } + + java_library { + name: "libfoo", + srcs: ["foo/bar/MyClass.java"], + apex_available: ["myapex"], + } + ` + + ctx := testDexpreoptWithApexes(t, bp, "", transform) + checkBootDexJarPath(ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") + }) + + t.Run("prebuilt with source apex preferred", func(t *testing.T) { + bp := ` + apex { + name: "myapex", + key: "myapex.key", + java_libs: ["libfoo"], + } + + 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"], + } + + java_import { + name: "libfoo", + jars: ["libfoo.jar"], + apex_available: ["myapex"], + } + + java_library { + name: "libfoo", + srcs: ["foo/bar/MyClass.java"], + apex_available: ["myapex"], + } + ` + + ctx := testDexpreoptWithApexes(t, bp, "", transform) + checkBootDexJarPath(ctx, ".intermediates/libfoo/android_common_apex10000/aligned/libfoo.jar") + }) + + t.Run("prebuilt preferred with source apex disabled", func(t *testing.T) { + bp := ` + apex { + name: "myapex", + enabled: false, + key: "myapex.key", + java_libs: ["libfoo"], + } + + 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"], + } + + java_import { + name: "libfoo", + prefer: true, + jars: ["libfoo.jar"], + apex_available: ["myapex"], + } + + java_library { + name: "libfoo", + srcs: ["foo/bar/MyClass.java"], + apex_available: ["myapex"], + } + ` + + ctx := testDexpreoptWithApexes(t, bp, "", transform) + checkBootDexJarPath(ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") + }) +} + func TestApexWithTests(t *testing.T) { ctx, config := testApex(t, ` apex_test { @@ -5934,10 +6211,11 @@ func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreopt "build/make/target/product/security": 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, + "framework/aidl/a.aidl": nil, } cc.GatherRequiredFilesForTest(fs) diff --git a/apex/boot_image_test.go b/apex/boot_image_test.go new file mode 100644 index 000000000..07feb0358 --- /dev/null +++ b/apex/boot_image_test.go @@ -0,0 +1,128 @@ +// 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/dexpreopt" + "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. + +func TestBootImages(t *testing.T) { + ctx, _ := testApex(t, ` + java_sdk_library { + name: "foo", + srcs: ["b.java"], + unsafe_ignore_missing_latest_api: true, + } + + java_library { + name: "bar", + srcs: ["b.java"], + installable: true, + } + + apex { + name: "com.android.art", + key: "com.android.art.key", + java_libs: [ + "baz", + "quuz", + ], + } + + 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"], + } +`, + // Configure some libraries in the art and framework boot images. + withArtBootImageJars("com.android.art:baz", "com.android.art:quuz"), + withFrameworkBootImageJars("platform:foo", "platform:bar"), + withFiles(filesForSdkLibrary), + // Some additional files needed for the art apex. + withFiles(map[string][]byte{ + "com.android.art.avbpubkey": nil, + "com.android.art.pem": nil, + "system/sepolicy/apex/com.android.art-file_contexts": nil, + }), + ) + + // Make sure that the framework-boot-image is using the correct configuration. + checkBootImage(t, ctx, "framework-boot-image", "platform:foo,platform:bar") + + // Make sure that the art-boot-image is using the correct configuration. + checkBootImage(t, ctx, "art-boot-image", "com.android.art:baz,com.android.art:quuz") +} + +func checkBootImage(t *testing.T, ctx *android.TestContext, moduleName string, expectedConfiguredModules string) { + t.Helper() + + bootImage := ctx.ModuleForTests(moduleName, "android_common").Module().(*java.BootImageModule) + + bootImageInfo := ctx.ModuleProvider(bootImage, java.BootImageInfoProvider).(java.BootImageInfo) + modules := bootImageInfo.Modules() + if actual := modules.String(); actual != expectedConfiguredModules { + t.Errorf("invalid modules for %s: expected %q, actual %q", moduleName, expectedConfiguredModules, actual) + } +} + +func modifyDexpreoptConfig(configModifier func(dexpreoptConfig *dexpreopt.GlobalConfig)) func(fs map[string][]byte, config android.Config) { + return func(fs map[string][]byte, 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 := dexpreopt.GlobalConfigForTests(pathCtx) + dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig) + + // Retrieve the existing configuration and modify it. + dexpreoptConfig = dexpreopt.GetGlobalConfig(pathCtx) + configModifier(dexpreoptConfig) + } +} + +func withArtBootImageJars(bootJars ...string) func(fs map[string][]byte, config android.Config) { + return modifyDexpreoptConfig(func(dexpreoptConfig *dexpreopt.GlobalConfig) { + dexpreoptConfig.ArtApexJars = android.CreateTestConfiguredJarList(bootJars) + }) +} + +func withFrameworkBootImageJars(bootJars ...string) func(fs map[string][]byte, config android.Config) { + return modifyDexpreoptConfig(func(dexpreoptConfig *dexpreopt.GlobalConfig) { + dexpreoptConfig.BootJars = android.CreateTestConfiguredJarList(bootJars) + }) +} diff --git a/apex/builder.go b/apex/builder.go index 67314d85b..16ca74cf2 100644 --- a/apex/builder.go +++ b/apex/builder.go @@ -598,7 +598,7 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { // 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 @@ -780,7 +780,7 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { ctx.PropertyErrorf("test_only_force_compression", "not available") return } - compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.properties.Compressible, true) + compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.properties.Compressible, false) if apexType == imageApex && (compressionEnabled || a.testOnlyShouldForceCompression()) { a.isCompressed = true unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+".capex.unsigned") diff --git a/cc/androidmk.go b/cc/androidmk.go index 040aa0b9f..934572844 100644 --- a/cc/androidmk.go +++ b/cc/androidmk.go @@ -168,7 +168,7 @@ func androidMkWriteExtraTestConfigs(extraTestConfigs android.Paths, entries *and } } -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) { @@ -353,7 +353,7 @@ func (benchmark *benchmarkDecorator) AndroidMkEntries(ctx AndroidMkContext, entr 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) { @@ -378,7 +378,7 @@ func (test *testBinary) AndroidMkEntries(ctx AndroidMkContext, entries *android. } }) - androidMkWriteTestData(test.data, ctx, entries) + AndroidMkWriteTestData(test.data, entries) androidMkWriteExtraTestConfigs(test.extraTestConfigs, entries) } diff --git a/java/Android.bp b/java/Android.bp index 9c28968f6..364566a8b 100644 --- a/java/Android.bp +++ b/java/Android.bp @@ -24,6 +24,7 @@ bootstrap_go_package { "app.go", "app_import.go", "app_set.go", + "boot_image.go", "boot_jars.go", "builder.go", "device_host_converter.go", @@ -63,6 +64,7 @@ bootstrap_go_package { "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", diff --git a/java/boot_image.go b/java/boot_image.go new file mode 100644 index 000000000..07ef0d841 --- /dev/null +++ b/java/boot_image.go @@ -0,0 +1,85 @@ +// 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 ( + "strings" + + "android/soong/android" + "github.com/google/blueprint" +) + +func init() { + RegisterBootImageBuildComponents(android.InitRegistrationContext) +} + +func RegisterBootImageBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("boot_image", bootImageFactory) +} + +type bootImageProperties struct { + // The name of the image this represents. + // + // Must be one of "art" or "boot". + Image_name string +} + +type BootImageModule struct { + android.ModuleBase + + properties bootImageProperties +} + +func bootImageFactory() android.Module { + m := &BootImageModule{} + m.AddProperties(&m.properties) + android.InitAndroidArchModule(m, android.HostAndDeviceDefault, android.MultilibCommon) + return m +} + +var BootImageInfoProvider = blueprint.NewProvider(BootImageInfo{}) + +type BootImageInfo struct { + // The image config, internal to this module (and the dex_bootjars singleton). + imageConfig *bootImageConfig +} + +func (i BootImageInfo) Modules() android.ConfiguredJarList { + return i.imageConfig.modules +} + +func (b *BootImageModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + // Nothing to do if skipping the dexpreopt of boot image jars. + if SkipDexpreoptBootJars(ctx) { + return + } + + // Get a map of the image configs that are supported. + imageConfigs := genBootImageConfigs(ctx) + + // Retrieve the config for this image. + imageName := b.properties.Image_name + 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 + } + + // Construct the boot image info from the config. + info := BootImageInfo{imageConfig: imageConfig} + + // Make it available for other modules. + ctx.SetProvider(BootImageInfoProvider, info) +} diff --git a/java/boot_image_test.go b/java/boot_image_test.go new file mode 100644 index 000000000..a29578292 --- /dev/null +++ b/java/boot_image_test.go @@ -0,0 +1,31 @@ +// 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" +) + +// 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. + +func TestUnknownBootImage(t *testing.T) { + testJavaError(t, "image_name: Unknown image name \\\"unknown\\\", expected one of art, boot", ` + boot_image { + name: "unknown-boot-image", + image_name: "unknown", + } +`) +} diff --git a/java/boot_jars.go b/java/boot_jars.go index 823275b1d..ac8107b7c 100644 --- a/java/boot_jars.go +++ b/java/boot_jars.go @@ -49,14 +49,36 @@ func populateMapFromConfiguredJarList(ctx android.SingletonContext, moduleToApex 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 + } + if module.IsReplacedByPrebuilt() { + // A source module that has been replaced by a prebuilt counterpart. + return false + } + if prebuilt, ok := module.(android.PrebuiltInterface); ok { + if p := prebuilt.Prebuilt(); p != nil { + return p.UsePrebuilt() + } + } + return true +} + 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 +91,14 @@ func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) { // 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/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index 004cbbb5a..36d0d3017 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -35,6 +35,13 @@ import ( // // 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: @@ -479,7 +486,7 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul // A platform variant is required but this is for an apex so ignore it. return -1, 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 } @@ -489,7 +496,7 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul switch image.name { case artBootImageName: - if len(apexInfo.InApexes) > 0 && allHavePrefix(apexInfo.InApexes, "com.android.art") { + if apexInfo.InApexByBaseName("com.android.art") || apexInfo.InApexByBaseName("com.android.art.debug") || apexInfo.InApexByBaseName("com.android.art,testing") { // 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 @@ -516,21 +523,17 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul return index, jar.DexJarBuildPath() } -func allHavePrefix(list []string, prefix string) bool { - for _, s := range list { - if s != prefix && !strings.HasPrefix(s, prefix+".") { - return false - } - } - return true -} - // 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()) + ctx.VisitAllModules(func(module android.Module) { + if !isActiveModule(module) { + return + } + 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", @@ -860,6 +863,9 @@ func updatableBcpPackagesRule(ctx android.SingletonContext, image *bootImageConf // 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 { diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go index 0ffbaaa40..1e90149ea 100644 --- a/java/prebuilt_apis.go +++ b/java/prebuilt_apis.go @@ -15,12 +15,14 @@ package java import ( + "fmt" "strconv" "strings" "github.com/google/blueprint/proptools" "android/soong/android" + "android/soong/genrule" ) func init() { @@ -35,6 +37,12 @@ type prebuiltApisProperties struct { // 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,46 @@ func createImport(mctx android.LoadHookContext, module, scope, apiver, path, sdk mctx.CreateModule(ImportFactory, &props) } -func createFilegroup(mctx android.LoadHookContext, module string, scope string, apiver string, path string) { - fgName := module + ".api." + scope + "." + apiver +func createFilegroup(mctx android.LoadHookContext, name string, path string) { filegroupProps := struct { Name *string Srcs []string }{} - filegroupProps.Name = proptools.StringPtr(fgName) + filegroupProps.Name = proptools.StringPtr(name) filegroupProps.Srcs = []string{path} mctx.CreateModule(android.FileGroupFactory, &filegroupProps) } +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 } @@ -181,11 +207,14 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { // Create filegroups for all (<module>, <scope, <version>) triplets, // and a "latest" filegroup variant for each (<module>, <scope>) pair + moduleName := func(module, scope, version string) string { + return module + ".api." + scope + "." + version + } m := make(map[string]latestApiInfo) for _, f := range files { localPath := strings.TrimPrefix(f, mydir) module, apiver, scope := parseApiFilePath(mctx, localPath) - createFilegroup(mctx, module, scope, apiver, localPath) + createFilegroup(mctx, moduleName(module, scope, apiver), localPath) version, err := strconv.Atoi(apiver) if err != nil { @@ -193,20 +222,52 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { return } - 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 + // 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 + } } } + // 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 := moduleName(info.module, info.scope, "latest") + createFilegroup(mctx, name, info.path) + } + + // Create incompatibilities tracking files for all modules, if we have a "next" api. + if nextApiDir := String(p.properties.Next_api_dir); nextApiDir != "" { + files := getPrebuiltFilesInSubdir(mctx, nextApiDir, "api/*incompatibilities.txt") + incompatibilities := make(map[string]bool) + for _, f := range files { + localPath := strings.TrimPrefix(f, mydir) + module, _, scope := parseApiFilePath(mctx, localPath) + + // Figure out which module is referenced by this file. Special case for "android". + referencedModule := strings.TrimSuffix(module, "incompatibilities") + referencedModule = strings.TrimSuffix(referencedModule, "-") + if referencedModule == "" { + referencedModule = "android" + } + + createFilegroup(mctx, moduleName(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, moduleName(m[k].module+"-incompatibilities", m[k].scope, "latest")) + } + } } } diff --git a/java/testing.go b/java/testing.go index 0b1f2d137..31ff47ff4 100644 --- a/java/testing.go +++ b/java/testing.go @@ -107,6 +107,7 @@ func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) { RegisterAppBuildComponents(ctx) RegisterAppImportBuildComponents(ctx) RegisterAppSetBuildComponents(ctx) + RegisterBootImageBuildComponents(ctx) RegisterDexpreoptBootJarsComponents(ctx) RegisterDocsBuildComponents(ctx) RegisterGenRuleBuildComponents(ctx) @@ -218,6 +219,16 @@ func GatherRequiredDepsForTest() string { dex_bootjars { name: "dex_bootjars", } + + boot_image { + name: "art-boot-image", + image_name: "art", + } + + boot_image { + name: "framework-boot-image", + image_name: "boot", + } ` return bp diff --git a/rust/androidmk.go b/rust/androidmk.go index 030772722..1a286f738 100644 --- a/rust/androidmk.go +++ b/rust/androidmk.go @@ -18,6 +18,7 @@ import ( "path/filepath" "android/soong/android" + "android/soong/cc" ) type AndroidMkContext interface { @@ -85,7 +86,8 @@ func (binary *binaryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.Andr } 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...) @@ -95,7 +97,8 @@ func (test *testDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidM 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) + + cc.AndroidMkWriteTestData(test.data, ret) } func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) { diff --git a/rust/rust_test.go b/rust/rust_test.go index fc7f47e08..abc9af9b1 100644 --- a/rust/rust_test.go +++ b/rust/rust_test.go @@ -116,6 +116,7 @@ func (tctx *testRustCtx) useMockedFs() { "foo.proto": nil, "liby.so": nil, "libz.so": nil, + "data.txt": nil, } } diff --git a/rust/test.go b/rust/test.go index 35e04fff6..92b486070 100644 --- a/rust/test.go +++ b/rust/test.go @@ -43,6 +43,10 @@ type TestProperties struct { // 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 +66,12 @@ type testDecorator struct { *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 +99,6 @@ func NewRustTest(hod android.HostOrDeviceSupported) (*Module, *testDecorator) { } module.compiler = test - module.AddProperties(&test.Properties) return module, test } @@ -105,6 +114,12 @@ func (test *testDecorator) install(ctx ModuleContext) { 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() diff --git a/rust/test_test.go b/rust/test_test.go index fea2ad059..892761a07 100644 --- a/rust/test_test.go +++ b/rust/test_test.go @@ -26,6 +26,7 @@ func TestRustTest(t *testing.T) { 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 @@ func TestRustTest(t *testing.T) { 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) { |