diff --git a/android/sdk.go b/android/sdk.go index 3a5624030..9317910ee 100644 --- a/android/sdk.go +++ b/android/sdk.go @@ -961,3 +961,10 @@ type ExportedComponentsInfo struct { } var ExportedComponentsInfoProvider = blueprint.NewProvider(ExportedComponentsInfo{}) + +// AdditionalSdkInfo contains additional properties to add to the generated SDK info file. +type AdditionalSdkInfo struct { + Properties map[string]interface{} +} + +var AdditionalSdkInfoProvider = blueprint.NewProvider(AdditionalSdkInfo{}) diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go index 44650a6d0..944970783 100644 --- a/java/prebuilt_apis.go +++ b/java/prebuilt_apis.go @@ -212,6 +212,10 @@ func createSystemModules(mctx android.LoadHookContext, version, scope string) { mctx.CreateModule(systemModulesImportFactory, &props) } +func PrebuiltApiModuleName(module, scope, version string) string { + return module + ".api." + scope + "." + version +} + func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { // //api/.txt apiLevelFiles := globApiDirs(mctx, p, "api/*.txt") @@ -220,12 +224,9 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { } // Create modules for all (, ) triplets, - apiModuleName := func(module, scope, version string) string { - return module + ".api." + scope + "." + version - } for _, f := range apiLevelFiles { module, version, scope := parseFinalizedPrebuiltPath(mctx, f) - createApiModule(mctx, apiModuleName(module, scope, strconv.Itoa(version)), f) + createApiModule(mctx, PrebuiltApiModuleName(module, scope, strconv.Itoa(version)), f) } // Figure out the latest version of each module/scope @@ -266,7 +267,7 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { // Sort the keys in order to make build.ninja stable for _, k := range android.SortedStringKeys(latest) { info := latest[k] - name := apiModuleName(info.module, info.scope, "latest") + name := PrebuiltApiModuleName(info.module, info.scope, "latest") createApiModule(mctx, name, info.path) } @@ -278,7 +279,7 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { filename, _, scope := parsePrebuiltPath(mctx, f) referencedModule := strings.TrimSuffix(filename, "-incompatibilities") - createApiModule(mctx, apiModuleName(referencedModule+"-incompatibilities", scope, "latest"), f) + createApiModule(mctx, PrebuiltApiModuleName(referencedModule+"-incompatibilities", scope, "latest"), f) incompatibilities[referencedModule+"."+scope] = true } @@ -286,7 +287,7 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { // Create empty incompatibilities files for remaining modules for _, k := range android.SortedStringKeys(latest) { if _, ok := incompatibilities[k]; !ok { - createEmptyFile(mctx, apiModuleName(latest[k].module+"-incompatibilities", latest[k].scope, "latest")) + createEmptyFile(mctx, PrebuiltApiModuleName(latest[k].module+"-incompatibilities", latest[k].scope, "latest")) } } } diff --git a/java/sdk_library.go b/java/sdk_library.go index cd8e8755e..8778937d5 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -97,6 +97,13 @@ type apiScope struct { // The tag to use to depend on the stubs source and API module. stubsSourceAndApiTag scopeDependencyTag + // The tag to use to depend on the module that provides the latest version of the API .txt file. + latestApiModuleTag scopeDependencyTag + + // The tag to use to depend on the module that provides the latest version of the API removed.txt + // file. + latestRemovedApiModuleTag scopeDependencyTag + // The scope specific prefix to add to the api file base of "current.txt" or "removed.txt". apiFilePrefix string @@ -158,6 +165,16 @@ func initApiScope(scope *apiScope) *apiScope { apiScope: scope, depInfoExtractor: (*scopePaths).extractStubsSourceAndApiInfoFromApiStubsProvider, } + scope.latestApiModuleTag = scopeDependencyTag{ + name: name + "-latest-api", + apiScope: scope, + depInfoExtractor: (*scopePaths).extractLatestApiPath, + } + scope.latestRemovedApiModuleTag = scopeDependencyTag{ + name: name + "-latest-removed-api", + apiScope: scope, + depInfoExtractor: (*scopePaths).extractLatestRemovedApiPath, + } // To get the args needed to generate the stubs source append all the args from // this scope and all the scopes it extends as each set of args adds additional @@ -203,6 +220,24 @@ func (scope *apiScope) String() string { return scope.name } +// snapshotRelativeDir returns the snapshot directory into which the files related to scopes will +// be stored. +func (scope *apiScope) snapshotRelativeDir() string { + return filepath.Join("sdk_library", scope.name) +} + +// snapshotRelativeCurrentApiTxtPath returns the snapshot path to the API .txt file for the named +// library. +func (scope *apiScope) snapshotRelativeCurrentApiTxtPath(name string) string { + return filepath.Join(scope.snapshotRelativeDir(), name+".txt") +} + +// snapshotRelativeRemovedApiTxtPath returns the snapshot path to the removed API .txt file for the +// named library. +func (scope *apiScope) snapshotRelativeRemovedApiTxtPath(name string) string { + return filepath.Join(scope.snapshotRelativeDir(), name+"-removed.txt") +} + type apiScopes []*apiScope func (scopes apiScopes) Strings(accessor func(*apiScope) string) []string { @@ -539,6 +574,12 @@ type scopePaths struct { // Extracted annotations. annotationsZip android.OptionalPath + + // The path to the latest API file. + latestApiPath android.OptionalPath + + // The path to the latest removed API file. + latestRemovedApiPath android.OptionalPath } func (paths *scopePaths) extractStubsLibraryInfoFromDependency(ctx android.ModuleContext, dep android.Module) error { @@ -602,6 +643,31 @@ func (paths *scopePaths) extractStubsSourceAndApiInfoFromApiStubsProvider(ctx an }) } +func extractSingleOptionalOutputPath(dep android.Module) (android.OptionalPath, error) { + var paths android.Paths + if sourceFileProducer, ok := dep.(android.SourceFileProducer); ok { + paths = sourceFileProducer.Srcs() + } else { + return android.OptionalPath{}, fmt.Errorf("module %q does not produce source files", dep) + } + if len(paths) != 1 { + return android.OptionalPath{}, fmt.Errorf("expected one path from %q, got %q", dep, paths) + } + return android.OptionalPathForPath(paths[0]), nil +} + +func (paths *scopePaths) extractLatestApiPath(ctx android.ModuleContext, dep android.Module) error { + outputPath, err := extractSingleOptionalOutputPath(dep) + paths.latestApiPath = outputPath + return err +} + +func (paths *scopePaths) extractLatestRemovedApiPath(ctx android.ModuleContext, dep android.Module) error { + outputPath, err := extractSingleOptionalOutputPath(dep) + paths.latestRemovedApiPath = outputPath + return err +} + type commonToSdkLibraryAndImportProperties struct { // The naming scheme to use for the components that this module creates. // @@ -1174,6 +1240,16 @@ func (module *SdkLibrary) ComponentDepsMutator(ctx android.BottomUpMutatorContex // Add a dependency on the stubs source in order to access both stubs source and api information. ctx.AddVariationDependencies(nil, apiScope.stubsSourceAndApiTag, module.stubsSourceModuleName(apiScope)) + + if module.compareAgainstLatestApi(apiScope) { + // Add dependencies on the latest finalized version of the API .txt file. + latestApiModuleName := module.latestApiModuleName(apiScope) + ctx.AddDependency(module, apiScope.latestApiModuleTag, latestApiModuleName) + + // Add dependencies on the latest finalized version of the remove API .txt file. + latestRemovedApiModuleName := module.latestRemovedApiModuleName(apiScope) + ctx.AddDependency(module, apiScope.latestRemovedApiModuleTag, latestRemovedApiModuleName) + } } if module.requiresRuntimeImplementationLibrary() { @@ -1194,13 +1270,13 @@ func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { if apiScope.unstable { continue } - if m := android.SrcIsModule(module.latestApiFilegroupName(apiScope)); !ctx.OtherModuleExists(m) { + if m := module.latestApiModuleName(apiScope); !ctx.OtherModuleExists(m) { missingApiModules = append(missingApiModules, m) } - if m := android.SrcIsModule(module.latestRemovedApiFilegroupName(apiScope)); !ctx.OtherModuleExists(m) { + if m := module.latestRemovedApiModuleName(apiScope); !ctx.OtherModuleExists(m) { missingApiModules = append(missingApiModules, m) } - if m := android.SrcIsModule(module.latestIncompatibilitiesFilegroupName(apiScope)); !ctx.OtherModuleExists(m) { + if m := module.latestIncompatibilitiesModuleName(apiScope); !ctx.OtherModuleExists(m) { missingApiModules = append(missingApiModules, m) } } @@ -1274,6 +1350,26 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) // Make the set of components exported by this module available for use elsewhere. exportedComponentInfo := android.ExportedComponentsInfo{Components: android.SortedStringKeys(exportedComponents)} ctx.SetProvider(android.ExportedComponentsInfoProvider, exportedComponentInfo) + + // Provide additional information for inclusion in an sdk's generated .info file. + additionalSdkInfo := map[string]interface{}{} + additionalSdkInfo["dist_stem"] = module.distStem() + baseModuleName := module.BaseModuleName() + scopes := map[string]interface{}{} + additionalSdkInfo["scopes"] = scopes + for scope, scopePaths := range module.scopePaths { + scopeInfo := map[string]interface{}{} + scopes[scope.name] = scopeInfo + scopeInfo["current_api"] = scope.snapshotRelativeCurrentApiTxtPath(baseModuleName) + scopeInfo["removed_api"] = scope.snapshotRelativeRemovedApiTxtPath(baseModuleName) + if p := scopePaths.latestApiPath; p.Valid() { + scopeInfo["latest_api"] = p.Path().String() + } + if p := scopePaths.latestRemovedApiPath; p.Valid() { + scopeInfo["latest_removed_api"] = p.Path().String() + } + } + ctx.SetProvider(android.AdditionalSdkInfoProvider, android.AdditionalSdkInfo{additionalSdkInfo}) } func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries { @@ -1319,16 +1415,32 @@ func (module *SdkLibrary) distGroup() string { return proptools.StringDefault(module.sdkLibraryProperties.Dist_group, "unknown") } +func latestPrebuiltApiModuleName(name string, apiScope *apiScope) string { + return PrebuiltApiModuleName(name, apiScope.name, "latest") +} + func (module *SdkLibrary) latestApiFilegroupName(apiScope *apiScope) string { - return ":" + module.distStem() + ".api." + apiScope.name + ".latest" + return ":" + module.latestApiModuleName(apiScope) +} + +func (module *SdkLibrary) latestApiModuleName(apiScope *apiScope) string { + return latestPrebuiltApiModuleName(module.distStem(), apiScope) } func (module *SdkLibrary) latestRemovedApiFilegroupName(apiScope *apiScope) string { - return ":" + module.distStem() + "-removed.api." + apiScope.name + ".latest" + return ":" + module.latestRemovedApiModuleName(apiScope) +} + +func (module *SdkLibrary) latestRemovedApiModuleName(apiScope *apiScope) string { + return latestPrebuiltApiModuleName(module.distStem()+"-removed", apiScope) } func (module *SdkLibrary) latestIncompatibilitiesFilegroupName(apiScope *apiScope) string { - return ":" + module.distStem() + "-incompatibilities.api." + apiScope.name + ".latest" + return ":" + module.latestIncompatibilitiesModuleName(apiScope) +} + +func (module *SdkLibrary) latestIncompatibilitiesModuleName(apiScope *apiScope) string { + return latestPrebuiltApiModuleName(module.distStem()+"-incompatibilities", apiScope) } func childModuleVisibility(childVisibility []string) []string { @@ -1557,7 +1669,7 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName) props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName) - if !(apiScope.unstable || module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api) { + if module.compareAgainstLatestApi(apiScope) { // check against the latest released API latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope)) props.Previous_api = latestApiFilegroupName @@ -1609,6 +1721,10 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC mctx.CreateModule(DroidstubsFactory, &props) } +func (module *SdkLibrary) compareAgainstLatestApi(apiScope *apiScope) bool { + return !(apiScope.unstable || module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api) +} + // Implements android.ApexModule func (module *SdkLibrary) DepIsInSameApex(mctx android.BaseModuleContext, dep android.Module) bool { depTag := mctx.OtherModuleDependencyTag(dep) @@ -2903,7 +3019,7 @@ func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberCo if properties, ok := s.Scopes[apiScope]; ok { scopeSet := propertySet.AddPropertySet(apiScope.propertyName) - scopeDir := filepath.Join("sdk_library", s.OsPrefix(), apiScope.name) + scopeDir := apiScope.snapshotRelativeDir() var jars []string for _, p := range properties.Jars { @@ -2927,13 +3043,13 @@ func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberCo } if properties.CurrentApiFile != nil { - currentApiSnapshotPath := filepath.Join(scopeDir, ctx.Name()+".txt") + currentApiSnapshotPath := apiScope.snapshotRelativeCurrentApiTxtPath(ctx.Name()) ctx.SnapshotBuilder().CopyToSnapshot(properties.CurrentApiFile, currentApiSnapshotPath) scopeSet.AddProperty("current_api", currentApiSnapshotPath) } if properties.RemovedApiFile != nil { - removedApiSnapshotPath := filepath.Join(scopeDir, ctx.Name()+"-removed.txt") + removedApiSnapshotPath := apiScope.snapshotRelativeRemovedApiTxtPath(ctx.Name()) ctx.SnapshotBuilder().CopyToSnapshot(properties.RemovedApiFile, removedApiSnapshotPath) scopeSet.AddProperty("removed_api", removedApiSnapshotPath) } diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go index 3500c84d2..805bc226f 100644 --- a/java/sdk_library_test.go +++ b/java/sdk_library_test.go @@ -126,6 +126,10 @@ func TestJavaSdkLibrary(t *testing.T) { exportedComponentsInfo := result.ModuleProvider(foo.Module(), android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo) expectedFooExportedComponents := []string{ + "foo-removed.api.public.latest", + "foo-removed.api.system.latest", + "foo.api.public.latest", + "foo.api.system.latest", "foo.stubs", "foo.stubs.source", "foo.stubs.source.system", @@ -529,6 +533,8 @@ func TestJavaSdkLibrary_Deps(t *testing.T) { CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{ `dex2oatd`, + `sdklib-removed.api.public.latest`, + `sdklib.api.public.latest`, `sdklib.impl`, `sdklib.stubs`, `sdklib.stubs.source`, @@ -851,6 +857,8 @@ func TestJavaSdkLibraryImport_WithSource(t *testing.T) { CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{ `dex2oatd`, `prebuilt_sdklib`, + `sdklib-removed.api.public.latest`, + `sdklib.api.public.latest`, `sdklib.impl`, `sdklib.stubs`, `sdklib.stubs.source`, @@ -894,6 +902,8 @@ func TestJavaSdkLibraryImport_Preferred(t *testing.T) { CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{ `prebuilt_sdklib`, + `sdklib-removed.api.public.latest`, + `sdklib.api.public.latest`, `sdklib.impl`, `sdklib.stubs`, `sdklib.stubs.source`, diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go index 9d0c3deb9..f33aa268e 100644 --- a/sdk/java_sdk_test.go +++ b/sdk/java_sdk_test.go @@ -651,7 +651,19 @@ module_exports_snapshot { } func TestSnapshotWithJavaSystemModules(t *testing.T) { - result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, ` + result := android.GroupFixturePreparers( + prepareForSdkTestWithJava, + java.PrepareForTestWithJavaDefaultModules, + java.PrepareForTestWithJavaSdkLibraryFiles, + java.FixtureWithPrebuiltApisAndExtensions(map[string][]string{ + "31": {"myjavalib"}, + "32": {"myjavalib"}, + "current": {"myjavalib"}, + }, map[string][]string{ + "1": {"myjavalib"}, + "2": {"myjavalib"}, + }), + ).RunTestWithBp(t, ` sdk { name: "mysdk", java_header_libs: ["exported-system-module"], @@ -796,7 +808,7 @@ sdk_snapshot { .intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt .intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt `), - checkInfoContents(` + checkInfoContents(result.Config, ` [ { "@type": "sdk", @@ -826,7 +838,16 @@ sdk_snapshot { }, { "@type": "java_sdk_library", - "@name": "myjavalib" + "@name": "myjavalib", + "dist_stem": "myjavalib", + "scopes": { + "public": { + "current_api": "sdk_library/public/myjavalib.txt", + "latest_api": "out/soong/.intermediates/prebuilts/sdk/myjavalib.api.public.latest/gen/myjavalib.api.public.latest", + "latest_removed_api": "out/soong/.intermediates/prebuilts/sdk/myjavalib-removed.api.public.latest/gen/myjavalib-removed.api.public.latest", + "removed_api": "sdk_library/public/myjavalib-removed.txt" + } + } }, { "@type": "java_library", diff --git a/sdk/testing.go b/sdk/testing.go index b0f5fdc62..72344de25 100644 --- a/sdk/testing.go +++ b/sdk/testing.go @@ -406,11 +406,11 @@ func checkMergeZips(expected ...string) snapshotBuildInfoChecker { // Check that the snapshot's info contents are ciorrect. // // Both the expected and actual string are both trimmed before comparing. -func checkInfoContents(expected string) snapshotBuildInfoChecker { +func checkInfoContents(config android.Config, expected string) snapshotBuildInfoChecker { return func(info *snapshotBuildInfo) { info.t.Helper() android.AssertTrimmedStringEquals(info.t, "info contents do not match", - expected, info.infoContents) + expected, android.StringRelativeToTop(config, info.infoContents)) } } diff --git a/sdk/update.go b/sdk/update.go index 71bd042a6..e61ae0d0a 100644 --- a/sdk/update.go +++ b/sdk/update.go @@ -566,8 +566,9 @@ type moduleInfo struct { name string // A list of additional dependencies of the module. deps []string - // Additional dynamic properties. - dynamic map[string]interface{} + // Additional member specific properties. + // These will be added into the generated JSON alongside the above properties. + memberSpecific map[string]interface{} } func (m *moduleInfo) MarshalJSON() ([]byte, error) { @@ -590,8 +591,8 @@ func (m *moduleInfo) MarshalJSON() ([]byte, error) { if m.deps != nil { writeObjectPair("@deps", m.deps) } - for _, k := range android.SortedStringKeys(m.dynamic) { - v := m.dynamic[k] + for _, k := range android.SortedStringKeys(m.memberSpecific) { + v := m.memberSpecific[k] writeObjectPair(k, v) } buffer.WriteString("}") @@ -604,9 +605,9 @@ var _ json.Marshaler = (*moduleInfo)(nil) func (s *sdk) generateInfoData(ctx android.ModuleContext, memberVariantDeps []sdkMemberVariantDep) interface{} { modules := []*moduleInfo{} sdkInfo := moduleInfo{ - moduleType: "sdk", - name: ctx.ModuleName(), - dynamic: map[string]interface{}{}, + moduleType: "sdk", + name: ctx.ModuleName(), + memberSpecific: map[string]interface{}{}, } modules = append(modules, &sdkInfo) @@ -622,6 +623,10 @@ func (s *sdk) generateInfoData(ctx android.ModuleContext, memberVariantDeps []sd moduleType: moduleType, name: name, } + + additionalSdkInfo := ctx.OtherModuleProvider(module, android.AdditionalSdkInfoProvider).(android.AdditionalSdkInfo) + info.memberSpecific = additionalSdkInfo.Properties + name2Info[name] = info } return info @@ -630,13 +635,13 @@ func (s *sdk) generateInfoData(ctx android.ModuleContext, memberVariantDeps []sd for _, memberVariantDep := range memberVariantDeps { propertyName := memberVariantDep.memberType.SdkPropertyName() var list []string - if v, ok := sdkInfo.dynamic[propertyName]; ok { + if v, ok := sdkInfo.memberSpecific[propertyName]; ok { list = v.([]string) } memberName := memberVariantDep.variant.Name() list = append(list, memberName) - sdkInfo.dynamic[propertyName] = android.SortedUniqueStrings(list) + sdkInfo.memberSpecific[propertyName] = android.SortedUniqueStrings(list) if memberVariantDep.container != nil { containerInfo := getModuleInfo(memberVariantDep.container) @@ -1217,6 +1222,7 @@ type snapshotBuilder struct { // The target build release for which the snapshot is to be generated. targetBuildRelease *buildRelease + // The contents of the .info file that describes the sdk contents. infoContents string }