diff options
author | Scott Lobdell <slobdell@google.com> | 2021-07-07 17:54:01 +0000 |
---|---|---|
committer | Scott Lobdell <slobdell@google.com> | 2021-07-07 17:54:01 +0000 |
commit | 99f82c6f4a8511ded6de5e2b5e00565fad7f65de (patch) | |
tree | a1ecabe68c443bf3d596fe06417e98cc54dc7470 /java | |
parent | c57cdd121a8d5a0883a689f6d9fbe6fc440e2202 (diff) | |
parent | 5abcf9267173b9e80dd14fca037b0f01cc2dbd3b (diff) |
Merge SP1A.210624.001
Change-Id: I8507f8b65c47a5b425464bbbc8d932a6ca359fd3
Diffstat (limited to 'java')
-rw-r--r-- | java/Android.bp | 1 | ||||
-rw-r--r-- | java/base.go | 4 | ||||
-rw-r--r-- | java/boot_jars.go | 101 | ||||
-rw-r--r-- | java/bootclasspath_fragment.go | 341 | ||||
-rw-r--r-- | java/classpath_element.go | 229 | ||||
-rw-r--r-- | java/dexpreopt_bootjars.go | 15 | ||||
-rw-r--r-- | java/hiddenapi_modular.go | 167 | ||||
-rw-r--r-- | java/hiddenapi_monolithic.go | 37 | ||||
-rw-r--r-- | java/java.go | 29 | ||||
-rw-r--r-- | java/platform_bootclasspath.go | 81 | ||||
-rw-r--r-- | java/platform_bootclasspath_test.go | 44 | ||||
-rw-r--r-- | java/sdk_library.go | 85 | ||||
-rw-r--r-- | java/sdk_library_test.go | 13 | ||||
-rw-r--r-- | java/testing.go | 17 |
14 files changed, 835 insertions, 329 deletions
diff --git a/java/Android.bp b/java/Android.bp index 680f3a17c..59526024a 100644 --- a/java/Android.bp +++ b/java/Android.bp @@ -33,6 +33,7 @@ bootstrap_go_package { "bootclasspath.go", "bootclasspath_fragment.go", "builder.go", + "classpath_element.go", "classpath_fragment.go", "device_host_converter.go", "dex.go", diff --git a/java/base.go b/java/base.go index cf6fafaaa..34514ffc3 100644 --- a/java/base.go +++ b/java/base.go @@ -253,8 +253,8 @@ type embeddableInModuleAndImport struct { EmbeddableSdkLibraryComponent } -func (e *embeddableInModuleAndImport) initModuleAndImport(moduleBase *android.ModuleBase) { - e.initSdkLibraryComponent(moduleBase) +func (e *embeddableInModuleAndImport) initModuleAndImport(module android.Module) { + e.initSdkLibraryComponent(module) } // Module/Import's DepIsInSameApex(...) delegates to this method. diff --git a/java/boot_jars.go b/java/boot_jars.go index 7abda8031..86ebe36b4 100644 --- a/java/boot_jars.go +++ b/java/boot_jars.go @@ -18,37 +18,6 @@ import ( "android/soong/android" ) -func init() { - android.RegisterSingletonType("boot_jars", bootJarsSingletonFactory) -} - -func bootJarsSingletonFactory() android.Singleton { - return &bootJarsSingleton{} -} - -type bootJarsSingleton struct{} - -func populateMapFromConfiguredJarList(ctx android.SingletonContext, moduleToApex map[string]string, list android.ConfiguredJarList, name string) bool { - for i := 0; i < list.Len(); i++ { - module := list.Jar(i) - // Ignore jacocoagent it is only added when instrumenting and so has no impact on - // app compatibility. - if module == "jacocoagent" { - continue - } - apex := list.Apex(i) - if existing, ok := moduleToApex[module]; ok { - ctx.Errorf("Configuration property %q is invalid as it contains multiple references to module (%s) in APEXes (%s and %s)", - module, existing, apex) - return false - } - - moduleToApex[module] = apex - } - - 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. @@ -59,73 +28,17 @@ func isActiveModule(module android.Module) bool { 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. 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") { - return - } - - // Map from module name to the correct apex variant. - nameToApexVariant := make(map[string]android.Module) - - // Scan all the modules looking for the module/apex variants corresponding to the - // boot jars. - ctx.VisitAllModules(func(module android.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.InApexModule(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 { - ctx.Errorf("found multiple variants matching %s:%s: %q and %q", apex, name, existing, module) - } - nameToApexVariant[name] = module - } - } - }) - +// buildRuleForBootJarsPackageCheck generates the build rule to perform the boot jars package +// check. +func buildRuleForBootJarsPackageCheck(ctx android.ModuleContext, bootDexJarByModule bootDexJarByModule) { timestamp := android.PathForOutput(ctx, "boot-jars-package-check/stamp") rule := android.NewRuleBuilder(pctx, ctx) - checkBootJars := rule.Command().BuiltTool("check_boot_jars"). + rule.Command().BuiltTool("check_boot_jars"). Input(ctx.Config().HostToolPath(ctx, "dexdump")). - Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt")) - - // If this is not an unbundled build and missing dependencies are not allowed - // then all the boot jars listed must have been found. - strict := !config.UnbundledBuild() && !config.AllowMissingDependencies() - - // Iterate over the module names on the boot classpath in order - for _, name := range android.SortedStringKeys(moduleToApex) { - if apexVariant, ok := nameToApexVariant[name]; ok { - if dep, ok := apexVariant.(interface{ DexJarBuildPath() android.Path }); ok { - // Add the dex implementation jar for the module to be checked. - checkBootJars.Input(dep.DexJarBuildPath()) - } else { - ctx.Errorf("module %q is of type %q which is not supported as a boot jar", name, ctx.ModuleType(apexVariant)) - } - } else if strict { - ctx.Errorf("boot jars package check failed as it could not find module %q for apex %q", name, moduleToApex[name]) - } - } - - checkBootJars.Text("&& touch").Output(timestamp) + Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt")). + Inputs(bootDexJarByModule.bootDexJarsWithoutCoverage()). + Text("&& touch").Output(timestamp) rule.Build("boot_jars_package_check", "check boot jar packages") // The check-boot-jars phony target depends on the timestamp created if the check succeeds. diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index fc8f5571e..03faf3495 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -81,6 +81,9 @@ func (b bootclasspathFragmentContentDependencyTag) ExportMember() bool { // they were listed in java_libs. func (b bootclasspathFragmentContentDependencyTag) CopyDirectlyInAnyApex() {} +// Contents of bootclasspath fragments require files from prebuilt apex files. +func (b bootclasspathFragmentContentDependencyTag) RequiresFilesFromPrebuiltApex() {} + // The tag used for the dependency between the bootclasspath_fragment module and its contents. var bootclasspathFragmentContentDepTag = bootclasspathFragmentContentDependencyTag{} @@ -88,6 +91,7 @@ var _ android.ExcludeFromVisibilityEnforcementTag = bootclasspathFragmentContent var _ android.ReplaceSourceWithPrebuilt = bootclasspathFragmentContentDepTag var _ android.SdkMemberTypeDependencyTag = bootclasspathFragmentContentDepTag var _ android.CopyDirectlyInAnyApexTag = bootclasspathFragmentContentDepTag +var _ android.RequiresFilesFromPrebuiltApexTag = bootclasspathFragmentContentDepTag func IsBootclasspathFragmentContentDepTag(tag blueprint.DependencyTag) bool { return tag == bootclasspathFragmentContentDepTag @@ -137,14 +141,29 @@ type BootclasspathFragmentModule struct { // commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt // bootclasspath fragment modules. type commonBootclasspathFragment interface { - // produceHiddenAPIAllFlagsFile produces the all-flags.csv and intermediate files. + // produceHiddenAPIOutput produces the all-flags.csv and intermediate files and encodes the flags + // into dex files. + // + // Returns a *HiddenAPIOutput containing the paths for the generated files. Returns nil if the + // module cannot contribute to hidden API processing, e.g. because it is a prebuilt module in a + // versioned sdk. + produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput + + // produceBootImageFiles produces the boot image (i.e. .art, .oat and .vdex) files for each of the + // required android.ArchType values in the returned map. // - // Updates the supplied hiddenAPIInfo with the paths to the generated files set. - produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIFlagOutput + // It must return nil if the boot image files cannot be produced for whatever reason. + produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch } var _ commonBootclasspathFragment = (*BootclasspathFragmentModule)(nil) +// bootImageFilesByArch is a map from android.ArchType to the paths to the boot image files. +// +// The paths include the .art, .oat and .vdex files, one for each of the modules from which the boot +// image is created. +type bootImageFilesByArch map[android.ArchType]android.Paths + func bootclasspathFragmentFactory() android.Module { m := &BootclasspathFragmentModule{} m.AddProperties(&m.properties) @@ -230,14 +249,6 @@ func bootclasspathFragmentInitContentsFromImage(ctx android.EarlyModuleContext, commonApex, apex) } } - - if len(contents) != 0 { - // Nothing to do. - return - } - - // Store the jars in the Contents property so that they can be used to add dependencies. - m.properties.Contents = modules.CopyOfJars() } // bootclasspathImageNameContentsConsistencyCheck checks that the configuration that applies to this @@ -290,11 +301,11 @@ type BootclasspathFragmentApexContentInfo struct { modules android.ConfiguredJarList // Map from arch type to the boot image files. - bootImageFilesByArch map[android.ArchType]android.OutputPaths + bootImageFilesByArch bootImageFilesByArch - // Map from the name of the context module (as returned by Name()) to the hidden API encoded dex - // jar path. - contentModuleDexJarPaths map[string]android.Path + // Map from the base module name (without prebuilt_ prefix) of a fragment's contents module to the + // hidden API encoded dex jar path. + contentModuleDexJarPaths bootDexJarByModule } func (i BootclasspathFragmentApexContentInfo) Modules() android.ConfiguredJarList { @@ -304,7 +315,7 @@ func (i BootclasspathFragmentApexContentInfo) Modules() android.ConfiguredJarLis // 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 BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() map[android.ArchType]android.OutputPaths { +func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() bootImageFilesByArch { return i.bootImageFilesByArch } @@ -312,6 +323,8 @@ func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() // // The dex boot jar is one which has had hidden API encoding performed on it. func (i BootclasspathFragmentApexContentInfo) DexBootJarPathForContentModule(module android.Module) (android.Path, error) { + // A bootclasspath_fragment cannot use a prebuilt library so Name() will return the base name + // without a prebuilt_ prefix so is safe to use as the key for the contentModuleDexJarPaths. name := module.Name() if dexJar, ok := i.contentModuleDexJarPaths[name]; ok { return dexJar, nil @@ -400,89 +413,80 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo fragments := gatherApexModulePairDepsWithTag(ctx, bootclasspathFragmentDepTag) - // Perform hidden API processing. - hiddenAPIFlagOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments) - // Verify that the image_name specified on a bootclasspath_fragment is valid even if this is a // prebuilt which will not use the image config. imageConfig := b.getImageConfig(ctx) - // A prebuilt fragment cannot contribute to the apex. - if !android.IsModulePrebuilt(ctx.Module()) { - // Provide the apex content info. - b.provideApexContentInfo(ctx, imageConfig, contents, hiddenAPIFlagOutput) + // A versioned prebuilt_bootclasspath_fragment cannot and does not need to perform hidden API + // processing. It cannot do it because it is not part of a prebuilt_apex and so has no access to + // the correct dex implementation jar. It does not need to because the platform-bootclasspath + // always references the latest bootclasspath_fragments. + if !android.IsModuleInVersionedSdk(ctx.Module()) { + // Perform hidden API processing. + hiddenAPIOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments) + + var bootImageFilesByArch bootImageFilesByArch + if imageConfig != nil { + // Delegate the production of the boot image files to a module type specific method. + common := ctx.Module().(commonBootclasspathFragment) + bootImageFilesByArch = common.produceBootImageFiles(ctx, imageConfig, contents) + + if shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) { + // Copy the dex jars of this fragment's content modules to their predefined locations. + copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule) + } + } + + // A prebuilt fragment cannot contribute to an apex. + if !android.IsModulePrebuilt(ctx.Module()) { + // Provide the apex content info. + b.provideApexContentInfo(ctx, imageConfig, hiddenAPIOutput, bootImageFilesByArch) + } + } +} + +// shouldCopyBootFilesToPredefinedLocations determines whether the current module should copy boot +// files, e.g. boot dex jars or boot image files, to the predefined location expected by the rest +// of the build. +// +// This ensures that only a single module will copy its files to the image configuration. +func shouldCopyBootFilesToPredefinedLocations(ctx android.ModuleContext, imageConfig *bootImageConfig) bool { + // Bootclasspath fragment modules that are for the platform do not produce boot related files. + apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + if apexInfo.IsForPlatform() { + return false + } + + // If the image configuration has no modules specified then it means that the build has been + // configured to build something other than a boot image, e.g. an sdk, so do not try and copy the + // files. + if imageConfig.modules.Len() == 0 { + return false } + + // Only copy files from the module that is preferred. + return isActiveModule(ctx.Module()) } // provideApexContentInfo creates, initializes and stores the apex content info for use by other // modules. -func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module, hiddenAPIFlagOutput *HiddenAPIFlagOutput) { +func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, hiddenAPIOutput *HiddenAPIOutput, bootImageFilesByArch bootImageFilesByArch) { // Construct the apex content info from the config. - info := BootclasspathFragmentApexContentInfo{} - - // Populate the apex content info with paths to the dex jars. - b.populateApexContentInfoDexJars(ctx, &info, contents, hiddenAPIFlagOutput) + info := BootclasspathFragmentApexContentInfo{ + // Populate the apex content info with paths to the dex jars. + contentModuleDexJarPaths: hiddenAPIOutput.EncodedBootDexFilesByModule, + } if imageConfig != nil { info.modules = imageConfig.modules - - if !SkipDexpreoptBootJars(ctx) { - // 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) - - // Only generate the boot image if the configuration does not skip it. - if b.generateBootImageBuildActions(ctx, contents, imageConfig) { - // Allow the apex to access the boot image files. - files := map[android.ArchType]android.OutputPaths{} - for _, variant := range 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 - } - } - info.bootImageFilesByArch = files - } - } } + info.bootImageFilesByArch = bootImageFilesByArch + // Make the apex content info available for other modules. ctx.SetProvider(BootclasspathFragmentApexContentInfoProvider, info) } -// populateApexContentInfoDexJars adds paths to the dex jars provided by this fragment to the -// apex content info. -func (b *BootclasspathFragmentModule) populateApexContentInfoDexJars(ctx android.ModuleContext, info *BootclasspathFragmentApexContentInfo, contents []android.Module, hiddenAPIFlagOutput *HiddenAPIFlagOutput) { - - info.contentModuleDexJarPaths = map[string]android.Path{} - if hiddenAPIFlagOutput != nil { - // Hidden API encoding has been performed. - flags := hiddenAPIFlagOutput.AllFlagsPath - for _, m := range contents { - h := m.(hiddenAPIModule) - unencodedDex := h.bootDexJar() - if unencodedDex == nil { - // This is an error. Sometimes Soong will report the error directly, other times it will - // defer the error reporting to happen only when trying to use the missing file in ninja. - // Either way it is handled by extractBootDexJarsFromModules which must have been - // called before this as it generates the flags that are used to encode these files. - continue - } - - outputDir := android.PathForModuleOut(ctx, "hiddenapi-modular/encoded").OutputPath - encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, flags, *h.uncompressDex(), outputDir) - info.contentModuleDexJarPaths[m.Name()] = encodedDex - } - } else { - for _, m := range contents { - j := m.(UsesLibraryDependency) - dexJar := j.DexJarBuildPath() - info.contentModuleDexJarPaths[m.Name()] = dexJar - } - } -} - // generateClasspathProtoBuildActions generates all required build actions for classpath.proto config func (b *BootclasspathFragmentModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) { var classpathJars []classpathJar @@ -548,12 +552,12 @@ func (b *BootclasspathFragmentModule) getImageConfig(ctx android.EarlyModuleCont } // generateHiddenAPIBuildActions generates all the hidden API related build rules. -func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIFlagOutput { +func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIOutput { // Create hidden API input structure. input := b.createHiddenAPIFlagInput(ctx, contents, fragments) - var output *HiddenAPIFlagOutput + var output *HiddenAPIOutput // Hidden API processing is conditional as a temporary workaround as not all // bootclasspath_fragments provide the appropriate information needed for hidden API processing @@ -563,7 +567,14 @@ func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android. if input.canPerformHiddenAPIProcessing(ctx, b.properties) { // Delegate the production of the hidden API all-flags.csv file to a module type specific method. common := ctx.Module().(commonBootclasspathFragment) - output = common.produceHiddenAPIAllFlagsFile(ctx, contents, input) + output = common.produceHiddenAPIOutput(ctx, contents, input) + } else { + // As hidden API processing cannot be performed fall back to trying to retrieve the legacy + // encoded boot dex files, i.e. those files encoded by the individual libraries and returned + // from the DexJarBuildPath() method. + output = &HiddenAPIOutput{ + EncodedBootDexFilesByModule: retrieveLegacyEncodedBootDexFiles(ctx, contents), + } } // Initialize a HiddenAPIInfo structure. @@ -580,11 +591,9 @@ func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android. TransitiveStubDexJarsByKind: input.transitiveStubDexJarsByKind(), } - if output != nil { - // The monolithic hidden API processing also needs access to all the output files produced by - // hidden API processing of this fragment. - hiddenAPIInfo.HiddenAPIFlagOutput = *output - } + // The monolithic hidden API processing also needs access to all the output files produced by + // hidden API processing of this fragment. + hiddenAPIInfo.HiddenAPIFlagOutput = (*output).HiddenAPIFlagOutput // Provide it for use by other modules. ctx.SetProvider(HiddenAPIInfoProvider, hiddenAPIInfo) @@ -592,10 +601,24 @@ func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android. return output } +// retrieveLegacyEncodedBootDexFiles attempts to retrieve the legacy encoded boot dex jar files. +func retrieveLegacyEncodedBootDexFiles(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule { + // If the current bootclasspath_fragment is the active module or a source module then retrieve the + // encoded dex files, otherwise return an empty map. + // + // An inactive (i.e. not preferred) bootclasspath_fragment needs to retrieve the encoded dex jars + // as they are still needed by an apex. An inactive prebuilt_bootclasspath_fragment does not need + // to do so and may not yet have access to dex boot jars from a prebuilt_apex/apex_set. + if isActiveModule(ctx.Module()) || !android.IsModulePrebuilt(ctx.Module()) { + return extractEncodedDexJarsFromModules(ctx, contents) + } else { + return nil + } +} + // createHiddenAPIFlagInput creates a HiddenAPIFlagInput struct and initializes it with information derived // from the properties on this module and its dependencies. func (b *BootclasspathFragmentModule) createHiddenAPIFlagInput(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) HiddenAPIFlagInput { - // Merge the HiddenAPIInfo from all the fragment dependencies. dependencyHiddenApiInfo := newHiddenAPIInfo() dependencyHiddenApiInfo.mergeFromFragmentDeps(ctx, fragments) @@ -615,12 +638,36 @@ func (b *BootclasspathFragmentModule) createHiddenAPIFlagInput(ctx android.Modul return input } -// produceHiddenAPIAllFlagsFile produces the hidden API all-flags.csv file (and supporting files) -// for the fragment. -func (b *BootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIFlagOutput { +// produceHiddenAPIOutput produces the hidden API all-flags.csv file (and supporting files) +// for the fragment as well as encoding the flags in the boot dex jars. +func (b *BootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput { // Generate the rules to create the hidden API flags and update the supplied hiddenAPIInfo with the // paths to the created files. - return hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx, contents, input) + return hiddenAPIRulesForBootclasspathFragment(ctx, contents, input) +} + +// produceBootImageFiles builds the boot image files from the source if it is required. +func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch { + if SkipDexpreoptBootJars(ctx) { + return nil + } + + // 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) + + // Only generate the boot image if the configuration does not skip it. + if !b.generateBootImageBuildActions(ctx, contents, imageConfig) { + return nil + } + + // Only make the files available to an apex if they were actually generated. + files := bootImageFilesByArch{} + for _, variant := range imageConfig.apexVariants() { + files[variant.target.Arch.ArchType] = variant.imagesDeps.Paths() + } + + return files } // generateBootImageBuildActions generates ninja rules to create the boot image if required for this @@ -644,10 +691,6 @@ func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android. return false } - // Copy the dex jars of this fragment's content modules to their predefined locations. - bootDexJarByModule := extractEncodedDexJarsFromModules(ctx, contents) - copyBootJarsToPredefinedLocations(ctx, bootDexJarByModule, imageConfig.dexPathsByModule) - // Build a profile for the image config and then use that to build the boot image. profile := bootImageProfileRule(ctx, imageConfig) buildBootImage(ctx, imageConfig, profile) @@ -836,9 +879,8 @@ func (module *prebuiltBootclasspathFragmentModule) Name() string { return module.prebuilt.Name(module.ModuleBase.Name()) } -// produceHiddenAPIAllFlagsFile returns a path to the prebuilt all-flags.csv or nil if none is -// specified. -func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []android.Module, _ HiddenAPIFlagInput) *HiddenAPIFlagOutput { +// produceHiddenAPIOutput returns a path to the prebuilt all-flags.csv or nil if none is specified. +func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput { pathForOptionalSrc := func(src *string) android.Path { if src == nil { // TODO(b/179354495): Fail if this is not provided once prebuilts have been updated. @@ -847,19 +889,106 @@ func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIAllFlagsFile( return android.PathForModuleSrc(ctx, *src) } - output := HiddenAPIFlagOutput{ - StubFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags), - AnnotationFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Annotation_flags), - MetadataPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Metadata), - IndexPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Index), - AllFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags), + // Retrieve the dex files directly from the content modules. They in turn should retrieve the + // encoded dex jars from the prebuilt .apex files. + encodedBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, contents) + + output := HiddenAPIOutput{ + HiddenAPIFlagOutput: HiddenAPIFlagOutput{ + StubFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags), + AnnotationFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Annotation_flags), + MetadataPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Metadata), + IndexPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Index), + AllFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags), + }, + EncodedBootDexFilesByModule: encodedBootDexJarsByModule, } return &output } +// produceBootImageFiles extracts the boot image files from the APEX if available. +func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch { + if !shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) { + return nil + } + + var deapexerModule android.Module + ctx.VisitDirectDeps(func(module android.Module) { + tag := ctx.OtherModuleDependencyTag(module) + // Save away the `deapexer` module on which this depends, if any. + if tag == android.DeapexerTag { + deapexerModule = module + } + }) + + if deapexerModule == nil { + // This should never happen as a variant for a prebuilt_apex is only created if the + // deapexer module has been configured to export the dex implementation jar for this module. + ctx.ModuleErrorf("internal error: module does not depend on a `deapexer` module") + return nil + } + + di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo) + for _, variant := range imageConfig.apexVariants() { + arch := variant.target.Arch.ArchType + for _, toPath := range variant.imagesDeps { + apexRelativePath := apexRootRelativePathToBootImageFile(arch, toPath.Base()) + // Get the path to the file that the deapexer extracted from the prebuilt apex file. + fromPath := di.PrebuiltExportPath(apexRelativePath) + + // Copy the file to the predefined location. + ctx.Build(pctx, android.BuildParams{ + Rule: android.Cp, + Input: fromPath, + Output: toPath, + }) + } + } + + // The returned files will be made available to APEXes that include a bootclasspath_fragment. + // However, as a prebuilt_bootclasspath_fragment can never contribute to an APEX there is no point + // in returning any files. + return nil +} + var _ commonBootclasspathFragment = (*prebuiltBootclasspathFragmentModule)(nil) +// createBootImageTag creates the tag to uniquely identify the boot image file among all of the +// files that a module requires from the prebuilt .apex file. +func createBootImageTag(arch android.ArchType, baseName string) string { + tag := fmt.Sprintf(".bootimage-%s-%s", arch, baseName) + return tag +} + +// RequiredFilesFromPrebuiltApex returns the list of all files the prebuilt_bootclasspath_fragment +// requires from a prebuilt .apex file. +// +// If there is no image config associated with this fragment then it returns nil. Otherwise, it +// returns the files that are listed in the image config. +func (module *prebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string { + imageConfig := module.getImageConfig(ctx) + if imageConfig != nil { + // Add the boot image files, e.g. .art, .oat and .vdex files. + files := []string{} + for _, variant := range imageConfig.apexVariants() { + arch := variant.target.Arch.ArchType + for _, path := range variant.imagesDeps.Paths() { + base := path.Base() + files = append(files, apexRootRelativePathToBootImageFile(arch, base)) + } + } + return files + } + return nil +} + +func apexRootRelativePathToBootImageFile(arch android.ArchType, base string) string { + return filepath.Join("javalib", arch.String(), base) +} + +var _ android.RequiredFilesFromPrebuiltApex = (*prebuiltBootclasspathFragmentModule)(nil) + func prebuiltBootclasspathFragmentFactory() android.Module { m := &prebuiltBootclasspathFragmentModule{} m.AddProperties(&m.properties, &m.prebuiltProperties) diff --git a/java/classpath_element.go b/java/classpath_element.go new file mode 100644 index 000000000..753e7f888 --- /dev/null +++ b/java/classpath_element.go @@ -0,0 +1,229 @@ +/* + * 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" + "github.com/google/blueprint" +) + +// Supports constructing a list of ClasspathElement from a set of fragments and modules. + +// ClasspathElement represents a component that contributes to a classpath. That can be +// either a java module or a classpath fragment module. +type ClasspathElement interface { + Module() android.Module + String() string +} + +type ClasspathElements []ClasspathElement + +// ClasspathFragmentElement is a ClasspathElement that encapsulates a classpath fragment module. +type ClasspathFragmentElement struct { + Fragment android.Module + Contents []android.Module +} + +func (b *ClasspathFragmentElement) Module() android.Module { + return b.Fragment +} + +func (b *ClasspathFragmentElement) String() string { + contents := []string{} + for _, module := range b.Contents { + contents = append(contents, module.String()) + } + return fmt.Sprintf("fragment(%s, %s)", b.Fragment, strings.Join(contents, ", ")) +} + +var _ ClasspathElement = (*ClasspathFragmentElement)(nil) + +// ClasspathLibraryElement is a ClasspathElement that encapsulates a java library. +type ClasspathLibraryElement struct { + Library android.Module +} + +func (b *ClasspathLibraryElement) Module() android.Module { + return b.Library +} + +func (b *ClasspathLibraryElement) String() string { + return fmt.Sprintf("library{%s}", b.Library) +} + +var _ ClasspathElement = (*ClasspathLibraryElement)(nil) + +// ClasspathElementContext defines the context methods needed by CreateClasspathElements +type ClasspathElementContext interface { + OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool + OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{} + ModuleErrorf(fmt string, args ...interface{}) +} + +// CreateClasspathElements creates a list of ClasspathElement objects from a list of libraries and +// a list of fragments. +// +// The libraries parameter contains the set of libraries from which the classpath is constructed. +// The fragments parameter contains the classpath fragment modules whose contents are libraries that +// are part of the classpath. Each library in the libraries parameter may be part of a fragment. The +// determination as to which libraries belong to fragments and which do not is based on the apex to +// which they belong, if any. +// +// Every fragment in the fragments list must be part of one or more apexes and each apex is assumed +// to contain only a single fragment from the fragments list. A library in the libraries parameter +// that is part of an apex must be provided by a classpath fragment in the corresponding apex. +// +// This will return a ClasspathElements list that contains a ClasspathElement for each standalone +// library and each fragment. The order of the elements in the list is such that if the list was +// flattened into a list of library modules that it would result in the same list or modules as the +// input libraries. Flattening the list can be done by replacing each ClasspathFragmentElement in +// the list with its Contents field. +// +// Requirements/Assumptions: +// * A fragment can be associated with more than one apex but each apex must only be associated with +// a single fragment from the fragments list. +// * All of a fragment's contents must appear as a contiguous block in the same order in the +// libraries list. +// * Each library must only appear in a single fragment. +// +// The apex is used to identify which libraries belong to which fragment. First a mapping is created +// from apex to fragment. Then the libraries are iterated over and any library in an apex is +// associated with an element for the fragment to which it belongs. Otherwise, the libraries are +// standalone and have their own element. +// +// e.g. Given the following input: +// libraries: com.android.art:core-oj, com.android.art:core-libart, framework, ext +// fragments: com.android.art:art-bootclasspath-fragment +// +// Then this will return: +// ClasspathFragmentElement(art-bootclasspath-fragment, [core-oj, core-libart]), +// ClasspathLibraryElement(framework), +// ClasspathLibraryElement(ext), +func CreateClasspathElements(ctx ClasspathElementContext, libraries []android.Module, fragments []android.Module) ClasspathElements { + // Create a map from apex name to the fragment module. This makes it easy to find the fragment + // associated with a particular apex. + apexToFragment := map[string]android.Module{} + for _, fragment := range fragments { + if !ctx.OtherModuleHasProvider(fragment, android.ApexInfoProvider) { + ctx.ModuleErrorf("fragment %s is not part of an apex", fragment) + continue + } + + apexInfo := ctx.OtherModuleProvider(fragment, android.ApexInfoProvider).(android.ApexInfo) + for _, apex := range apexInfo.InApexVariants { + if existing, ok := apexToFragment[apex]; ok { + ctx.ModuleErrorf("apex %s has multiple fragments, %s and %s", apex, fragment, existing) + continue + } + apexToFragment[apex] = fragment + } + } + + fragmentToElement := map[android.Module]*ClasspathFragmentElement{} + elements := []ClasspathElement{} + var currentElement ClasspathElement + +skipLibrary: + // Iterate over the libraries to construct the ClasspathElements list. + for _, library := range libraries { + var element ClasspathElement + if ctx.OtherModuleHasProvider(library, android.ApexInfoProvider) { + apexInfo := ctx.OtherModuleProvider(library, android.ApexInfoProvider).(android.ApexInfo) + + var fragment android.Module + + // Make sure that the library is in only one fragment of the classpath. + for _, apex := range apexInfo.InApexVariants { + if f, ok := apexToFragment[apex]; ok { + if fragment == nil { + // This is the first fragment so just save it away. + fragment = f + } else if f != fragment { + // This apex variant of the library is in a different fragment. + ctx.ModuleErrorf("library %s is in two separate fragments, %s and %s", library, fragment, f) + // Skip over this library entirely as otherwise the resulting classpath elements would + // be invalid. + continue skipLibrary + } + } else { + // There is no fragment associated with the library's apex. + } + } + + if fragment == nil { + ctx.ModuleErrorf("library %s is from apexes %s which have no corresponding fragment in %s", + library, apexInfo.InApexVariants, fragments) + // Skip over this library entirely as otherwise the resulting classpath elements would + // be invalid. + continue skipLibrary + } else if existingFragmentElement, ok := fragmentToElement[fragment]; ok { + // This library is in a fragment element that has already been added. + + // If the existing fragment element is still the current element then this library is + // contiguous with other libraries in that fragment so there is nothing more to do. + // Otherwise this library is not contiguous with other libraries in the same fragment which + // is an error. + if existingFragmentElement != currentElement { + separator := "" + if fragmentElement, ok := currentElement.(*ClasspathFragmentElement); ok { + separator = fmt.Sprintf("libraries from fragment %s like %s", fragmentElement.Fragment, fragmentElement.Contents[0]) + } else { + libraryElement := currentElement.(*ClasspathLibraryElement) + separator = fmt.Sprintf("library %s", libraryElement.Library) + } + + // Get the library that precedes this library in the fragment. That is the last library as + // this library has not yet been added. + precedingLibraryInFragment := existingFragmentElement.Contents[len(existingFragmentElement.Contents)-1] + ctx.ModuleErrorf("libraries from the same fragment must be contiguous, however %s and %s from fragment %s are separated by %s", + precedingLibraryInFragment, library, fragment, separator) + } + + // Add this library to the fragment element's contents. + existingFragmentElement.Contents = append(existingFragmentElement.Contents, library) + } else { + // This is the first library in this fragment so add a new element for the fragment, + // including the library. + fragmentElement := &ClasspathFragmentElement{ + Fragment: fragment, + Contents: []android.Module{library}, + } + + // Store it away so we can detect when attempting to create another element for the same + // fragment. + fragmentToElement[fragment] = fragmentElement + element = fragmentElement + } + } else { + // The library is from the platform so just add an element for it. + element = &ClasspathLibraryElement{Library: library} + } + + // If no element was created then it means that the library has been added to an existing + // fragment element so the list of elements and current element are unaffected. + if element != nil { + // Add the element to the list and make it the current element for the next iteration. + elements = append(elements, element) + currentElement = element + } + } + + return elements +} diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index dc8df5ec1..bb857845a 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -358,6 +358,19 @@ func (image bootImageConfig) moduleFiles(ctx android.PathContext, dir android.Ou return ret } +// apexVariants returns a list of all *bootImageVariant that could be included in an apex. +func (image *bootImageConfig) apexVariants() []*bootImageVariant { + variants := []*bootImageVariant{} + for _, variant := range image.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 { + variants = append(variants, variant) + } + } + return variants +} + // Return boot image locations (as a list of symbolic paths). // // The image "location" is a symbolic path that, with multiarchitecture support, doesn't really @@ -489,7 +502,7 @@ func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJars } } -// buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image. +// buildBootImage takes a bootImageConfig, and creates rules to build it. func buildBootImage(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) { var zipFiles android.Paths for _, variant := range image.variants { diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index 643c5cba2..6e2261480 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -524,7 +524,7 @@ func (i *HiddenAPIFlagInput) gatherStubLibInfo(ctx android.ModuleContext, conten } } - ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) { + ctx.VisitDirectDeps(func(module android.Module) { tag := ctx.OtherModuleDependencyTag(module) if hiddenAPIStubsTag, ok := tag.(hiddenAPIStubsDependencyTag); ok { kind := hiddenAPIStubsTag.sdkKind @@ -580,6 +580,36 @@ func (b bootDexJarByModule) addPath(module android.Module, path android.Path) { b[android.RemoveOptionalPrebuiltPrefix(module.Name())] = path } +// bootDexJars returns the boot dex jar paths sorted by their keys. +func (b bootDexJarByModule) bootDexJars() android.Paths { + paths := android.Paths{} + for _, k := range android.SortedStringKeys(b) { + paths = append(paths, b[k]) + } + return paths +} + +// bootDexJarsWithoutCoverage returns the boot dex jar paths sorted by their keys without coverage +// libraries if present. +func (b bootDexJarByModule) bootDexJarsWithoutCoverage() android.Paths { + paths := android.Paths{} + for _, k := range android.SortedStringKeys(b) { + if k == "jacocoagent" { + continue + } + paths = append(paths, b[k]) + } + return paths +} + +// HiddenAPIOutput encapsulates the output from the hidden API processing. +type HiddenAPIOutput struct { + HiddenAPIFlagOutput + + // The map from base module name to the path to the encoded boot dex file. + EncodedBootDexFilesByModule bootDexJarByModule +} + // pathForValidation creates a path of the same type as the supplied type but with a name of // <path>.valid. // @@ -604,7 +634,7 @@ func pathForValidation(ctx android.PathContext, path android.WritablePath) andro // hiddenAPIInfo is a struct containing paths to files that augment the information provided by // the annotationFlags. func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc string, - outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlags android.Path, + outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlagPaths android.Paths, flagFilesByCategory FlagFilesByCategory, allFlagsPaths android.Paths, generatedRemovedDexSignatures android.OptionalPath) { // The file which is used to record that the flags file is valid. @@ -635,7 +665,7 @@ func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc st command := rule.Command(). BuiltTool("generate_hiddenapi_lists"). FlagWithInput("--csv ", baseFlagsPath). - Input(annotationFlags). + Inputs(annotationFlagPaths). FlagWithOutput("--output ", tempPath) // Add the options for the different categories of flag files. @@ -665,8 +695,8 @@ func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc st rule.Build(name, desc) } -// hiddenAPIGenerateAllFlagsForBootclasspathFragment will generate all the flags for a fragment -// of the bootclasspath. +// hiddenAPIRulesForBootclasspathFragment will generate all the flags for a fragment of the +// bootclasspath and then encode the flags into the boot dex files. // // It takes: // * Map from android.SdkKind to stub dex jar paths defining the API for that sdk kind. @@ -679,15 +709,16 @@ func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc st // * metadata.csv // * index.csv // * all-flags.csv -func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIFlagOutput { +// * encoded boot dex files +func hiddenAPIRulesForBootclasspathFragment(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput { hiddenApiSubDir := "modular-hiddenapi" - // Gather the dex files for the boot libraries provided by this fragment. - bootDexJars := extractBootDexJarsFromModules(ctx, contents) + // Gather information about the boot dex files for the boot libraries provided by this fragment. + bootDexInfoByModule := extractBootDexInfoFromModules(ctx, contents) // Generate the stub-flags.csv. stubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "stub-flags.csv") - rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlagsCSV, bootDexJars, input) + rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlagsCSV, bootDexInfoByModule.bootDexJars(), input) rule.Build("modularHiddenAPIStubFlagsFile", "modular hiddenapi stub flags") // Extract the classes jars from the contents. @@ -715,16 +746,29 @@ func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext // Generate the all-flags.csv which are the flags that will, in future, be encoded into the dex // files. - outputPath := android.PathForModuleOut(ctx, hiddenApiSubDir, "all-flags.csv") - buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags", "modular hiddenapi all flags", outputPath, stubFlagsCSV, annotationFlagsCSV, input.FlagFilesByCategory, nil, removedDexSignatures) + allFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "all-flags.csv") + buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags", "modular hiddenapi all flags", allFlagsCSV, stubFlagsCSV, android.Paths{annotationFlagsCSV}, input.FlagFilesByCategory, nil, removedDexSignatures) + + // Encode the flags into the boot dex files. + encodedBootDexJarsByModule := map[string]android.Path{} + outputDir := android.PathForModuleOut(ctx, "hiddenapi-modular/encoded").OutputPath + for _, name := range android.SortedStringKeys(bootDexInfoByModule) { + bootDexInfo := bootDexInfoByModule[name] + unencodedDex := bootDexInfo.path + encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, allFlagsCSV, bootDexInfo.uncompressDex, outputDir) + encodedBootDexJarsByModule[name] = encodedDex + } // Store the paths in the info for use by other modules and sdk snapshot generation. - output := HiddenAPIFlagOutput{ - StubFlagsPath: stubFlagsCSV, - AnnotationFlagsPath: annotationFlagsCSV, - MetadataPath: metadataCSV, - IndexPath: indexCSV, - AllFlagsPath: outputPath, + output := HiddenAPIOutput{ + HiddenAPIFlagOutput: HiddenAPIFlagOutput{ + StubFlagsPath: stubFlagsCSV, + AnnotationFlagsPath: annotationFlagsCSV, + MetadataPath: metadataCSV, + IndexPath: indexCSV, + AllFlagsPath: allFlagsCSV, + }, + EncodedBootDexFilesByModule: encodedBootDexJarsByModule, } return &output } @@ -747,37 +791,15 @@ func buildRuleToGenerateRemovedDexSignatures(ctx android.ModuleContext, removedT } // extractBootDexJarsFromModules extracts the boot dex jars from the supplied modules. -func extractBootDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) android.Paths { - bootDexJars := android.Paths{} +func extractBootDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule { + bootDexJars := bootDexJarByModule{} for _, module := range contents { hiddenAPIModule := hiddenAPIModuleFromModule(ctx, module) if hiddenAPIModule == nil { continue } - bootDexJar := hiddenAPIModule.bootDexJar() - if bootDexJar == nil { - if ctx.Config().AlwaysUsePrebuiltSdks() { - // TODO(b/179354495): Remove this workaround when it is unnecessary. - // Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So, - // create a fake one that will cause a build error only if it is used. - fake := android.PathForModuleOut(ctx, "fake/boot-dex/%s.jar", module.Name()) - - // Create an error rule that pretends to create the output file but will actually fail if it - // is run. - ctx.Build(pctx, android.BuildParams{ - Rule: android.ErrorRule, - Output: fake, - Args: map[string]string{ - "error": fmt.Sprintf("missing dependencies: boot dex jar for %s", module), - }, - }) - bootDexJars = append(bootDexJars, fake) - } else { - ctx.ModuleErrorf("module %s does not provide a dex jar", module) - } - } else { - bootDexJars = append(bootDexJars, bootDexJar) - } + bootDexJar := retrieveBootDexJarFromHiddenAPIModule(ctx, hiddenAPIModule) + bootDexJars.addPath(module, bootDexJar) } return bootDexJars } @@ -794,6 +816,60 @@ func hiddenAPIModuleFromModule(ctx android.BaseModuleContext, module android.Mod return nil } +// bootDexInfo encapsulates both the path and uncompressDex status retrieved from a hiddenAPIModule. +type bootDexInfo struct { + // The path to the dex jar that has not had hidden API flags encoded into it. + path android.Path + + // Indicates whether the dex jar needs uncompressing before encoding. + uncompressDex bool +} + +// bootDexInfoByModule is a map from module name (as returned by module.Name()) to the boot dex +// path (as returned by hiddenAPIModule.bootDexJar()) and the uncompressDex flag. +type bootDexInfoByModule map[string]bootDexInfo + +// bootDexJars returns the boot dex jar paths sorted by their keys. +func (b bootDexInfoByModule) bootDexJars() android.Paths { + paths := android.Paths{} + for _, m := range android.SortedStringKeys(b) { + paths = append(paths, b[m].path) + } + return paths +} + +// extractBootDexInfoFromModules extracts the boot dex jar and uncompress dex state from +// each of the supplied modules which must implement hiddenAPIModule. +func extractBootDexInfoFromModules(ctx android.ModuleContext, contents []android.Module) bootDexInfoByModule { + bootDexJarsByModule := bootDexInfoByModule{} + for _, module := range contents { + hiddenAPIModule := module.(hiddenAPIModule) + bootDexJar := retrieveBootDexJarFromHiddenAPIModule(ctx, hiddenAPIModule) + bootDexJarsByModule[module.Name()] = bootDexInfo{ + path: bootDexJar, + uncompressDex: *hiddenAPIModule.uncompressDex(), + } + } + + return bootDexJarsByModule +} + +// retrieveBootDexJarFromHiddenAPIModule retrieves the boot dex jar from the hiddenAPIModule. +// +// If the module does not provide a boot dex jar, i.e. the returned boot dex jar is nil, then that +// create a fake path and either report an error immediately or defer reporting of the error until +// the path is actually used. +func retrieveBootDexJarFromHiddenAPIModule(ctx android.ModuleContext, module hiddenAPIModule) android.Path { + bootDexJar := module.bootDexJar() + if bootDexJar == nil { + fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/boot-dex/%s.jar", module.Name())) + bootDexJar = fake + + handleMissingDexBootFile(ctx, module, fake) + } + return bootDexJar +} + // extractClassesJarsFromModules extracts the class jars from the supplied modules. func extractClassesJarsFromModules(contents []android.Module) android.Paths { classesJars := android.Paths{} @@ -822,6 +898,11 @@ func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.M return true } + // Any missing dependency should be allowed. + if ctx.Config().AllowMissingDependencies() { + return true + } + // This is called for both platform_bootclasspath and bootclasspath_fragment modules. // // A bootclasspath_fragment module should only use the APEX variant of source or prebuilt modules. diff --git a/java/hiddenapi_monolithic.go b/java/hiddenapi_monolithic.go index edf42351f..52f0770f3 100644 --- a/java/hiddenapi_monolithic.go +++ b/java/hiddenapi_monolithic.go @@ -43,22 +43,45 @@ type MonolithicHiddenAPIInfo struct { // The paths to the generated all-flags.csv files. AllFlagsPaths android.Paths + + // The classes jars from the libraries on the platform bootclasspath. + ClassesJars android.Paths } // newMonolithicHiddenAPIInfo creates a new MonolithicHiddenAPIInfo from the flagFilesByCategory // plus information provided by each of the fragments. -func newMonolithicHiddenAPIInfo(ctx android.ModuleContext, flagFilesByCategory FlagFilesByCategory, fragments []android.Module) MonolithicHiddenAPIInfo { +func newMonolithicHiddenAPIInfo(ctx android.ModuleContext, flagFilesByCategory FlagFilesByCategory, classpathElements ClasspathElements) MonolithicHiddenAPIInfo { monolithicInfo := MonolithicHiddenAPIInfo{} monolithicInfo.FlagsFilesByCategory = flagFilesByCategory - // Merge all the information from the fragments. The fragments form a DAG so it is possible that - // this will introduce duplicates so they will be resolved after processing all the fragments. - for _, fragment := range fragments { - if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) { - info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo) - monolithicInfo.append(&info) + // Merge all the information from the classpathElements. The fragments form a DAG so it is possible that + // this will introduce duplicates so they will be resolved after processing all the classpathElements. + for _, element := range classpathElements { + var classesJars android.Paths + switch e := element.(type) { + case *ClasspathLibraryElement: + classesJars = retrieveClassesJarsFromModule(e.Module()) + + case *ClasspathFragmentElement: + fragment := e.Module() + if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) { + info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo) + monolithicInfo.append(&info) + + // If the bootclasspath fragment actually perform hidden API processing itself then use the + // CSV files it provides and do not bother processing the classesJars files. This ensures + // consistent behavior between source and prebuilt as prebuilt modules do not provide + // classesJars. + if info.AllFlagsPath != nil { + continue + } + } + + classesJars = extractClassesJarsFromModules(e.Contents) } + + monolithicInfo.ClassesJars = append(monolithicInfo.ClassesJars, classesJars...) } // Dedup paths. diff --git a/java/java.go b/java/java.go index 2bbb5b102..71c1b3324 100644 --- a/java/java.go +++ b/java/java.go @@ -643,7 +643,7 @@ func LibraryFactory() android.Module { module.addHostAndDeviceProperties() - module.initModuleAndImport(&module.ModuleBase) + module.initModuleAndImport(module) android.InitApexModule(module) android.InitSdkAwareModule(module) @@ -1309,7 +1309,7 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Get the path of the dex implementation jar from the `deapexer` module. di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo) - if dexOutputPath := di.PrebuiltExportPath(j.BaseModuleName(), ".dexjar"); dexOutputPath != nil { + if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(j.BaseModuleName())); dexOutputPath != nil { j.dexJarFile = dexOutputPath // Initialize the hiddenapi structure. @@ -1426,6 +1426,29 @@ func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext, return nil } +// requiredFilesFromPrebuiltApexForImport returns information about the files that a java_import or +// java_sdk_library_import with the specified base module name requires to be exported from a +// prebuilt_apex/apex_set. +func requiredFilesFromPrebuiltApexForImport(name string) []string { + // Add the dex implementation jar to the set of exported files. + return []string{ + apexRootRelativePathToJavaLib(name), + } +} + +// apexRootRelativePathToJavaLib returns the path, relative to the root of the apex's contents, for +// the java library with the specified name. +func apexRootRelativePathToJavaLib(name string) string { + return filepath.Join("javalib", name+".jar") +} + +var _ android.RequiredFilesFromPrebuiltApex = (*Import)(nil) + +func (j *Import) RequiredFilesFromPrebuiltApex(_ android.BaseModuleContext) []string { + name := j.BaseModuleName() + return requiredFilesFromPrebuiltApexForImport(name) +} + // Add compile time check for interface implementation var _ android.IDEInfo = (*Import)(nil) var _ android.IDECustomizedModuleName = (*Import)(nil) @@ -1473,7 +1496,7 @@ func ImportFactory() android.Module { &module.dexer.dexProperties, ) - module.initModuleAndImport(&module.ModuleBase) + module.initModuleAndImport(module) module.dexProperties.Optimize.EnabledByDefault = false diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index fba73d0c1..7b651baff 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -44,13 +44,9 @@ type platformBootclasspathModule struct { 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. @@ -189,7 +185,8 @@ func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.Mo b.generateClasspathProtoBuildActions(ctx) - b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments) + bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments) + buildRuleForBootJarsPackageCheck(ctx, bootDexJarByModule) // Nothing to do if skipping the dexpreopt of boot image jars. if SkipDexpreoptBootJars(ctx) { @@ -258,13 +255,15 @@ func (b *platformBootclasspathModule) getImageConfig(ctx android.EarlyModuleCont } // generateHiddenAPIBuildActions generates all the hidden API related build rules. -func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) { +func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) bootDexJarByModule { // 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 + bootDexJarByModule := extractBootDexJarsFromModules(ctx, modules) + // 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. @@ -276,10 +275,18 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. Output: path, }) } - return + return bootDexJarByModule } - monolithicInfo := b.createAndProvideMonolithicHiddenAPIInfo(ctx, fragments) + // Construct a list of ClasspathElement objects from the modules and fragments. + classpathElements := CreateClasspathElements(ctx, modules, fragments) + + monolithicInfo := b.createAndProvideMonolithicHiddenAPIInfo(ctx, classpathElements) + + // Extract the classes jars only from those libraries that do not have corresponding fragments as + // the fragments will have already provided the flags that are needed. + classesJars := monolithicInfo.ClassesJars + // Create the input to pass to ruleToGenerateHiddenAPIStubFlagsFile input := newHiddenAPIFlagInput() @@ -291,42 +298,60 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. input.FlagFilesByCategory = monolithicInfo.FlagsFilesByCategory // Generate the monolithic stub-flags.csv file. - bootDexJars := extractBootDexJarsFromModules(ctx, modules) stubFlags := hiddenAPISingletonPaths(ctx).stubFlags - rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlags, bootDexJars, input) + rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlags, bootDexJarByModule.bootDexJars(), input) rule.Build("platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags") - // Extract the classes jars from the contents. - classesJars := extractClassesJarsFromModules(modules) - // Generate the annotation-flags.csv file from all the module annotations. - annotationFlags := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "annotation-flags.csv") - buildRuleToGenerateAnnotationFlags(ctx, "monolithic hiddenapi flags", classesJars, stubFlags, annotationFlags) + annotationFlags := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "annotation-flags-from-classes.csv") + buildRuleToGenerateAnnotationFlags(ctx, "intermediate hidden API flags", classesJars, stubFlags, annotationFlags) - // Generate the monotlithic hiddenapi-flags.csv file. + // Generate the monolithic hiddenapi-flags.csv file. + // + // Use annotation flags generated directly from the classes jars as well as annotation flag files + // provided by prebuilts. + allAnnotationFlagFiles := android.Paths{annotationFlags} + allAnnotationFlagFiles = append(allAnnotationFlagFiles, monolithicInfo.AnnotationFlagsPaths...) allFlags := hiddenAPISingletonPaths(ctx).flags - buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "hiddenapi flags", allFlags, stubFlags, annotationFlags, monolithicInfo.FlagsFilesByCategory, monolithicInfo.AllFlagsPaths, android.OptionalPath{}) + buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "monolithic hidden API flags", allFlags, stubFlags, allAnnotationFlagFiles, monolithicInfo.FlagsFilesByCategory, monolithicInfo.AllFlagsPaths, android.OptionalPath{}) // Generate an intermediate monolithic hiddenapi-metadata.csv file directly from the annotations // in the source code. - intermediateMetadataCSV := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "intermediate-metadata.csv") - buildRuleToGenerateMetadata(ctx, "monolithic hidden API metadata", classesJars, stubFlags, intermediateMetadataCSV) + intermediateMetadataCSV := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "metadata-from-classes.csv") + buildRuleToGenerateMetadata(ctx, "intermediate hidden API metadata", classesJars, stubFlags, intermediateMetadataCSV) - // Reformat the intermediate file to add | quotes just in case that is important for the tools - // that consume the metadata file. - // TODO(b/179354495): Investigate whether it is possible to remove this reformatting step. + // Generate the monolithic hiddenapi-metadata.csv file. + // + // Use metadata files generated directly from the classes jars as well as metadata files provided + // by prebuilts. + // + // This has the side effect of ensuring that the output file uses | quotes just in case that is + // important for the tools that consume the metadata file. + allMetadataFlagFiles := android.Paths{intermediateMetadataCSV} + allMetadataFlagFiles = append(allMetadataFlagFiles, monolithicInfo.MetadataPaths...) metadataCSV := hiddenAPISingletonPaths(ctx).metadata - b.buildRuleMergeCSV(ctx, "reformat monolithic hidden API metadata", android.Paths{intermediateMetadataCSV}, metadataCSV) + b.buildRuleMergeCSV(ctx, "monolithic hidden API metadata", allMetadataFlagFiles, metadataCSV) - // Generate the monolithic hiddenapi-index.csv file directly from the CSV files in the classes - // jars. + // Generate an intermediate monolithic hiddenapi-index.csv file directly from the CSV files in the + // classes jars. + intermediateIndexCSV := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "index-from-classes.csv") + buildRuleToGenerateIndex(ctx, "intermediate hidden API index", classesJars, intermediateIndexCSV) + + // Generate the monolithic hiddenapi-index.csv file. + // + // Use index files generated directly from the classes jars as well as index files provided + // by prebuilts. + allIndexFlagFiles := android.Paths{intermediateIndexCSV} + allIndexFlagFiles = append(allIndexFlagFiles, monolithicInfo.IndexPaths...) indexCSV := hiddenAPISingletonPaths(ctx).index - buildRuleToGenerateIndex(ctx, "monolithic hidden API index", classesJars, indexCSV) + b.buildRuleMergeCSV(ctx, "monolithic hidden API index", allIndexFlagFiles, indexCSV) + + return bootDexJarByModule } // createAndProvideMonolithicHiddenAPIInfo creates a MonolithicHiddenAPIInfo and provides it for // testing. -func (b *platformBootclasspathModule) createAndProvideMonolithicHiddenAPIInfo(ctx android.ModuleContext, fragments []android.Module) MonolithicHiddenAPIInfo { +func (b *platformBootclasspathModule) createAndProvideMonolithicHiddenAPIInfo(ctx android.ModuleContext, classpathElements ClasspathElements) MonolithicHiddenAPIInfo { // Create a temporary input structure in which to collate information provided directly by this // module, either through properties or direct dependencies. temporaryInput := newHiddenAPIFlagInput() @@ -336,7 +361,7 @@ func (b *platformBootclasspathModule) createAndProvideMonolithicHiddenAPIInfo(ct // Create the monolithic info, by starting with the flag files specified on this and then merging // in information from all the fragment dependencies of this. - monolithicInfo := newMonolithicHiddenAPIInfo(ctx, temporaryInput.FlagFilesByCategory, fragments) + monolithicInfo := newMonolithicHiddenAPIInfo(ctx, temporaryInput.FlagFilesByCategory, classpathElements) // Store the information for testing. ctx.SetProvider(MonolithicHiddenAPIInfoProvider, monolithicInfo) diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go index 0318a07d4..1c2a3aee5 100644 --- a/java/platform_bootclasspath_test.go +++ b/java/platform_bootclasspath_test.go @@ -318,11 +318,41 @@ func TestPlatformBootclasspath_HiddenAPIMonolithicFiles(t *testing.T) { // Make sure that the foo-hiddenapi-annotations.jar is included in the inputs to the rules that // creates the index.csv file. platformBootclasspath := result.ModuleForTests("myplatform-bootclasspath", "android_common") - indexRule := platformBootclasspath.Rule("monolithic_hidden_API_index") - CheckHiddenAPIRuleInputs(t, ` -.intermediates/bar/android_common/javac/bar.jar -.intermediates/foo-hiddenapi-annotations/android_common/javac/foo-hiddenapi-annotations.jar -.intermediates/foo/android_common/javac/foo.jar -`, - indexRule) + + var rule android.TestingBuildParams + + // All the intermediate rules use the same inputs. + expectedIntermediateInputs := ` + out/soong/.intermediates/bar/android_common/javac/bar.jar + out/soong/.intermediates/foo-hiddenapi-annotations/android_common/javac/foo-hiddenapi-annotations.jar + out/soong/.intermediates/foo/android_common/javac/foo.jar + ` + + // Check flags output. + rule = platformBootclasspath.Output("hiddenapi-monolithic/annotation-flags-from-classes.csv") + CheckHiddenAPIRuleInputs(t, "intermediate flags", expectedIntermediateInputs, rule) + + rule = platformBootclasspath.Output("out/soong/hiddenapi/hiddenapi-flags.csv") + CheckHiddenAPIRuleInputs(t, "monolithic flags", ` + out/soong/.intermediates/myplatform-bootclasspath/android_common/hiddenapi-monolithic/annotation-flags-from-classes.csv + out/soong/hiddenapi/hiddenapi-stub-flags.txt + `, rule) + + // Check metadata output. + rule = platformBootclasspath.Output("hiddenapi-monolithic/metadata-from-classes.csv") + CheckHiddenAPIRuleInputs(t, "intermediate metadata", expectedIntermediateInputs, rule) + + rule = platformBootclasspath.Output("out/soong/hiddenapi/hiddenapi-unsupported.csv") + CheckHiddenAPIRuleInputs(t, "monolithic metadata", ` + out/soong/.intermediates/myplatform-bootclasspath/android_common/hiddenapi-monolithic/metadata-from-classes.csv + `, rule) + + // Check index output. + rule = platformBootclasspath.Output("hiddenapi-monolithic/index-from-classes.csv") + CheckHiddenAPIRuleInputs(t, "intermediate index", expectedIntermediateInputs, rule) + + rule = platformBootclasspath.Output("out/soong/hiddenapi/hiddenapi-index.csv") + CheckHiddenAPIRuleInputs(t, "monolithic index", ` + out/soong/.intermediates/myplatform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv + `, rule) } diff --git a/java/sdk_library.go b/java/sdk_library.go index 133deda75..56c40f045 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -631,9 +631,17 @@ type commonToSdkLibraryAndImportProperties struct { Doctag_files []string `android:"path"` } +// commonSdkLibraryAndImportModule defines the interface that must be provided by a module that +// embeds the commonToSdkLibraryAndImport struct. +type commonSdkLibraryAndImportModule interface { + android.SdkAware + + BaseModuleName() string +} + // Common code between sdk library and sdk library import type commonToSdkLibraryAndImport struct { - moduleBase *android.ModuleBase + module commonSdkLibraryAndImportModule scopePaths map[*apiScope]*scopePaths @@ -648,13 +656,13 @@ type commonToSdkLibraryAndImport struct { EmbeddableSdkLibraryComponent } -func (c *commonToSdkLibraryAndImport) initCommon(moduleBase *android.ModuleBase) { - c.moduleBase = moduleBase +func (c *commonToSdkLibraryAndImport) initCommon(module commonSdkLibraryAndImportModule) { + c.module = module - moduleBase.AddProperties(&c.commonSdkLibraryProperties) + module.AddProperties(&c.commonSdkLibraryProperties) // Initialize this as an sdk library component. - c.initSdkLibraryComponent(moduleBase) + c.initSdkLibraryComponent(module) } func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied(ctx android.DefaultableHookContext) bool { @@ -670,7 +678,7 @@ func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied(ctx android // Only track this sdk library if this can be used as a shared library. if c.sharedLibrary() { // Use the name specified in the module definition as the owner. - c.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.moduleBase.BaseModuleName()) + c.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.module.BaseModuleName()) } return true @@ -682,29 +690,29 @@ func (c *commonToSdkLibraryAndImport) generateCommonBuildActions(ctx android.Mod // Module name of the runtime implementation library func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string { - return c.moduleBase.BaseModuleName() + ".impl" + return c.module.BaseModuleName() + ".impl" } // Module name of the XML file for the lib func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string { - return c.moduleBase.BaseModuleName() + sdkXmlFileSuffix + return c.module.BaseModuleName() + sdkXmlFileSuffix } // Name of the java_library module that compiles the stubs source. func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string { - return c.namingScheme.stubsLibraryModuleName(apiScope, c.moduleBase.BaseModuleName()) + baseName := c.module.BaseModuleName() + return c.module.SdkMemberComponentName(baseName, func(name string) string { + return c.namingScheme.stubsLibraryModuleName(apiScope, name) + }) } // Name of the droidstubs module that generates the stubs source and may also // generate/check the API. func (c *commonToSdkLibraryAndImport) stubsSourceModuleName(apiScope *apiScope) string { - return c.namingScheme.stubsSourceModuleName(apiScope, c.moduleBase.BaseModuleName()) -} - -// Name of the droidstubs module that generates/checks the API. Only used if it -// requires different arts to the stubs source generating module. -func (c *commonToSdkLibraryAndImport) apiModuleName(apiScope *apiScope) string { - return c.namingScheme.apiModuleName(apiScope, c.moduleBase.BaseModuleName()) + baseName := c.module.BaseModuleName() + return c.module.SdkMemberComponentName(baseName, func(name string) string { + return c.namingScheme.stubsSourceModuleName(apiScope, name) + }) } // The component names for different outputs of the java_sdk_library. @@ -753,7 +761,7 @@ func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Pat if scope, ok := scopeByName[scopeName]; ok { paths := c.findScopePaths(scope) if paths == nil { - return nil, fmt.Errorf("%q does not provide api scope %s", c.moduleBase.BaseModuleName(), scopeName) + return nil, fmt.Errorf("%q does not provide api scope %s", c.module.BaseModuleName(), scopeName) } switch component { @@ -784,7 +792,7 @@ func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Pat if c.doctagPaths != nil { return c.doctagPaths, nil } else { - return nil, fmt.Errorf("no doctag_files specified on %s", c.moduleBase.BaseModuleName()) + return nil, fmt.Errorf("no doctag_files specified on %s", c.module.BaseModuleName()) } } return nil, nil @@ -830,7 +838,7 @@ func (c *commonToSdkLibraryAndImport) selectHeaderJarsForSdkVersion(ctx android. // If a specific numeric version has been requested then use prebuilt versions of the sdk. if !sdkVersion.ApiLevel.IsPreview() { - return PrebuiltJars(ctx, c.moduleBase.BaseModuleName(), sdkVersion) + return PrebuiltJars(ctx, c.module.BaseModuleName(), sdkVersion) } paths := c.selectScopePaths(ctx, sdkVersion.Kind) @@ -857,7 +865,7 @@ func (c *commonToSdkLibraryAndImport) selectScopePaths(ctx android.BaseModuleCon scopes = append(scopes, s.name) } } - ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.moduleBase.BaseModuleName(), scopes) + ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.module.BaseModuleName(), scopes) return nil } @@ -913,7 +921,7 @@ func (c *commonToSdkLibraryAndImport) sdkComponentPropertiesForChildLibrary() in // any app that includes code which depends (directly or indirectly) on the stubs // library will have the appropriate <uses-library> invocation inserted into its // manifest if necessary. - componentProps.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.moduleBase.BaseModuleName()) + componentProps.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.module.BaseModuleName()) } return componentProps @@ -945,8 +953,8 @@ type EmbeddableSdkLibraryComponent struct { sdkLibraryComponentProperties SdkLibraryComponentProperties } -func (e *EmbeddableSdkLibraryComponent) initSdkLibraryComponent(moduleBase *android.ModuleBase) { - moduleBase.AddProperties(&e.sdkLibraryComponentProperties) +func (e *EmbeddableSdkLibraryComponent) initSdkLibraryComponent(module android.Module) { + module.AddProperties(&e.sdkLibraryComponentProperties) } // to satisfy SdkLibraryComponentDependency @@ -1168,6 +1176,10 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) module.Library.GenerateAndroidBuildActions(ctx) } + // Collate the components exported by this module. All scope specific modules are exported but + // the impl and xml component modules are not. + exportedComponents := map[string]struct{}{} + // Record the paths to the header jars of the library (stubs and impl). // When this java_sdk_library is depended upon from others via "libs" property, // the recorded paths will be returned depending on the link type of the caller. @@ -1182,8 +1194,14 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) // Extract information from the dependency. The exact information extracted // is determined by the nature of the dependency which is determined by the tag. scopeTag.extractDepInfo(ctx, to, scopePaths) + + exportedComponents[ctx.OtherModuleName(to)] = struct{}{} } }) + + // Make the set of components exported by this module available for use elsewhere. + exportedComponentInfo := android.ExportedComponentsInfo{Components: android.SortedStringKeys(exportedComponents)} + ctx.SetProvider(android.ExportedComponentsInfoProvider, exportedComponentInfo) } func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries { @@ -1709,7 +1727,7 @@ func (module *SdkLibrary) InitSdkLibraryProperties() { module.addHostAndDeviceProperties() module.AddProperties(&module.sdkLibraryProperties) - module.initSdkLibraryComponent(&module.ModuleBase) + module.initSdkLibraryComponent(module) module.properties.Installable = proptools.BoolPtr(true) module.deviceProperties.IsSDKLibrary = true @@ -1728,8 +1746,6 @@ type sdkLibraryComponentNamingScheme interface { stubsLibraryModuleName(scope *apiScope, baseName string) string stubsSourceModuleName(scope *apiScope, baseName string) string - - apiModuleName(scope *apiScope, baseName string) string } type defaultNamingScheme struct { @@ -1743,10 +1759,6 @@ func (s *defaultNamingScheme) stubsSourceModuleName(scope *apiScope, baseName st return scope.stubsSourceModuleName(baseName) } -func (s *defaultNamingScheme) apiModuleName(scope *apiScope, baseName string) string { - return scope.apiModuleName(baseName) -} - var _ sdkLibraryComponentNamingScheme = (*defaultNamingScheme)(nil) func moduleStubLinkType(name string) (stub bool, ret sdkLinkType) { @@ -1780,7 +1792,7 @@ func SdkLibraryFactory() android.Module { module := &SdkLibrary{} // Initialize information common between source and prebuilt. - module.initCommon(&module.ModuleBase) + module.initCommon(module) module.InitSdkLibraryProperties() android.InitApexModule(module) @@ -1928,7 +1940,7 @@ func sdkLibraryImportFactory() android.Module { module.AddProperties(&module.properties, allScopeProperties) // Initialize information common between source and prebuilt. - module.initCommon(&module.ModuleBase) + module.initCommon(module) android.InitPrebuiltModule(module, &[]string{""}) android.InitApexModule(module) @@ -2140,7 +2152,7 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo // 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 { + if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(module.BaseModuleName())); dexOutputPath != nil { module.dexJarFile = dexOutputPath module.initHiddenAPI(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil) } else { @@ -2265,6 +2277,13 @@ func (module *SdkLibraryImport) ImplementationAndResourcesJars() android.Paths { } } +var _ android.RequiredFilesFromPrebuiltApex = (*SdkLibraryImport)(nil) + +func (module *SdkLibraryImport) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string { + name := module.BaseModuleName() + return requiredFilesFromPrebuiltApexForImport(name) +} + // // java_sdk_library_xml // diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go index 2520dde6e..65af95314 100644 --- a/java/sdk_library_test.go +++ b/java/sdk_library_test.go @@ -110,7 +110,7 @@ func TestJavaSdkLibrary(t *testing.T) { `) // check the existence of the internal modules - result.ModuleForTests("foo", "android_common") + foo := 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") @@ -122,6 +122,17 @@ func TestJavaSdkLibrary(t *testing.T) { result.ModuleForTests("foo.api.system.28", "") result.ModuleForTests("foo.api.test.28", "") + exportedComponentsInfo := result.ModuleProvider(foo.Module(), ExportedComponentsInfoProvider).(ExportedComponentsInfo) + expectedFooExportedComponents := []string{ + "foo.stubs", + "foo.stubs.source", + "foo.stubs.source.system", + "foo.stubs.source.test", + "foo.stubs.system", + "foo.stubs.test", + } + android.AssertArrayString(t, "foo exported components", expectedFooExportedComponents, exportedComponentsInfo.Components) + bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac") // tests if baz is actually linked to the stubs lib android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.system.jar") diff --git a/java/testing.go b/java/testing.go index 1fef337cc..7b452f762 100644 --- a/java/testing.go +++ b/java/testing.go @@ -17,6 +17,7 @@ package java import ( "fmt" "reflect" + "regexp" "sort" "strings" "testing" @@ -393,12 +394,20 @@ func CheckPlatformBootclasspathFragments(t *testing.T, result *android.TestResul android.AssertDeepEquals(t, fmt.Sprintf("%s fragments", "platform-bootclasspath"), expected, pairs) } -func CheckHiddenAPIRuleInputs(t *testing.T, expected string, hiddenAPIRule android.TestingBuildParams) { +func CheckHiddenAPIRuleInputs(t *testing.T, message string, expected string, hiddenAPIRule android.TestingBuildParams) { t.Helper() - actual := strings.TrimSpace(strings.Join(android.NormalizePathsForTesting(hiddenAPIRule.Implicits), "\n")) - expected = strings.TrimSpace(expected) + inputs := android.Paths{} + if hiddenAPIRule.Input != nil { + inputs = append(inputs, hiddenAPIRule.Input) + } + inputs = append(inputs, hiddenAPIRule.Inputs...) + inputs = append(inputs, hiddenAPIRule.Implicits...) + inputs = android.SortedUniquePaths(inputs) + actual := strings.TrimSpace(strings.Join(inputs.RelativeToTop().Strings(), "\n")) + re := regexp.MustCompile(`\n\s+`) + expected = strings.TrimSpace(re.ReplaceAllString(expected, "\n")) if actual != expected { - t.Errorf("Expected hiddenapi rule inputs:\n%s\nactual inputs:\n%s", expected, actual) + t.Errorf("Expected hiddenapi rule inputs - %s:\n%s\nactual inputs:\n%s", message, expected, actual) } } |