Merge changes from topic "retry-jsl-stuff" into rvc-dev
* changes:
Retry: "java_sdk_library: Do not expose stubs implementation jar"
Retry: "java_sdk_library: Access outputs using tags"
Retry: "java_sdk_library: Improve consistency with ..._import"
Retry: "java_sdk_library: Add redirection to module-lib stubs"
Retry: "java_sdk_library: Extract common stubs redirect code"
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 4b4a245..c1cecb6 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -350,11 +350,16 @@
ApiFilePath() android.Path
}
+type ApiStubsSrcProvider interface {
+ StubsSrcJar() android.Path
+}
+
// Provider of information about API stubs, used by java_sdk_library.
type ApiStubsProvider interface {
ApiFilePath
RemovedApiFilePath() android.Path
- StubsSrcJar() android.Path
+
+ ApiStubsSrcProvider
}
//
@@ -1912,6 +1917,10 @@
}
}
+func (d *PrebuiltStubsSources) StubsSrcJar() android.Path {
+ return d.stubsSrcJar
+}
+
func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
diff --git a/java/java_test.go b/java/java_test.go
index 01ddccf..4f3a803 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -575,6 +575,7 @@
},
test: {
jars: ["c.jar"],
+ stub_srcs: ["c.java"],
},
}
`)
@@ -1229,6 +1230,113 @@
}
}
+func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) {
+ testJava(t, `
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java"],
+ api_packages: ["foo"],
+ public: {
+ enabled: true,
+ },
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.java", ":foo{.public.stubs.source}"],
+ }
+ `)
+}
+
+func TestJavaSdkLibrary_AccessOutputFiles_MissingScope(t *testing.T) {
+ testJavaError(t, `"foo" does not provide api scope system`, `
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java"],
+ api_packages: ["foo"],
+ public: {
+ enabled: true,
+ },
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.java", ":foo{.system.stubs.source}"],
+ }
+ `)
+}
+
+func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) {
+ testJava(t, `
+ java_sdk_library_import {
+ name: "foo",
+ public: {
+ jars: ["a.jar"],
+ stub_srcs: ["a.java"],
+ current_api: "api/current.txt",
+ removed_api: "api/removed.txt",
+ },
+ }
+
+ java_library {
+ name: "bar",
+ srcs: [":foo{.public.stubs.source}"],
+ java_resources: [
+ ":foo{.public.api.txt}",
+ ":foo{.public.removed-api.txt}",
+ ],
+ }
+ `)
+}
+
+func TestJavaSdkLibraryImport_AccessOutputFiles_Invalid(t *testing.T) {
+ bp := `
+ java_sdk_library_import {
+ name: "foo",
+ public: {
+ jars: ["a.jar"],
+ },
+ }
+ `
+
+ t.Run("stubs.source", func(t *testing.T) {
+ testJavaError(t, `stubs.source not available for api scope public`, bp+`
+ java_library {
+ name: "bar",
+ srcs: [":foo{.public.stubs.source}"],
+ java_resources: [
+ ":foo{.public.api.txt}",
+ ":foo{.public.removed-api.txt}",
+ ],
+ }
+ `)
+ })
+
+ t.Run("api.txt", func(t *testing.T) {
+ testJavaError(t, `api.txt not available for api scope public`, bp+`
+ java_library {
+ name: "bar",
+ srcs: ["a.java"],
+ java_resources: [
+ ":foo{.public.api.txt}",
+ ],
+ }
+ `)
+ })
+
+ t.Run("removed-api.txt", func(t *testing.T) {
+ testJavaError(t, `removed-api.txt not available for api scope public`, bp+`
+ java_library {
+ name: "bar",
+ srcs: ["a.java"],
+ java_resources: [
+ ":foo{.public.removed-api.txt}",
+ ],
+ }
+ `)
+ })
+}
+
func TestJavaSdkLibrary_InvalidScopes(t *testing.T) {
testJavaError(t, `module "foo": enabled api scope "system" depends on disabled scope "public"`, `
java_sdk_library {
@@ -1261,6 +1369,45 @@
`)
}
+func TestJavaSdkLibrary_MissingScope(t *testing.T) {
+ testJavaError(t, `requires api scope module-lib from foo but it only has \[\] available`, `
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java"],
+ public: {
+ enabled: false,
+ },
+ }
+
+ java_library {
+ name: "baz",
+ srcs: ["a.java"],
+ libs: ["foo"],
+ sdk_version: "module_current",
+ }
+ `)
+}
+
+func TestJavaSdkLibrary_FallbackScope(t *testing.T) {
+ testJava(t, `
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java"],
+ system: {
+ enabled: true,
+ },
+ }
+
+ java_library {
+ name: "baz",
+ srcs: ["a.java"],
+ libs: ["foo"],
+ // foo does not have module-lib scope so it should fallback to system
+ sdk_version: "module_current",
+ }
+ `)
+}
+
var compilerFlagsTestCases = []struct {
in string
out bool
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 9d5c7b6..adbb5d2 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -19,6 +19,7 @@
"path"
"path/filepath"
"reflect"
+ "regexp"
"sort"
"strings"
"sync"
@@ -145,6 +146,8 @@
// Initialize a scope, creating and adding appropriate dependency tags
func initApiScope(scope *apiScope) *apiScope {
name := scope.name
+ scopeByName[name] = scope
+ allScopeNames = append(allScopeNames, name)
scope.propertyName = strings.ReplaceAll(name, "-", "_")
scope.fieldName = proptools.FieldNameForProperty(scope.propertyName)
scope.stubsTag = scopeDependencyTag{
@@ -217,6 +220,8 @@
}
var (
+ scopeByName = make(map[string]*apiScope)
+ allScopeNames []string
apiScopePublic = initApiScope(&apiScope{
name: "public",
@@ -433,12 +438,30 @@
//Html_doc *bool
}
+// Paths to outputs from java_sdk_library and java_sdk_library_import.
+//
+// Fields that are android.Paths are always set (during GenerateAndroidBuildActions).
+// OptionalPaths are always set by java_sdk_library but may not be set by
+// java_sdk_library_import as not all instances provide that information.
type scopePaths struct {
- stubsHeaderPath android.Paths
- stubsImplPath android.Paths
- currentApiFilePath android.Path
- removedApiFilePath android.Path
- stubsSrcJar android.Path
+ // The path (represented as Paths for convenience when returning) to the stubs header jar.
+ //
+ // That is the jar that is created by turbine.
+ stubsHeaderPath android.Paths
+
+ // The path (represented as Paths for convenience when returning) to the stubs implementation jar.
+ //
+ // This is not the implementation jar, it still only contains stubs.
+ stubsImplPath android.Paths
+
+ // The API specification file, e.g. system_current.txt.
+ currentApiFilePath android.OptionalPath
+
+ // The specification of API elements removed since the last release.
+ removedApiFilePath android.OptionalPath
+
+ // The stubs source jar.
+ stubsSrcJar android.OptionalPath
}
func (paths *scopePaths) extractStubsLibraryInfoFromDependency(dep android.Module) error {
@@ -460,9 +483,18 @@
}
}
+func (paths *scopePaths) treatDepAsApiStubsSrcProvider(dep android.Module, action func(provider ApiStubsSrcProvider)) error {
+ if apiStubsProvider, ok := dep.(ApiStubsSrcProvider); ok {
+ action(apiStubsProvider)
+ return nil
+ } else {
+ return fmt.Errorf("expected module that implements ApiStubsSrcProvider, e.g. droidstubs")
+ }
+}
+
func (paths *scopePaths) extractApiInfoFromApiStubsProvider(provider ApiStubsProvider) {
- paths.currentApiFilePath = provider.ApiFilePath()
- paths.removedApiFilePath = provider.RemovedApiFilePath()
+ paths.currentApiFilePath = android.OptionalPathForPath(provider.ApiFilePath())
+ paths.removedApiFilePath = android.OptionalPathForPath(provider.RemovedApiFilePath())
}
func (paths *scopePaths) extractApiInfoFromDep(dep android.Module) error {
@@ -471,12 +503,12 @@
})
}
-func (paths *scopePaths) extractStubsSourceInfoFromApiStubsProviders(provider ApiStubsProvider) {
- paths.stubsSrcJar = provider.StubsSrcJar()
+func (paths *scopePaths) extractStubsSourceInfoFromApiStubsProviders(provider ApiStubsSrcProvider) {
+ paths.stubsSrcJar = android.OptionalPathForPath(provider.StubsSrcJar())
}
func (paths *scopePaths) extractStubsSourceInfoFromDep(dep android.Module) error {
- return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) {
+ return paths.treatDepAsApiStubsSrcProvider(dep, func(provider ApiStubsSrcProvider) {
paths.extractStubsSourceInfoFromApiStubsProviders(provider)
})
}
@@ -551,7 +583,83 @@
return c.namingScheme.apiModuleName(apiScope, c.moduleBase.BaseModuleName())
}
-func (c *commonToSdkLibraryAndImport) getScopePaths(scope *apiScope) *scopePaths {
+// The component names for different outputs of the java_sdk_library.
+//
+// They are similar to the names used for the child modules it creates
+const (
+ stubsSourceComponentName = "stubs.source"
+
+ apiTxtComponentName = "api.txt"
+
+ removedApiTxtComponentName = "removed-api.txt"
+)
+
+// A regular expression to match tags that reference a specific stubs component.
+//
+// It will only match if given a valid scope and a valid component. It is verfy strict
+// to ensure it does not accidentally match a similar looking tag that should be processed
+// by the embedded Library.
+var tagSplitter = func() *regexp.Regexp {
+ // Given a list of literal string items returns a regular expression that will
+ // match any one of the items.
+ choice := func(items ...string) string {
+ return `\Q` + strings.Join(items, `\E|\Q`) + `\E`
+ }
+
+ // Regular expression to match one of the scopes.
+ scopesRegexp := choice(allScopeNames...)
+
+ // Regular expression to match one of the components.
+ componentsRegexp := choice(stubsSourceComponentName, apiTxtComponentName, removedApiTxtComponentName)
+
+ // Regular expression to match any combination of one scope and one component.
+ return regexp.MustCompile(fmt.Sprintf(`^\.(%s)\.(%s)$`, scopesRegexp, componentsRegexp))
+}()
+
+// For OutputFileProducer interface
+//
+// .<scope>.stubs.source
+// .<scope>.api.txt
+// .<scope>.removed-api.txt
+func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Paths, error) {
+ if groups := tagSplitter.FindStringSubmatch(tag); groups != nil {
+ scopeName := groups[1]
+ component := groups[2]
+
+ if scope, ok := scopeByName[scopeName]; ok {
+ paths := c.findScopePaths(scope)
+ if paths == nil {
+ return nil, fmt.Errorf("%q does not provide api scope %s", c.moduleBase.BaseModuleName(), scopeName)
+ }
+
+ switch component {
+ case stubsSourceComponentName:
+ if paths.stubsSrcJar.Valid() {
+ return android.Paths{paths.stubsSrcJar.Path()}, nil
+ }
+
+ case apiTxtComponentName:
+ if paths.currentApiFilePath.Valid() {
+ return android.Paths{paths.currentApiFilePath.Path()}, nil
+ }
+
+ case removedApiTxtComponentName:
+ if paths.removedApiFilePath.Valid() {
+ return android.Paths{paths.removedApiFilePath.Path()}, nil
+ }
+ }
+
+ return nil, fmt.Errorf("%s not available for api scope %s", component, scopeName)
+ } else {
+ return nil, fmt.Errorf("unknown scope %s in %s", scope, tag)
+ }
+
+ } else {
+ return nil, nil
+ }
+}
+
+func (c *commonToSdkLibraryAndImport) getScopePathsCreateIfNeeded(scope *apiScope) *scopePaths {
if c.scopePaths == nil {
c.scopePaths = make(map[*apiScope]*scopePaths)
}
@@ -564,6 +672,62 @@
return paths
}
+func (c *commonToSdkLibraryAndImport) findScopePaths(scope *apiScope) *scopePaths {
+ if c.scopePaths == nil {
+ return nil
+ }
+
+ return c.scopePaths[scope]
+}
+
+// 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 {
+ if paths := c.findScopePaths(s); paths != nil {
+ return paths
+ }
+ }
+
+ // This should never happen outside tests as public should be the base scope for every
+ // scope and is enabled by default.
+ return nil
+}
+
+func (c *commonToSdkLibraryAndImport) selectHeaderJarsForSdkVersion(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
+
+ // If a specific numeric version has been requested then use prebuilt versions of the sdk.
+ if sdkVersion.version.isNumbered() {
+ return PrebuiltJars(ctx, c.moduleBase.BaseModuleName(), sdkVersion)
+ }
+
+ var apiScope *apiScope
+ switch sdkVersion.kind {
+ case sdkSystem:
+ apiScope = apiScopeSystem
+ case sdkModule:
+ apiScope = apiScopeModuleLib
+ case sdkTest:
+ apiScope = apiScopeTest
+ default:
+ apiScope = apiScopePublic
+ }
+
+ paths := c.findClosestScopePath(apiScope)
+ if paths == nil {
+ var scopes []string
+ for _, s := range allApiScopes {
+ if c.findScopePaths(s) != nil {
+ scopes = append(scopes, s.name)
+ }
+ }
+ ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.moduleBase.BaseModuleName(), scopes)
+ return nil
+ }
+
+ return paths.stubsHeaderPath
+}
+
type SdkLibrary struct {
Library
@@ -664,6 +828,15 @@
module.Library.deps(ctx)
}
+func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) {
+ paths, err := module.commonOutputFiles(tag)
+ if paths == nil && err == nil {
+ return module.Library.OutputFiles(tag)
+ } else {
+ return paths, err
+ }
+}
+
func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// Don't build an implementation library if this is api only.
if !proptools.Bool(module.sdkLibraryProperties.Api_only) {
@@ -679,7 +852,7 @@
// Extract information from any of the scope specific dependencies.
if scopeTag, ok := tag.(scopeDependencyTag); ok {
apiScope := scopeTag.apiScope
- scopePaths := module.getScopePaths(apiScope)
+ scopePaths := module.getScopePathsCreateIfNeeded(apiScope)
// Extract information from the dependency. The exact information extracted
// is determined by the nature of the dependency which is determined by the tag.
@@ -1012,41 +1185,20 @@
return android.Paths{jarPath.Path()}
}
-func (module *SdkLibrary) sdkJars(
- ctx android.BaseModuleContext,
- sdkVersion sdkSpec,
- headerJars bool) android.Paths {
+func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion sdkSpec, headerJars bool) android.Paths {
- // If a specific numeric version has been requested then use prebuilt versions of the sdk.
- if sdkVersion.version.isNumbered() {
- return PrebuiltJars(ctx, module.BaseModuleName(), sdkVersion)
- } else {
- if !sdkVersion.specified() {
- if headerJars {
- return module.HeaderJars()
- } else {
- return module.ImplementationJars()
- }
- }
- var apiScope *apiScope
- switch sdkVersion.kind {
- case sdkSystem:
- apiScope = apiScopeSystem
- case sdkTest:
- apiScope = apiScopeTest
- case sdkPrivate:
- return module.HeaderJars()
- default:
- apiScope = apiScopePublic
- }
-
- paths := module.getScopePaths(apiScope)
+ // Check any special cases for java_sdk_library.
+ if !sdkVersion.specified() {
if headerJars {
- return paths.stubsHeaderPath
+ return module.HeaderJars()
} else {
- return paths.stubsImplPath
+ return module.ImplementationJars()
}
+ } else if sdkVersion.kind == sdkPrivate {
+ return module.HeaderJars()
}
+
+ return module.selectHeaderJarsForSdkVersion(ctx, sdkVersion)
}
// to satisfy SdkLibraryDependency interface
@@ -1277,10 +1429,10 @@
Stub_srcs []string `android:"path"`
// The current.txt
- Current_api string `android:"path"`
+ Current_api *string `android:"path"`
// The removed.txt
- Removed_api string `android:"path"`
+ Removed_api *string `android:"path"`
}
type sdkLibraryImportProperties struct {
@@ -1390,7 +1542,9 @@
module.createJavaImportForStubs(mctx, apiScope, scopeProperties)
- module.createPrebuiltStubsSources(mctx, apiScope, scopeProperties)
+ if len(scopeProperties.Stub_srcs) > 0 {
+ module.createPrebuiltStubsSources(mctx, apiScope, scopeProperties)
+ }
}
javaSdkLibraries := javaSdkLibraries(mctx.Config())
@@ -1442,45 +1596,48 @@
// Add dependencies to the prebuilt stubs library
ctx.AddVariationDependencies(nil, apiScope.stubsTag, module.stubsLibraryModuleName(apiScope))
+
+ if len(scopeProperties.Stub_srcs) > 0 {
+ // Add dependencies to the prebuilt stubs source library
+ ctx.AddVariationDependencies(nil, apiScope.stubsSourceTag, module.stubsSourceModuleName(apiScope))
+ }
}
}
+func (module *sdkLibraryImport) OutputFiles(tag string) (android.Paths, error) {
+ return module.commonOutputFiles(tag)
+}
+
func (module *sdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- // Record the paths to the prebuilt stubs library.
+ // Record the paths to the prebuilt stubs library and stubs source.
ctx.VisitDirectDeps(func(to android.Module) {
tag := ctx.OtherModuleDependencyTag(to)
- if lib, ok := to.(Dependency); ok {
- if scopeTag, ok := tag.(scopeDependencyTag); ok {
- apiScope := scopeTag.apiScope
- scopePaths := module.getScopePaths(apiScope)
- scopePaths.stubsHeaderPath = lib.HeaderJars()
- }
+ // Extract information from any of the scope specific dependencies.
+ if scopeTag, ok := tag.(scopeDependencyTag); ok {
+ apiScope := scopeTag.apiScope
+ scopePaths := module.getScopePathsCreateIfNeeded(apiScope)
+
+ // Extract information from the dependency. The exact information extracted
+ // is determined by the nature of the dependency which is determined by the tag.
+ scopeTag.extractDepInfo(ctx, to, scopePaths)
}
})
+
+ // Populate the scope paths with information from the properties.
+ for apiScope, scopeProperties := range module.scopeProperties {
+ if len(scopeProperties.Jars) == 0 {
+ continue
+ }
+
+ paths := module.getScopePathsCreateIfNeeded(apiScope)
+ paths.currentApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Current_api)
+ paths.removedApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Removed_api)
+ }
}
-func (module *sdkLibraryImport) sdkJars(
- ctx android.BaseModuleContext,
- sdkVersion sdkSpec) android.Paths {
-
- // If a specific numeric version has been requested then use prebuilt versions of the sdk.
- if sdkVersion.version.isNumbered() {
- return PrebuiltJars(ctx, module.BaseModuleName(), sdkVersion)
- }
-
- var apiScope *apiScope
- switch sdkVersion.kind {
- case sdkSystem:
- apiScope = apiScopeSystem
- case sdkTest:
- apiScope = apiScopeTest
- default:
- apiScope = apiScopePublic
- }
-
- paths := module.getScopePaths(apiScope)
- return paths.stubsHeaderPath
+func (module *sdkLibraryImport) sdkJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
+ return module.selectHeaderJarsForSdkVersion(ctx, sdkVersion)
}
// to satisfy SdkLibraryDependency interface
@@ -1653,15 +1810,19 @@
s.Scopes = make(map[*apiScope]scopeProperties)
for _, apiScope := range allApiScopes {
- paths := sdk.getScopePaths(apiScope)
+ paths := sdk.findScopePaths(apiScope)
+ if paths == nil {
+ continue
+ }
+
jars := paths.stubsImplPath
if len(jars) > 0 {
properties := scopeProperties{}
properties.Jars = jars
properties.SdkVersion = sdk.sdkVersionForStubsLibrary(ctx.SdkModuleContext(), apiScope)
- properties.StubsSrcJar = paths.stubsSrcJar
- properties.CurrentApiFile = paths.currentApiFilePath
- properties.RemovedApiFile = paths.removedApiFilePath
+ properties.StubsSrcJar = paths.stubsSrcJar.Path()
+ properties.CurrentApiFile = paths.currentApiFilePath.Path()
+ properties.RemovedApiFile = paths.removedApiFilePath.Path()
s.Scopes[apiScope] = properties
}
}