diff options
Diffstat (limited to 'java')
| -rw-r--r-- | java/Android.bp | 2 | ||||
| -rw-r--r-- | java/androidmk.go | 4 | ||||
| -rwxr-xr-x | java/app.go | 1 | ||||
| -rw-r--r-- | java/app_import.go | 1 | ||||
| -rw-r--r-- | java/boot_image.go | 136 | ||||
| -rw-r--r-- | java/boot_image_test.go | 31 | ||||
| -rw-r--r-- | java/boot_jars.go | 34 | ||||
| -rw-r--r-- | java/dex.go | 6 | ||||
| -rw-r--r-- | java/dexpreopt.go | 44 | ||||
| -rw-r--r-- | java/dexpreopt_bootjars.go | 44 | ||||
| -rw-r--r-- | java/dexpreopt_test.go | 2 | ||||
| -rw-r--r-- | java/hiddenapi.go | 55 | ||||
| -rw-r--r-- | java/hiddenapi_singleton.go | 61 | ||||
| -rw-r--r-- | java/hiddenapi_singleton_test.go | 10 | ||||
| -rw-r--r-- | java/java.go | 50 | ||||
| -rw-r--r-- | java/prebuilt_apis.go | 101 | ||||
| -rw-r--r-- | java/proto.go | 2 | ||||
| -rw-r--r-- | java/rro.go | 6 | ||||
| -rw-r--r-- | java/testing.go | 1 |
19 files changed, 490 insertions, 101 deletions
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/androidmk.go b/java/androidmk.go index cc454b03d..21f3012a4 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -125,6 +125,10 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { 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) + } }, }, }) diff --git a/java/app.go b/java/app.go index e6c9a2d98..ce89e9bb6 100755 --- a/java/app.go +++ b/java/app.go @@ -455,6 +455,7 @@ func (a *AndroidApp) installPath(ctx android.ModuleContext) android.InstallPath 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)) diff --git a/java/app_import.go b/java/app_import.go index df940f1ff..6f21bfbbf 100644 --- a/java/app_import.go +++ b/java/app_import.go @@ -255,6 +255,7 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext 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) diff --git a/java/boot_image.go b/java/boot_image.go new file mode 100644 index 000000000..0a525b752 --- /dev/null +++ b/java/boot_image.go @@ -0,0 +1,136 @@ +// 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" +) + +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 + android.ApexModuleBase + + properties bootImageProperties +} + +func bootImageFactory() android.Module { + m := &BootImageModule{} + m.AddProperties(&m.properties) + android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon) + android.InitApexModule(m) + return m +} + +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 == dexpreopt.Dex2oatDepTag { + // The dex2oat tool is only needed for building and is not required in the apex. + 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) { + 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) + + // 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/dex.go b/java/dex.go index 055d47983..24600c20f 100644 --- a/java/dex.go +++ b/java/dex.go @@ -263,10 +263,10 @@ func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Fl } func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, minSdkVersion sdkSpec, - classesJar android.Path, jarName string) android.ModuleOutPath { + 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 +329,7 @@ func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, mi }) } 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 b5830c744..29c73c11f 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -30,6 +30,7 @@ type dexpreopter struct { installPath android.InstallPath uncompressedDex bool isSDKLibrary bool + isApp bool isTest bool isPresignedPrebuilt bool @@ -38,6 +39,11 @@ type dexpreopter struct { 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,12 +118,42 @@ func odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPat 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 } @@ -157,8 +193,6 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo // 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,6 +211,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo } } + // Full dexpreopt config, used to create dexpreopt build rules. dexpreoptConfig := &dexpreopt.ModuleConfig{ Name: ctx.ModuleName(), DexLocation: dexLocation, @@ -192,6 +227,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo ProfileBootListing: profileBootListing, EnforceUsesLibraries: d.enforceUsesLibs, + ProvidesUsesLibrary: providesUsesLib, ClassLoaderContexts: d.classLoaderContexts, Archs: archs, diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index 004cbbb5a..2a7eb42dc 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: @@ -385,22 +392,6 @@ type dexpreoptBootJars struct { 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(). @@ -479,7 +470,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 +480,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 +507,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 +847,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/dexpreopt_test.go b/java/dexpreopt_test.go index 5550a4c17..a9e0773b7 100644 --- a/java/dexpreopt_test.go +++ b/java/dexpreopt_test.go @@ -148,7 +148,7 @@ func TestDexpreoptEnabled(t *testing.T) { 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/hiddenapi.go b/java/hiddenapi.go index 71f1e576d..eafbf5df0 100644 --- a/java/hiddenapi.go +++ b/java/hiddenapi.go @@ -28,10 +28,40 @@ var hiddenAPIGenerateCSVRule = pctx.AndroidStaticRule("hiddenAPIGenerateCSV", bl }, "outFlag", "stubAPIFlags") type hiddenAPI struct { - bootDexJarPath android.Path - flagsCSVPath android.Path - indexCSVPath android.Path + // 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. + 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,8 +89,8 @@ type hiddenAPIIntf interface { 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 { +func (h *hiddenAPI) hiddenAPI(ctx android.ModuleContext, name string, primary bool, dexJar android.OutputPath, + implementationJar android.Path, uncompressDex bool) android.OutputPath { if !ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") { // Modules whose names are of the format <x>-hiddenapi provide hiddenapi information @@ -78,17 +108,15 @@ func (h *hiddenAPI) hiddenAPI(ctx android.ModuleContext, name string, primary bo // 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) + // Create ninja rules to generate various CSV files needed by hiddenapi and store the paths + // in the hiddenAPI structure. + h.hiddenAPIGenerateCSV(ctx, implementationJar) // 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") + hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", name+".jar").OutputPath // 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 @@ -106,9 +134,10 @@ func (h *hiddenAPI) hiddenAPI(ctx android.ModuleContext, name string, primary bo return dexJar } -func (h *hiddenAPI) hiddenAPIGenerateCSV(ctx android.ModuleContext, flagsCSV, metadataCSV, indexCSV android.WritablePath, classesJar android.Path) { +func (h *hiddenAPI) hiddenAPIGenerateCSV(ctx android.ModuleContext, classesJar android.Path) { stubFlagsCSV := hiddenAPISingletonPaths(ctx).stubFlags + flagsCSV := android.PathForModuleOut(ctx, "hiddenapi", "flags.csv") ctx.Build(pctx, android.BuildParams{ Rule: hiddenAPIGenerateCSVRule, Description: "hiddenapi flags", @@ -122,6 +151,7 @@ func (h *hiddenAPI) hiddenAPIGenerateCSV(ctx android.ModuleContext, flagsCSV, me }) h.flagsCSVPath = flagsCSV + metadataCSV := android.PathForModuleOut(ctx, "hiddenapi", "metadata.csv") ctx.Build(pctx, android.BuildParams{ Rule: hiddenAPIGenerateCSVRule, Description: "hiddenapi metadata", @@ -135,6 +165,7 @@ func (h *hiddenAPI) hiddenAPIGenerateCSV(ctx android.ModuleContext, flagsCSV, me }) h.metadataCSVPath = metadataCSV + indexCSV := android.PathForModuleOut(ctx, "hiddenapi", "index.csv") rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). BuiltTool("merge_csv"). diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go index 4bd255cbf..ccb874506 100644 --- a/java/hiddenapi_singleton.go +++ b/java/hiddenapi_singleton.go @@ -28,9 +28,64 @@ func init() { } 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 } diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go index 0f9ef5841..27f363e0c 100644 --- a/java/hiddenapi_singleton_test.go +++ b/java/hiddenapi_singleton_test.go @@ -65,7 +65,7 @@ func TestHiddenAPISingleton(t *testing.T) { srcs: ["a.java"], compile_dex: true, } - `, []string{":foo"}, nil) + `, []string{"platform:foo"}, nil) hiddenAPI := ctx.SingletonForTests("hiddenapi") hiddenapiRule := hiddenAPI.Rule("hiddenapi") @@ -82,7 +82,7 @@ func TestHiddenAPISingletonWithPrebuilt(t *testing.T) { jars: ["a.jar"], compile_dex: true, } - `, []string{":foo"}, nil) + `, []string{"platform:foo"}, nil) hiddenAPI := ctx.SingletonForTests("hiddenapi") hiddenapiRule := hiddenAPI.Rule("hiddenapi") @@ -106,7 +106,7 @@ func TestHiddenAPISingletonWithPrebuiltUseSource(t *testing.T) { compile_dex: true, prefer: false, } - `, []string{":foo"}, nil) + `, []string{"platform:foo"}, nil) hiddenAPI := ctx.SingletonForTests("hiddenapi") hiddenapiRule := hiddenAPI.Rule("hiddenapi") @@ -135,7 +135,7 @@ func TestHiddenAPISingletonWithPrebuiltOverrideSource(t *testing.T) { compile_dex: true, prefer: true, } - `, []string{":foo"}, nil) + `, []string{"platform:foo"}, nil) hiddenAPI := ctx.SingletonForTests("hiddenapi") hiddenapiRule := hiddenAPI.Rule("hiddenapi") @@ -236,7 +236,7 @@ func TestHiddenAPISingletonWithPrebuiltCsvFile(t *testing.T) { jars: ["a.jar"], compile_dex: true, } - `, []string{":foo"}, &prebuiltHiddenApiDir) + `, []string{"platform:foo"}, &prebuiltHiddenApiDir) expectedCpInput := prebuiltHiddenApiDir + "/hiddenapi-flags.csv" expectedCpOutput := buildDir + "/hiddenapi/hiddenapi-flags.csv" diff --git a/java/java.go b/java/java.go index 59ec94d5b..91944f6e3 100644 --- a/java/java.go +++ b/java/java.go @@ -1689,35 +1689,44 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { // 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 + 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 { - // 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 + // 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 + outputFile = combinedJar.OutputPath } } else { combinedJar := android.PathForModuleOut(ctx, "combined", jarName) TransformJarsToJar(ctx, combinedJar, "for javac", jars, manifest, false, nil, nil) - outputFile = combinedJar + 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) + jarjarFile := android.PathForModuleOut(ctx, "jarjar", jarName).OutputPath TransformJarJar(ctx, jarjarFile, outputFile, j.expandJarjarRules) outputFile = jarjarFile @@ -1762,7 +1771,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { implementationAndResourcesJar := outputFile if j.resourceJar != nil { jars := android.Paths{j.resourceJar, implementationAndResourcesJar} - combinedJar := android.PathForModuleOut(ctx, "withres", jarName) + combinedJar := android.PathForModuleOut(ctx, "withres", jarName).OutputPath TransformJarsToJar(ctx, combinedJar, "for resources", jars, manifest, false, nil, nil) implementationAndResourcesJar = combinedJar @@ -1788,7 +1797,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags")) } // Dex compilation - var dexOutputFile android.ModuleOutPath + var dexOutputFile android.OutputPath dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName) if ctx.Failed() { return @@ -1807,11 +1816,11 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { // merge dex jar with resources if necessary if j.resourceJar != nil { jars := android.Paths{dexOutputFile, j.resourceJar} - combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName) + 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) + combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName).OutputPath TransformZipAlign(ctx, combinedAlignedJar, combinedJar) dexOutputFile = combinedAlignedJar } else { @@ -1875,7 +1884,7 @@ func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, i jarName += strconv.Itoa(idx) } - classes := android.PathForModuleOut(ctx, "javac", jarName) + classes := android.PathForModuleOut(ctx, "javac", jarName).OutputPath TransformJavaToClasses(ctx, classes, idx, srcFiles, srcJars, flags, extraJarDeps) if ctx.Config().EmitXrefRules() { @@ -1955,12 +1964,12 @@ func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars } func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags, - classesJar android.Path, jarName string) android.ModuleOutPath { + 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) + instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName).OutputPath jacocoInstrumentJar(ctx, instrumentedJar, jacocoReportClassesFile, classesJar, specs) @@ -2374,7 +2383,7 @@ type testProperties struct { // list of files or filegroup modules that provide data that should be installed alongside // the test - Data []string `android:"path"` + 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 @@ -2678,9 +2687,10 @@ func (j *Binary) GenerateAndroidBuildActions(ctx android.ModuleContext) { } 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...) @@ -2937,7 +2947,7 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { } j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex - var dexOutputFile android.ModuleOutPath + var dexOutputFile android.OutputPath dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName) if ctx.Failed() { return 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/proto.go b/java/proto.go index dc5519f41..652a4daec 100644 --- a/java/proto.go +++ b/java/proto.go @@ -82,7 +82,7 @@ func protoDeps(ctx android.BottomUpMutatorContext, p *android.ProtoProperties) { 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") diff --git a/java/rro.go b/java/rro.go index 98cd3793e..aafa88e3b 100644 --- a/java/rro.go +++ b/java/rro.go @@ -55,7 +55,11 @@ type RuntimeResourceOverlayProperties struct { // 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 diff --git a/java/testing.go b/java/testing.go index 0b1f2d137..5fcf84c6b 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) |