diff --git a/java/java.go b/java/java.go index fbad4f390..bf692be24 100644 --- a/java/java.go +++ b/java/java.go @@ -1667,11 +1667,6 @@ type JavaApiLibraryProperties struct { // This is a list of Soong modules Api_contributions []string - // list of api.txt files relative to this directory that contribute to the - // API surface. - // This is a list of relative paths - Api_files []string `android:"path"` - // List of flags to be passed to the javac compiler to generate jar file Javacflags []string @@ -1819,37 +1814,22 @@ func (al *ApiLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { } } -// API signature file names sorted from -// the narrowest api scope to the widest api scope -var scopeOrderedSourceFileNames = allApiScopes.Strings( - func(s *apiScope) string { return s.apiFilePrefix + "current.txt" }) +// Map where key is the api scope name and value is the int value +// representing the order of the api scope, narrowest to the widest +var scopeOrderMap = allApiScopes.MapToIndex( + func(s *apiScope) string { return s.name }) -func (al *ApiLibrary) sortApiFilesByApiScope(ctx android.ModuleContext, srcFilesInfo []JavaApiImportInfo, apiFiles android.Paths) android.Paths { - var sortedSrcFiles android.Paths - - for i, apiScope := range allApiScopes { - for _, srcFileInfo := range srcFilesInfo { - if srcFileInfo.ApiFile.Base() == scopeOrderedSourceFileNames[i] || srcFileInfo.ApiSurface == apiScope.name { - sortedSrcFiles = append(sortedSrcFiles, android.PathForSource(ctx, srcFileInfo.ApiFile.String())) - } - } - // TODO: b/300964421 - Remove when api_files property is removed - for _, apiFileName := range apiFiles { - if apiFileName.Base() == scopeOrderedSourceFileNames[i] { - sortedSrcFiles = append(sortedSrcFiles, apiFileName) - } +func (al *ApiLibrary) sortApiFilesByApiScope(ctx android.ModuleContext, srcFilesInfo []JavaApiImportInfo) []JavaApiImportInfo { + for _, srcFileInfo := range srcFilesInfo { + if srcFileInfo.ApiSurface == "" { + ctx.ModuleErrorf("Api surface not defined for the associated api file %s", srcFileInfo.ApiFile) } } + sort.Slice(srcFilesInfo, func(i, j int) bool { + return scopeOrderMap[srcFilesInfo[i].ApiSurface] < scopeOrderMap[srcFilesInfo[j].ApiSurface] + }) - if len(srcFilesInfo)+len(apiFiles) != len(sortedSrcFiles) { - var srcFiles android.Paths - for _, srcFileInfo := range srcFilesInfo { - srcFiles = append(srcFiles, srcFileInfo.ApiFile) - } - ctx.ModuleErrorf("Unrecognizable source file found within %s", append(srcFiles, apiFiles...)) - } - - return sortedSrcFiles + return srcFilesInfo } func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -1892,16 +1872,12 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { } }) - // Add the api_files inputs - // These are api files in the module subdirectory, which are not provided by - // java_api_contribution but provided directly as module property. - var apiFiles android.Paths - for _, api := range al.properties.Api_files { - apiFiles = append(apiFiles, android.PathForModuleSrc(ctx, api)) + srcFilesInfo = al.sortApiFilesByApiScope(ctx, srcFilesInfo) + var srcFiles android.Paths + for _, srcFileInfo := range srcFilesInfo { + srcFiles = append(srcFiles, android.PathForSource(ctx, srcFileInfo.ApiFile.String())) } - srcFiles := al.sortApiFilesByApiScope(ctx, srcFilesInfo, apiFiles) - if srcFiles == nil && !ctx.Config().AllowMissingDependencies() { ctx.ModuleErrorf("Error: %s has an empty api file.", ctx.ModuleName()) } diff --git a/java/java_test.go b/java/java_test.go index 2ee05ec73..b555a9513 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -1885,7 +1885,6 @@ func TestJavaApiLibraryAndProviderLink(t *testing.T) { name: "bar2", api_surface: "system", api_contributions: ["foo1", "foo2"], - api_files: ["api1/current.txt", "api2/current.txt"] } `, map[string][]byte{ @@ -1903,7 +1902,7 @@ func TestJavaApiLibraryAndProviderLink(t *testing.T) { }, { moduleName: "bar2", - sourceTextFileDirs: []string{"a/current.txt", "b/current.txt", "api1/current.txt", "api2/current.txt"}, + sourceTextFileDirs: []string{"a/current.txt", "b/current.txt"}, }, } for _, c := range testcases { @@ -1975,7 +1974,6 @@ func TestJavaApiLibraryAndDefaultsLink(t *testing.T) { api_surface: "system", defaults:["baz1", "baz2"], api_contributions: ["foo4"], - api_files: ["api1/current.txt", "api2/current.txt"] } `, map[string][]byte{ @@ -2000,7 +1998,7 @@ func TestJavaApiLibraryAndDefaultsLink(t *testing.T) { { moduleName: "bar3", // API text files need to be sorted from the narrower api scope to the wider api scope - sourceTextFileDirs: []string{"a/current.txt", "b/current.txt", "api1/current.txt", "api2/current.txt", "c/system-current.txt", "d/system-current.txt"}, + sourceTextFileDirs: []string{"a/current.txt", "b/current.txt", "c/system-current.txt", "d/system-current.txt"}, }, } for _, c := range testcases { @@ -2265,29 +2263,6 @@ func TestJavaApiLibraryFullApiSurfaceStub(t *testing.T) { android.AssertStringDoesContain(t, "Command expected to contain full_api_surface_stub output jar", manifestCommand, "lib1.jar") } -func TestJavaApiLibraryFilegroupInput(t *testing.T) { - ctx, _ := testJavaWithFS(t, ` - filegroup { - name: "default_current.txt", - srcs: ["current.txt"], - } - - java_api_library { - name: "foo", - api_files: [":default_current.txt"], - } - `, - map[string][]byte{ - "current.txt": nil, - }) - - m := ctx.ModuleForTests("foo", "android_common") - outputs := fmt.Sprint(m.AllOutputs()) - if !strings.Contains(outputs, "foo/foo.jar") { - t.Errorf("Module output does not contain expected jar %s", "foo/foo.jar") - } -} - func TestTradefedOptions(t *testing.T) { result := PrepareForTestWithJavaBuildComponents.RunTestWithBp(t, ` java_test_host { @@ -2423,3 +2398,30 @@ func TestJavaApiContributionImport(t *testing.T) { sourceFilesFlag := "--source-files current.txt" android.AssertStringDoesContain(t, "source text files not present", manifestCommand, sourceFilesFlag) } + +func TestJavaApiLibraryApiFilesSorting(t *testing.T) { + ctx, _ := testJava(t, ` + java_api_library { + name: "foo", + api_contributions: [ + "system-server-api-stubs-docs-non-updatable.api.contribution", + "test-api-stubs-docs-non-updatable.api.contribution", + "system-api-stubs-docs-non-updatable.api.contribution", + "module-lib-api-stubs-docs-non-updatable.api.contribution", + "api-stubs-docs-non-updatable.api.contribution", + ], + } + `) + m := ctx.ModuleForTests("foo", "android_common") + manifest := m.Output("metalava.sbox.textproto") + sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest) + manifestCommand := sboxProto.Commands[0].GetCommand() + + // Api files are sorted from the narrowest api scope to the widest api scope. + // test api and module lib api surface do not have subset/superset relationship, + // but they will never be passed as inputs at the same time. + sourceFilesFlag := "--source-files default/java/api/current.txt " + + "default/java/api/system-current.txt default/java/api/test-current.txt " + + "default/java/api/module-lib-current.txt default/java/api/system-server-current.txt" + android.AssertStringDoesContain(t, "source text files not in api scope order", manifestCommand, sourceFilesFlag) +} diff --git a/java/sdk_library.go b/java/sdk_library.go index 27f862662..e410a41fa 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -286,6 +286,17 @@ func (scopes apiScopes) Strings(accessor func(*apiScope) string) []string { return list } +// Method that maps the apiScopes properties to the index of each apiScopes elements. +// apiScopes property to be used as the key can be specified with the input accessor. +// Only a string property of apiScope can be used as the key of the map. +func (scopes apiScopes) MapToIndex(accessor func(*apiScope) string) map[string]int { + ret := make(map[string]int) + for i, scope := range scopes { + ret[accessor(scope)] = i + } + return ret +} + var ( scopeByName = make(map[string]*apiScope) allScopeNames []string diff --git a/java/testing.go b/java/testing.go index f2bcccfce..98bea7f14 100644 --- a/java/testing.go +++ b/java/testing.go @@ -424,30 +424,80 @@ func gatherRequiredDepsForTest() string { `, extra) } - extraApiLibraryModules := map[string]string{ - "android_stubs_current.from-text": "api/current.txt", - "android_system_stubs_current.from-text": "api/system-current.txt", - "android_test_stubs_current.from-text": "api/test-current.txt", - "android_module_lib_stubs_current.from-text": "api/module-lib-current.txt", - "android_module_lib_stubs_current_full.from-text": "api/module-lib-current.txt", - "android_system_server_stubs_current.from-text": "api/system-server-current.txt", - "core.current.stubs.from-text": "api/current.txt", - "legacy.core.platform.api.stubs.from-text": "api/current.txt", - "stable.core.platform.api.stubs.from-text": "api/current.txt", - "core-lambda-stubs.from-text": "api/current.txt", - "android-non-updatable.stubs.from-text": "api/current.txt", - "android-non-updatable.stubs.system.from-text": "api/system-current.txt", - "android-non-updatable.stubs.test.from-text": "api/test-current.txt", - "android-non-updatable.stubs.module_lib.from-text": "api/module-lib-current.txt", + type apiContributionStruct struct { + name string + apiSurface string + apiFile string } - for libName, apiFile := range extraApiLibraryModules { + var publicApiContribution = apiContributionStruct{ + name: "api-stubs-docs-non-updatable.api.contribution", + apiSurface: "public", + apiFile: "api/current.txt", + } + var systemApiContribution = apiContributionStruct{ + name: "system-api-stubs-docs-non-updatable.api.contribution", + apiSurface: "system", + apiFile: "api/system-current.txt", + } + var testApiContribution = apiContributionStruct{ + name: "test-api-stubs-docs-non-updatable.api.contribution", + apiSurface: "test", + apiFile: "api/test-current.txt", + } + var moduleLibApiContribution = apiContributionStruct{ + name: "module-lib-api-stubs-docs-non-updatable.api.contribution", + apiSurface: "module-lib", + apiFile: "api/module-lib-current.txt", + } + var systemServerApiContribution = apiContributionStruct{ + // This module does not exist but is named this way for consistency + name: "system-server-api-stubs-docs-non-updatable.api.contribution", + apiSurface: "system-server", + apiFile: "api/system-server-current.txt", + } + var apiContributionStructs = []apiContributionStruct{ + publicApiContribution, + systemApiContribution, + testApiContribution, + moduleLibApiContribution, + systemServerApiContribution, + } + + extraApiLibraryModules := map[string]apiContributionStruct{ + "android_stubs_current.from-text": publicApiContribution, + "android_system_stubs_current.from-text": systemApiContribution, + "android_test_stubs_current.from-text": testApiContribution, + "android_module_lib_stubs_current.from-text": moduleLibApiContribution, + "android_module_lib_stubs_current_full.from-text": moduleLibApiContribution, + "android_system_server_stubs_current.from-text": systemServerApiContribution, + "core.current.stubs.from-text": publicApiContribution, + "legacy.core.platform.api.stubs.from-text": publicApiContribution, + "stable.core.platform.api.stubs.from-text": publicApiContribution, + "core-lambda-stubs.from-text": publicApiContribution, + "android-non-updatable.stubs.from-text": publicApiContribution, + "android-non-updatable.stubs.system.from-text": systemApiContribution, + "android-non-updatable.stubs.test.from-text": testApiContribution, + "android-non-updatable.stubs.module_lib.from-text": moduleLibApiContribution, + } + + for _, apiContribution := range apiContributionStructs { + bp += fmt.Sprintf(` + java_api_contribution { + name: "%s", + api_surface: "%s", + api_file: "%s", + } + `, apiContribution.name, apiContribution.apiSurface, apiContribution.apiFile) + } + + for libName, apiContribution := range extraApiLibraryModules { bp += fmt.Sprintf(` java_api_library { name: "%s", - api_files: ["%s"], + api_contributions: ["%s"], } - `, libName, apiFile) + `, libName, apiContribution.name) } bp += `