diff options
Diffstat (limited to 'java')
-rw-r--r-- | java/Android.bp | 1 | ||||
-rw-r--r-- | java/base.go | 48 | ||||
-rw-r--r-- | java/boot_jars.go | 101 | ||||
-rw-r--r-- | java/bootclasspath_fragment.go | 364 | ||||
-rw-r--r-- | java/builder.go | 17 | ||||
-rw-r--r-- | java/classpath_element.go | 229 | ||||
-rw-r--r-- | java/classpath_fragment.go | 49 | ||||
-rw-r--r-- | java/dexpreopt_bootjars.go | 110 | ||||
-rw-r--r-- | java/dexpreopt_config.go | 8 | ||||
-rw-r--r-- | java/droiddoc.go | 17 | ||||
-rw-r--r-- | java/droidstubs.go | 11 | ||||
-rw-r--r-- | java/hiddenapi.go | 1 | ||||
-rw-r--r-- | java/hiddenapi_modular.go | 350 | ||||
-rw-r--r-- | java/hiddenapi_monolithic.go | 2 | ||||
-rw-r--r-- | java/java.go | 25 | ||||
-rw-r--r-- | java/java_test.go | 92 | ||||
-rw-r--r-- | java/kotlin_test.go | 1 | ||||
-rw-r--r-- | java/legacy_core_platform_api_usage.go | 3 | ||||
-rw-r--r-- | java/lint.go | 5 | ||||
-rw-r--r-- | java/platform_bootclasspath.go | 30 | ||||
-rw-r--r-- | java/platform_bootclasspath_test.go | 112 | ||||
-rw-r--r-- | java/rro.go | 19 | ||||
-rw-r--r-- | java/rro_test.go | 56 | ||||
-rw-r--r-- | java/sdk_library.go | 30 | ||||
-rw-r--r-- | java/sdk_library_test.go | 34 | ||||
-rw-r--r-- | java/systemserver_classpath_fragment.go | 8 |
26 files changed, 1141 insertions, 582 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 440b00478..a7cc58e33 100644 --- a/java/base.go +++ b/java/base.go @@ -155,6 +155,13 @@ type CommonProperties struct { // List of java_plugin modules that provide extra errorprone checks. Extra_check_modules []string + + // This property can be in 3 states. When set to true, errorprone will + // be run during the regular build. When set to false, errorprone will + // never be run. When unset, errorprone will be run when the RUN_ERROR_PRONE + // environment variable is true. Setting this to false will improve build + // performance more than adding -XepDisableAllChecks in javacflags. + Enabled *bool } Proto struct { @@ -701,7 +708,8 @@ func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaB // javaVersion flag. flags.javaVersion = getJavaVersion(ctx, String(j.properties.Java_version), android.SdkContext(j)) - if ctx.Config().RunErrorProne() { + epEnabled := j.properties.Errorprone.Enabled + if (ctx.Config().RunErrorProne() && epEnabled == nil) || Bool(epEnabled) { if config.ErrorProneClasspath == nil && ctx.Config().TestProductVariables == nil { ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?") } @@ -972,14 +980,23 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { } if len(uniqueSrcFiles) > 0 || len(srcJars) > 0 { var extraJarDeps android.Paths - if ctx.Config().RunErrorProne() { - // If error-prone is enabled, add an additional rule to compile the java files into - // a separate set of classes (so that they don't overwrite the normal ones and require - // a rebuild when error-prone is turned off). - // TODO(ccross): Once we always compile with javac9 we may be able to conditionally - // enable error-prone without affecting the output class files. + if Bool(j.properties.Errorprone.Enabled) { + // If error-prone is enabled, enable errorprone flags on the regular + // build. + flags = enableErrorproneFlags(flags) + } else if ctx.Config().RunErrorProne() && j.properties.Errorprone.Enabled == nil { + // Otherwise, if the RUN_ERROR_PRONE environment variable is set, create + // a new jar file just for compiling with the errorprone compiler to. + // This is because we don't want to cause the java files to get completely + // rebuilt every time the state of the RUN_ERROR_PRONE variable changes. + // We also don't want to run this if errorprone is enabled by default for + // this module, or else we could have duplicated errorprone messages. + errorproneFlags := enableErrorproneFlags(flags) errorprone := android.PathForModuleOut(ctx, "errorprone", jarName) - RunErrorProne(ctx, errorprone, uniqueSrcFiles, srcJars, flags) + + transformJavaToClasses(ctx, errorprone, -1, uniqueSrcFiles, srcJars, errorproneFlags, nil, + "errorprone", "errorprone") + extraJarDeps = append(extraJarDeps, errorprone) } @@ -1303,6 +1320,21 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { j.outputFile = outputFile.WithoutRel() } +// Returns a copy of the supplied flags, but with all the errorprone-related +// fields copied to the regular build's fields. +func enableErrorproneFlags(flags javaBuilderFlags) javaBuilderFlags { + flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...) + + if len(flags.errorProneExtraJavacFlags) > 0 { + if len(flags.javacFlags) > 0 { + flags.javacFlags += " " + flags.errorProneExtraJavacFlags + } else { + flags.javacFlags = flags.errorProneExtraJavacFlags + } + } + return flags +} + func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, idx int, srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.WritablePath { 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 7936c2bb4..4d23820fc 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 @@ -140,14 +144,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 []hiddenAPIModule, 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) @@ -233,14 +252,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 @@ -293,11 +304,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 { @@ -307,7 +318,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 } @@ -315,6 +326,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 @@ -406,102 +419,93 @@ 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 extractBootDexJarsFromHiddenAPIModules 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 if "art" == proptools.String(b.properties.Image_name) { // ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH - classpathJars = configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH) + classpathJars = configuredJarListToClasspathJars(ctx, b.configuredJars(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH) } else { - classpathJars = configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), b.classpathType) + classpathJars = configuredJarListToClasspathJars(ctx, b.configuredJars(ctx), b.classpathType) } b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars) } -func (b *BootclasspathFragmentModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList { +func (b *BootclasspathFragmentModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList { if "art" == proptools.String(b.properties.Image_name) { return b.getImageConfig(ctx).modules } @@ -522,7 +526,16 @@ func (b *BootclasspathFragmentModule) ClasspathFragmentToConfiguredJarList(ctx a // Only create configs for updatable boot jars. Non-updatable boot jars must be part of the // platform_bootclasspath's classpath proto config to guarantee that they come before any // updatable jars at runtime. - return global.UpdatableBootJars.Filter(stems) + jars := global.UpdatableBootJars.Filter(stems) + + // TODO(satayev): for apex_test we want to include all contents unconditionally to classpaths + // config. However, any test specific jars would not be present in UpdatableBootJars. Instead, + // we should check if we are creating a config for apex_test via ApexInfo and amend the values. + // This is an exception to support end-to-end test for SdkExtensions, until such support exists. + if android.InList("test_framework-sdkextensions", stems) { + jars = jars.Append("com.android.sdkext", "test_framework-sdkextensions") + } + return jars } func (b *BootclasspathFragmentModule) getImageConfig(ctx android.EarlyModuleContext) *bootImageConfig { @@ -545,12 +558,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 @@ -558,12 +571,16 @@ func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android. // TODO(b/179354495): Stop hidden API processing being conditional once all bootclasspath_fragment // modules have been updated to support it. if input.canPerformHiddenAPIProcessing(ctx, b.properties) { - // Get the content modules that contribute to the hidden API processing. - hiddenAPIModules := gatherHiddenAPIModuleFromContents(ctx, contents) - // 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, hiddenAPIModules, 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 +597,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 +607,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 +644,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 []hiddenAPIModule, 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,9 +697,6 @@ func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android. return false } - // Copy the dex jars of this fragment's content modules to their predefined locations. - copyBootJarsToPredefinedLocations(ctx, contents, imageConfig.modules, imageConfig.dexPaths) - // Build a profile for the image config and then use that to build the boot image. profile := bootImageProfileRule(ctx, imageConfig) buildBootImage(ctx, imageConfig, profile) @@ -766,7 +816,7 @@ func (b *bootclasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android. // Copy manually curated flag files specified on the bootclasspath_fragment. if b.Flag_files_by_category != nil { - for _, category := range hiddenAPIFlagFileCategories { + for _, category := range HiddenAPIFlagFileCategories { paths := b.Flag_files_by_category[category] if len(paths) > 0 { dests := []string{} @@ -775,7 +825,7 @@ func (b *bootclasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android. builder.CopyToSnapshot(p, dest) dests = append(dests, dest) } - hiddenAPISet.AddProperty(category.propertyName, dests) + hiddenAPISet.AddProperty(category.PropertyName, dests) } } } @@ -841,9 +891,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 []hiddenAPIModule, _ 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. @@ -852,19 +901,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/builder.go b/java/builder.go index cde87310f..ea011b8e1 100644 --- a/java/builder.go +++ b/java/builder.go @@ -279,23 +279,6 @@ func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.Writab transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, flags, deps, "javac", desc) } -func RunErrorProne(ctx android.ModuleContext, outputFile android.WritablePath, - srcFiles, srcJars android.Paths, flags javaBuilderFlags) { - - flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...) - - if len(flags.errorProneExtraJavacFlags) > 0 { - if len(flags.javacFlags) > 0 { - flags.javacFlags += " " + flags.errorProneExtraJavacFlags - } else { - flags.javacFlags = flags.errorProneExtraJavacFlags - } - } - - transformJavaToClasses(ctx, outputFile, -1, srcFiles, srcJars, flags, nil, - "errorprone", "errorprone") -} - // Emits the rule to generate Xref input file (.kzip file) for the given set of source files and source jars // to compile with given set of builder flags, etc. func emitXrefRule(ctx android.ModuleContext, xrefFile android.WritablePath, idx int, 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/classpath_fragment.go b/java/classpath_fragment.go index 0e14d24f2..3d7258086 100644 --- a/java/classpath_fragment.go +++ b/java/classpath_fragment.go @@ -19,6 +19,7 @@ package java import ( "fmt" "github.com/google/blueprint" + "github.com/google/blueprint/proptools" "strings" "android/soong/android" @@ -44,6 +45,11 @@ func (c classpathType) String() string { } type classpathFragmentProperties struct { + // Whether to generated classpaths.proto config instance for the fragment. If the config is not + // generated, then relevant boot jars are added to platform classpath, i.e. platform_bootclasspath + // or platform_systemserverclasspath. This is useful for non-updatable APEX boot jars, to keep + // them as part of dexopt on device. Defaults to true. + Generate_classpaths_proto *bool } // classpathFragment interface is implemented by a module that contributes jars to a *CLASSPATH @@ -52,10 +58,6 @@ type classpathFragment interface { android.Module classpathFragmentBase() *ClasspathFragmentBase - - // ClasspathFragmentToConfiguredJarList returns android.ConfiguredJarList representation of all - // the jars in this classpath fragment. - ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList } // ClasspathFragmentBase is meant to be embedded in any module types that implement classpathFragment; @@ -105,24 +107,28 @@ func configuredJarListToClasspathJars(ctx android.ModuleContext, configuredJars } func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, jars []classpathJar) { - outputFilename := strings.ToLower(c.classpathType.String()) + ".pb" - c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath - c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths") - - generatedJson := android.PathForModuleOut(ctx, outputFilename+".json") - writeClasspathsJson(ctx, generatedJson, jars) - - rule := android.NewRuleBuilder(pctx, ctx) - rule.Command(). - BuiltTool("conv_classpaths_proto"). - Flag("encode"). - Flag("--format=json"). - FlagWithInput("--input=", generatedJson). - FlagWithOutput("--output=", c.outputFilepath) - - rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String()) + generateProto := proptools.BoolDefault(c.properties.Generate_classpaths_proto, true) + if generateProto { + outputFilename := strings.ToLower(c.classpathType.String()) + ".pb" + c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath + c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths") + + generatedJson := android.PathForModuleOut(ctx, outputFilename+".json") + writeClasspathsJson(ctx, generatedJson, jars) + + rule := android.NewRuleBuilder(pctx, ctx) + rule.Command(). + BuiltTool("conv_classpaths_proto"). + Flag("encode"). + Flag("--format=json"). + FlagWithInput("--input=", generatedJson). + FlagWithOutput("--output=", c.outputFilepath) + + rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String()) + } classpathProtoInfo := ClasspathFragmentProtoContentInfo{ + ClasspathFragmentProtoGenerated: generateProto, ClasspathFragmentProtoInstallDir: c.installDirPath, ClasspathFragmentProtoOutput: c.outputFilepath, } @@ -168,6 +174,9 @@ func (c *ClasspathFragmentBase) androidMkEntries() []android.AndroidMkEntries { var ClasspathFragmentProtoContentInfoProvider = blueprint.NewProvider(ClasspathFragmentProtoContentInfo{}) type ClasspathFragmentProtoContentInfo struct { + // Whether the classpaths.proto config is generated for the fragment. + ClasspathFragmentProtoGenerated bool + // ClasspathFragmentProtoOutput is an output path for the generated classpaths.proto config of this module. // // The file should be copied to a relevant place on device, see ClasspathFragmentProtoInstallDir diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index e1a36507a..bb857845a 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -15,7 +15,6 @@ package java import ( - "fmt" "path/filepath" "sort" "strings" @@ -254,6 +253,9 @@ type bootImageConfig struct { dexPaths android.WritablePaths // for this image dexPathsDeps android.WritablePaths // for the dependency images and in this image + // Map from module name (without prebuilt_ prefix) to the predefined build path. + dexPathsByModule map[string]android.WritablePath + // File path to a zip archive with all image files (or nil, if not needed). zip android.WritablePath @@ -276,13 +278,24 @@ type bootImageVariant struct { dexLocationsDeps []string // for the dependency images and in this image // Paths to image files. - imagePathOnHost android.OutputPath // first image file path on host - imagePathOnDevice string // first image file path on device - imagesDeps android.OutputPaths // all files + imagePathOnHost android.OutputPath // first image file path on host + imagePathOnDevice string // first image file path on device + + // All the files that constitute this image variant, i.e. .art, .oat and .vdex files. + imagesDeps android.OutputPaths - // Only for extensions, paths to the primary boot images. + // The path to the primary image variant's imagePathOnHost field, where primary image variant + // means the image variant that this extends. + // + // This is only set for a variant of an image that extends another image. primaryImages android.OutputPath + // The paths to the primary image variant's imagesDeps field, where primary image variant + // means the image variant that this extends. + // + // This is only set for a variant of an image that extends another image. + primaryImagesDeps android.Paths + // Rules which should be used in make to install the outputs. installs android.RuleBuilderInstalls vdexInstalls android.RuleBuilderInstalls @@ -345,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 @@ -450,59 +476,33 @@ func shouldBuildBootImages(config android.Config, global *dexpreopt.GlobalConfig return true } -// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to -// predefined paths in the global config. -func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, bootModules []android.Module, bootjars android.ConfiguredJarList, jarPathsPredefined android.WritablePaths) { - jarPaths := make(android.Paths, bootjars.Len()) - for i, module := range bootModules { - if module != nil { - bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath() - jarPaths[i] = bootDexJar - - name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(module)) - if bootjars.Jar(i) != name { - ctx.ModuleErrorf("expected module %s at position %d but found %s", bootjars.Jar(i), i, name) - } - } - } - - // The paths to bootclasspath DEX files need to be known at module GenerateAndroidBuildAction - // time, before the boot images are built (these paths are used in dexpreopt rule generation for - // Java libraries and apps). Generate rules that copy bootclasspath DEX jars to the predefined - // paths. - for i := range jarPaths { - input := jarPaths[i] - output := jarPathsPredefined[i] - module := bootjars.Jar(i) - if input == nil { - if ctx.Config().AllowMissingDependencies() { - apex := bootjars.Apex(i) - - // 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: output, - Args: map[string]string{ - "error": fmt.Sprintf("missing dependencies: dex jar for %s:%s", module, apex), - }, - }) - } else { - ctx.ModuleErrorf("failed to find a dex jar path for module '%s'"+ - ", note that some jars may be filtered out by module constraints", module) - } - +// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to predefined +// paths in the global config. +func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJarsByModule bootDexJarByModule, dstBootJarsByModule map[string]android.WritablePath) { + // Create the super set of module names. + names := []string{} + names = append(names, android.SortedStringKeys(srcBootDexJarsByModule)...) + names = append(names, android.SortedStringKeys(dstBootJarsByModule)...) + names = android.SortedUniqueStrings(names) + for _, name := range names { + src := srcBootDexJarsByModule[name] + dst := dstBootJarsByModule[name] + + if src == nil { + ctx.ModuleErrorf("module %s does not provide a dex boot jar", name) + } else if dst == nil { + ctx.ModuleErrorf("module %s is not part of the boot configuration", name) } else { ctx.Build(pctx, android.BuildParams{ Rule: android.Cp, - Input: input, - Output: output, + Input: src, + Output: dst, }) } } } -// 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 { @@ -588,7 +588,15 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p cmd. Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", image.dexPathsDeps.Paths(), ":"). Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", image.dexLocationsDeps, ":"). - FlagWithArg("--boot-image=", dexpreopt.PathToLocation(artImage, arch)).Implicit(artImage) + // Add the path to the first file in the boot image with the arch specific directory removed, + // dex2oat will reconstruct the path to the actual file when it needs it. As the actual path + // to the file cannot be passed to the command make sure to add the actual path as an Implicit + // dependency to ensure that it is built before the command runs. + FlagWithArg("--boot-image=", dexpreopt.PathToLocation(artImage, arch)).Implicit(artImage). + // Similarly, the dex2oat tool will automatically find the paths to other files in the base + // boot image so make sure to add them as implicit dependencies to ensure that they are built + // before this command is run. + Implicits(image.primaryImagesDeps) } else { // It is a primary image, so it needs a base address. cmd.FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress()) diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go index 39a3e11a5..b13955fba 100644 --- a/java/dexpreopt_config.go +++ b/java/dexpreopt_config.go @@ -100,6 +100,7 @@ func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { // TODO(b/143682396): use module dependencies instead inputDir := deviceDir.Join(ctx, "dex_"+c.name+"jars_input") c.dexPaths = c.modules.BuildPaths(ctx, inputDir) + c.dexPathsByModule = c.modules.BuildPathsByModule(ctx, inputDir) c.dexPathsDeps = c.dexPaths // Create target-specific variants. @@ -125,6 +126,7 @@ func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { frameworkCfg.dexPathsDeps = append(artCfg.dexPathsDeps, frameworkCfg.dexPathsDeps...) for i := range targets { frameworkCfg.variants[i].primaryImages = artCfg.variants[i].imagePathOnHost + frameworkCfg.variants[i].primaryImagesDeps = artCfg.variants[i].imagesDeps.Paths() frameworkCfg.variants[i].dexLocationsDeps = append(artCfg.variants[i].dexLocations, frameworkCfg.variants[i].dexLocationsDeps...) } @@ -152,6 +154,9 @@ type updatableBootConfig struct { // later on a singleton adds commands to copy actual jars to the predefined paths. dexPaths android.WritablePaths + // Map from module name (without prebuilt_ prefix) to the predefined build path. + dexPathsByModule map[string]android.WritablePath + // A list of dex locations (a.k.a. on-device paths) to the boot jars. dexLocations []string } @@ -165,10 +170,11 @@ func GetUpdatableBootConfig(ctx android.PathContext) updatableBootConfig { dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "updatable_bootjars") dexPaths := updatableBootJars.BuildPaths(ctx, dir) + dexPathsByModuleName := updatableBootJars.BuildPathsByModule(ctx, dir) dexLocations := updatableBootJars.DevicePaths(ctx.Config(), android.Android) - return updatableBootConfig{updatableBootJars, dexPaths, dexLocations} + return updatableBootConfig{updatableBootJars, dexPaths, dexPathsByModuleName, dexLocations} }).(updatableBootConfig) } diff --git a/java/droiddoc.go b/java/droiddoc.go index 56e62473f..869a5982d 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -425,23 +425,6 @@ func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps { } srcFiles = filterByPackage(srcFiles, j.properties.Filter_packages) - // While metalava needs package html files, it does not need them to be explicit on the command - // line. javadoc complains if it receives html files on the command line. The filter - // below excludes html files from the rsp file metalava. Note that the html - // files are still included as implicit inputs for successful remote execution and correct - // incremental builds. - filterHtml := func(srcs []android.Path) []android.Path { - filtered := []android.Path{} - for _, src := range srcs { - if src.Ext() == ".html" { - continue - } - filtered = append(filtered, src) - } - return filtered - } - srcFiles = filterHtml(srcFiles) - aidlFlags := j.collectAidlFlags(ctx, deps) srcFiles = j.genSources(ctx, srcFiles, aidlFlags) diff --git a/java/droidstubs.go b/java/droidstubs.go index 17c7a7bf7..d348b5556 100644 --- a/java/droidstubs.go +++ b/java/droidstubs.go @@ -392,8 +392,7 @@ func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *a } func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths, - srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths, - homeDir android.WritablePath) *android.RuleBuilderCommand { + srcJarList android.Path, bootclasspath, classpath classpath, homeDir android.WritablePath) *android.RuleBuilderCommand { rule.Command().Text("rm -rf").Flag(homeDir.String()) rule.Command().Text("mkdir -p").Flag(homeDir.String()) @@ -430,12 +429,6 @@ func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersi cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":") } - if len(sourcepaths) > 0 { - cmd.FlagWithList("-sourcepath ", sourcepaths.Strings(), ":") - } else { - cmd.FlagWithArg("-sourcepath ", `""`) - } - cmd.Flag("--no-banner"). Flag("--color"). Flag("--quiet"). @@ -479,7 +472,7 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { homeDir := android.PathForModuleOut(ctx, "metalava", "home") cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList, - deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths, homeDir) + deps.bootClasspath, deps.classpath, homeDir) cmd.Implicits(d.Javadoc.implicits) d.stubsFlags(ctx, cmd, stubsDir) diff --git a/java/hiddenapi.go b/java/hiddenapi.go index e9693c68e..f901434a0 100644 --- a/java/hiddenapi.go +++ b/java/hiddenapi.go @@ -219,7 +219,6 @@ func buildRuleToGenerateIndex(ctx android.ModuleContext, desc string, classesJar BuiltTool("merge_csv"). Flag("--zip_input"). Flag("--key_field signature"). - FlagWithArg("--header=", "signature,file,startline,startcol,endline,endcol,properties"). FlagWithOutput("--output=", indexCSV). Inputs(classesJars) rule.Build(desc, desc) diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index f2649d3c0..de72b39aa 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -248,8 +248,8 @@ type HiddenAPIFlagFileProperties struct { } type hiddenAPIFlagFileCategory struct { - // propertyName is the name of the property for this category. - propertyName string + // PropertyName is the name of the property for this category. + PropertyName string // propertyValueReader retrieves the value of the property for this category from the set of // properties. @@ -262,12 +262,12 @@ type hiddenAPIFlagFileCategory struct { // The flag file category for removed members of the API. // -// This is extracted from hiddenAPIFlagFileCategories as it is needed to add the dex signatures +// This is extracted from HiddenAPIFlagFileCategories as it is needed to add the dex signatures // list of removed API members that are generated automatically from the removed.txt files provided // by API stubs. var hiddenAPIRemovedFlagFileCategory = &hiddenAPIFlagFileCategory{ // See HiddenAPIFlagFileProperties.Removed - propertyName: "removed", + PropertyName: "removed", propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { return properties.Removed }, @@ -276,10 +276,10 @@ var hiddenAPIRemovedFlagFileCategory = &hiddenAPIFlagFileCategory{ }, } -var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ +var HiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ // See HiddenAPIFlagFileProperties.Unsupported { - propertyName: "unsupported", + PropertyName: "unsupported", propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { return properties.Unsupported }, @@ -290,7 +290,7 @@ var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ hiddenAPIRemovedFlagFileCategory, // See HiddenAPIFlagFileProperties.Max_target_r_low_priority { - propertyName: "max_target_r_low_priority", + PropertyName: "max_target_r_low_priority", propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { return properties.Max_target_r_low_priority }, @@ -300,7 +300,7 @@ var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ }, // See HiddenAPIFlagFileProperties.Max_target_q { - propertyName: "max_target_q", + PropertyName: "max_target_q", propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { return properties.Max_target_q }, @@ -310,7 +310,7 @@ var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ }, // See HiddenAPIFlagFileProperties.Max_target_p { - propertyName: "max_target_p", + PropertyName: "max_target_p", propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { return properties.Max_target_p }, @@ -320,7 +320,7 @@ var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ }, // See HiddenAPIFlagFileProperties.Max_target_o_low_priority { - propertyName: "max_target_o_low_priority", + PropertyName: "max_target_o_low_priority", propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { return properties.Max_target_o_low_priority }, @@ -330,7 +330,7 @@ var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ }, // See HiddenAPIFlagFileProperties.Blocked { - propertyName: "blocked", + PropertyName: "blocked", propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { return properties.Blocked }, @@ -340,7 +340,7 @@ var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ }, // See HiddenAPIFlagFileProperties.Unsupported_packages { - propertyName: "unsupported_packages", + PropertyName: "unsupported_packages", propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { return properties.Unsupported_packages }, @@ -355,7 +355,7 @@ type FlagFilesByCategory map[*hiddenAPIFlagFileCategory]android.Paths // append appends the supplied flags files to the corresponding category in this map. func (s FlagFilesByCategory) append(other FlagFilesByCategory) { - for _, category := range hiddenAPIFlagFileCategories { + for _, category := range HiddenAPIFlagFileCategories { s[category] = append(s[category], other[category]...) } } @@ -540,7 +540,7 @@ func (i *HiddenAPIFlagInput) gatherStubLibInfo(ctx android.ModuleContext, conten // extractFlagFilesFromProperties extracts the paths to flag files that are specified in the // supplied properties and stores them in this struct. func (i *HiddenAPIFlagInput) extractFlagFilesFromProperties(ctx android.ModuleContext, p *HiddenAPIFlagFileProperties) { - for _, category := range hiddenAPIFlagFileCategories { + for _, category := range HiddenAPIFlagFileCategories { paths := android.PathsForModuleSrc(ctx, category.propertyValueReader(p)) i.FlagFilesByCategory[category] = paths } @@ -571,6 +571,45 @@ type HiddenAPIFlagOutput struct { AllFlagsPath android.Path } +// bootDexJarByModule is a map from base module name (without prebuilt_ prefix) to the boot dex +// path. +type bootDexJarByModule map[string]android.Path + +// addPath adds the path for a module to the map. +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. // @@ -630,7 +669,7 @@ func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc st FlagWithOutput("--output ", tempPath) // Add the options for the different categories of flag files. - for _, category := range hiddenAPIFlagFileCategories { + for _, category := range HiddenAPIFlagFileCategories { paths := flagFilesByCategory[category] for _, path := range paths { category.commandMutator(command, path) @@ -656,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. @@ -670,19 +709,20 @@ func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc st // * metadata.csv // * index.csv // * all-flags.csv -func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext, contents []hiddenAPIModule, 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 := extractBootDexJarsFromHiddenAPIModules(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. - classesJars := extractClassJarsFromHiddenAPIModules(ctx, contents) + classesJars := extractClassesJarsFromModules(contents) // Generate the set of flags from the annotations in the source code. annotationFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "annotation-flags.csv") @@ -706,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, 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 } @@ -737,58 +790,215 @@ func buildRuleToGenerateRemovedDexSignatures(ctx android.ModuleContext, removedT return android.OptionalPathForPath(output) } -// gatherHiddenAPIModuleFromContents gathers the hiddenAPIModule from the supplied contents. -func gatherHiddenAPIModuleFromContents(ctx android.ModuleContext, contents []android.Module) []hiddenAPIModule { - hiddenAPIModules := []hiddenAPIModule{} +// extractBootDexJarsFromModules extracts the boot dex jars from the supplied modules. +func extractBootDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule { + bootDexJars := bootDexJarByModule{} for _, module := range contents { - if hiddenAPI, ok := module.(hiddenAPIModule); ok { - hiddenAPIModules = append(hiddenAPIModules, hiddenAPI) - } else if _, ok := module.(*DexImport); ok { - // Ignore this for the purposes of hidden API processing - } else { - ctx.ModuleErrorf("module %s does not implement hiddenAPIModule", module) + hiddenAPIModule := hiddenAPIModuleFromModule(ctx, module) + if hiddenAPIModule == nil { + continue } + bootDexJar := retrieveBootDexJarFromHiddenAPIModule(ctx, hiddenAPIModule) + bootDexJars.addPath(module, bootDexJar) + } + return bootDexJars +} + +func hiddenAPIModuleFromModule(ctx android.BaseModuleContext, module android.Module) hiddenAPIModule { + if hiddenAPIModule, ok := module.(hiddenAPIModule); ok { + return hiddenAPIModule + } else if _, ok := module.(*DexImport); ok { + // Ignore this for the purposes of hidden API processing + } else { + ctx.ModuleErrorf("module %s does not implement hiddenAPIModule", module) } - return hiddenAPIModules + + return nil } -// extractBootDexJarsFromHiddenAPIModules extracts the boot dex jars from the supplied modules. -func extractBootDexJarsFromHiddenAPIModules(ctx android.ModuleContext, contents []hiddenAPIModule) android.Paths { - bootDexJars := android.Paths{} +// 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 { - bootDexJar := module.bootDexJar() - if bootDexJar == nil { - if ctx.Config().AlwaysUsePrebuiltSdks() { - // TODO(b/179354495): Remove this work around 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) + hiddenAPIModule := module.(hiddenAPIModule) + bootDexJar := retrieveBootDexJarFromHiddenAPIModule(ctx, hiddenAPIModule) + bootDexJarsByModule[module.Name()] = bootDexInfo{ + path: bootDexJar, + uncompressDex: *hiddenAPIModule.uncompressDex(), } } - return bootDexJars + + 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 } -// extractClassJarsFromHiddenAPIModules extracts the class jars from the supplied modules. -func extractClassJarsFromHiddenAPIModules(ctx android.ModuleContext, contents []hiddenAPIModule) android.Paths { +// extractClassesJarsFromModules extracts the class jars from the supplied modules. +func extractClassesJarsFromModules(contents []android.Module) android.Paths { classesJars := android.Paths{} for _, module := range contents { - classesJars = append(classesJars, module.classesJars()...) + classesJars = append(classesJars, retrieveClassesJarsFromModule(module)...) } return classesJars } + +// retrieveClassesJarsFromModule retrieves the classes jars from the supplied module. +func retrieveClassesJarsFromModule(module android.Module) android.Paths { + if hiddenAPIModule, ok := module.(hiddenAPIModule); ok { + return hiddenAPIModule.classesJars() + } + + return nil +} + +// deferReportingMissingBootDexJar returns true if a missing boot dex jar should not be reported by +// Soong but should instead only be reported in ninja if the file is actually built. +func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.Module) bool { + // 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. + if ctx.Config().AlwaysUsePrebuiltSdks() { + 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. + // Ideally, a bootclasspath_fragment module should never have a platform variant created for it + // but unfortunately, due to b/187910671 it does. + // + // That causes issues when obtaining a boot dex jar for a prebuilt module as a prebuilt module + // used by a bootclasspath_fragment can only provide a boot dex jar when it is part of APEX, i.e. + // has an APEX variant not a platform variant. + // + // There are some other situations when a prebuilt module used by a bootclasspath_fragment cannot + // provide a boot dex jar: + // 1. If the bootclasspath_fragment is not exported by the prebuilt_apex/apex_set module then it + // does not have an APEX variant and only has a platform variant and neither do its content + // modules. + // 2. Some build configurations, e.g. setting TARGET_BUILD_USE_PREBUILT_SDKS causes all + // java_sdk_library_import modules to be treated as preferred and as many of them are not part + // of an apex they cannot provide a boot dex jar. + // + // The first case causes problems when the affected prebuilt modules are preferred but that is an + // invalid configuration and it is ok for it to fail as the work to enable that is not yet + // complete. The second case is used for building targets that do not use boot dex jars and so + // deferring error reporting to ninja is fine as the affected ninja targets should never be built. + // That is handled above. + // + // A platform_bootclasspath module can use libraries from both platform and APEX variants. Unlike + // the bootclasspath_fragment it supports dex_import modules which provides the dex file. So, it + // can obtain a boot dex jar from a prebuilt that is not part of an APEX. However, it is assumed + // that if the library can be part of an APEX then it is the APEX variant that is used. + // + // This check handles the slightly different requirements of the bootclasspath_fragment and + // platform_bootclasspath modules by only deferring error reporting for the platform variant of + // a prebuilt modules that has other variants which are part of an APEX. + // + // TODO(b/187910671): Remove this once platform variants are no longer created unnecessarily. + if android.IsModulePrebuilt(module) { + if am, ok := module.(android.ApexModule); ok && am.InAnyApex() { + apexInfo := ctx.OtherModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) + if apexInfo.IsForPlatform() { + return true + } + } + } + + // A bootclasspath module that is part of a versioned sdk never provides a boot dex jar as there + // is no equivalently versioned prebuilt APEX file from which it can be obtained. However, + // versioned bootclasspath modules are processed by Soong so in order to avoid them causing build + // failures missing boot dex jars need to be deferred. + if android.IsModuleInVersionedSdk(ctx.Module()) { + return true + } + + return false +} + +// handleMissingDexBootFile will either log a warning or create an error rule to create the fake +// file depending on the value returned from deferReportingMissingBootDexJar. +func handleMissingDexBootFile(ctx android.ModuleContext, module android.Module, fake android.WritablePath) { + if deferReportingMissingBootDexJar(ctx, module) { + // 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), + }, + }) + } else { + ctx.ModuleErrorf("module %s does not provide a dex jar", module) + } +} + +// retrieveEncodedBootDexJarFromModule returns a path to the boot dex jar from the supplied module's +// DexJarBuildPath() method. +// +// The returned path will usually be to a dex jar file that has been encoded with hidden API flags. +// However, under certain conditions, e.g. errors, or special build configurations it will return +// a path to a fake file. +func retrieveEncodedBootDexJarFromModule(ctx android.ModuleContext, module android.Module) android.Path { + bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath() + if bootDexJar == nil { + fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/encoded-dex/%s.jar", module.Name())) + bootDexJar = fake + + handleMissingDexBootFile(ctx, module, fake) + } + return bootDexJar +} + +// extractEncodedDexJarsFromModules extracts the encoded dex jars from the supplied modules. +func extractEncodedDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule { + encodedDexJarsByModuleName := bootDexJarByModule{} + for _, module := range contents { + path := retrieveEncodedBootDexJarFromModule(ctx, module) + encodedDexJarsByModuleName.addPath(module, path) + } + return encodedDexJarsByModuleName +} diff --git a/java/hiddenapi_monolithic.go b/java/hiddenapi_monolithic.go index a6bf8c705..edf42351f 100644 --- a/java/hiddenapi_monolithic.go +++ b/java/hiddenapi_monolithic.go @@ -99,4 +99,4 @@ func (i *MonolithicHiddenAPIInfo) dedup() { i.AllFlagsPaths = android.FirstUniquePaths(i.AllFlagsPaths) } -var monolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{}) +var MonolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{}) diff --git a/java/java.go b/java/java.go index 2bbb5b102..ae8adf277 100644 --- a/java/java.go +++ b/java/java.go @@ -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) diff --git a/java/java_test.go b/java/java_test.go index bd373c177..0f9965d03 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -1392,3 +1392,95 @@ func TestDefaultInstallable(t *testing.T) { assertDeepEquals(t, "Default installable value should be true.", proptools.BoolPtr(true), module.properties.Installable) } + +func TestErrorproneEnabled(t *testing.T) { + ctx, _ := testJava(t, ` + java_library { + name: "foo", + srcs: ["a.java"], + errorprone: { + enabled: true, + }, + } + `) + + javac := ctx.ModuleForTests("foo", "android_common").Description("javac") + + // Test that the errorprone plugins are passed to javac + expectedSubstring := "-Xplugin:ErrorProne" + if !strings.Contains(javac.Args["javacFlags"], expectedSubstring) { + t.Errorf("expected javacFlags to contain %q, got %q", expectedSubstring, javac.Args["javacFlags"]) + } + + // Modules with errorprone { enabled: true } will include errorprone checks + // in the main javac build rule. Only when RUN_ERROR_PRONE is true will + // the explicit errorprone build rule be created. + errorprone := ctx.ModuleForTests("foo", "android_common").MaybeDescription("errorprone") + if errorprone.RuleParams.Description != "" { + t.Errorf("expected errorprone build rule to not exist, but it did") + } +} + +func TestErrorproneDisabled(t *testing.T) { + bp := ` + java_library { + name: "foo", + srcs: ["a.java"], + errorprone: { + enabled: false, + }, + } + ` + ctx := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + android.FixtureMergeEnv(map[string]string{ + "RUN_ERROR_PRONE": "true", + }), + ).RunTestWithBp(t, bp) + + javac := ctx.ModuleForTests("foo", "android_common").Description("javac") + + // Test that the errorprone plugins are not passed to javac, like they would + // be if enabled was true. + expectedSubstring := "-Xplugin:ErrorProne" + if strings.Contains(javac.Args["javacFlags"], expectedSubstring) { + t.Errorf("expected javacFlags to not contain %q, got %q", expectedSubstring, javac.Args["javacFlags"]) + } + + // Check that no errorprone build rule is created, like there would be + // if enabled was unset and RUN_ERROR_PRONE was true. + errorprone := ctx.ModuleForTests("foo", "android_common").MaybeDescription("errorprone") + if errorprone.RuleParams.Description != "" { + t.Errorf("expected errorprone build rule to not exist, but it did") + } +} + +func TestErrorproneEnabledOnlyByEnvironmentVariable(t *testing.T) { + bp := ` + java_library { + name: "foo", + srcs: ["a.java"], + } + ` + ctx := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + android.FixtureMergeEnv(map[string]string{ + "RUN_ERROR_PRONE": "true", + }), + ).RunTestWithBp(t, bp) + + javac := ctx.ModuleForTests("foo", "android_common").Description("javac") + errorprone := ctx.ModuleForTests("foo", "android_common").Description("errorprone") + + // Check that the errorprone plugins are not passed to javac, because they + // will instead be passed to the separate errorprone compilation + expectedSubstring := "-Xplugin:ErrorProne" + if strings.Contains(javac.Args["javacFlags"], expectedSubstring) { + t.Errorf("expected javacFlags to not contain %q, got %q", expectedSubstring, javac.Args["javacFlags"]) + } + + // Check that the errorprone plugin is enabled + if !strings.Contains(errorprone.Args["javacFlags"], expectedSubstring) { + t.Errorf("expected errorprone to contain %q, got %q", expectedSubstring, javac.Args["javacFlags"]) + } +} diff --git a/java/kotlin_test.go b/java/kotlin_test.go index 1c146a192..fd2f3ca61 100644 --- a/java/kotlin_test.go +++ b/java/kotlin_test.go @@ -185,7 +185,6 @@ func TestKapt(t *testing.T) { buildOS := android.BuildOs.String() kapt := result.ModuleForTests("foo", "android_common").Rule("kapt") - //kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc") javac := result.ModuleForTests("foo", "android_common").Description("javac") errorprone := result.ModuleForTests("foo", "android_common").Description("errorprone") diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go index 5949edd36..8c401a7d7 100644 --- a/java/legacy_core_platform_api_usage.go +++ b/java/legacy_core_platform_api_usage.go @@ -20,6 +20,7 @@ import ( ) var legacyCorePlatformApiModules = []string{ + "ArcSettings", "ahat-test-dump", "android.car", "android.test.mock", @@ -42,6 +43,7 @@ var legacyCorePlatformApiModules = []string{ "car-service-test-lib", "car-service-test-static-lib", "CertInstaller", + "com.qti.media.secureprocessor", "ConnectivityManagerTest", "ContactsProvider", "CorePerfTests", @@ -120,6 +122,7 @@ var legacyCorePlatformApiModules = []string{ "services.usage", "services.usb", "Settings-core", + "SettingsGoogle", "SettingsLib", "SettingsProvider", "SettingsProviderTest", diff --git a/java/lint.go b/java/lint.go index 9f769dfe3..1511cfe26 100644 --- a/java/lint.go +++ b/java/lint.go @@ -361,10 +361,7 @@ func (l *linter) lint(ctx android.ModuleContext) { Labels: map[string]string{"type": "tool", "name": "lint"}, ExecStrategy: lintRBEExecStrategy(ctx), ToolchainInputs: []string{config.JavaCmd(ctx).String()}, - EnvironmentVariables: []string{ - "LANG", - }, - Platform: map[string]string{remoteexec.PoolKey: pool}, + Platform: map[string]string{remoteexec.PoolKey: pool}, }) } diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index 87c695cb5..02343adba 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -189,7 +189,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) { @@ -202,11 +203,11 @@ func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.Mo // Generate classpaths.proto config func (b *platformBootclasspathModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) { // ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH - classpathJars := configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH) + classpathJars := configuredJarListToClasspathJars(ctx, b.configuredJars(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH) b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars) } -func (b *platformBootclasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList { +func (b *platformBootclasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList { return b.getImageConfig(ctx).modules } @@ -258,7 +259,7 @@ 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 @@ -276,11 +277,10 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. Output: path, }) } - return + return nil } monolithicInfo := b.createAndProvideMonolithicHiddenAPIInfo(ctx, fragments) - // Create the input to pass to ruleToGenerateHiddenAPIStubFlagsFile input := newHiddenAPIFlagInput() @@ -291,16 +291,14 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. // Use the flag files from this module and all the fragments. input.FlagFilesByCategory = monolithicInfo.FlagsFilesByCategory - hiddenAPIModules := gatherHiddenAPIModuleFromContents(ctx, modules) - // Generate the monolithic stub-flags.csv file. - bootDexJars := extractBootDexJarsFromHiddenAPIModules(ctx, hiddenAPIModules) + bootDexJarByModule := 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 := extractClassJarsFromHiddenAPIModules(ctx, hiddenAPIModules) + classesJars := extractClassesJarsFromModules(modules) // Generate the annotation-flags.csv file from all the module annotations. annotationFlags := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "annotation-flags.csv") @@ -325,6 +323,8 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. // jars. indexCSV := hiddenAPISingletonPaths(ctx).index buildRuleToGenerateIndex(ctx, "monolithic hidden API index", classesJars, indexCSV) + + return bootDexJarByModule } // createAndProvideMonolithicHiddenAPIInfo creates a MonolithicHiddenAPIInfo and provides it for @@ -342,7 +342,7 @@ func (b *platformBootclasspathModule) createAndProvideMonolithicHiddenAPIInfo(ct monolithicInfo := newMonolithicHiddenAPIInfo(ctx, temporaryInput.FlagFilesByCategory, fragments) // Store the information for testing. - ctx.SetProvider(monolithicHiddenAPIInfoProvider, monolithicInfo) + ctx.SetProvider(MonolithicHiddenAPIInfoProvider, monolithicInfo) return monolithicInfo } @@ -390,11 +390,13 @@ func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android. generateUpdatableBcpPackagesRule(ctx, imageConfig, updatableModules) // Copy non-updatable module dex jars to their predefined locations. - copyBootJarsToPredefinedLocations(ctx, nonUpdatableModules, imageConfig.modules, imageConfig.dexPaths) + nonUpdatableBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, nonUpdatableModules) + copyBootJarsToPredefinedLocations(ctx, nonUpdatableBootDexJarsByModule, imageConfig.dexPathsByModule) // Copy updatable module dex jars to their predefined locations. config := GetUpdatableBootConfig(ctx) - copyBootJarsToPredefinedLocations(ctx, updatableModules, config.modules, config.dexPaths) + updatableBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, updatableModules) + copyBootJarsToPredefinedLocations(ctx, updatableBootDexJarsByModule, config.dexPathsByModule) // Build a profile for the image config and then use that to build the boot image. profile := bootImageProfileRule(ctx, imageConfig) diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go index ed5549d35..0318a07d4 100644 --- a/java/platform_bootclasspath_test.go +++ b/java/platform_bootclasspath_test.go @@ -15,8 +15,6 @@ package java import ( - "fmt" - "strings" "testing" "android/soong/android" @@ -152,116 +150,6 @@ func TestPlatformBootclasspath(t *testing.T) { }) } -func TestPlatformBootclasspath_Fragments(t *testing.T) { - result := android.GroupFixturePreparers( - prepareForTestWithPlatformBootclasspath, - PrepareForTestWithJavaSdkLibraryFiles, - FixtureWithLastReleaseApis("foo"), - android.FixtureWithRootAndroidBp(` - platform_bootclasspath { - name: "platform-bootclasspath", - fragments: [ - {module:"bar-fragment"}, - ], - hidden_api: { - unsupported: [ - "unsupported.txt", - ], - removed: [ - "removed.txt", - ], - max_target_r_low_priority: [ - "max-target-r-low-priority.txt", - ], - max_target_q: [ - "max-target-q.txt", - ], - max_target_p: [ - "max-target-p.txt", - ], - max_target_o_low_priority: [ - "max-target-o-low-priority.txt", - ], - blocked: [ - "blocked.txt", - ], - unsupported_packages: [ - "unsupported-packages.txt", - ], - }, - } - - bootclasspath_fragment { - name: "bar-fragment", - contents: ["bar"], - api: { - stub_libs: ["foo"], - }, - hidden_api: { - unsupported: [ - "bar-unsupported.txt", - ], - removed: [ - "bar-removed.txt", - ], - max_target_r_low_priority: [ - "bar-max-target-r-low-priority.txt", - ], - max_target_q: [ - "bar-max-target-q.txt", - ], - max_target_p: [ - "bar-max-target-p.txt", - ], - max_target_o_low_priority: [ - "bar-max-target-o-low-priority.txt", - ], - blocked: [ - "bar-blocked.txt", - ], - unsupported_packages: [ - "bar-unsupported-packages.txt", - ], - }, - } - - java_library { - name: "bar", - srcs: ["a.java"], - system_modules: "none", - sdk_version: "none", - compile_dex: true, - } - - java_sdk_library { - name: "foo", - srcs: ["a.java"], - public: { - enabled: true, - }, - compile_dex: true, - } - `), - ).RunTest(t) - - pbcp := result.Module("platform-bootclasspath", "android_common") - info := result.ModuleProvider(pbcp, monolithicHiddenAPIInfoProvider).(MonolithicHiddenAPIInfo) - - for _, category := range hiddenAPIFlagFileCategories { - name := category.propertyName - message := fmt.Sprintf("category %s", name) - filename := strings.ReplaceAll(name, "_", "-") - expected := []string{fmt.Sprintf("%s.txt", filename), fmt.Sprintf("bar-%s.txt", filename)} - android.AssertPathsRelativeToTopEquals(t, message, expected, info.FlagsFilesByCategory[category]) - } - - android.AssertPathsRelativeToTopEquals(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/stub-flags.csv"}, info.StubFlagsPaths) - android.AssertPathsRelativeToTopEquals(t, "annotation flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/annotation-flags.csv"}, info.AnnotationFlagsPaths) - android.AssertPathsRelativeToTopEquals(t, "metadata flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/metadata.csv"}, info.MetadataPaths) - android.AssertPathsRelativeToTopEquals(t, "index flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/index.csv"}, info.IndexPaths) - android.AssertPathsRelativeToTopEquals(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/all-flags.csv"}, info.AllFlagsPaths) -} - func TestPlatformBootclasspathVariant(t *testing.T) { result := android.GroupFixturePreparers( prepareForTestWithPlatformBootclasspath, diff --git a/java/rro.go b/java/rro.go index 2e58c042f..0b4d0916a 100644 --- a/java/rro.go +++ b/java/rro.go @@ -90,6 +90,22 @@ type RuntimeResourceOverlayModule interface { Theme() string } +// RRO's partition logic is different from the partition logic of other modules defined in soong/android/paths.go +// The default partition for RRO is "/product" and not "/system" +func rroPartition(ctx android.ModuleContext) string { + var partition string + if ctx.DeviceSpecific() { + partition = ctx.DeviceConfig().OdmPath() + } else if ctx.SocSpecific() { + partition = ctx.DeviceConfig().VendorPath() + } else if ctx.SystemExtSpecific() { + partition = ctx.DeviceConfig().SystemExtPath() + } else { + partition = ctx.DeviceConfig().ProductPath() + } + return partition +} + func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) { sdkDep := decodeSdkDep(ctx, android.SdkContext(r)) if sdkDep.hasFrameworkLibs() { @@ -137,7 +153,8 @@ func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleC r.certificate = certificates[0] r.outputFile = signed - r.installDir = android.PathForModuleInstall(ctx, "overlay", String(r.properties.Theme)) + partition := rroPartition(ctx) + r.installDir = android.PathForModuleInPartitionInstall(ctx, partition, "overlay", String(r.properties.Theme)) ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile) } diff --git a/java/rro_test.go b/java/rro_test.go index bad60bc16..27abbe4f3 100644 --- a/java/rro_test.go +++ b/java/rro_test.go @@ -177,7 +177,7 @@ func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) { // Check device location. path = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"] - expectedPath = []string{shared.JoinPath("out/target/product/test_device/system/overlay")} + expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay")} android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path) } @@ -343,3 +343,57 @@ func TestEnforceRRO_propagatesToDependencies(t *testing.T) { }) } } + +func TestRuntimeResourceOverlayPartition(t *testing.T) { + bp := ` + runtime_resource_overlay { + name: "device_specific", + device_specific: true, + } + runtime_resource_overlay { + name: "soc_specific", + soc_specific: true, + } + runtime_resource_overlay { + name: "system_ext_specific", + system_ext_specific: true, + } + runtime_resource_overlay { + name: "product_specific", + product_specific: true, + } + runtime_resource_overlay { + name: "default" + } + ` + testCases := []struct { + name string + expectedPath string + }{ + { + name: "device_specific", + expectedPath: "out/soong/target/product/test_device/odm/overlay", + }, + { + name: "soc_specific", + expectedPath: "out/soong/target/product/test_device/vendor/overlay", + }, + { + name: "system_ext_specific", + expectedPath: "out/soong/target/product/test_device/system_ext/overlay", + }, + { + name: "product_specific", + expectedPath: "out/soong/target/product/test_device/product/overlay", + }, + { + name: "default", + expectedPath: "out/soong/target/product/test_device/product/overlay", + }, + } + for _, testCase := range testCases { + ctx, _ := testJava(t, bp) + mod := ctx.ModuleForTests(testCase.name, "android_common").Module().(*RuntimeResourceOverlay) + android.AssertPathRelativeToTopEquals(t, "Install dir is not correct for "+testCase.name, testCase.expectedPath, mod.installDir) + } +} diff --git a/java/sdk_library.go b/java/sdk_library.go index ecf2b1a03..41097ca13 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -435,9 +435,6 @@ type sdkLibraryProperties struct { // a list of top-level directories containing Java stub files to merge show/hide annotations from. Merge_inclusion_annotations_dirs []string - // If set to true, the path of dist files is apistubs/core. Defaults to false. - Core_lib *bool - // If set to true then don't create dist rules. No_dist *bool @@ -458,7 +455,7 @@ type sdkLibraryProperties struct { Dist_stem *string // The subdirectory for the artifacts that are copied to the dist directory. If not specified - // then defaults to "android". Should be set to "android" for anything that should be published + // then defaults to "unknown". Should be set to "android" for anything that should be published // in the public Android SDK. Dist_group *string @@ -1203,11 +1200,7 @@ func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries { // The dist path of the stub artifacts func (module *SdkLibrary) apiDistPath(apiScope *apiScope) string { - if Bool(module.sdkLibraryProperties.Core_lib) { - return path.Join("apistubs", "core", apiScope.name) - } else { - return path.Join("apistubs", module.distGroup(), apiScope.name) - } + return path.Join("apistubs", module.distGroup(), apiScope.name) } // Get the sdk version for use when compiling the stubs library. @@ -1233,15 +1226,7 @@ func (module *SdkLibrary) distStem() string { // distGroup returns the subdirectory of the dist path of the stub artifacts. func (module *SdkLibrary) distGroup() string { - if group := proptools.String(module.sdkLibraryProperties.Dist_group); group != "" { - return group - } - // TODO(b/186723288): Remove this once everything uses dist_group. - if owner := module.ModuleBase.Owner(); owner != "" { - return owner - } - // TODO(b/186723288): Make this "unknown". - return "android" + return proptools.StringDefault(module.sdkLibraryProperties.Dist_group, "unknown") } func (module *SdkLibrary) latestApiFilegroupName(apiScope *apiScope) string { @@ -2159,7 +2144,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 { @@ -2284,6 +2269,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 0fe6e72ef..2520dde6e 100644 --- a/java/sdk_library_test.go +++ b/java/sdk_library_test.go @@ -844,40 +844,34 @@ func TestJavaSdkLibraryDist(t *testing.T) { PrepareForTestWithJavaBuildComponents, PrepareForTestWithJavaDefaultModules, PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis( + "sdklib_no_group", + "sdklib_group_foo", + "sdklib_owner_foo", + "foo"), ).RunTestWithBp(t, ` java_sdk_library { - name: "sdklib_no_owner", - unsafe_ignore_missing_latest_api: true, + name: "sdklib_no_group", srcs: ["foo.java"], } java_sdk_library { name: "sdklib_group_foo", - unsafe_ignore_missing_latest_api: true, srcs: ["foo.java"], dist_group: "foo", } java_sdk_library { name: "sdklib_owner_foo", - unsafe_ignore_missing_latest_api: true, srcs: ["foo.java"], owner: "foo", } java_sdk_library { name: "sdklib_stem_foo", - unsafe_ignore_missing_latest_api: true, srcs: ["foo.java"], dist_stem: "foo", } - - java_sdk_library { - name: "sdklib_core_lib", - unsafe_ignore_missing_latest_api: true, - srcs: ["foo.java"], - core_lib: true, - } `) type testCase struct { @@ -887,9 +881,9 @@ func TestJavaSdkLibraryDist(t *testing.T) { } testCases := []testCase{ { - module: "sdklib_no_owner", - distDir: "apistubs/android/public", - distStem: "sdklib_no_owner.jar", + module: "sdklib_no_group", + distDir: "apistubs/unknown/public", + distStem: "sdklib_no_group.jar", }, { module: "sdklib_group_foo", @@ -897,20 +891,16 @@ func TestJavaSdkLibraryDist(t *testing.T) { distStem: "sdklib_group_foo.jar", }, { + // Owner doesn't affect distDir after b/186723288. module: "sdklib_owner_foo", - distDir: "apistubs/foo/public", + distDir: "apistubs/unknown/public", distStem: "sdklib_owner_foo.jar", }, { module: "sdklib_stem_foo", - distDir: "apistubs/android/public", + distDir: "apistubs/unknown/public", distStem: "foo.jar", }, - { - module: "sdklib_core_lib", - distDir: "apistubs/core/public", - distStem: "sdklib_core_lib.jar", - }, } for _, tt := range testCases { diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go index 5bd25cd51..a0decb7e9 100644 --- a/java/systemserver_classpath_fragment.go +++ b/java/systemserver_classpath_fragment.go @@ -48,11 +48,11 @@ func (p *platformSystemServerClasspathModule) AndroidMkEntries() (entries []andr } func (p *platformSystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { - classpathJars := configuredJarListToClasspathJars(ctx, p.ClasspathFragmentToConfiguredJarList(ctx), p.classpathType) + classpathJars := configuredJarListToClasspathJars(ctx, p.configuredJars(ctx), p.classpathType) p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars) } -func (p *platformSystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList { +func (p *platformSystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList { global := dexpreopt.GetGlobalConfig(ctx) return global.SystemServerJars } @@ -94,14 +94,14 @@ func (s *SystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.Mo ctx.PropertyErrorf("contents", "empty contents are not allowed") } - classpathJars := configuredJarListToClasspathJars(ctx, s.ClasspathFragmentToConfiguredJarList(ctx), s.classpathType) + classpathJars := configuredJarListToClasspathJars(ctx, s.configuredJars(ctx), s.classpathType) s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars) // Collect the module directory for IDE info in java/jdeps.go. s.modulePaths = append(s.modulePaths, ctx.ModuleDir()) } -func (s *SystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList { +func (s *SystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList { global := dexpreopt.GetGlobalConfig(ctx) // Convert content names to their appropriate stems, in case a test library is overriding an actual boot jar |