platform_build_soong/cc/library_sdk_member.go

345 lines
12 KiB
Go
Raw Normal View History

// Copyright 2019 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cc
import (
"path/filepath"
"reflect"
"android/soong/android"
"github.com/google/blueprint"
)
// This file contains support for using cc library modules within an sdk.
var sharedLibrarySdkMemberType = &librarySdkMemberType{
SdkMemberTypeBase: android.SdkMemberTypeBase{
PropertyName: "native_shared_libs",
SupportsSdk: true,
},
prebuiltModuleType: "cc_prebuilt_library_shared",
linkTypes: []string{"shared"},
}
var staticLibrarySdkMemberType = &librarySdkMemberType{
SdkMemberTypeBase: android.SdkMemberTypeBase{
PropertyName: "native_static_libs",
SupportsSdk: true,
},
prebuiltModuleType: "cc_prebuilt_library_static",
linkTypes: []string{"static"},
}
Decouple addition of new sdk member types from sdk code Previously, adding a new SdkMemberType would require adding a new sdkMemberListProperty instance to the sdkMemberListProperties as well as adding a new property into the sdkProperties struct. They are potential sources of conflict and couple the sdk code with all the packages that add members to it. This change switched to a registration model that allows each package to register its sdk member types decoupling them from the sdk code. Adds an SdkPropertyName() method to SdkMemberType that specifies the name of the property to use in the sdk/sdk_snapshot. Also provides an SdkMemberTypeBase struct to be used by providers of SdkMemberType implementations. SdkMemberType instances are registered using the RegisterSdkMemberType() func which sorts the registered instances by their SdkPropertyName() to ensure the behavior is consistent and not affected by order of registration. When creating a new sdk module a dynamicSdkMemberTypes instance is created that contains the following: * A properties struct is created dynamically that contains a field for each registered SdkMemberType, corresponding to that type's SdkPropertyName(). * A list of sdkMemberListProperty instances is also created, one for each registered SdkMemberType. The dynamicSdkMemberTypes instance is cached using a key that uniquely identifies the set of registered types just in case new types are registered after one has been created, e.g. by tests. Bug: 142918168 Test: m checkbuild Change-Id: I4bf2bf56a2a49025aa41454048bc1e8ccc6baca2
2019-12-13 12:22:16 +01:00
func init() {
// Register sdk member types.
android.RegisterSdkMemberType(sharedLibrarySdkMemberType)
android.RegisterSdkMemberType(staticLibrarySdkMemberType)
}
type librarySdkMemberType struct {
Decouple addition of new sdk member types from sdk code Previously, adding a new SdkMemberType would require adding a new sdkMemberListProperty instance to the sdkMemberListProperties as well as adding a new property into the sdkProperties struct. They are potential sources of conflict and couple the sdk code with all the packages that add members to it. This change switched to a registration model that allows each package to register its sdk member types decoupling them from the sdk code. Adds an SdkPropertyName() method to SdkMemberType that specifies the name of the property to use in the sdk/sdk_snapshot. Also provides an SdkMemberTypeBase struct to be used by providers of SdkMemberType implementations. SdkMemberType instances are registered using the RegisterSdkMemberType() func which sorts the registered instances by their SdkPropertyName() to ensure the behavior is consistent and not affected by order of registration. When creating a new sdk module a dynamicSdkMemberTypes instance is created that contains the following: * A properties struct is created dynamically that contains a field for each registered SdkMemberType, corresponding to that type's SdkPropertyName(). * A list of sdkMemberListProperty instances is also created, one for each registered SdkMemberType. The dynamicSdkMemberTypes instance is cached using a key that uniquely identifies the set of registered types just in case new types are registered after one has been created, e.g. by tests. Bug: 142918168 Test: m checkbuild Change-Id: I4bf2bf56a2a49025aa41454048bc1e8ccc6baca2
2019-12-13 12:22:16 +01:00
android.SdkMemberTypeBase
prebuiltModuleType string
// The set of link types supported, set of "static", "shared".
linkTypes []string
}
func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
targets := mctx.MultiTargets()
for _, lib := range names {
for _, target := range targets {
name, version := StubsLibNameAndVersion(lib)
if version == "" {
version = LatestStubsVersionFor(mctx.Config(), name)
}
for _, linkType := range mt.linkTypes {
mctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
{Mutator: "image", Variation: android.CoreVariation},
{Mutator: "link", Variation: linkType},
{Mutator: "version", Variation: version},
}...), dependencyTag, name)
}
}
}
}
func (mt *librarySdkMemberType) IsInstance(module android.Module) bool {
// Check the module to see if it can be used with this module type.
if m, ok := module.(*Module); ok {
for _, allowableMemberType := range m.sdkMemberTypes {
if allowableMemberType == mt {
return true
}
}
}
return false
}
// copy exported header files and stub *.so files
func (mt *librarySdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
info := mt.organizeVariants(member)
buildSharedNativeLibSnapshot(sdkModuleContext, info, builder, member)
}
// Organize the variants by architecture.
func (mt *librarySdkMemberType) organizeVariants(member android.SdkMember) *nativeLibInfo {
memberName := member.Name()
info := &nativeLibInfo{
name: memberName,
memberType: mt,
}
for _, variant := range member.Variants() {
ccModule := variant.(*Module)
// Separate out the generated include dirs (which are arch specific) from the
// include dirs (which may not be).
exportedIncludeDirs, exportedGeneratedIncludeDirs := android.FilterPathListPredicate(
ccModule.ExportedIncludeDirs(), isGeneratedHeaderDirectory)
info.archVariantProperties = append(info.archVariantProperties, nativeLibInfoProperties{
name: memberName,
archType: ccModule.Target().Arch.ArchType.String(),
ExportedIncludeDirs: exportedIncludeDirs,
ExportedGeneratedIncludeDirs: exportedGeneratedIncludeDirs,
ExportedSystemIncludeDirs: ccModule.ExportedSystemIncludeDirs(),
ExportedFlags: ccModule.ExportedFlags(),
exportedGeneratedHeaders: ccModule.ExportedGeneratedHeaders(),
outputFile: ccModule.OutputFile().Path(),
})
}
// Initialize the unexported properties that will not be set during the
// extraction process.
info.commonProperties.name = memberName
// Extract common properties from the arch specific properties.
extractCommonProperties(&info.commonProperties, info.archVariantProperties)
return info
}
func isGeneratedHeaderDirectory(p android.Path) bool {
_, gen := p.(android.WritablePath)
return gen
}
// Extract common properties from a slice of property structures of the same type.
//
// All the property structures must be of the same type.
// commonProperties - must be a pointer to the structure into which common properties will be added.
// inputPropertiesSlice - must be a slice of input properties structures.
//
// Iterates over each exported field (capitalized name) and checks to see whether they
// have the same value (using DeepEquals) across all the input properties. If it does not then no
// change is made. Otherwise, the common value is stored in the field in the commonProperties
// and the field in each of the input properties structure is set to its default value.
func extractCommonProperties(commonProperties interface{}, inputPropertiesSlice interface{}) {
commonStructValue := reflect.ValueOf(commonProperties).Elem()
propertiesStructType := commonStructValue.Type()
// Create an empty structure from which default values for the field can be copied.
emptyStructValue := reflect.New(propertiesStructType).Elem()
for f := 0; f < propertiesStructType.NumField(); f++ {
// Check to see if all the structures have the same value for the field. The commonValue
// is nil on entry to the loop and if it is nil on exit then there is no common value,
// otherwise it points to the common value.
var commonValue *reflect.Value
sliceValue := reflect.ValueOf(inputPropertiesSlice)
for i := 0; i < sliceValue.Len(); i++ {
structValue := sliceValue.Index(i)
fieldValue := structValue.Field(f)
if !fieldValue.CanInterface() {
// The field is not exported so ignore it.
continue
}
if commonValue == nil {
// Use the first value as the commonProperties value.
commonValue = &fieldValue
} else {
// If the value does not match the current common value then there is
// no value in common so break out.
if !reflect.DeepEqual(fieldValue.Interface(), commonValue.Interface()) {
commonValue = nil
break
}
}
}
// If the fields all have a common value then store it in the common struct field
// and set the input struct's field to the empty value.
if commonValue != nil {
emptyValue := emptyStructValue.Field(f)
commonStructValue.Field(f).Set(*commonValue)
for i := 0; i < sliceValue.Len(); i++ {
structValue := sliceValue.Index(i)
fieldValue := structValue.Field(f)
fieldValue.Set(emptyValue)
}
}
}
}
func buildSharedNativeLibSnapshot(sdkModuleContext android.ModuleContext, info *nativeLibInfo, builder android.SnapshotBuilder, member android.SdkMember) {
// a function for emitting include dirs
addExportedDirCopyCommandsForNativeLibs := func(lib nativeLibInfoProperties) {
// Do not include ExportedGeneratedIncludeDirs in the list of directories whose
// contents are copied as they are copied from exportedGeneratedHeaders below.
includeDirs := lib.ExportedIncludeDirs
includeDirs = append(includeDirs, lib.ExportedSystemIncludeDirs...)
for _, dir := range includeDirs {
// lib.ArchType is "" for common properties.
targetDir := filepath.Join(lib.archType, nativeIncludeDir)
// TODO(jiyong) copy headers having other suffixes
headers, _ := sdkModuleContext.GlobWithDeps(dir.String()+"/**/*.h", nil)
for _, file := range headers {
src := android.PathForSource(sdkModuleContext, file)
dest := filepath.Join(targetDir, file)
builder.CopyToSnapshot(src, dest)
}
}
genHeaders := lib.exportedGeneratedHeaders
for _, file := range genHeaders {
// lib.ArchType is "" for common properties.
targetDir := filepath.Join(lib.archType, nativeGeneratedIncludeDir)
dest := filepath.Join(targetDir, lib.name, file.Rel())
builder.CopyToSnapshot(file, dest)
}
}
addExportedDirCopyCommandsForNativeLibs(info.commonProperties)
// for each architecture
for _, av := range info.archVariantProperties {
builder.CopyToSnapshot(av.outputFile, nativeLibraryPathFor(av))
addExportedDirCopyCommandsForNativeLibs(av)
}
info.generatePrebuiltLibrary(sdkModuleContext, builder, member)
}
func (info *nativeLibInfo) generatePrebuiltLibrary(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
// a function for emitting include dirs
addExportedDirsForNativeLibs := func(lib nativeLibInfoProperties, properties android.BpPropertySet, systemInclude bool) {
includeDirs := nativeIncludeDirPathsFor(lib, systemInclude)
if len(includeDirs) == 0 {
return
}
var propertyName string
if !systemInclude {
propertyName = "export_include_dirs"
} else {
propertyName = "export_system_include_dirs"
}
properties.AddProperty(propertyName, includeDirs)
}
pbm := builder.AddPrebuiltModule(member, info.memberType.prebuiltModuleType)
addExportedDirsForNativeLibs(info.commonProperties, pbm, false /*systemInclude*/)
addExportedDirsForNativeLibs(info.commonProperties, pbm, true /*systemInclude*/)
archProperties := pbm.AddPropertySet("arch")
for _, av := range info.archVariantProperties {
archTypeProperties := archProperties.AddPropertySet(av.archType)
archTypeProperties.AddProperty("srcs", []string{nativeLibraryPathFor(av)})
// export_* properties are added inside the arch: {<arch>: {...}} block
addExportedDirsForNativeLibs(av, archTypeProperties, false /*systemInclude*/)
addExportedDirsForNativeLibs(av, archTypeProperties, true /*systemInclude*/)
}
pbm.AddProperty("stl", "none")
pbm.AddProperty("system_shared_libs", []string{})
}
const (
nativeIncludeDir = "include"
nativeGeneratedIncludeDir = "include_gen"
nativeStubDir = "lib"
)
// path to the native library. Relative to <sdk_root>/<api_dir>
func nativeLibraryPathFor(lib nativeLibInfoProperties) string {
return filepath.Join(lib.archType,
nativeStubDir, lib.outputFile.Base())
}
// paths to the include dirs of a native shared library. Relative to <sdk_root>/<api_dir>
func nativeIncludeDirPathsFor(lib nativeLibInfoProperties, systemInclude bool) []string {
var result []string
var includeDirs []android.Path
if !systemInclude {
// Include the generated include dirs in the exported include dirs.
includeDirs = append(lib.ExportedIncludeDirs, lib.ExportedGeneratedIncludeDirs...)
} else {
includeDirs = lib.ExportedSystemIncludeDirs
}
for _, dir := range includeDirs {
var path string
if isGeneratedHeaderDirectory(dir) {
path = filepath.Join(nativeGeneratedIncludeDir, lib.name)
} else {
path = filepath.Join(nativeIncludeDir, dir.String())
}
// lib.ArchType is "" for common properties.
path = filepath.Join(lib.archType, path)
result = append(result, path)
}
return result
}
// nativeLibInfoProperties represents properties of a native lib
//
// The exported (capitalized) fields will be examined and may be changed during common value extraction.
// The unexported fields will be left untouched.
type nativeLibInfoProperties struct {
// The name of the library, is not exported as this must not be changed during optimization.
name string
// archType is not exported as if set (to a non default value) it is always arch specific.
// This is "" for common properties.
archType string
ExportedIncludeDirs android.Paths
ExportedGeneratedIncludeDirs android.Paths
ExportedSystemIncludeDirs android.Paths
ExportedFlags []string
// exportedGeneratedHeaders is not exported as if set it is always arch specific.
exportedGeneratedHeaders android.Paths
// outputFile is not exported as it is always arch specific.
outputFile android.Path
}
// nativeLibInfo represents a collection of arch-specific modules having the same name
type nativeLibInfo struct {
name string
memberType *librarySdkMemberType
archVariantProperties []nativeLibInfoProperties
commonProperties nativeLibInfoProperties
}