Merge changes Ibbb14b0d,I9aa552e3

* changes:
  Create config_setting per apex_name
  Add a function to create config_setting(s)
This commit is contained in:
Spandan Das
2023-04-29 03:52:55 +00:00
committed by Gerrit Code Review
10 changed files with 208 additions and 4 deletions

View File

@@ -273,6 +273,12 @@ type TopDownMutatorContext interface {
// This function can be used to create alias definitions in a directory that is different
// from the directory of the visited Soong module.
CreateBazelTargetAliasInDir(dir string, name string, actual bazel.Label)
// CreateBazelConfigSetting creates a config_setting in <dir>/BUILD.bazel
// build/bazel has several static config_setting(s) that are used in Bazel builds.
// This function can be used to createa additional config_setting(s) based on the build graph
// (e.g. a config_setting specific to an apex variant)
CreateBazelConfigSetting(csa bazel.ConfigSettingAttributes, ca CommonAttributes, dir string)
}
type topDownMutatorContext struct {
@@ -738,6 +744,23 @@ func (t *topDownMutatorContext) CreateBazelTargetAliasInDir(
mod.base().addBp2buildInfo(info)
}
func (t *topDownMutatorContext) CreateBazelConfigSetting(
csa bazel.ConfigSettingAttributes,
ca CommonAttributes,
dir string) {
mod := t.Module()
info := bp2buildInfo{
Dir: dir,
BazelProps: bazel.BazelTargetModuleProperties{
Rule_class: "config_setting",
},
CommonAttrs: ca,
ConstraintAttrs: constraintAttributes{},
Attrs: &csa,
}
mod.base().addBp2buildInfo(info)
}
// ApexAvailableTags converts the apex_available property value of an ApexModule
// module and returns it as a list of keyed tags.
func ApexAvailableTags(mod Module) bazel.StringListAttribute {

View File

@@ -268,9 +268,8 @@ func (ct configurationType) validateConfig(config string) {
case productVariables:
// do nothing
case osAndInApex:
if _, ok := osAndInApexMap[config]; !ok {
panic(fmt.Errorf("Unknown os+in_apex config: %s", config))
}
// do nothing
// this axis can contain additional per-apex keys
case inApex:
if _, ok := inApexMap[config]; !ok {
panic(fmt.Errorf("Unknown in_apex config: %s", config))
@@ -299,7 +298,10 @@ func (ca ConfigurationAxis) SelectKey(config string) string {
}
return fmt.Sprintf("%s:%s", productVariableBazelPackage, config)
case osAndInApex:
return osAndInApexMap[config]
if ret, exists := osAndInApexMap[config]; exists {
return ret
}
return config
case inApex:
return inApexMap[config]
default:

View File

@@ -1424,3 +1424,14 @@ func TryVariableSubstitution(s string, productVariable string) (string, bool) {
sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
return sub, s != sub
}
// StringMapAttribute is a map of strings.
// The use case for this is storing the flag_values in a config_setting object.
// Bazel rules do not support map attributes, and this should NOT be used in Bazel rules.
type StringMapAttribute map[string]string
// ConfigSettingAttributes stores the keys of a config_setting object.
type ConfigSettingAttributes struct {
// Each key in Flag_values is a label to a custom string_setting
Flag_values StringMapAttribute
}

View File

@@ -600,6 +600,11 @@ func prettyPrint(propertyValue reflect.Value, indent int, emitZeroValues bool) (
// TODO(b/164227191): implement pretty print for interfaces.
// Interfaces are used for for arch, multilib and target properties.
return "", nil
case reflect.Map:
if v, ok := propertyValue.Interface().(bazel.StringMapAttribute); ok {
return starlark_fmt.PrintStringStringDict(v, indent), nil
}
return "", fmt.Errorf("bp2build expects map of type map[string]string for field: %s", propertyValue)
default:
return "", fmt.Errorf(
"unexpected kind for property struct field: %s", propertyValue.Kind())

View File

@@ -1898,3 +1898,36 @@ func TestGenerateApiBazelTargets(t *testing.T) {
Description: "Generating API contribution Bazel targets for custom module",
})
}
func TestGenerateConfigSetting(t *testing.T) {
bp := `
custom {
name: "foo",
test_config_setting: true,
}
`
expectedBazelTargets := []string{
MakeBazelTargetNoRestrictions(
"config_setting",
"foo_config_setting",
AttrNameToString{
"flag_values": `{
"//build/bazel/rules/my_string_setting": "foo",
}`,
},
),
MakeBazelTarget(
"custom",
"foo",
AttrNameToString{},
),
}
registerCustomModule := func(ctx android.RegistrationContext) {
ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
}
RunBp2BuildTestCase(t, registerCustomModule, Bp2buildTestCase{
Blueprint: bp,
ExpectedBazelTargets: expectedBazelTargets,
Description: "Generating API contribution Bazel targets for custom module",
})
}

View File

@@ -108,6 +108,7 @@ custom = rule(
"string_literal_prop": attr.string(),
"string_prop": attr.string(),
"string_ptr_prop": attr.string(),
"test_config_setting": attr.bool(),
},
)
@@ -139,6 +140,7 @@ custom_defaults = rule(
"string_literal_prop": attr.string(),
"string_prop": attr.string(),
"string_ptr_prop": attr.string(),
"test_config_setting": attr.bool(),
},
)
@@ -170,6 +172,7 @@ custom_test_ = rule(
"string_literal_prop": attr.string(),
"string_prop": attr.string(),
"string_ptr_prop": attr.string(),
"test_config_setting": attr.bool(),
# test_prop start
# "test_string_prop": attr.string(),
# test_prop end

View File

@@ -4454,3 +4454,32 @@ cc_library {
},
})
}
// Test that a config_setting specific to an apex is created by cc_library.
func TestCcLibraryCreatesInApexConfigSetting(t *testing.T) {
runCcLibraryTestCase(t, Bp2buildTestCase{
Description: "cc_library creates a config_setting for each apex in apex_available",
ModuleTypeUnderTest: "cc_library",
ModuleTypeUnderTestFactory: cc.LibraryFactory,
Dir: "build/bazel/rules/apex",
Blueprint: `
cc_library {
name: "foo",
apex_available: [
"//apex_available:platform", // This will be skipped, since it is equivalent to //build/bazel/rules/apex:android-non_apex
"myapex"
],
}`,
ExpectedBazelTargets: []string{
MakeBazelTargetNoRestrictions(
"config_setting",
"android-in_myapex",
AttrNameToString{
"flag_values": `{
"//build/bazel/rules/apex:apex_name": "myapex",
}`,
},
),
},
})
}

View File

@@ -317,6 +317,8 @@ type customProps struct {
One_to_many_prop *bool
Api *string // File describing the APIs of this module
Test_config_setting *bool // Used to test generation of config_setting targets
}
type customModule struct {
@@ -490,6 +492,27 @@ func (m *customModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
if proptools.Bool(m.props.Test_config_setting) {
m.createConfigSetting(ctx)
}
}
func (m *customModule) createConfigSetting(ctx android.TopDownMutatorContext) {
csa := bazel.ConfigSettingAttributes{
Flag_values: bazel.StringMapAttribute{
"//build/bazel/rules/my_string_setting": m.Name(),
},
}
ca := android.CommonAttributes{
Name: m.Name() + "_config_setting",
}
ctx.CreateBazelConfigSetting(
csa,
ca,
ctx.ModuleDir(),
)
}
var _ android.ApiProvider = (*customModule)(nil)

View File

@@ -17,6 +17,7 @@ import (
"fmt"
"path/filepath"
"strings"
"sync"
"android/soong/android"
"android/soong/bazel"
@@ -1196,6 +1197,63 @@ func availableToSameApexes(a, b []string) bool {
return !differ
}
var (
apexConfigSettingKey = android.NewOnceKey("apexConfigSetting")
apexConfigSettingLock sync.Mutex
)
func getApexConfigSettingMap(config android.Config) *map[string]bool {
return config.Once(apexConfigSettingKey, func() interface{} {
return &map[string]bool{}
}).(*map[string]bool)
}
// Create a config setting for this apex in build/bazel/rules/apex
// The use case for this is stub/impl selection in cc libraries
// Long term, these config_setting(s) should be colocated with the respective apex definitions.
// Note that this is an anti-pattern: The config_setting should be created from the apex definition
// and not from a cc_library.
// This anti-pattern is needed today since not all apexes have been allowlisted.
func createInApexConfigSetting(ctx android.TopDownMutatorContext, apexName string) {
if apexName == android.AvailableToPlatform || apexName == android.AvailableToAnyApex {
// These correspond to android-non_apex and android-in_apex
return
}
apexConfigSettingLock.Lock()
defer apexConfigSettingLock.Unlock()
// Return if a config_setting has already been created
acsm := getApexConfigSettingMap(ctx.Config())
if _, exists := (*acsm)[apexName]; exists {
return
}
(*acsm)[apexName] = true
csa := bazel.ConfigSettingAttributes{
Flag_values: bazel.StringMapAttribute{
"//build/bazel/rules/apex:apex_name": apexName,
},
}
ca := android.CommonAttributes{
Name: "android-in_" + apexName,
}
ctx.CreateBazelConfigSetting(
csa,
ca,
"build/bazel/rules/apex",
)
}
func inApexConfigSetting(apexAvailable string) string {
if apexAvailable == android.AvailableToPlatform {
return bazel.AndroidAndNonApex
}
if apexAvailable == android.AvailableToAnyApex {
return bazel.AndroidAndInApex
}
return "//build/bazel/rules/apex:android-in_" + apexAvailable
}
func setStubsForDynamicDeps(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis,
config string, apexAvailable []string, dynamicLibs bazel.LabelList, dynamicDeps *bazel.LabelListAttribute, ind int, buildNonApexWithStubs bool) {
@@ -1241,6 +1299,13 @@ func setStubsForDynamicDeps(ctx android.BazelConversionPathContext, axis bazel.C
dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, bazel.FirstUniqueBazelLabelList(nonApexSelectValue))
}
}
// Create a config_setting for each apex_available.
// This will be used to select impl of a dep if dep is available to the same apex.
for _, aa := range apexAvailable {
createInApexConfigSetting(ctx.(android.TopDownMutatorContext), aa)
}
}
func (la *linkerAttributes) convertStripProps(ctx android.BazelConversionPathContext, module *Module) {

View File

@@ -99,6 +99,16 @@ func PrintStringIntDict(dict map[string]int, indentLevel int) string {
return PrintDict(valDict, indentLevel)
}
// PrintStringStringDict returns a Starlark-compatible string formatted as dictionary with
// string keys and string values.
func PrintStringStringDict(dict map[string]string, indentLevel int) string {
valDict := make(map[string]string, len(dict))
for k, v := range dict {
valDict[k] = fmt.Sprintf(`"%s"`, v)
}
return PrintDict(valDict, indentLevel)
}
// PrintDict returns a starlark-compatible string containing a dictionary with string keys and
// values printed with no additional formatting.
func PrintDict(dict map[string]string, indentLevel int) string {