Merge "Remove duplicate component from sdk snapshot"

This commit is contained in:
Treehugger Robot 2021-06-24 00:04:42 +00:00 committed by Gerrit Code Review
commit a8de9fb2ae
5 changed files with 195 additions and 45 deletions

View file

@ -38,6 +38,36 @@ type RequiredSdks interface {
type sdkAwareWithoutModule interface {
RequiredSdks
// SdkMemberComponentName will return the name to use for a component of this module based on the
// base name of this module.
//
// The baseName is the name returned by ModuleBase.BaseModuleName(), i.e. the name specified in
// the name property in the .bp file so will not include the prebuilt_ prefix.
//
// The componentNameCreator is a func for creating the name of a component from the base name of
// the module, e.g. it could just append ".component" to the name passed in.
//
// This is intended to be called by prebuilt modules that create component models. It is because
// prebuilt module base names come in a variety of different forms:
// * unversioned - this is the same as the source module.
// * internal to an sdk - this is the unversioned name prefixed by the base name of the sdk
// module.
// * versioned - this is the same as the internal with the addition of an "@<version>" suffix.
//
// While this can be called from a source module in that case it will behave the same way as the
// unversioned name and return the result of calling the componentNameCreator func on the supplied
// base name.
//
// e.g. Assuming the componentNameCreator func simply appends ".component" to the name passed in
// then this will work as follows:
// * An unversioned name of "foo" will return "foo.component".
// * An internal to the sdk name of "sdk_foo" will return "sdk_foo.component".
// * A versioned name of "sdk_foo@current" will return "sdk_foo.component@current".
//
// Note that in the latter case the ".component" suffix is added before the version. Adding it
// after would change the version.
SdkMemberComponentName(baseName string, componentNameCreator func(string) string) string
sdkBase() *SdkBase
MakeMemberOf(sdk SdkRef)
IsInAnySdk() bool
@ -135,6 +165,18 @@ func (s *SdkBase) sdkBase() *SdkBase {
return s
}
func (s *SdkBase) SdkMemberComponentName(baseName string, componentNameCreator func(string) string) string {
if s.MemberName() == "" {
return componentNameCreator(baseName)
} else {
index := strings.LastIndex(baseName, "@")
unversionedName := baseName[:index]
unversionedComponentName := componentNameCreator(unversionedName)
versionSuffix := baseName[index:]
return unversionedComponentName + versionSuffix
}
}
// MakeMemberOf sets this module to be a member of a specific SDK
func (s *SdkBase) MakeMemberOf(sdk SdkRef) {
s.properties.ContainingSdk = &sdk
@ -659,3 +701,30 @@ type SdkMemberContext interface {
// into which to copy the prebuilt files.
Name() string
}
// ExportedComponentsInfo contains information about the components that this module exports to an
// sdk snapshot.
//
// A component of a module is a child module that the module creates and which forms an integral
// part of the functionality that the creating module provides. A component module is essentially
// owned by its creator and is tightly coupled to the creator and other components.
//
// e.g. the child modules created by prebuilt_apis are not components because they are not tightly
// coupled to the prebuilt_apis module. Once they are created the prebuilt_apis ignores them. The
// child impl and stub library created by java_sdk_library (and corresponding import) are components
// because the creating module depends upon them in order to provide some of its own functionality.
//
// A component is exported if it is part of an sdk snapshot. e.g. The xml and impl child modules are
// components but they are not exported as they are not part of an sdk snapshot.
//
// This information is used by the sdk snapshot generation code to ensure that it does not create
// an sdk snapshot that contains a declaration of the component module and the module that creates
// it as that would result in duplicate modules when attempting to use the snapshot. e.g. a snapshot
// that included the java_sdk_library_import "foo" and also a java_import "foo.stubs" would fail
// as there would be two modules called "foo.stubs".
type ExportedComponentsInfo struct {
// The names of the exported components.
Components []string
}
var ExportedComponentsInfoProvider = blueprint.NewProvider(ExportedComponentsInfo{})

View file

@ -634,7 +634,7 @@ type commonToSdkLibraryAndImportProperties struct {
// commonSdkLibraryAndImportModule defines the interface that must be provided by a module that
// embeds the commonToSdkLibraryAndImport struct.
type commonSdkLibraryAndImportModule interface {
android.Module
android.SdkAware
BaseModuleName() string
}
@ -700,13 +700,19 @@ func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string {
// Name of the java_library module that compiles the stubs source.
func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string {
return c.namingScheme.stubsLibraryModuleName(apiScope, c.module.BaseModuleName())
baseName := c.module.BaseModuleName()
return c.module.SdkMemberComponentName(baseName, func(name string) string {
return c.namingScheme.stubsLibraryModuleName(apiScope, name)
})
}
// Name of the droidstubs module that generates the stubs source and may also
// generate/check the API.
func (c *commonToSdkLibraryAndImport) stubsSourceModuleName(apiScope *apiScope) string {
return c.namingScheme.stubsSourceModuleName(apiScope, c.module.BaseModuleName())
baseName := c.module.BaseModuleName()
return c.module.SdkMemberComponentName(baseName, func(name string) string {
return c.namingScheme.stubsSourceModuleName(apiScope, name)
})
}
// The component names for different outputs of the java_sdk_library.
@ -1170,6 +1176,10 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext)
module.Library.GenerateAndroidBuildActions(ctx)
}
// Collate the components exported by this module. All scope specific modules are exported but
// the impl and xml component modules are not.
exportedComponents := map[string]struct{}{}
// Record the paths to the header jars of the library (stubs and impl).
// When this java_sdk_library is depended upon from others via "libs" property,
// the recorded paths will be returned depending on the link type of the caller.
@ -1184,8 +1194,14 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext)
// 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)
exportedComponents[ctx.OtherModuleName(to)] = struct{}{}
}
})
// 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)
}
func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {

View file

@ -110,7 +110,7 @@ func TestJavaSdkLibrary(t *testing.T) {
`)
// check the existence of the internal modules
result.ModuleForTests("foo", "android_common")
foo := result.ModuleForTests("foo", "android_common")
result.ModuleForTests(apiScopePublic.stubsLibraryModuleName("foo"), "android_common")
result.ModuleForTests(apiScopeSystem.stubsLibraryModuleName("foo"), "android_common")
result.ModuleForTests(apiScopeTest.stubsLibraryModuleName("foo"), "android_common")
@ -122,6 +122,17 @@ func TestJavaSdkLibrary(t *testing.T) {
result.ModuleForTests("foo.api.system.28", "")
result.ModuleForTests("foo.api.test.28", "")
exportedComponentsInfo := result.ModuleProvider(foo.Module(), ExportedComponentsInfoProvider).(ExportedComponentsInfo)
expectedFooExportedComponents := []string{
"foo.stubs",
"foo.stubs.source",
"foo.stubs.source.system",
"foo.stubs.source.test",
"foo.stubs.system",
"foo.stubs.test",
}
android.AssertArrayString(t, "foo exported components", expectedFooExportedComponents, exportedComponentsInfo.Components)
bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac")
// tests if baz is actually linked to the stubs lib
android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.system.jar")

View file

@ -722,14 +722,6 @@ java_import {
jars: ["java/system-module.jar"],
}
java_import {
name: "mysdk_myjavalib.stubs",
prefer: false,
visibility: ["//visibility:private"],
apex_available: ["//apex_available:platform"],
jars: ["java/myjavalib.stubs.jar"],
}
java_sdk_library_import {
name: "myjavalib",
prefer: false,
@ -752,7 +744,7 @@ java_system_modules_import {
libs: [
"mysdk_system-module",
"exported-system-module",
"mysdk_myjavalib.stubs",
"myjavalib.stubs",
],
}
`),
@ -775,14 +767,6 @@ java_import {
jars: ["java/system-module.jar"],
}
java_import {
name: "mysdk_myjavalib.stubs@current",
sdk_member_name: "myjavalib.stubs",
visibility: ["//visibility:private"],
apex_available: ["//apex_available:platform"],
jars: ["java/myjavalib.stubs.jar"],
}
java_sdk_library_import {
name: "mysdk_myjavalib@current",
sdk_member_name: "myjavalib",
@ -820,7 +804,6 @@ sdk_snapshot {
checkAllCopyRules(`
.intermediates/exported-system-module/android_common/turbine-combined/exported-system-module.jar -> java/exported-system-module.jar
.intermediates/system-module/android_common/turbine-combined/system-module.jar -> java/system-module.jar
.intermediates/myjavalib.stubs/android_common/turbine-combined/myjavalib.stubs.jar -> java/myjavalib.stubs.jar
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
.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
@ -1153,10 +1136,10 @@ sdk_snapshot {
".intermediates/mysdk/common_os/tmp/sdk_library/test/myjavalib_stub_sources.zip",
),
snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
// Show that the existing behavior is incorrect as the suffix for the child modules is added
// to the version not before it.
result.Module("mysdk_myjavalib@current.stubs", "android_common")
result.Module("mysdk_myjavalib@current.stubs.source", "android_common")
// Make sure that the name of the child modules created by a versioned java_sdk_library_import
// module is correct, i.e. the suffix is added before the version and not after.
result.Module("mysdk_myjavalib.stubs@current", "android_common")
result.Module("mysdk_myjavalib.stubs.source@current", "android_common")
}),
)
}

View file

@ -22,7 +22,6 @@ import (
"android/soong/apex"
"android/soong/cc"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@ -148,8 +147,8 @@ func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderC
// Collect all the members.
//
// Updates the sdk module with a list of sdkMemberVariantDeps and details as to which multilibs
// (32/64/both) are used by this sdk variant.
// Updates the sdk module with a list of sdkMemberVariantDep instances and details as to which
// multilibs (32/64/both) are used by this sdk variant.
func (s *sdk) collectMembers(ctx android.ModuleContext) {
s.multilibUsages = multilibNone
ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
@ -165,8 +164,15 @@ func (s *sdk) collectMembers(ctx android.ModuleContext) {
// Keep track of which multilib variants are used by the sdk.
s.multilibUsages = s.multilibUsages.addArchType(child.Target().Arch.ArchType)
var exportedComponentsInfo android.ExportedComponentsInfo
if ctx.OtherModuleHasProvider(child, android.ExportedComponentsInfoProvider) {
exportedComponentsInfo = ctx.OtherModuleProvider(child, android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo)
}
export := memberTag.ExportMember()
s.memberVariantDeps = append(s.memberVariantDeps, sdkMemberVariantDep{s, memberType, child.(android.SdkAware), export})
s.memberVariantDeps = append(s.memberVariantDeps, sdkMemberVariantDep{
s, memberType, child.(android.SdkAware), export, exportedComponentsInfo,
})
// Recurse down into the member's dependencies as it may have dependencies that need to be
// automatically added to the sdk.
@ -253,28 +259,43 @@ func versionedSdkMemberName(ctx android.ModuleContext, memberName string, versio
// the contents (header files, stub libraries, etc) into the zip file.
func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) android.OutputPath {
allMembersByName := make(map[string]struct{})
exportedMembersByName := make(map[string]struct{})
// Aggregate all the sdkMemberVariantDep instances from all the sdk variants.
hasLicenses := false
var memberVariantDeps []sdkMemberVariantDep
for _, sdkVariant := range sdkVariants {
memberVariantDeps = append(memberVariantDeps, sdkVariant.memberVariantDeps...)
}
// Filter out any sdkMemberVariantDep that is a component of another.
memberVariantDeps = filterOutComponents(ctx, memberVariantDeps)
// Record the names of all the members, both explicitly specified and implicitly
// included.
for _, memberVariantDep := range sdkVariant.memberVariantDeps {
name := memberVariantDep.variant.Name()
allMembersByName[name] = struct{}{}
allMembersByName := make(map[string]struct{})
exportedMembersByName := make(map[string]struct{})
if memberVariantDep.export {
addMember := func(name string, export bool) {
allMembersByName[name] = struct{}{}
if export {
exportedMembersByName[name] = struct{}{}
}
}
for _, memberVariantDep := range memberVariantDeps {
name := memberVariantDep.variant.Name()
export := memberVariantDep.export
addMember(name, export)
// Add any components provided by the module.
for _, component := range memberVariantDep.exportedComponentsInfo.Components {
addMember(component, export)
}
if memberVariantDep.memberType == android.LicenseModuleSdkMemberType {
hasLicenses = true
}
}
}
snapshotDir := android.PathForModuleOut(ctx, "snapshot")
@ -431,6 +452,47 @@ be unnecessary as every module in the sdk already has its own licenses property.
return outputZipFile
}
// filterOutComponents removes any item from the deps list that is a component of another item in
// the deps list, e.g. if the deps list contains "foo" and "foo.stubs" which is component of "foo"
// then it will remove "foo.stubs" from the deps.
func filterOutComponents(ctx android.ModuleContext, deps []sdkMemberVariantDep) []sdkMemberVariantDep {
// Collate the set of components that all the modules added to the sdk provide.
components := map[string]*sdkMemberVariantDep{}
for i, _ := range deps {
dep := &deps[i]
for _, c := range dep.exportedComponentsInfo.Components {
components[c] = dep
}
}
// If no module provides components then return the input deps unfiltered.
if len(components) == 0 {
return deps
}
filtered := make([]sdkMemberVariantDep, 0, len(deps))
for _, dep := range deps {
name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(dep.variant))
if owner, ok := components[name]; ok {
// This is a component of another module that is a member of the sdk.
// If the component is exported but the owning module is not then the configuration is not
// supported.
if dep.export && !owner.export {
ctx.ModuleErrorf("Module %s is internal to the SDK but provides component %s which is used outside the SDK")
continue
}
// This module must not be added to the list of members of the sdk as that would result in a
// duplicate module in the sdk snapshot.
continue
}
filtered = append(filtered, dep)
}
return filtered
}
// addSnapshotModule adds the sdk_snapshot/module_exports_snapshot module to the builder.
func (s *sdk) addSnapshotModule(ctx android.ModuleContext, builder *snapshotBuilder, sdkVariants []*sdk, memberVariantDeps []sdkMemberVariantDep) {
bpFile := builder.bpFile
@ -1152,9 +1214,18 @@ func addSdkMemberPropertiesToSet(ctx *memberContext, memberProperties android.Sd
type sdkMemberVariantDep struct {
// The sdk variant that depends (possibly indirectly) on the member variant.
sdkVariant *sdk
// The type of sdk member the variant is to be treated as.
memberType android.SdkMemberType
// The variant that is added to the sdk.
variant android.SdkAware
// True if the member should be exported, i.e. accessible, from outside the sdk.
export bool
// The names of additional component modules provided by the variant.
exportedComponentsInfo android.ExportedComponentsInfo
}
var _ android.SdkMember = (*sdkMember)(nil)