java_sdk_library: Access outputs using tags

Previously, in order to access say the public stubs source jar it was
necessary to directly reference the module by name. This change adds
support for accessing them indirectly via tags.

Test: nothing
Bug: 155164730
Change-Id: Id6d76e56c7b46944b2d2a44a2163fb05a5b03de9
This commit is contained in:
Paul Duffin 2020-05-14 15:39:10 +01:00
parent 0f8faffdc0
commit 46dc45aba9
2 changed files with 201 additions and 0 deletions

View File

@ -1230,6 +1230,113 @@ func TestJavaSdkLibrary(t *testing.T) {
} }
} }
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) { func TestJavaSdkLibrary_InvalidScopes(t *testing.T) {
testJavaError(t, `module "foo": enabled api scope "system" depends on disabled scope "public"`, ` testJavaError(t, `module "foo": enabled api scope "system" depends on disabled scope "public"`, `
java_sdk_library { java_sdk_library {

View File

@ -19,6 +19,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"reflect" "reflect"
"regexp"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@ -145,6 +146,8 @@ type apiScope struct {
// Initialize a scope, creating and adding appropriate dependency tags // Initialize a scope, creating and adding appropriate dependency tags
func initApiScope(scope *apiScope) *apiScope { func initApiScope(scope *apiScope) *apiScope {
name := scope.name name := scope.name
scopeByName[name] = scope
allScopeNames = append(allScopeNames, name)
scope.propertyName = strings.ReplaceAll(name, "-", "_") scope.propertyName = strings.ReplaceAll(name, "-", "_")
scope.fieldName = proptools.FieldNameForProperty(scope.propertyName) scope.fieldName = proptools.FieldNameForProperty(scope.propertyName)
scope.stubsTag = scopeDependencyTag{ scope.stubsTag = scopeDependencyTag{
@ -217,6 +220,8 @@ func (scopes apiScopes) Strings(accessor func(*apiScope) string) []string {
} }
var ( var (
scopeByName = make(map[string]*apiScope)
allScopeNames []string
apiScopePublic = initApiScope(&apiScope{ apiScopePublic = initApiScope(&apiScope{
name: "public", name: "public",
@ -578,6 +583,82 @@ func (c *commonToSdkLibraryAndImport) apiModuleName(apiScope *apiScope) string {
return c.namingScheme.apiModuleName(apiScope, c.moduleBase.BaseModuleName()) return c.namingScheme.apiModuleName(apiScope, c.moduleBase.BaseModuleName())
} }
// 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 { func (c *commonToSdkLibraryAndImport) getScopePathsCreateIfNeeded(scope *apiScope) *scopePaths {
if c.scopePaths == nil { if c.scopePaths == nil {
c.scopePaths = make(map[*apiScope]*scopePaths) c.scopePaths = make(map[*apiScope]*scopePaths)
@ -751,6 +832,15 @@ func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
module.Library.deps(ctx) 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) { func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// Don't build an implementation library if this is api only. // Don't build an implementation library if this is api only.
if !proptools.Bool(module.sdkLibraryProperties.Api_only) { if !proptools.Bool(module.sdkLibraryProperties.Api_only) {
@ -1516,6 +1606,10 @@ func (module *sdkLibraryImport) DepsMutator(ctx android.BottomUpMutatorContext)
} }
} }
func (module *sdkLibraryImport) OutputFiles(tag string) (android.Paths, error) {
return module.commonOutputFiles(tag)
}
func (module *sdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { func (module *sdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// Record the paths to the prebuilt stubs library and stubs source. // Record the paths to the prebuilt stubs library and stubs source.
ctx.VisitDirectDeps(func(to android.Module) { ctx.VisitDirectDeps(func(to android.Module) {