From 32cf58a8fcfc043246a9c402b32c863e8aebc499 Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Tue, 18 May 2021 16:32:50 +0100 Subject: [PATCH] Support removed API members in modular hidden API processing Previously, the hidden API flags generated for a bootclasspath_fragment did not include removed API members. That was because it did not supply a file containing the dex signatures of the removed API members. The monolithic hidden API processing uses combined-removed-dex which is the output of a genrule that takes as input the *removed.txt files from all the APIs and uses metalava to construct the dex signatures file. This change does the equivalent for the *removed.txt files for the APIs provided by a bootclasspath_fragment and then passes them to the rule that generates the final all-flags.csv. Bug: 179354495 Test: - Update packages/modules/RuntimeI18N to enable hidden API processing. m out/soong/hiddenapi/hiddenapi-flags.csv - Before this change that fails as the flags generated for the i18n-bootclasspath-fragment differ from the monolithic flags. After this change that passes. - Verify that this change does not change any of the monolithic hidden API files. m com.android.i18n - Verify that the apex file before and after this change are byte for byte identical. Change-Id: I6a21edb8a5231666e3f35b2c99a8687f36dd98fd --- java/hiddenapi_modular.go | 73 +++++++++++++++++++++++++++------- java/platform_bootclasspath.go | 2 +- java/sdk_library.go | 48 +++++++++++++++------- 3 files changed, 93 insertions(+), 30 deletions(-) diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index 370794229..f2649d3c0 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -260,6 +260,22 @@ type hiddenAPIFlagFileCategory struct { commandMutator func(command *android.RuleBuilderCommand, path android.Path) } +// The flag file category for removed members of the API. +// +// 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", + propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { + return properties.Removed + }, + commandMutator: func(command *android.RuleBuilderCommand, path android.Path) { + command.FlagWithInput("--unsupported ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed") + }, +} + var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ // See HiddenAPIFlagFileProperties.Unsupported { @@ -271,16 +287,7 @@ var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ command.FlagWithInput("--unsupported ", path) }, }, - // See HiddenAPIFlagFileProperties.Removed - { - propertyName: "removed", - propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { - return properties.Removed - }, - commandMutator: func(command *android.RuleBuilderCommand, path android.Path) { - command.FlagWithInput("--unsupported ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed") - }, - }, + hiddenAPIRemovedFlagFileCategory, // See HiddenAPIFlagFileProperties.Max_target_r_low_priority { propertyName: "max_target_r_low_priority", @@ -436,6 +443,10 @@ type HiddenAPIFlagInput struct { // depends. It is the result of merging HiddenAPIInfo.TransitiveStubDexJarsByKind from each // fragment on which this depends. DependencyStubDexJarsByKind StubDexJarsByKind + + // RemovedTxtFiles is the list of removed.txt files provided by java_sdk_library modules that are + // specified in the bootclasspath_fragment's stub_libs and contents properties. + RemovedTxtFiles android.Paths } // newHiddenAPIFlagInput creates a new initialize HiddenAPIFlagInput struct. @@ -494,6 +505,11 @@ func (i *HiddenAPIFlagInput) gatherStubLibInfo(ctx android.ModuleContext, conten if dexJar != nil { i.StubDexJarsByKind[kind] = append(i.StubDexJarsByKind[kind], dexJar) } + + if sdkLibrary, ok := module.(SdkLibraryDependency); ok { + removedTxtFile := sdkLibrary.SdkRemovedTxtFile(ctx, kind) + i.RemovedTxtFiles = append(i.RemovedTxtFiles, removedTxtFile.AsPaths()...) + } } // If the contents includes any java_sdk_library modules then add them to the stubs. @@ -518,6 +534,7 @@ func (i *HiddenAPIFlagInput) gatherStubLibInfo(ctx android.ModuleContext, conten // Normalize the paths, i.e. remove duplicates and sort. i.StubDexJarsByKind.dedupAndSort() + i.RemovedTxtFiles = android.SortedUniquePaths(i.RemovedTxtFiles) } // extractFlagFilesFromProperties extracts the paths to flag files that are specified in the @@ -577,7 +594,9 @@ func pathForValidation(ctx android.PathContext, path android.WritablePath) andro // // hiddenAPIInfo is a struct containing paths to files that augment the information provided by // the annotationFlags. -func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlags android.Path, flagFilesByCategory FlagFilesByCategory, allFlagsPaths android.Paths) { +func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc string, + outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlags android.Path, + flagFilesByCategory FlagFilesByCategory, allFlagsPaths android.Paths, generatedRemovedDexSignatures android.OptionalPath) { // The file which is used to record that the flags file is valid. var validFile android.WritablePath @@ -618,6 +637,12 @@ func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc st } } + // If available then pass the automatically generated file containing dex signatures of removed + // API members to the rule so they can be marked as removed. + if generatedRemovedDexSignatures.Valid() { + hiddenAPIRemovedFlagFileCategory.commandMutator(command, generatedRemovedDexSignatures.Path()) + } + commitChangeForRestat(rule, tempPath, outputPath) if validFile != nil { @@ -674,14 +699,15 @@ func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext // Removed APIs need to be marked and in order to do that the hiddenAPIInfo needs to specify files // containing dex signatures of all the removed APIs. In the monolithic files that is done by // manually combining all the removed.txt files for each API and then converting them to dex - // signatures, see the combined-removed-dex module. That will all be done automatically in future. - // For now removed APIs are ignored. - // TODO(b/179354495): handle removed apis automatically. + // signatures, see the combined-removed-dex module. This does that automatically by using the + // *removed.txt files retrieved from the java_sdk_library modules that are specified in the + // stub_libs and contents properties of a bootclasspath_fragment. + removedDexSignatures := buildRuleToGenerateRemovedDexSignatures(ctx, input.RemovedTxtFiles) // 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) + buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags", "modular hiddenapi all flags", outputPath, stubFlagsCSV, annotationFlagsCSV, input.FlagFilesByCategory, nil, removedDexSignatures) // Store the paths in the info for use by other modules and sdk snapshot generation. output := HiddenAPIFlagOutput{ @@ -694,6 +720,23 @@ func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext return &output } +func buildRuleToGenerateRemovedDexSignatures(ctx android.ModuleContext, removedTxtFiles android.Paths) android.OptionalPath { + if len(removedTxtFiles) == 0 { + return android.OptionalPath{} + } + + output := android.PathForModuleOut(ctx, "modular-hiddenapi/removed-dex-signatures.txt") + + rule := android.NewRuleBuilder(pctx, ctx) + rule.Command(). + BuiltTool("metalava"). + Flag("--no-banner"). + Inputs(removedTxtFiles). + FlagWithOutput("--dex-api ", output) + rule.Build("modular-hiddenapi-removed-dex-signatures", "modular hiddenapi removed dex signatures") + return android.OptionalPathForPath(output) +} + // gatherHiddenAPIModuleFromContents gathers the hiddenAPIModule from the supplied contents. func gatherHiddenAPIModuleFromContents(ctx android.ModuleContext, contents []android.Module) []hiddenAPIModule { hiddenAPIModules := []hiddenAPIModule{} diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index 854179e2c..87c695cb5 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -308,7 +308,7 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. // Generate the monotlithic hiddenapi-flags.csv file. allFlags := hiddenAPISingletonPaths(ctx).flags - buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "hiddenapi flags", allFlags, stubFlags, annotationFlags, monolithicInfo.FlagsFilesByCategory, monolithicInfo.AllFlagsPaths) + buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "hiddenapi flags", allFlags, stubFlags, annotationFlags, monolithicInfo.FlagsFilesByCategory, monolithicInfo.AllFlagsPaths, android.OptionalPath{}) // Generate an intermediate monolithic hiddenapi-metadata.csv file directly from the annotations // in the source code. diff --git a/java/sdk_library.go b/java/sdk_library.go index 6e860e703..8f36758c8 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -845,19 +845,7 @@ func (c *commonToSdkLibraryAndImport) selectHeaderJarsForSdkVersion(ctx android. // closest kind which is a subset of the requested kind. e.g. if requesting android.SdkModule then // it will return *scopePaths for android.SdkSystem if available or android.SdkPublic of not. func (c *commonToSdkLibraryAndImport) selectScopePaths(ctx android.BaseModuleContext, kind android.SdkKind) *scopePaths { - var apiScope *apiScope - switch kind { - case android.SdkSystem: - apiScope = apiScopeSystem - case android.SdkModule: - apiScope = apiScopeModuleLib - case android.SdkTest: - apiScope = apiScopeTest - case android.SdkSystemServer: - apiScope = apiScopeSystemServer - default: - apiScope = apiScopePublic - } + apiScope := sdkKindToApiScope(kind) paths := c.findClosestScopePath(apiScope) if paths == nil { @@ -874,6 +862,24 @@ func (c *commonToSdkLibraryAndImport) selectScopePaths(ctx android.BaseModuleCon return paths } +// sdkKindToApiScope maps from android.SdkKind to apiScope. +func sdkKindToApiScope(kind android.SdkKind) *apiScope { + var apiScope *apiScope + switch kind { + case android.SdkSystem: + apiScope = apiScopeSystem + case android.SdkModule: + apiScope = apiScopeModuleLib + case android.SdkTest: + apiScope = apiScopeTest + case android.SdkSystemServer: + apiScope = apiScopeSystemServer + default: + apiScope = apiScopePublic + } + return apiScope +} + // to satisfy SdkLibraryDependency interface func (c *commonToSdkLibraryAndImport) SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) android.Path { paths := c.selectScopePaths(ctx, kind) @@ -884,6 +890,17 @@ func (c *commonToSdkLibraryAndImport) SdkApiStubDexJar(ctx android.BaseModuleCon return paths.stubsDexJarPath } +// to satisfy SdkLibraryDependency interface +func (c *commonToSdkLibraryAndImport) SdkRemovedTxtFile(ctx android.BaseModuleContext, kind android.SdkKind) android.OptionalPath { + apiScope := sdkKindToApiScope(kind) + paths := c.findScopePaths(apiScope) + if paths == nil { + return android.OptionalPath{} + } + + return paths.removedApiFilePath +} + func (c *commonToSdkLibraryAndImport) sdkComponentPropertiesForChildLibrary() interface{} { componentProps := &struct { SdkLibraryToImplicitlyTrack *string @@ -964,7 +981,7 @@ var _ SdkLibraryComponentDependency = (*Import)(nil) var _ SdkLibraryComponentDependency = (*SdkLibrary)(nil) var _ SdkLibraryComponentDependency = (*SdkLibraryImport)(nil) -// Provides access to sdk_version related header and implentation jars. +// Provides access to sdk_version related files, e.g. header and implementation jars. type SdkLibraryDependency interface { SdkLibraryComponentDependency @@ -985,6 +1002,9 @@ type SdkLibraryDependency interface { // tool which processes dex files. SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) android.Path + // SdkRemovedTxtFile returns the optional path to the removed.txt file for the specified sdk kind. + SdkRemovedTxtFile(ctx android.BaseModuleContext, kind android.SdkKind) android.OptionalPath + // sharedLibrary returns true if this can be used as a shared library. sharedLibrary() bool }