diff --git a/java/builder.go b/java/builder.go index d03c8e50c..6919236d5 100644 --- a/java/builder.go +++ b/java/builder.go @@ -274,11 +274,29 @@ var ( ` cat $$f; ` + `done > $out`, }) + + gatherReleasedFlaggedApisRule = pctx.AndroidStaticRule("gatherReleasedFlaggedApisRule", + blueprint.RuleParams{ + Command: `${aconfig} dump --format bool ` + + `--out ${out} ` + + `${flags_path} ` + + `${filter_args} `, + CommandDeps: []string{"${aconfig}"}, + Description: "aconfig_bool", + }, "flags_path", "filter_args") + + generateMetalavaRevertAnnotationsRule = pctx.AndroidStaticRule("generateMetalavaRevertAnnotationsRule", + blueprint.RuleParams{ + Command: `${keep-flagged-apis} ${in} > ${out}`, + }) ) func init() { pctx.Import("android/soong/android") pctx.Import("android/soong/java/config") + + pctx.HostBinToolVariable("aconfig", "aconfig") + pctx.HostBinToolVariable("keep-flagged-apis", "keep-flagged-apis") } type javaBuilderFlags struct { diff --git a/java/droiddoc.go b/java/droiddoc.go index 138c9c3bb..e05f2309f 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -397,6 +397,15 @@ func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps { sm := module.(SystemModulesProvider) outputDir, outputDeps := sm.OutputDirAndDeps() deps.systemModules = &systemModules{outputDir, outputDeps} + case aconfigDeclarationTag: + if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey); ok { + deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPath) + } else { + ctx.ModuleErrorf("Only aconfig_declarations module type is allowed for "+ + "flags_packages property, but %s is not aconfig_declarations module type", + module.Name(), + ) + } } }) // do not pass exclude_srcs directly when expanding srcFiles since exclude_srcs diff --git a/java/droidstubs.go b/java/droidstubs.go index e7ccc1ba4..c839dbaaa 100644 --- a/java/droidstubs.go +++ b/java/droidstubs.go @@ -30,6 +30,27 @@ import ( // The values allowed for Droidstubs' Api_levels_sdk_type var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib", "system-server"} +type StubsType int + +const ( + Everything StubsType = iota + Runtime + Exportable +) + +func (s StubsType) String() string { + switch s { + case Everything: + return "everything" + case Runtime: + return "runtime" + case Exportable: + return "exportable" + default: + return "" + } +} + func init() { RegisterStubsBuildComponents(android.InitRegistrationContext) } @@ -151,6 +172,10 @@ type DroidstubsProperties struct { // API surface of this module. If set, the module contributes to an API surface. // For the full list of available API surfaces, refer to soong/android/sdk_version.go Api_surface *string + + // a list of aconfig_declarations module names that the stubs generated in this module + // depend on. + Aconfig_declarations []string } // Used by xsd_config @@ -274,6 +299,12 @@ func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) { } } + if len(d.properties.Aconfig_declarations) != 0 { + for _, aconfigDeclarationModuleName := range d.properties.Aconfig_declarations { + ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfigDeclarationModuleName) + } + } + if d.properties.Api_levels_module != nil { ctx.AddDependency(ctx.Module(), metalavaAPILevelsModuleTag, proptools.String(d.properties.Api_levels_module)) } @@ -538,11 +569,59 @@ func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersi return cmd } +// Generate flagged apis related flags. Apply transformations and only revert the flagged apis +// that are not enabled via release configurations and are not specified in aconfig_declarations +func (d *Droidstubs) generateRevertAnnotationArgs(ctx android.ModuleContext, stubsType StubsType, aconfigFlagsPaths android.Paths) { + + releasedFlaggedApisFile := android.PathForModuleOut(ctx, fmt.Sprintf("released-flagged-apis-%s.txt", stubsType.String())) + revertAnnotationsFile := android.PathForModuleOut(ctx, fmt.Sprintf("revert-annotations-%s.txt", stubsType.String())) + + var filterArgs string + switch stubsType { + // No flagged apis specific flags need to be passed to metalava when generating + // everything stubs + case Everything: + return + + case Runtime: + filterArgs = "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'" + + case Exportable: + filterArgs = "--filter='state:ENABLED+permission:READ_ONLY'" + + } + + ctx.Build(pctx, android.BuildParams{ + Rule: gatherReleasedFlaggedApisRule, + Inputs: aconfigFlagsPaths, + Output: releasedFlaggedApisFile, + Description: fmt.Sprintf("%s gather aconfig flags", stubsType), + Args: map[string]string{ + "flags_path": android.JoinPathsWithPrefix(aconfigFlagsPaths, "--cache "), + "filter_args": filterArgs, + }, + }) + + ctx.Build(pctx, android.BuildParams{ + Rule: generateMetalavaRevertAnnotationsRule, + Input: releasedFlaggedApisFile, + Output: revertAnnotationsFile, + Description: fmt.Sprintf("%s revert annotations", stubsType), + }) +} + func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { deps := d.Javadoc.collectDeps(ctx) javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), android.SdkContext(d)) + // If the module specifies aconfig_declarations property, "exportable" (and "runtime" eventually) stubs are generated + if len(deps.aconfigProtoFiles) > 0 { + // Files required to generate "exportable" stubs + stubsType := Exportable + d.generateRevertAnnotationArgs(ctx, stubsType, deps.aconfigProtoFiles) + } + // Create rule for metalava srcJarDir := android.PathForModuleOut(ctx, "metalava", "srcjars") diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go index 7bcaca1d4..5544890af 100644 --- a/java/droidstubs_test.go +++ b/java/droidstubs_test.go @@ -395,3 +395,46 @@ func TestDroidstubsHideFlaggedApi(t *testing.T) { cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command) android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "--revert-annotation android.annotation.FlaggedApi") } + +func TestAconfigDeclarations(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + }), + android.FixtureMergeMockFs(map[string][]byte{ + "a/A.java": nil, + "a/current.txt": nil, + "a/removed.txt": nil, + }), + ).RunTestWithBp(t, ` + aconfig_declarations { + name: "bar", + package: "com.example.package", + srcs: [ + "bar.aconfig", + ], + } + droidstubs { + name: "foo", + srcs: ["a/A.java"], + api_surface: "public", + check_api: { + current: { + api_file: "a/current.txt", + removed_api_file: "a/removed.txt", + } + }, + aconfig_declarations: [ + "bar", + ], + } + `) + + // Check that droidstubs depend on aconfig_declarations + android.AssertBoolEquals(t, "foo expected to depend on bar", + CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar"), true) + + m := result.ModuleForTests("foo", "android_common") + android.AssertStringDoesContain(t, "foo generates revert annotations file", + strings.Join(m.AllOutputs(), ""), "revert-annotations-exportable.txt") +} diff --git a/java/java.go b/java/java.go index 2a4fafa8b..4227d368e 100644 --- a/java/java.go +++ b/java/java.go @@ -26,6 +26,7 @@ import ( "android/soong/remoteexec" "android/soong/testing" + "github.com/google/blueprint" "github.com/google/blueprint/proptools" @@ -519,6 +520,7 @@ type deps struct { kotlinStdlib android.Paths kotlinAnnotations android.Paths kotlinPlugins android.Paths + aconfigProtoFiles android.Paths disableTurbine bool } diff --git a/java/sdk_library.go b/java/sdk_library.go index ef34fb6cd..6998bd2c9 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -628,6 +628,10 @@ type sdkLibraryProperties struct { // Defaults to false. Contribute_to_android_api *bool + // a list of aconfig_declarations module names that the stubs generated in this module + // depend on. + Aconfig_declarations []string + // TODO: determines whether to create HTML doc or not // Html_doc *bool } @@ -1698,6 +1702,7 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC Merge_inclusion_annotations_dirs []string Generate_stubs *bool Previous_api *string + Aconfig_declarations []string Check_api struct { Current ApiToCheck Last_released ApiToCheck @@ -1742,6 +1747,7 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC props.Annotations_enabled = module.sdkLibraryProperties.Annotations_enabled props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs + props.Aconfig_declarations = module.sdkLibraryProperties.Aconfig_declarations droidstubsArgs := []string{} if len(module.sdkLibraryProperties.Api_packages) != 0 {