diff --git a/java/droidstubs.go b/java/droidstubs.go index 4bbe70ac4..7ea8d305a 100644 --- a/java/droidstubs.go +++ b/java/droidstubs.go @@ -148,6 +148,10 @@ type DroidstubsProperties struct { // path or filegroup to file defining extension an SDK name <-> numerical ID mapping and // what APIs exist in which SDKs; passed to metalava via --sdk-extensions-info Extensions_info_file *string `android:"path"` + + // 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 } // Used by xsd_config @@ -178,6 +182,10 @@ func DroidstubsFactory() android.Module { &module.Javadoc.properties) InitDroiddocModule(module, android.HostAndDeviceSupported) + + module.SetDefaultableHook(func(ctx android.DefaultableHookContext) { + module.createApiContribution(ctx) + }) return module } @@ -862,6 +870,23 @@ func (d *Droidstubs) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) { }, attrs) } +func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) { + api_file := d.properties.Check_api.Current.Api_file + api_surface := d.properties.Api_surface + + props := struct { + Name *string + Api_surface *string + Api_file *string + }{} + + props.Name = proptools.StringPtr(d.Name() + ".api.contribution") + props.Api_surface = api_surface + props.Api_file = api_file + + ctx.CreateModule(ApiContributionFactory, &props) +} + // TODO (b/262014796): Export the API contributions of CorePlatformApi // A map to populate the api surface of a droidstub from a substring appearing in its name // This map assumes that droidstubs (either checked-in or created by java_sdk_library) diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go index ef2e6dc8a..6c2293746 100644 --- a/java/droidstubs_test.go +++ b/java/droidstubs_test.go @@ -346,3 +346,27 @@ func TestApiSurfaceFromDroidStubsName(t *testing.T) { android.AssertStringEquals(t, tc.desc, tc.expectedApiSurface, bazelApiSurfaceName(tc.name)) } } + +func TestDroidStubsApiContributionGeneration(t *testing.T) { + ctx, _ := testJavaWithFS(t, ` + droidstubs { + name: "foo", + srcs: ["A/a.java"], + api_surface: "public", + check_api: { + current: { + api_file: "A/current.txt", + removed_api_file: "A/removed.txt", + } + } + } + `, + map[string][]byte{ + "A/a.java": nil, + "A/current.txt": nil, + "A/removed.txt": nil, + }, + ) + + ctx.ModuleForTests("foo.api.contribution", "") +} diff --git a/java/java.go b/java/java.go index 7078cc38f..5859e9091 100644 --- a/java/java.go +++ b/java/java.go @@ -1594,7 +1594,11 @@ type JavaApiImportInfo struct { var JavaApiImportProvider = blueprint.NewProvider(JavaApiImportInfo{}) func (ap *JavaApiContribution) GenerateAndroidBuildActions(ctx android.ModuleContext) { - apiFile := android.PathForModuleSrc(ctx, String(ap.properties.Api_file)) + var apiFile android.Path = nil + if apiFileString := ap.properties.Api_file; apiFileString != nil { + apiFile = android.PathForModuleSrc(ctx, String(apiFileString)) + } + ctx.SetProvider(JavaApiImportProvider, JavaApiImportInfo{ ApiFile: apiFile, }) @@ -1725,7 +1729,11 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { switch tag { case javaApiContributionTag: provider := ctx.OtherModuleProvider(dep, JavaApiImportProvider).(JavaApiImportInfo) - srcFiles = append(srcFiles, android.PathForSource(ctx, provider.ApiFile.String())) + providerApiFile := provider.ApiFile + if providerApiFile == nil { + ctx.ModuleErrorf("Error: %s has an empty api file.", dep.Name()) + } + srcFiles = append(srcFiles, android.PathForSource(ctx, providerApiFile.String())) case libTag: provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo) classPaths = append(classPaths, provider.HeaderJars...) diff --git a/java/java_test.go b/java/java_test.go index ae77842a5..04a112c0a 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -1840,6 +1840,20 @@ func TestDeviceBinaryWrapperGeneration(t *testing.T) { }`) } +func TestJavaApiContributionEmptyApiFile(t *testing.T) { + testJavaError(t, + "Error: foo has an empty api file.", + `java_api_contribution { + name: "foo", + } + java_api_library { + name: "bar", + api_surface: "public", + api_contributions: ["foo"], + } + `) +} + func TestJavaApiLibraryAndProviderLink(t *testing.T) { provider_bp_a := ` java_api_contribution { diff --git a/java/sdk_library.go b/java/sdk_library.go index 3b64bf733..b87236596 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -1599,6 +1599,7 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC Srcs []string Installable *bool Sdk_version *string + Api_surface *string System_modules *string Libs []string Output_javadoc_comments *bool @@ -1638,6 +1639,7 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC props.Srcs = append(props.Srcs, module.properties.Srcs...) props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...) props.Sdk_version = module.deviceProperties.Sdk_version + props.Api_surface = &apiScope.name props.System_modules = module.deviceProperties.System_modules props.Installable = proptools.BoolPtr(false) // A droiddoc module has only one Libs property and doesn't distinguish between