Merge changes I55dc71d2,I250ee4e1

* changes:
  Avoid separate call to module factory to create conditional properties
  Test using soong_config_module_type with singleton module type
This commit is contained in:
Paul Duffin
2023-01-10 10:42:06 +00:00
committed by Gerrit Code Review
3 changed files with 128 additions and 15 deletions

View File

@@ -20,7 +20,9 @@ package android
import (
"fmt"
"path/filepath"
"reflect"
"strings"
"sync"
"text/scanner"
"github.com/google/blueprint"
@@ -422,13 +424,43 @@ func loadSoongConfigModuleTypeDefinition(ctx LoadHookContext, from string) map[s
// configModuleFactory takes an existing soongConfigModuleFactory and a
// ModuleType to create a new ModuleFactory that uses a custom loadhook.
func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfig.ModuleType, bp2build bool) blueprint.ModuleFactory {
conditionalFactoryProps := soongconfig.CreateProperties(factory, moduleType)
if !conditionalFactoryProps.IsValid() {
return factory
// Defer creation of conditional properties struct until the first call from the factory
// method. That avoids having to make a special call to the factory to create the properties
// structs from which the conditional properties struct is created. This is needed in order to
// allow singleton modules to be customized by soong_config_module_type as the
// SingletonModuleFactoryAdaptor factory registers a load hook for the singleton module
// everytime that it is called. Calling the factory twice causes a build failure as the load
// hook is called twice, the first time it updates the singleton module to indicate that it has
// been registered as a module, and the second time it fails because it thinks it has been
// registered again and a singleton module can only be registered once.
//
// This is an issue for singleton modules because:
// * Load hooks are registered on the module object and are only called when the module object
// is created by Blueprint while processing the Android.bp file.
// * The module factory for a singleton module returns the same module object each time it is
// called, and registers its load hook on that same module object.
// * When the module factory is called by Blueprint it then calls all the load hooks that have
// been registered for every call to that module factory.
//
// It is not an issue for normal modules because they return a new module object each time the
// factory is called and so any load hooks registered on module objects which are discarded will
// not be run.
once := &sync.Once{}
conditionalFactoryProps := reflect.Value{}
getConditionalFactoryProps := func(props []interface{}) reflect.Value {
once.Do(func() {
conditionalFactoryProps = soongconfig.CreateProperties(props, moduleType)
})
return conditionalFactoryProps
}
return func() (blueprint.Module, []interface{}) {
module, props := factory()
conditionalFactoryProps := getConditionalFactoryProps(props)
if !conditionalFactoryProps.IsValid() {
return module, props
}
conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
props = append(props, conditionalProps.Interface())

View File

@@ -15,12 +15,10 @@
package android
import (
"fmt"
"testing"
)
type soongConfigTestDefaultsModuleProperties struct {
}
type soongConfigTestDefaultsModule struct {
ModuleBase
DefaultsModuleBase
@@ -413,10 +411,95 @@ func TestDuplicateStringValueInSoongConfigStringVariable(t *testing.T) {
})).RunTest(t)
}
func testConfigWithVendorVars(buildDir, bp string, fs map[string][]byte, vendorVars map[string]map[string]string) Config {
config := TestConfig(buildDir, nil, bp, fs)
config.TestProductVariables.VendorVars = vendorVars
return config
type soongConfigTestSingletonModule struct {
SingletonModuleBase
props soongConfigTestSingletonModuleProperties
}
type soongConfigTestSingletonModuleProperties struct {
Fragments []struct {
Apex string
Module string
}
}
func soongConfigTestSingletonModuleFactory() SingletonModule {
m := &soongConfigTestSingletonModule{}
m.AddProperties(&m.props)
InitAndroidModule(m)
return m
}
func (t *soongConfigTestSingletonModule) GenerateAndroidBuildActions(ModuleContext) {}
func (t *soongConfigTestSingletonModule) GenerateSingletonBuildActions(SingletonContext) {}
var prepareForSoongConfigTestSingletonModule = FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterSingletonModuleType("test_singleton", soongConfigTestSingletonModuleFactory)
})
func TestSoongConfigModuleSingletonModule(t *testing.T) {
bp := `
soong_config_module_type {
name: "acme_test_singleton",
module_type: "test_singleton",
config_namespace: "acme",
bool_variables: ["coyote"],
properties: ["fragments"],
}
acme_test_singleton {
name: "wiley",
fragments: [
{
apex: "com.android.acme",
module: "road-runner",
},
],
soong_config_variables: {
coyote: {
fragments: [
{
apex: "com.android.acme",
module: "wiley",
},
],
},
},
}
`
for _, test := range []struct {
coyote bool
expectedFragments string
}{
{
coyote: false,
expectedFragments: "[{Apex:com.android.acme Module:road-runner}]",
},
{
coyote: true,
expectedFragments: "[{Apex:com.android.acme Module:road-runner} {Apex:com.android.acme Module:wiley}]",
},
} {
t.Run(fmt.Sprintf("coyote:%t", test.coyote), func(t *testing.T) {
result := GroupFixturePreparers(
PrepareForTestWithSoongConfigModuleBuildComponents,
prepareForSoongConfigTestSingletonModule,
FixtureWithRootAndroidBp(bp),
FixtureModifyProductVariables(func(variables FixtureProductVariables) {
variables.VendorVars = map[string]map[string]string{
"acme": {
"coyote": fmt.Sprintf("%t", test.coyote),
},
}
}),
).RunTest(t)
// Make sure that the singleton was created.
result.SingletonForTests("test_singleton")
m := result.ModuleForTests("wiley", "").module.(*soongConfigTestSingletonModule)
AssertStringEquals(t, "fragments", test.expectedFragments, fmt.Sprintf("%+v", m.props.Fragments))
})
}
}

View File

@@ -22,7 +22,6 @@ import (
"strings"
"sync"
"github.com/google/blueprint"
"github.com/google/blueprint/parser"
"github.com/google/blueprint/proptools"
@@ -363,10 +362,9 @@ func (defs Bp2BuildSoongConfigDefinitions) String() string {
// },
// },
// }
func CreateProperties(factory blueprint.ModuleFactory, moduleType *ModuleType) reflect.Value {
func CreateProperties(factoryProps []interface{}, moduleType *ModuleType) reflect.Value {
var fields []reflect.StructField
_, factoryProps := factory()
affectablePropertiesType := createAffectablePropertiesType(moduleType.affectableProperties, factoryProps)
if affectablePropertiesType == nil {
return reflect.Value{}