bp2build: add support for soong_config_module_type.
Test: CI, go unit test Bug: 198556411 Change-Id: Idf862904d51d822f92af0c072341c31b7a02fc64
This commit is contained in:
parent
925942127a
commit
a47f28d28e
16 changed files with 710 additions and 96 deletions
|
@ -15,6 +15,7 @@
|
||||||
package android
|
package android
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"android/soong/bazel"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -24,34 +25,33 @@ import (
|
||||||
"github.com/google/blueprint/proptools"
|
"github.com/google/blueprint/proptools"
|
||||||
)
|
)
|
||||||
|
|
||||||
type bazelModuleProperties struct {
|
|
||||||
// The label of the Bazel target replacing this Soong module. When run in conversion mode, this
|
|
||||||
// will import the handcrafted build target into the autogenerated file. Note: this may result in
|
|
||||||
// a conflict due to duplicate targets if bp2build_available is also set.
|
|
||||||
Label *string
|
|
||||||
|
|
||||||
// If true, bp2build will generate the converted Bazel target for this module. Note: this may
|
|
||||||
// cause a conflict due to the duplicate targets if label is also set.
|
|
||||||
//
|
|
||||||
// This is a bool pointer to support tristates: true, false, not set.
|
|
||||||
//
|
|
||||||
// To opt-in a module, set bazel_module: { bp2build_available: true }
|
|
||||||
// To opt-out a module, set bazel_module: { bp2build_available: false }
|
|
||||||
// To defer the default setting for the directory, do not set the value.
|
|
||||||
Bp2build_available *bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Properties contains common module properties for Bazel migration purposes.
|
// Properties contains common module properties for Bazel migration purposes.
|
||||||
type properties struct {
|
type properties struct {
|
||||||
// In USE_BAZEL_ANALYSIS=1 mode, this represents the Bazel target replacing
|
// In USE_BAZEL_ANALYSIS=1 mode, this represents the Bazel target replacing
|
||||||
// this Soong module.
|
// this Soong module.
|
||||||
Bazel_module bazelModuleProperties
|
Bazel_module bazel.BazelModuleProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type namespacedVariableProperties map[string]interface{}
|
||||||
|
|
||||||
// BazelModuleBase contains the property structs with metadata for modules which can be converted to
|
// BazelModuleBase contains the property structs with metadata for modules which can be converted to
|
||||||
// Bazel.
|
// Bazel.
|
||||||
type BazelModuleBase struct {
|
type BazelModuleBase struct {
|
||||||
bazelProperties properties
|
bazelProperties properties
|
||||||
|
|
||||||
|
// namespacedVariableProperties is used for soong_config_module_type support
|
||||||
|
// in bp2build. Soong config modules allow users to set module properties
|
||||||
|
// based on custom product variables defined in Android.bp files. These
|
||||||
|
// variables are namespaced to prevent clobbering, especially when set from
|
||||||
|
// Makefiles.
|
||||||
|
namespacedVariableProperties namespacedVariableProperties
|
||||||
|
|
||||||
|
// baseModuleType is set when this module was created from a module type
|
||||||
|
// defined by a soong_config_module_type. Every soong_config_module_type
|
||||||
|
// "wraps" another module type, e.g. a soong_config_module_type can wrap a
|
||||||
|
// cc_defaults to a custom_cc_defaults, or cc_binary to a custom_cc_binary.
|
||||||
|
// This baseModuleType is set to the wrapped module type.
|
||||||
|
baseModuleType string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bazelable is specifies the interface for modules that can be converted to Bazel.
|
// Bazelable is specifies the interface for modules that can be converted to Bazel.
|
||||||
|
@ -63,6 +63,12 @@ type Bazelable interface {
|
||||||
ConvertWithBp2build(ctx BazelConversionContext) bool
|
ConvertWithBp2build(ctx BazelConversionContext) bool
|
||||||
convertWithBp2build(ctx BazelConversionContext, module blueprint.Module) bool
|
convertWithBp2build(ctx BazelConversionContext, module blueprint.Module) bool
|
||||||
GetBazelBuildFileContents(c Config, path, name string) (string, error)
|
GetBazelBuildFileContents(c Config, path, name string) (string, error)
|
||||||
|
|
||||||
|
// For namespaced config variable support
|
||||||
|
namespacedVariableProps() namespacedVariableProperties
|
||||||
|
setNamespacedVariableProps(props namespacedVariableProperties)
|
||||||
|
BaseModuleType() string
|
||||||
|
SetBaseModuleType(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
|
// BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
|
||||||
|
@ -82,6 +88,22 @@ func (b *BazelModuleBase) bazelProps() *properties {
|
||||||
return &b.bazelProperties
|
return &b.bazelProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BazelModuleBase) namespacedVariableProps() namespacedVariableProperties {
|
||||||
|
return b.namespacedVariableProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BazelModuleBase) setNamespacedVariableProps(props namespacedVariableProperties) {
|
||||||
|
b.namespacedVariableProperties = props
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BazelModuleBase) BaseModuleType() string {
|
||||||
|
return b.baseModuleType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BazelModuleBase) SetBaseModuleType(baseModuleType string) {
|
||||||
|
b.baseModuleType = baseModuleType
|
||||||
|
}
|
||||||
|
|
||||||
// HasHandcraftedLabel returns whether this module has a handcrafted Bazel label.
|
// HasHandcraftedLabel returns whether this module has a handcrafted Bazel label.
|
||||||
func (b *BazelModuleBase) HasHandcraftedLabel() bool {
|
func (b *BazelModuleBase) HasHandcraftedLabel() bool {
|
||||||
return b.bazelProperties.Bazel_module.Label != nil
|
return b.bazelProperties.Bazel_module.Label != nil
|
||||||
|
@ -399,7 +421,15 @@ func (b *BazelModuleBase) convertWithBp2build(ctx BazelConversionContext, module
|
||||||
// prevents mixed builds from using auto-converted modules just by matching
|
// prevents mixed builds from using auto-converted modules just by matching
|
||||||
// the package dir; it also has to have a bp2build mutator as well.
|
// the package dir; it also has to have a bp2build mutator as well.
|
||||||
if ctx.Config().bp2buildModuleTypeConfig[ctx.OtherModuleType(module)] == false {
|
if ctx.Config().bp2buildModuleTypeConfig[ctx.OtherModuleType(module)] == false {
|
||||||
return false
|
if b, ok := module.(Bazelable); ok && b.BaseModuleType() != "" {
|
||||||
|
// For modules with custom types from soong_config_module_types,
|
||||||
|
// check that their _base module type_ has a bp2build mutator.
|
||||||
|
if ctx.Config().bp2buildModuleTypeConfig[b.BaseModuleType()] == false {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
packagePath := ctx.OtherModuleDir(module)
|
packagePath := ctx.OtherModuleDir(module)
|
||||||
|
|
|
@ -155,6 +155,7 @@ type config struct {
|
||||||
fs pathtools.FileSystem
|
fs pathtools.FileSystem
|
||||||
mockBpList string
|
mockBpList string
|
||||||
|
|
||||||
|
runningAsBp2Build bool
|
||||||
bp2buildPackageConfig Bp2BuildConfig
|
bp2buildPackageConfig Bp2BuildConfig
|
||||||
bp2buildModuleTypeConfig map[string]bool
|
bp2buildModuleTypeConfig map[string]bool
|
||||||
|
|
||||||
|
|
|
@ -510,9 +510,9 @@ func registerTestPrebuiltModules(ctx RegistrationContext) {
|
||||||
ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
|
ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
|
||||||
ctx.RegisterModuleType("source", newSourceModule)
|
ctx.RegisterModuleType("source", newSourceModule)
|
||||||
ctx.RegisterModuleType("override_source", newOverrideSourceModule)
|
ctx.RegisterModuleType("override_source", newOverrideSourceModule)
|
||||||
ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
|
ctx.RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
|
||||||
ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
|
ctx.RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
|
||||||
ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
|
ctx.RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
|
||||||
}
|
}
|
||||||
|
|
||||||
type prebuiltModule struct {
|
type prebuiltModule struct {
|
||||||
|
|
|
@ -161,6 +161,10 @@ func NewContext(config Config) *Context {
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctx *Context) SetRunningAsBp2build() {
|
||||||
|
ctx.config.runningAsBp2Build = true
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterForBazelConversion registers an alternate shadow pipeline of
|
// RegisterForBazelConversion registers an alternate shadow pipeline of
|
||||||
// singletons, module types and mutators to register for converting Blueprint
|
// singletons, module types and mutators to register for converting Blueprint
|
||||||
// files to semantically equivalent BUILD files.
|
// files to semantically equivalent BUILD files.
|
||||||
|
|
|
@ -31,10 +31,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
|
RegisterModuleType("soong_config_module_type_import", SoongConfigModuleTypeImportFactory)
|
||||||
RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
|
RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
|
||||||
RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
|
RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
|
||||||
RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
|
RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
|
||||||
}
|
}
|
||||||
|
|
||||||
type soongConfigModuleTypeImport struct {
|
type soongConfigModuleTypeImport struct {
|
||||||
|
@ -153,7 +153,7 @@ type soongConfigModuleTypeImportProperties struct {
|
||||||
// Then libacme_foo would build with cflags:
|
// Then libacme_foo would build with cflags:
|
||||||
// "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
|
// "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
|
||||||
|
|
||||||
func soongConfigModuleTypeImportFactory() Module {
|
func SoongConfigModuleTypeImportFactory() Module {
|
||||||
module := &soongConfigModuleTypeImport{}
|
module := &soongConfigModuleTypeImport{}
|
||||||
|
|
||||||
module.AddProperties(&module.properties)
|
module.AddProperties(&module.properties)
|
||||||
|
@ -179,6 +179,7 @@ func (*soongConfigModuleTypeImport) GenerateAndroidBuildActions(ModuleContext) {
|
||||||
|
|
||||||
type soongConfigModuleTypeModule struct {
|
type soongConfigModuleTypeModule struct {
|
||||||
ModuleBase
|
ModuleBase
|
||||||
|
BazelModuleBase
|
||||||
properties soongconfig.ModuleTypeProperties
|
properties soongconfig.ModuleTypeProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,7 +263,7 @@ type soongConfigModuleTypeModule struct {
|
||||||
// SOONG_CONFIG_acme_width := 200
|
// SOONG_CONFIG_acme_width := 200
|
||||||
//
|
//
|
||||||
// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
|
// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
|
||||||
func soongConfigModuleTypeFactory() Module {
|
func SoongConfigModuleTypeFactory() Module {
|
||||||
module := &soongConfigModuleTypeModule{}
|
module := &soongConfigModuleTypeModule{}
|
||||||
|
|
||||||
module.AddProperties(&module.properties)
|
module.AddProperties(&module.properties)
|
||||||
|
@ -296,7 +297,7 @@ type soongConfigBoolVariableDummyModule struct {
|
||||||
|
|
||||||
// soong_config_string_variable defines a variable and a set of possible string values for use
|
// soong_config_string_variable defines a variable and a set of possible string values for use
|
||||||
// in a soong_config_module_type definition.
|
// in a soong_config_module_type definition.
|
||||||
func soongConfigStringVariableDummyFactory() Module {
|
func SoongConfigStringVariableDummyFactory() Module {
|
||||||
module := &soongConfigStringVariableDummyModule{}
|
module := &soongConfigStringVariableDummyModule{}
|
||||||
module.AddProperties(&module.properties, &module.stringProperties)
|
module.AddProperties(&module.properties, &module.stringProperties)
|
||||||
initAndroidModuleBase(module)
|
initAndroidModuleBase(module)
|
||||||
|
@ -305,7 +306,7 @@ func soongConfigStringVariableDummyFactory() Module {
|
||||||
|
|
||||||
// soong_config_string_variable defines a variable with true or false values for use
|
// soong_config_string_variable defines a variable with true or false values for use
|
||||||
// in a soong_config_module_type definition.
|
// in a soong_config_module_type definition.
|
||||||
func soongConfigBoolVariableDummyFactory() Module {
|
func SoongConfigBoolVariableDummyFactory() Module {
|
||||||
module := &soongConfigBoolVariableDummyModule{}
|
module := &soongConfigBoolVariableDummyModule{}
|
||||||
module.AddProperties(&module.properties)
|
module.AddProperties(&module.properties)
|
||||||
initAndroidModuleBase(module)
|
initAndroidModuleBase(module)
|
||||||
|
@ -324,6 +325,9 @@ func (m *soongConfigBoolVariableDummyModule) Name() string {
|
||||||
func (*soongConfigBoolVariableDummyModule) Nameless() {}
|
func (*soongConfigBoolVariableDummyModule) Nameless() {}
|
||||||
func (*soongConfigBoolVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
|
func (*soongConfigBoolVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
|
||||||
|
|
||||||
|
// importModuleTypes registers the module factories for a list of module types defined
|
||||||
|
// in an Android.bp file. These module factories are scoped for the current Android.bp
|
||||||
|
// file only.
|
||||||
func importModuleTypes(ctx LoadHookContext, from string, moduleTypes ...string) {
|
func importModuleTypes(ctx LoadHookContext, from string, moduleTypes ...string) {
|
||||||
from = filepath.Clean(from)
|
from = filepath.Clean(from)
|
||||||
if filepath.Ext(from) != ".bp" {
|
if filepath.Ext(from) != ".bp" {
|
||||||
|
@ -389,7 +393,7 @@ func loadSoongConfigModuleTypeDefinition(ctx LoadHookContext, from string) map[s
|
||||||
for name, moduleType := range mtDef.ModuleTypes {
|
for name, moduleType := range mtDef.ModuleTypes {
|
||||||
factory := globalModuleTypes[moduleType.BaseModuleType]
|
factory := globalModuleTypes[moduleType.BaseModuleType]
|
||||||
if factory != nil {
|
if factory != nil {
|
||||||
factories[name] = soongConfigModuleFactory(factory, moduleType)
|
factories[name] = configModuleFactory(factory, moduleType, ctx.Config().runningAsBp2Build)
|
||||||
} else {
|
} else {
|
||||||
reportErrors(ctx, from,
|
reportErrors(ctx, from,
|
||||||
fmt.Errorf("missing global module type factory for %q", moduleType.BaseModuleType))
|
fmt.Errorf("missing global module type factory for %q", moduleType.BaseModuleType))
|
||||||
|
@ -404,20 +408,40 @@ func loadSoongConfigModuleTypeDefinition(ctx LoadHookContext, from string) map[s
|
||||||
}).(map[string]blueprint.ModuleFactory)
|
}).(map[string]blueprint.ModuleFactory)
|
||||||
}
|
}
|
||||||
|
|
||||||
// soongConfigModuleFactory takes an existing soongConfigModuleFactory and a ModuleType and returns
|
// configModuleFactory takes an existing soongConfigModuleFactory and a
|
||||||
// a new soongConfigModuleFactory that wraps the existing soongConfigModuleFactory and adds conditional on Soong config
|
// ModuleType to create a new ModuleFactory that uses a custom loadhook.
|
||||||
// variables.
|
func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfig.ModuleType, bp2build bool) blueprint.ModuleFactory {
|
||||||
func soongConfigModuleFactory(factory blueprint.ModuleFactory,
|
|
||||||
moduleType *soongconfig.ModuleType) blueprint.ModuleFactory {
|
|
||||||
|
|
||||||
conditionalFactoryProps := soongconfig.CreateProperties(factory, moduleType)
|
conditionalFactoryProps := soongconfig.CreateProperties(factory, moduleType)
|
||||||
if conditionalFactoryProps.IsValid() {
|
if !conditionalFactoryProps.IsValid() {
|
||||||
return func() (blueprint.Module, []interface{}) {
|
return factory
|
||||||
module, props := factory()
|
}
|
||||||
|
useBp2buildHook := bp2build && proptools.BoolDefault(moduleType.Bp2buildAvailable, false)
|
||||||
|
|
||||||
conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
|
return func() (blueprint.Module, []interface{}) {
|
||||||
props = append(props, conditionalProps.Interface())
|
module, props := factory()
|
||||||
|
conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
|
||||||
|
props = append(props, conditionalProps.Interface())
|
||||||
|
|
||||||
|
if useBp2buildHook {
|
||||||
|
// The loadhook is different for bp2build, since we don't want to set a specific
|
||||||
|
// set of property values based on a vendor var -- we want __all of them__ to
|
||||||
|
// generate select statements, so we put the entire soong_config_variables
|
||||||
|
// struct, together with the namespace representing those variables, while
|
||||||
|
// creating the custom module with the factory.
|
||||||
|
AddLoadHook(module, func(ctx LoadHookContext) {
|
||||||
|
if m, ok := module.(Bazelable); ok {
|
||||||
|
m.SetBaseModuleType(moduleType.BaseModuleType)
|
||||||
|
// Instead of applying all properties, keep the entire conditionalProps struct as
|
||||||
|
// part of the custom module so dependent modules can create the selects accordingly
|
||||||
|
m.setNamespacedVariableProps(namespacedVariableProperties{
|
||||||
|
moduleType.ConfigNamespace: conditionalProps.Interface(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Regular Soong operation wraps the existing module factory with a
|
||||||
|
// conditional on Soong config variables by reading the product
|
||||||
|
// config variables from Make.
|
||||||
AddLoadHook(module, func(ctx LoadHookContext) {
|
AddLoadHook(module, func(ctx LoadHookContext) {
|
||||||
config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
|
config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
|
||||||
newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config)
|
newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config)
|
||||||
|
@ -429,10 +453,7 @@ func soongConfigModuleFactory(factory blueprint.ModuleFactory,
|
||||||
ctx.AppendProperties(ps)
|
ctx.AppendProperties(ps)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return module, props
|
|
||||||
}
|
}
|
||||||
} else {
|
return module, props
|
||||||
return factory
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -310,10 +310,10 @@ func TestSoongConfigModule(t *testing.T) {
|
||||||
tc.preparer,
|
tc.preparer,
|
||||||
PrepareForTestWithDefaults,
|
PrepareForTestWithDefaults,
|
||||||
FixtureRegisterWithContext(func(ctx RegistrationContext) {
|
FixtureRegisterWithContext(func(ctx RegistrationContext) {
|
||||||
ctx.RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
|
ctx.RegisterModuleType("soong_config_module_type_import", SoongConfigModuleTypeImportFactory)
|
||||||
ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
|
ctx.RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
|
||||||
ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
|
ctx.RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
|
||||||
ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
|
ctx.RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
|
||||||
ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
|
ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
|
||||||
ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
|
ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
|
||||||
}),
|
}),
|
||||||
|
@ -372,10 +372,10 @@ func TestNonExistentPropertyInSoongConfigModule(t *testing.T) {
|
||||||
fixtureForVendorVars(map[string]map[string]string{"acme": {"feature1": "1"}}),
|
fixtureForVendorVars(map[string]map[string]string{"acme": {"feature1": "1"}}),
|
||||||
PrepareForTestWithDefaults,
|
PrepareForTestWithDefaults,
|
||||||
FixtureRegisterWithContext(func(ctx RegistrationContext) {
|
FixtureRegisterWithContext(func(ctx RegistrationContext) {
|
||||||
ctx.RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
|
ctx.RegisterModuleType("soong_config_module_type_import", SoongConfigModuleTypeImportFactory)
|
||||||
ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
|
ctx.RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
|
||||||
ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
|
ctx.RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
|
||||||
ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
|
ctx.RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
|
||||||
ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
|
ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
|
||||||
ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
|
ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -9,6 +9,7 @@ bootstrap_go_package {
|
||||||
"blueprint",
|
"blueprint",
|
||||||
"blueprint-parser",
|
"blueprint-parser",
|
||||||
"blueprint-proptools",
|
"blueprint-proptools",
|
||||||
|
"soong-bazel",
|
||||||
],
|
],
|
||||||
srcs: [
|
srcs: [
|
||||||
"config.go",
|
"config.go",
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
package soongconfig
|
package soongconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"android/soong/bazel"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -28,7 +29,7 @@ import (
|
||||||
|
|
||||||
const conditionsDefault = "conditions_default"
|
const conditionsDefault = "conditions_default"
|
||||||
|
|
||||||
var soongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
|
var SoongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
|
||||||
|
|
||||||
// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
|
// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
|
||||||
// result so each file is only parsed once.
|
// result so each file is only parsed once.
|
||||||
|
@ -120,6 +121,8 @@ type ModuleTypeProperties struct {
|
||||||
|
|
||||||
// the list of properties that this module type will extend.
|
// the list of properties that this module type will extend.
|
||||||
Properties []string
|
Properties []string
|
||||||
|
|
||||||
|
Bazel_module bazel.BazelModuleProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
|
func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
|
||||||
|
@ -271,12 +274,12 @@ func CreateProperties(factory blueprint.ModuleFactory, moduleType *ModuleType) r
|
||||||
}
|
}
|
||||||
|
|
||||||
typ := reflect.StructOf([]reflect.StructField{{
|
typ := reflect.StructOf([]reflect.StructField{{
|
||||||
Name: soongConfigProperty,
|
Name: SoongConfigProperty,
|
||||||
Type: reflect.StructOf(fields),
|
Type: reflect.StructOf(fields),
|
||||||
}})
|
}})
|
||||||
|
|
||||||
props := reflect.New(typ)
|
props := reflect.New(typ)
|
||||||
structConditions := props.Elem().FieldByName(soongConfigProperty)
|
structConditions := props.Elem().FieldByName(SoongConfigProperty)
|
||||||
|
|
||||||
for i, c := range moduleType.Variables {
|
for i, c := range moduleType.Variables {
|
||||||
c.initializeProperties(structConditions.Field(i), affectablePropertiesType)
|
c.initializeProperties(structConditions.Field(i), affectablePropertiesType)
|
||||||
|
@ -415,7 +418,7 @@ func typeForPropertyFromPropertyStruct(ps interface{}, property string) reflect.
|
||||||
// soong_config_variables are expected to be in the same order as moduleType.Variables.
|
// soong_config_variables are expected to be in the same order as moduleType.Variables.
|
||||||
func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
|
func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
|
||||||
var ret []interface{}
|
var ret []interface{}
|
||||||
props = props.Elem().FieldByName(soongConfigProperty)
|
props = props.Elem().FieldByName(SoongConfigProperty)
|
||||||
for i, c := range moduleType.Variables {
|
for i, c := range moduleType.Variables {
|
||||||
if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
|
if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -433,6 +436,7 @@ type ModuleType struct {
|
||||||
|
|
||||||
affectableProperties []string
|
affectableProperties []string
|
||||||
variableNames []string
|
variableNames []string
|
||||||
|
Bp2buildAvailable *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
|
func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
|
||||||
|
@ -441,6 +445,7 @@ func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
|
||||||
ConfigNamespace: props.Config_namespace,
|
ConfigNamespace: props.Config_namespace,
|
||||||
BaseModuleType: props.Module_type,
|
BaseModuleType: props.Module_type,
|
||||||
variableNames: props.Variables,
|
variableNames: props.Variables,
|
||||||
|
Bp2buildAvailable: props.Bazel_module.Bp2build_available,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, name := range props.Bool_variables {
|
for _, name := range props.Bool_variables {
|
||||||
|
|
|
@ -458,6 +458,7 @@ func (ctx *TestContext) Register() {
|
||||||
|
|
||||||
// RegisterForBazelConversion prepares a test context for bp2build conversion.
|
// RegisterForBazelConversion prepares a test context for bp2build conversion.
|
||||||
func (ctx *TestContext) RegisterForBazelConversion() {
|
func (ctx *TestContext) RegisterForBazelConversion() {
|
||||||
|
ctx.SetRunningAsBp2build()
|
||||||
RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch, ctx.bp2buildMutators)
|
RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch, ctx.bp2buildMutators)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
package android
|
package android
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"android/soong/android/soongconfig"
|
||||||
|
"android/soong/bazel"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -487,13 +489,97 @@ type ProductConfigContext interface {
|
||||||
// ProductConfigProperty contains the information for a single property (may be a struct) paired
|
// ProductConfigProperty contains the information for a single property (may be a struct) paired
|
||||||
// with the appropriate ProductConfigVariable.
|
// with the appropriate ProductConfigVariable.
|
||||||
type ProductConfigProperty struct {
|
type ProductConfigProperty struct {
|
||||||
|
// The name of the product variable, e.g. "safestack", "malloc_not_svelte",
|
||||||
|
// "board"
|
||||||
ProductConfigVariable string
|
ProductConfigVariable string
|
||||||
FullConfig string
|
|
||||||
Property interface{}
|
// Namespace of the variable, if this is a soong_config_module_type variable
|
||||||
|
// e.g. "acme", "ANDROID", "vendor_nae"
|
||||||
|
Namespace string // for soong config variables
|
||||||
|
|
||||||
|
// Unique configuration to identify this product config property (i.e. a
|
||||||
|
// primary key), as just using the product variable name is not sufficient.
|
||||||
|
//
|
||||||
|
// For product variables, this is the product variable name + optional
|
||||||
|
// archvariant information. e.g.
|
||||||
|
//
|
||||||
|
// product_variables: {
|
||||||
|
// foo: {
|
||||||
|
// cflags: ["-Dfoo"],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
//
|
||||||
|
// FullConfig would be "foo".
|
||||||
|
//
|
||||||
|
// target: {
|
||||||
|
// android: {
|
||||||
|
// product_variables: {
|
||||||
|
// foo: {
|
||||||
|
// cflags: ["-Dfoo-android"],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
//
|
||||||
|
// FullConfig would be "foo-android".
|
||||||
|
//
|
||||||
|
// For soong config variables, this is the namespace + product variable name
|
||||||
|
// + value of the variable, if applicable. The value can also be
|
||||||
|
// conditions_default.
|
||||||
|
//
|
||||||
|
// e.g.
|
||||||
|
//
|
||||||
|
// soong_config_variables: {
|
||||||
|
// feature1: {
|
||||||
|
// conditions_default: {
|
||||||
|
// cflags: ["-DDEFAULT1"],
|
||||||
|
// },
|
||||||
|
// cflags: ["-DFEATURE1"],
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// where feature1 is created in the "acme" namespace, so FullConfig would be
|
||||||
|
// "acme__feature1" and "acme__feature1__conditions_default".
|
||||||
|
//
|
||||||
|
// e.g.
|
||||||
|
//
|
||||||
|
// soong_config_variables: {
|
||||||
|
// board: {
|
||||||
|
// soc_a: {
|
||||||
|
// cflags: ["-DSOC_A"],
|
||||||
|
// },
|
||||||
|
// soc_b: {
|
||||||
|
// cflags: ["-DSOC_B"],
|
||||||
|
// },
|
||||||
|
// soc_c: {},
|
||||||
|
// conditions_default: {
|
||||||
|
// cflags: ["-DSOC_DEFAULT"]
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// where board is created in the "acme" namespace, so FullConfig would be
|
||||||
|
// "acme__board__soc_a", "acme__board__soc_b", and
|
||||||
|
// "acme__board__conditions_default"
|
||||||
|
FullConfig string
|
||||||
|
|
||||||
|
// The actual property value: list, bool, string..
|
||||||
|
Property interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProductConfigProperties is a map of property name to a slice of ProductConfigProperty such that
|
func (p *ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
|
||||||
// all it all product variable-specific versions of a property are easily accessed together
|
if p.Namespace == "" {
|
||||||
|
return bazel.ProductVariableConfigurationAxis(p.FullConfig)
|
||||||
|
} else {
|
||||||
|
// Soong config variables can be uniquely identified by the namespace
|
||||||
|
// (e.g. acme, android) and the product variable name (e.g. board, size)
|
||||||
|
return bazel.ProductVariableConfigurationAxis(p.Namespace + "__" + p.ProductConfigVariable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProductConfigProperties is a map of property name to a slice of
|
||||||
|
// ProductConfigProperty such that all product variable-specific versions of a
|
||||||
|
// property are easily accessed together
|
||||||
type ProductConfigProperties map[string]map[string]ProductConfigProperty
|
type ProductConfigProperties map[string]map[string]ProductConfigProperty
|
||||||
|
|
||||||
// ProductVariableProperties returns a ProductConfigProperties containing only the properties which
|
// ProductVariableProperties returns a ProductConfigProperties containing only the properties which
|
||||||
|
@ -504,36 +590,165 @@ func ProductVariableProperties(ctx BazelConversionPathContext) ProductConfigProp
|
||||||
|
|
||||||
productConfigProperties := ProductConfigProperties{}
|
productConfigProperties := ProductConfigProperties{}
|
||||||
|
|
||||||
if moduleBase.variableProperties == nil {
|
if moduleBase.variableProperties != nil {
|
||||||
return productConfigProperties
|
productVariablesProperty := proptools.FieldNameForProperty("product_variables")
|
||||||
|
productVariableValues(
|
||||||
|
productVariablesProperty,
|
||||||
|
moduleBase.variableProperties,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
&productConfigProperties)
|
||||||
|
|
||||||
|
for _, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
|
||||||
|
for config, props := range configToProps {
|
||||||
|
// GetArchVariantProperties is creating an instance of the requested type
|
||||||
|
// and productVariablesValues expects an interface, so no need to cast
|
||||||
|
productVariableValues(
|
||||||
|
productVariablesProperty,
|
||||||
|
props,
|
||||||
|
"",
|
||||||
|
config,
|
||||||
|
&productConfigProperties)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
productVariableValues(moduleBase.variableProperties, "", &productConfigProperties)
|
if m, ok := module.(Bazelable); ok && m.namespacedVariableProps() != nil {
|
||||||
|
for namespace, namespacedVariableProp := range m.namespacedVariableProps() {
|
||||||
for _, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
|
productVariableValues(
|
||||||
for config, props := range configToProps {
|
soongconfig.SoongConfigProperty,
|
||||||
// GetArchVariantProperties is creating an instance of the requested type
|
namespacedVariableProp,
|
||||||
// and productVariablesValues expects an interface, so no need to cast
|
namespace,
|
||||||
productVariableValues(props, config, &productConfigProperties)
|
"",
|
||||||
|
&productConfigProperties)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return productConfigProperties
|
return productConfigProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
func productVariableValues(variableProps interface{}, suffix string, productConfigProperties *ProductConfigProperties) {
|
func (p *ProductConfigProperties) AddProductConfigProperty(
|
||||||
|
propertyName, namespace, productVariableName, config string, property interface{}) {
|
||||||
|
if (*p)[propertyName] == nil {
|
||||||
|
(*p)[propertyName] = make(map[string]ProductConfigProperty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize config to be all lowercase. It's the "primary key" of this
|
||||||
|
// unique property value. This can be the conditions_default value of the
|
||||||
|
// product variable as well.
|
||||||
|
config = strings.ToLower(config)
|
||||||
|
(*p)[propertyName][config] = ProductConfigProperty{
|
||||||
|
Namespace: namespace, // e.g. acme, android
|
||||||
|
ProductConfigVariable: productVariableName, // e.g. size, feature1, feature2, FEATURE3, board
|
||||||
|
FullConfig: config, // e.g. size, feature1-x86, size__conditions_default
|
||||||
|
Property: property, // e.g. ["-O3"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
conditionsDefaultField string = proptools.FieldNameForProperty(bazel.ConditionsDefaultConfigKey)
|
||||||
|
)
|
||||||
|
|
||||||
|
// maybeExtractConfigVarProp attempts to read this value as a config var struct
|
||||||
|
// wrapped by interfaces and ptrs. If it's not the right type, the second return
|
||||||
|
// value is false.
|
||||||
|
func maybeExtractConfigVarProp(v reflect.Value) (reflect.Value, bool) {
|
||||||
|
if v.Kind() == reflect.Interface {
|
||||||
|
// The conditions_default value can be either
|
||||||
|
// 1) an ptr to an interface of a struct (bool config variables and product variables)
|
||||||
|
// 2) an interface of 1) (config variables with nested structs, like string vars)
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
if v.Kind() != reflect.Ptr {
|
||||||
|
return v, false
|
||||||
|
}
|
||||||
|
v = reflect.Indirect(v)
|
||||||
|
if v.Kind() == reflect.Interface {
|
||||||
|
// Extract the struct from the interface
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !v.IsValid() {
|
||||||
|
return v, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Kind() != reflect.Struct {
|
||||||
|
return v, false
|
||||||
|
}
|
||||||
|
return v, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// productVariableValues uses reflection to convert a property struct for
|
||||||
|
// product_variables and soong_config_variables to structs that can be generated
|
||||||
|
// as select statements.
|
||||||
|
func productVariableValues(
|
||||||
|
fieldName string, variableProps interface{}, namespace, suffix string, productConfigProperties *ProductConfigProperties) {
|
||||||
if suffix != "" {
|
if suffix != "" {
|
||||||
suffix = "-" + suffix
|
suffix = "-" + suffix
|
||||||
}
|
}
|
||||||
variableValues := reflect.ValueOf(variableProps).Elem().FieldByName("Product_variables")
|
|
||||||
|
// variableValues represent the product_variables or soong_config_variables
|
||||||
|
// struct.
|
||||||
|
variableValues := reflect.ValueOf(variableProps).Elem().FieldByName(fieldName)
|
||||||
|
|
||||||
|
// Example of product_variables:
|
||||||
|
//
|
||||||
|
// product_variables: {
|
||||||
|
// malloc_not_svelte: {
|
||||||
|
// shared_libs: ["malloc_not_svelte_shared_lib"],
|
||||||
|
// whole_static_libs: ["malloc_not_svelte_whole_static_lib"],
|
||||||
|
// exclude_static_libs: [
|
||||||
|
// "malloc_not_svelte_static_lib_excludes",
|
||||||
|
// "malloc_not_svelte_whole_static_lib_excludes",
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
//
|
||||||
|
// Example of soong_config_variables:
|
||||||
|
//
|
||||||
|
// soong_config_variables: {
|
||||||
|
// feature1: {
|
||||||
|
// conditions_default: {
|
||||||
|
// ...
|
||||||
|
// },
|
||||||
|
// cflags: ...
|
||||||
|
// },
|
||||||
|
// feature2: {
|
||||||
|
// cflags: ...
|
||||||
|
// conditions_default: {
|
||||||
|
// ...
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// board: {
|
||||||
|
// soc_a: {
|
||||||
|
// ...
|
||||||
|
// },
|
||||||
|
// soc_a: {
|
||||||
|
// ...
|
||||||
|
// },
|
||||||
|
// soc_c: {},
|
||||||
|
// conditions_default: {
|
||||||
|
// ...
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// }
|
||||||
for i := 0; i < variableValues.NumField(); i++ {
|
for i := 0; i < variableValues.NumField(); i++ {
|
||||||
|
// e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc.
|
||||||
|
productVariableName := variableValues.Type().Field(i).Name
|
||||||
|
|
||||||
variableValue := variableValues.Field(i)
|
variableValue := variableValues.Field(i)
|
||||||
// Check if any properties were set for the module
|
// Check if any properties were set for the module
|
||||||
if variableValue.IsZero() {
|
if variableValue.IsZero() {
|
||||||
|
// e.g. feature1: {}, malloc_not_svelte: {}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc.
|
|
||||||
productVariableName := variableValues.Type().Field(i).Name
|
// Unlike product variables, config variables require a few more
|
||||||
|
// indirections to extract the struct from the reflect.Value.
|
||||||
|
if v, ok := maybeExtractConfigVarProp(variableValue); ok {
|
||||||
|
variableValue = v
|
||||||
|
}
|
||||||
|
|
||||||
for j := 0; j < variableValue.NumField(); j++ {
|
for j := 0; j < variableValue.NumField(); j++ {
|
||||||
property := variableValue.Field(j)
|
property := variableValue.Field(j)
|
||||||
// If the property wasn't set, no need to pass it along
|
// If the property wasn't set, no need to pass it along
|
||||||
|
@ -543,14 +758,57 @@ func productVariableValues(variableProps interface{}, suffix string, productConf
|
||||||
|
|
||||||
// e.g. Asflags, Cflags, Enabled, etc.
|
// e.g. Asflags, Cflags, Enabled, etc.
|
||||||
propertyName := variableValue.Type().Field(j).Name
|
propertyName := variableValue.Type().Field(j).Name
|
||||||
if (*productConfigProperties)[propertyName] == nil {
|
|
||||||
(*productConfigProperties)[propertyName] = make(map[string]ProductConfigProperty)
|
if v, ok := maybeExtractConfigVarProp(property); ok {
|
||||||
}
|
// The field is a struct, which is used by:
|
||||||
config := productVariableName + suffix
|
// 1) soong_config_string_variables
|
||||||
(*productConfigProperties)[propertyName][config] = ProductConfigProperty{
|
//
|
||||||
ProductConfigVariable: productVariableName,
|
// soc_a: {
|
||||||
FullConfig: config,
|
// cflags: ...,
|
||||||
Property: property.Interface(),
|
// }
|
||||||
|
//
|
||||||
|
// soc_b: {
|
||||||
|
// cflags: ...,
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// 2) conditions_default structs for all soong config variable types.
|
||||||
|
//
|
||||||
|
// conditions_default: {
|
||||||
|
// cflags: ...,
|
||||||
|
// static_libs: ...
|
||||||
|
// }
|
||||||
|
field := v
|
||||||
|
for k := 0; k < field.NumField(); k++ {
|
||||||
|
// Iterate over fields of this struct prop.
|
||||||
|
if field.Field(k).IsZero() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
productVariableValue := proptools.PropertyNameForField(propertyName)
|
||||||
|
config := strings.Join([]string{namespace, productVariableName, productVariableValue}, "__")
|
||||||
|
actualPropertyName := field.Type().Field(k).Name
|
||||||
|
|
||||||
|
productConfigProperties.AddProductConfigProperty(
|
||||||
|
actualPropertyName, // e.g. cflags, static_libs
|
||||||
|
namespace, // e.g. acme, android
|
||||||
|
productVariableName, // e.g. size, feature1, FEATURE2, board
|
||||||
|
config,
|
||||||
|
field.Field(k).Interface(), // e.g. ["-DDEFAULT"], ["foo", "bar"]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Not a conditions_default or a struct prop, i.e. regular
|
||||||
|
// product variables, or not a string-typed config var.
|
||||||
|
config := productVariableName + suffix
|
||||||
|
if namespace != "" {
|
||||||
|
config = namespace + "__" + config
|
||||||
|
}
|
||||||
|
productConfigProperties.AddProductConfigProperty(
|
||||||
|
propertyName,
|
||||||
|
namespace,
|
||||||
|
productVariableName,
|
||||||
|
config,
|
||||||
|
property.Interface(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,9 +158,9 @@ func (ct configurationType) validateConfig(config string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectKey returns the Bazel select key for a given configurationType and config string.
|
// SelectKey returns the Bazel select key for a given configurationType and config string.
|
||||||
func (ct configurationType) SelectKey(config string) string {
|
func (ca ConfigurationAxis) SelectKey(config string) string {
|
||||||
ct.validateConfig(config)
|
ca.validateConfig(config)
|
||||||
switch ct {
|
switch ca.configurationType {
|
||||||
case noConfig:
|
case noConfig:
|
||||||
panic(fmt.Errorf("SelectKey is unnecessary for noConfig ConfigurationType "))
|
panic(fmt.Errorf("SelectKey is unnecessary for noConfig ConfigurationType "))
|
||||||
case arch:
|
case arch:
|
||||||
|
@ -170,12 +170,13 @@ func (ct configurationType) SelectKey(config string) string {
|
||||||
case osArch:
|
case osArch:
|
||||||
return platformOsArchMap[config]
|
return platformOsArchMap[config]
|
||||||
case productVariables:
|
case productVariables:
|
||||||
if config == ConditionsDefaultConfigKey {
|
if strings.HasSuffix(config, ConditionsDefaultConfigKey) {
|
||||||
|
// e.g. "acme__feature1__conditions_default" or "android__board__conditions_default"
|
||||||
return ConditionsDefaultSelectKey
|
return ConditionsDefaultSelectKey
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s:%s", productVariableBazelPackage, strings.ToLower(config))
|
return fmt.Sprintf("%s:%s", productVariableBazelPackage, config)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
|
panic(fmt.Errorf("Unrecognized ConfigurationType %d", ca.configurationType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,23 @@ import (
|
||||||
"github.com/google/blueprint"
|
"github.com/google/blueprint"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type BazelModuleProperties struct {
|
||||||
|
// The label of the Bazel target replacing this Soong module. When run in conversion mode, this
|
||||||
|
// will import the handcrafted build target into the autogenerated file. Note: this may result in
|
||||||
|
// a conflict due to duplicate targets if bp2build_available is also set.
|
||||||
|
Label *string
|
||||||
|
|
||||||
|
// If true, bp2build will generate the converted Bazel target for this module. Note: this may
|
||||||
|
// cause a conflict due to the duplicate targets if label is also set.
|
||||||
|
//
|
||||||
|
// This is a bool pointer to support tristates: true, false, not set.
|
||||||
|
//
|
||||||
|
// To opt-in a module, set bazel_module: { bp2build_available: true }
|
||||||
|
// To opt-out a module, set bazel_module: { bp2build_available: false }
|
||||||
|
// To defer the default setting for the directory, do not set the value.
|
||||||
|
Bp2build_available *bool
|
||||||
|
}
|
||||||
|
|
||||||
// BazelTargetModuleProperties contain properties and metadata used for
|
// BazelTargetModuleProperties contain properties and metadata used for
|
||||||
// Blueprint to BUILD file conversion.
|
// Blueprint to BUILD file conversion.
|
||||||
type BazelTargetModuleProperties struct {
|
type BazelTargetModuleProperties struct {
|
||||||
|
|
257
bp2build/soong_config_module_type_conversion_test.go
Normal file
257
bp2build/soong_config_module_type_conversion_test.go
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
// Copyright 2021 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 bp2build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"android/soong/android"
|
||||||
|
"android/soong/cc"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func runSoongConfigModuleTypeTest(t *testing.T, tc bp2buildTestCase) {
|
||||||
|
t.Helper()
|
||||||
|
runBp2BuildTestCase(t, registerSoongConfigModuleTypes, tc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerSoongConfigModuleTypes(ctx android.RegistrationContext) {
|
||||||
|
cc.RegisterCCBuildComponents(ctx)
|
||||||
|
|
||||||
|
ctx.RegisterModuleType("soong_config_module_type_import", android.SoongConfigModuleTypeImportFactory)
|
||||||
|
ctx.RegisterModuleType("soong_config_module_type", android.SoongConfigModuleTypeFactory)
|
||||||
|
ctx.RegisterModuleType("soong_config_string_variable", android.SoongConfigStringVariableDummyFactory)
|
||||||
|
ctx.RegisterModuleType("soong_config_bool_variable", android.SoongConfigBoolVariableDummyFactory)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSoongConfigModuleType(t *testing.T) {
|
||||||
|
bp := `
|
||||||
|
soong_config_module_type {
|
||||||
|
name: "custom_cc_library_static",
|
||||||
|
module_type: "cc_library_static",
|
||||||
|
config_namespace: "acme",
|
||||||
|
bool_variables: ["feature1"],
|
||||||
|
properties: ["cflags"],
|
||||||
|
bazel_module: { bp2build_available: true },
|
||||||
|
}
|
||||||
|
|
||||||
|
custom_cc_library_static {
|
||||||
|
name: "foo",
|
||||||
|
bazel_module: { bp2build_available: true },
|
||||||
|
soong_config_variables: {
|
||||||
|
feature1: {
|
||||||
|
conditions_default: {
|
||||||
|
cflags: ["-DDEFAULT1"],
|
||||||
|
},
|
||||||
|
cflags: ["-DFEATURE1"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
runSoongConfigModuleTypeTest(t, bp2buildTestCase{
|
||||||
|
description: "soong config variables - soong_config_module_type is supported in bp2build",
|
||||||
|
moduleTypeUnderTest: "cc_library_static",
|
||||||
|
moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
|
||||||
|
moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
|
||||||
|
blueprint: bp,
|
||||||
|
expectedBazelTargets: []string{`cc_library_static(
|
||||||
|
name = "foo",
|
||||||
|
copts = select({
|
||||||
|
"//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
|
||||||
|
"//conditions:default": ["-DDEFAULT1"],
|
||||||
|
}),
|
||||||
|
local_includes = ["."],
|
||||||
|
)`}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSoongConfigModuleTypeImport(t *testing.T) {
|
||||||
|
configBp := `
|
||||||
|
soong_config_module_type {
|
||||||
|
name: "custom_cc_library_static",
|
||||||
|
module_type: "cc_library_static",
|
||||||
|
config_namespace: "acme",
|
||||||
|
bool_variables: ["feature1"],
|
||||||
|
properties: ["cflags"],
|
||||||
|
bazel_module: { bp2build_available: true },
|
||||||
|
}
|
||||||
|
`
|
||||||
|
bp := `
|
||||||
|
soong_config_module_type_import {
|
||||||
|
from: "foo/bar/SoongConfig.bp",
|
||||||
|
module_types: ["custom_cc_library_static"],
|
||||||
|
}
|
||||||
|
|
||||||
|
custom_cc_library_static {
|
||||||
|
name: "foo",
|
||||||
|
bazel_module: { bp2build_available: true },
|
||||||
|
soong_config_variables: {
|
||||||
|
feature1: {
|
||||||
|
conditions_default: {
|
||||||
|
cflags: ["-DDEFAULT1"],
|
||||||
|
},
|
||||||
|
cflags: ["-DFEATURE1"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
runSoongConfigModuleTypeTest(t, bp2buildTestCase{
|
||||||
|
description: "soong config variables - soong_config_module_type_import is supported in bp2build",
|
||||||
|
moduleTypeUnderTest: "cc_library_static",
|
||||||
|
moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
|
||||||
|
moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
|
||||||
|
filesystem: map[string]string{
|
||||||
|
"foo/bar/SoongConfig.bp": configBp,
|
||||||
|
},
|
||||||
|
blueprint: bp,
|
||||||
|
expectedBazelTargets: []string{`cc_library_static(
|
||||||
|
name = "foo",
|
||||||
|
copts = select({
|
||||||
|
"//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
|
||||||
|
"//conditions:default": ["-DDEFAULT1"],
|
||||||
|
}),
|
||||||
|
local_includes = ["."],
|
||||||
|
)`}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSoongConfigModuleType_StringVar(t *testing.T) {
|
||||||
|
bp := `
|
||||||
|
soong_config_string_variable {
|
||||||
|
name: "board",
|
||||||
|
values: ["soc_a", "soc_b", "soc_c"],
|
||||||
|
}
|
||||||
|
|
||||||
|
soong_config_module_type {
|
||||||
|
name: "custom_cc_library_static",
|
||||||
|
module_type: "cc_library_static",
|
||||||
|
config_namespace: "acme",
|
||||||
|
variables: ["board"],
|
||||||
|
properties: ["cflags"],
|
||||||
|
bazel_module: { bp2build_available: true },
|
||||||
|
}
|
||||||
|
|
||||||
|
custom_cc_library_static {
|
||||||
|
name: "foo",
|
||||||
|
bazel_module: { bp2build_available: true },
|
||||||
|
soong_config_variables: {
|
||||||
|
board: {
|
||||||
|
soc_a: {
|
||||||
|
cflags: ["-DSOC_A"],
|
||||||
|
},
|
||||||
|
soc_b: {
|
||||||
|
cflags: ["-DSOC_B"],
|
||||||
|
},
|
||||||
|
soc_c: {},
|
||||||
|
conditions_default: {
|
||||||
|
cflags: ["-DSOC_DEFAULT"]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
runSoongConfigModuleTypeTest(t, bp2buildTestCase{
|
||||||
|
description: "soong config variables - generates selects for string vars",
|
||||||
|
moduleTypeUnderTest: "cc_library_static",
|
||||||
|
moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
|
||||||
|
moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
|
||||||
|
blueprint: bp,
|
||||||
|
expectedBazelTargets: []string{`cc_library_static(
|
||||||
|
name = "foo",
|
||||||
|
copts = select({
|
||||||
|
"//build/bazel/product_variables:acme__board__soc_a": ["-DSOC_A"],
|
||||||
|
"//build/bazel/product_variables:acme__board__soc_b": ["-DSOC_B"],
|
||||||
|
"//conditions:default": ["-DSOC_DEFAULT"],
|
||||||
|
}),
|
||||||
|
local_includes = ["."],
|
||||||
|
)`}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSoongConfigModuleType_StringAndBoolVar(t *testing.T) {
|
||||||
|
bp := `
|
||||||
|
soong_config_bool_variable {
|
||||||
|
name: "feature1",
|
||||||
|
}
|
||||||
|
|
||||||
|
soong_config_bool_variable {
|
||||||
|
name: "feature2",
|
||||||
|
}
|
||||||
|
|
||||||
|
soong_config_string_variable {
|
||||||
|
name: "board",
|
||||||
|
values: ["soc_a", "soc_b", "soc_c"],
|
||||||
|
}
|
||||||
|
|
||||||
|
soong_config_module_type {
|
||||||
|
name: "custom_cc_library_static",
|
||||||
|
module_type: "cc_library_static",
|
||||||
|
config_namespace: "acme",
|
||||||
|
variables: ["feature1", "feature2", "board"],
|
||||||
|
properties: ["cflags"],
|
||||||
|
bazel_module: { bp2build_available: true },
|
||||||
|
}
|
||||||
|
|
||||||
|
custom_cc_library_static {
|
||||||
|
name: "foo",
|
||||||
|
bazel_module: { bp2build_available: true },
|
||||||
|
soong_config_variables: {
|
||||||
|
feature1: {
|
||||||
|
conditions_default: {
|
||||||
|
cflags: ["-DDEFAULT1"],
|
||||||
|
},
|
||||||
|
cflags: ["-DFEATURE1"],
|
||||||
|
},
|
||||||
|
feature2: {
|
||||||
|
cflags: ["-DFEATURE2"],
|
||||||
|
conditions_default: {
|
||||||
|
cflags: ["-DDEFAULT2"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
board: {
|
||||||
|
soc_a: {
|
||||||
|
cflags: ["-DSOC_A"],
|
||||||
|
},
|
||||||
|
soc_b: {
|
||||||
|
cflags: ["-DSOC_B"],
|
||||||
|
},
|
||||||
|
soc_c: {},
|
||||||
|
conditions_default: {
|
||||||
|
cflags: ["-DSOC_DEFAULT"]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}`
|
||||||
|
|
||||||
|
runSoongConfigModuleTypeTest(t, bp2buildTestCase{
|
||||||
|
description: "soong config variables - generates selects for multiple variable types",
|
||||||
|
moduleTypeUnderTest: "cc_library_static",
|
||||||
|
moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
|
||||||
|
moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
|
||||||
|
blueprint: bp,
|
||||||
|
expectedBazelTargets: []string{`cc_library_static(
|
||||||
|
name = "foo",
|
||||||
|
copts = select({
|
||||||
|
"//build/bazel/product_variables:acme__board__soc_a": ["-DSOC_A"],
|
||||||
|
"//build/bazel/product_variables:acme__board__soc_b": ["-DSOC_B"],
|
||||||
|
"//conditions:default": ["-DSOC_DEFAULT"],
|
||||||
|
}) + select({
|
||||||
|
"//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
|
||||||
|
"//conditions:default": ["-DDEFAULT1"],
|
||||||
|
}) + select({
|
||||||
|
"//build/bazel/product_variables:acme__feature2": ["-DFEATURE2"],
|
||||||
|
"//conditions:default": ["-DDEFAULT2"],
|
||||||
|
}),
|
||||||
|
local_includes = ["."],
|
||||||
|
)`}})
|
||||||
|
}
|
|
@ -327,7 +327,7 @@ func (ca *compilerAttributes) convertProductVariables(ctx android.BazelConversio
|
||||||
ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName))
|
ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName))
|
||||||
}
|
}
|
||||||
newFlags, _ := bazel.TryVariableSubstitutions(flags, prop.ProductConfigVariable)
|
newFlags, _ := bazel.TryVariableSubstitutions(flags, prop.ProductConfigVariable)
|
||||||
attr.SetSelectValue(bazel.ProductVariableConfigurationAxis(prop.FullConfig), prop.FullConfig, newFlags)
|
attr.SetSelectValue(prop.ConfigurationAxis(), prop.FullConfig, newFlags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -611,7 +611,7 @@ func (la *linkerAttributes) convertProductVariables(ctx android.BazelConversionP
|
||||||
ctx.ModuleErrorf("Could not convert product variable %s property", dep.excludesField)
|
ctx.ModuleErrorf("Could not convert product variable %s property", dep.excludesField)
|
||||||
}
|
}
|
||||||
|
|
||||||
dep.attribute.SetSelectValue(bazel.ProductVariableConfigurationAxis(config), config, dep.depResolutionFunc(ctx, android.FirstUniqueStrings(includes), excludes))
|
dep.attribute.SetSelectValue(prop.ConfigurationAxis(), config, dep.depResolutionFunc(ctx, android.FirstUniqueStrings(includes), excludes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2358,9 +2358,6 @@ func ccSharedOrStaticBp2BuildMutator(ctx android.TopDownMutatorContext, modType
|
||||||
if !module.ConvertWithBp2build(ctx) {
|
if !module.ConvertWithBp2build(ctx) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ctx.ModuleType() != modType {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ccSharedOrStaticBp2BuildMutatorInternal(ctx, module, modType)
|
ccSharedOrStaticBp2BuildMutatorInternal(ctx, module, modType)
|
||||||
}
|
}
|
||||||
|
@ -2498,7 +2495,15 @@ type bazelCcLibraryStaticAttributes struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func CcLibraryStaticBp2Build(ctx android.TopDownMutatorContext) {
|
func CcLibraryStaticBp2Build(ctx android.TopDownMutatorContext) {
|
||||||
ccSharedOrStaticBp2BuildMutator(ctx, "cc_library_static")
|
isLibraryStatic := ctx.ModuleType() == "cc_library_static"
|
||||||
|
if b, ok := ctx.Module().(android.Bazelable); ok {
|
||||||
|
// This is created by a custom soong config module type, so its ctx.ModuleType() is not
|
||||||
|
// cc_library_static. Check its BaseModuleType.
|
||||||
|
isLibraryStatic = isLibraryStatic || b.BaseModuleType() == "cc_library_static"
|
||||||
|
}
|
||||||
|
if isLibraryStatic {
|
||||||
|
ccSharedOrStaticBp2BuildMutator(ctx, "cc_library_static")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(b/199902614): Can this be factored to share with the other Attributes?
|
// TODO(b/199902614): Can this be factored to share with the other Attributes?
|
||||||
|
@ -2529,5 +2534,13 @@ type bazelCcLibrarySharedAttributes struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func CcLibrarySharedBp2Build(ctx android.TopDownMutatorContext) {
|
func CcLibrarySharedBp2Build(ctx android.TopDownMutatorContext) {
|
||||||
ccSharedOrStaticBp2BuildMutator(ctx, "cc_library_shared")
|
isLibraryShared := ctx.ModuleType() == "cc_library_shared"
|
||||||
|
if b, ok := ctx.Module().(android.Bazelable); ok {
|
||||||
|
// This is created by a custom soong config module type, so its ctx.ModuleType() is not
|
||||||
|
// cc_library_shared. Check its BaseModuleType.
|
||||||
|
isLibraryShared = isLibraryShared || b.BaseModuleType() == "cc_library_shared"
|
||||||
|
}
|
||||||
|
if isLibraryShared {
|
||||||
|
ccSharedOrStaticBp2BuildMutator(ctx, "cc_library_shared")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -464,6 +464,11 @@ func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
|
||||||
// conversion for Bazel conversion.
|
// conversion for Bazel conversion.
|
||||||
bp2buildCtx := android.NewContext(configuration)
|
bp2buildCtx := android.NewContext(configuration)
|
||||||
|
|
||||||
|
// Soong internals like LoadHooks behave differently when running as
|
||||||
|
// bp2build. This is the bit to differentiate between Soong-as-Soong and
|
||||||
|
// Soong-as-bp2build.
|
||||||
|
bp2buildCtx.SetRunningAsBp2build()
|
||||||
|
|
||||||
// Propagate "allow misssing dependencies" bit. This is normally set in
|
// Propagate "allow misssing dependencies" bit. This is normally set in
|
||||||
// newContext(), but we create bp2buildCtx without calling that method.
|
// newContext(), but we create bp2buildCtx without calling that method.
|
||||||
bp2buildCtx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
|
bp2buildCtx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
|
||||||
|
|
Loading…
Reference in a new issue