diff --git a/java/sdk_library.go b/java/sdk_library.go index 59ffff5c9..fad1df77c 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -65,8 +65,27 @@ type apiScope struct { name string // The api scope that this scope extends. + // + // This organizes the scopes into an extension hierarchy. + // + // If set this means that the API provided by this scope includes the API provided by the scope + // set in this field. extends *apiScope + // The next api scope that a library that uses this scope can access. + // + // This organizes the scopes into an access hierarchy. + // + // If set this means that a library that can access this API can also access the API provided by + // the scope set in this field. + // + // A module that sets sdk_version: "_current" should have access to the API of + // every java_sdk_library that it depends on. If the library does not provide an API for + // then it will traverse up this access hierarchy to find an API that it does provide. + // + // If this is not set then it defaults to the scope set in extends. + canAccess *apiScope + // The legacy enabled status for a specific scope can be dependent on other // properties that have been specified on the library so it is provided by // a function that can determine the status by examining those properties. @@ -107,7 +126,7 @@ type apiScope struct { // The scope specific prefix to add to the api file base of "current.txt" or "removed.txt". apiFilePrefix string - // The scope specific prefix to add to the sdk library module name to construct a scope specific + // The scope specific suffix to add to the sdk library module name to construct a scope specific // module name. moduleSuffix string @@ -193,6 +212,11 @@ func initApiScope(scope *apiScope) *apiScope { } } + // By default, a library that can access a scope can also access the scope it extends. + if scope.canAccess == nil { + scope.canAccess = scope.extends + } + // Escape any special characters in the arguments. This is needed because droidstubs // passes these directly to the shell command. scope.droidstubsArgs = proptools.ShellEscapeList(scopeSpecificArgs) @@ -310,6 +334,14 @@ var ( apiScopeSystemServer = initApiScope(&apiScope{ name: "system-server", extends: apiScopePublic, + + // The system-server scope can access the module-lib scope. + // + // A module that provides a system-server API is appended to the standard bootclasspath that is + // used by the system server. So, it should be able to access module-lib APIs provided by + // libraries on the bootclasspath. + canAccess: apiScopeModuleLib, + // The system-server scope is disabled by default in legacy mode. // // Enabling this would break existing usages. @@ -926,7 +958,7 @@ func (c *commonToSdkLibraryAndImport) findScopePaths(scope *apiScope) *scopePath // If this does not support the requested api scope then find the closest available // scope it does support. Returns nil if no such scope is available. func (c *commonToSdkLibraryAndImport) findClosestScopePath(scope *apiScope) *scopePaths { - for s := scope; s != nil; s = s.extends { + for s := scope; s != nil; s = s.canAccess { if paths := c.findScopePaths(s); paths != nil { return paths } diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go index ea7b2f74f..096bca8a1 100644 --- a/java/sdk_library_test.go +++ b/java/sdk_library_test.go @@ -18,6 +18,7 @@ import ( "fmt" "path/filepath" "regexp" + "strings" "testing" "android/soong/android" @@ -699,6 +700,80 @@ func TestJavaSdkLibrary_SystemServer(t *testing.T) { `) } +func TestJavaSdkLibrary_SystemServer_AccessToStubScopeLibs(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("foo-public", "foo-system", "foo-module-lib", "foo-system-server"), + ).RunTestWithBp(t, ` + java_sdk_library { + name: "foo-public", + srcs: ["a.java"], + api_packages: ["foo"], + public: { + enabled: true, + }, + } + + java_sdk_library { + name: "foo-system", + srcs: ["a.java"], + api_packages: ["foo"], + system: { + enabled: true, + }, + } + + java_sdk_library { + name: "foo-module-lib", + srcs: ["a.java"], + api_packages: ["foo"], + system: { + enabled: true, + }, + module_lib: { + enabled: true, + }, + } + + java_sdk_library { + name: "foo-system-server", + srcs: ["a.java"], + api_packages: ["foo"], + system_server: { + enabled: true, + }, + } + + java_library { + name: "bar", + srcs: ["a.java"], + libs: ["foo-public", "foo-system", "foo-module-lib", "foo-system-server"], + sdk_version: "system_server_current", + } + `) + + stubsPath := func(name string, scope *apiScope) string { + name = scope.stubsLibraryModuleName(name) + return fmt.Sprintf("out/soong/.intermediates/%[1]s/android_common/turbine-combined/%[1]s.jar", name) + } + + // The bar library should depend on the highest (where system server is highest and public is + // lowest) API scopes provided by each of the foo-* modules. The highest API scope provided by the + // foo- module is . + barLibrary := result.ModuleForTests("bar", "android_common").Rule("javac") + stubLibraries := []string{ + stubsPath("foo-public", apiScopePublic), + stubsPath("foo-system", apiScopeSystem), + stubsPath("foo-module-lib", apiScopeModuleLib), + stubsPath("foo-system-server", apiScopeSystemServer), + } + expectedPattern := fmt.Sprintf(`^-classpath .*:\Q%s\E$`, strings.Join(stubLibraries, ":")) + if expected, actual := expectedPattern, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) { + t.Errorf("expected pattern %q to match %#q", expected, actual) + } +} + func TestJavaSdkLibrary_MissingScope(t *testing.T) { prepareForJavaTest. ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`requires api scope module-lib from foo but it only has \[\] available`)).