Support product variables
Allow modules to vary their properties based on product variables. For now, DEVICE_USES_LOGD, DEVICE_USES_JEMALLOC, and DEVICE_USES_DLMALLOC, and BOARD_MALLOC_ALIGNMENT are supported. Product variables can provide a value (only bool and int supported for now), and if any of the product variable properties contains a "%d" then Sprintf will be called with the property value as the format and the product variable value convert to an int as the only argument. For example: product_variables: { dlmalloc_alignment: { cflags: ["-DMALLOC_ALIGNMENT=%d"], }, }, will cause -DMALLOC_ALIGNMENT=16 to be added to any top level properties called "cflags". Change-Id: I74882a6ab4914d3e222f8d06cfac371b7b829ae5
This commit is contained in:
parent
8f301d583f
commit
7f64b6de31
7 changed files with 375 additions and 114 deletions
|
@ -97,10 +97,12 @@ bootstrap_go_package {
|
||||||
"common/config.go",
|
"common/config.go",
|
||||||
"common/defs.go",
|
"common/defs.go",
|
||||||
"common/env.go",
|
"common/env.go",
|
||||||
|
"common/extend.go",
|
||||||
"common/glob.go",
|
"common/glob.go",
|
||||||
"common/module.go",
|
"common/module.go",
|
||||||
"common/paths.go",
|
"common/paths.go",
|
||||||
"common/util.go",
|
"common/util.go",
|
||||||
|
"common/variable.go",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -175,6 +175,44 @@ func translateTargetConditionals(props []*bpparser.Property,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func translateProductVariableConditionals(props []*bpparser.Property) (computedProps []string, err error) {
|
||||||
|
for _, productVariable := range props {
|
||||||
|
v, ok := productVariableConditionals[productVariable.Name.Name]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Unsupported product variable %q", productVariable.Name.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
var scopedProps []string
|
||||||
|
for _, conditionalScopedProp := range productVariable.Value.MapValue {
|
||||||
|
if assignment, ok, err := translateSingleProperty(conditionalScopedProp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if ok {
|
||||||
|
assignment.assigner = "+="
|
||||||
|
a := assignment.assignment()
|
||||||
|
if v.value != "" && strings.Contains(a, "%d") {
|
||||||
|
a = strings.Replace(a, "%d", v.value, 1)
|
||||||
|
}
|
||||||
|
scopedProps = append(scopedProps, a)
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("Unsupported product variable property %q",
|
||||||
|
conditionalScopedProp.Name.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(scopedProps) > 0 {
|
||||||
|
if v.conditional != "" {
|
||||||
|
computedProps = append(computedProps, v.conditional)
|
||||||
|
computedProps = append(computedProps, scopedProps...)
|
||||||
|
computedProps = append(computedProps, "endif")
|
||||||
|
} else {
|
||||||
|
computedProps = append(computedProps, scopedProps...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return computedProps, nil
|
||||||
|
}
|
||||||
|
|
||||||
var secondTargetReplacer = strings.NewReplacer("TARGET_", "TARGET_2ND_")
|
var secondTargetReplacer = strings.NewReplacer("TARGET_", "TARGET_2ND_")
|
||||||
|
|
||||||
func translateSuffixProperties(suffixProps []*bpparser.Property,
|
func translateSuffixProperties(suffixProps []*bpparser.Property,
|
||||||
|
@ -303,6 +341,12 @@ func (w *androidMkWriter) parsePropsAndWriteModule(module *Module) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
standardProps = append(standardProps, props...)
|
standardProps = append(standardProps, props...)
|
||||||
|
} else if "product_variables" == prop.Name.Name {
|
||||||
|
props, err := translateProductVariableConditionals(prop.Value.MapValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
standardProps = append(standardProps, props...)
|
||||||
} else if _, ok := ignoredProperties[prop.Name.Name]; ok {
|
} else if _, ok := ignoredProperties[prop.Name.Name]; ok {
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("Unsupported property %q", prop.Name.Name)
|
return fmt.Errorf("Unsupported property %q", prop.Name.Name)
|
||||||
|
|
|
@ -172,3 +172,10 @@ var targetToHostModuleRule = map[string]string{
|
||||||
"BUILD_NATIVE_TEST": "BUILD_HOST_NATIVE_TEST",
|
"BUILD_NATIVE_TEST": "BUILD_HOST_NATIVE_TEST",
|
||||||
"BUILD_JAVA_LIBRARY": "BUILD_HOST_JAVA_LIBRARY",
|
"BUILD_JAVA_LIBRARY": "BUILD_HOST_JAVA_LIBRARY",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var productVariableConditionals = map[string]struct{conditional, value string}{
|
||||||
|
"device_uses_jemalloc": {"ifneq ($(MALLOC_IMPL),dlmalloc)", ""},
|
||||||
|
"device_uses_dlmalloc": {"ifeq ($(MALLOC_IMPL),dlmalloc)", ""},
|
||||||
|
"device_uses_logd": {"ifneq ($(TARGET_USES_LOGD),false)", ""},
|
||||||
|
"dlmalloc_alignment": {"ifdef DLMALLOC_ALIGNMENT", "$(DLMALLOC_ALIGNMENT)"},
|
||||||
|
}
|
||||||
|
|
140
common/arch.go
140
common/arch.go
|
@ -450,8 +450,12 @@ func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callback := func(srcPropertyName, dstPropertyName string) {
|
||||||
|
a.extendedProperties[dstPropertyName] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
for i := range a.generalProperties {
|
for i := range a.generalProperties {
|
||||||
generalPropsValue := reflect.ValueOf(a.generalProperties[i]).Elem()
|
generalPropsValue := []reflect.Value{reflect.ValueOf(a.generalProperties[i]).Elem()}
|
||||||
|
|
||||||
// Handle arch-specific properties in the form:
|
// Handle arch-specific properties in the form:
|
||||||
// arch: {
|
// arch: {
|
||||||
|
@ -461,8 +465,8 @@ func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext)
|
||||||
// },
|
// },
|
||||||
t := arch.ArchType
|
t := arch.ArchType
|
||||||
field := proptools.FieldNameForProperty(t.Name)
|
field := proptools.FieldNameForProperty(t.Name)
|
||||||
a.extendProperties(ctx, "arch", t.Name, generalPropsValue,
|
extendProperties(ctx, "arch_variant", "arch."+t.Name, generalPropsValue,
|
||||||
reflect.ValueOf(a.archProperties[i].Arch).FieldByName(field).Elem().Elem())
|
reflect.ValueOf(a.archProperties[i].Arch).FieldByName(field).Elem().Elem(), callback)
|
||||||
|
|
||||||
// Handle arch-variant-specific properties in the form:
|
// Handle arch-variant-specific properties in the form:
|
||||||
// arch: {
|
// arch: {
|
||||||
|
@ -473,8 +477,8 @@ func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext)
|
||||||
v := dashToUnderscoreReplacer.Replace(arch.ArchVariant)
|
v := dashToUnderscoreReplacer.Replace(arch.ArchVariant)
|
||||||
if v != "" {
|
if v != "" {
|
||||||
field := proptools.FieldNameForProperty(v)
|
field := proptools.FieldNameForProperty(v)
|
||||||
a.extendProperties(ctx, "arch", v, generalPropsValue,
|
extendProperties(ctx, "arch_variant", "arch."+v, generalPropsValue,
|
||||||
reflect.ValueOf(a.archProperties[i].Arch).FieldByName(field).Elem().Elem())
|
reflect.ValueOf(a.archProperties[i].Arch).FieldByName(field).Elem().Elem(), callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle cpu-variant-specific properties in the form:
|
// Handle cpu-variant-specific properties in the form:
|
||||||
|
@ -486,8 +490,8 @@ func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext)
|
||||||
c := dashToUnderscoreReplacer.Replace(arch.CpuVariant)
|
c := dashToUnderscoreReplacer.Replace(arch.CpuVariant)
|
||||||
if c != "" {
|
if c != "" {
|
||||||
field := proptools.FieldNameForProperty(c)
|
field := proptools.FieldNameForProperty(c)
|
||||||
a.extendProperties(ctx, "arch", c, generalPropsValue,
|
extendProperties(ctx, "arch_variant", "arch."+c, generalPropsValue,
|
||||||
reflect.ValueOf(a.archProperties[i].Arch).FieldByName(field).Elem().Elem())
|
reflect.ValueOf(a.archProperties[i].Arch).FieldByName(field).Elem().Elem(), callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle multilib-specific properties in the form:
|
// Handle multilib-specific properties in the form:
|
||||||
|
@ -497,8 +501,8 @@ func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext)
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
multilibField := proptools.FieldNameForProperty(t.Multilib)
|
multilibField := proptools.FieldNameForProperty(t.Multilib)
|
||||||
a.extendProperties(ctx, "multilib", t.Multilib, generalPropsValue,
|
extendProperties(ctx, "arch_variant", "multilib."+t.Multilib, generalPropsValue,
|
||||||
reflect.ValueOf(a.archProperties[i].Multilib).FieldByName(multilibField).Elem().Elem())
|
reflect.ValueOf(a.archProperties[i].Multilib).FieldByName(multilibField).Elem().Elem(), callback)
|
||||||
|
|
||||||
// Handle host-or-device-specific properties in the form:
|
// Handle host-or-device-specific properties in the form:
|
||||||
// target: {
|
// target: {
|
||||||
|
@ -508,8 +512,8 @@ func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext)
|
||||||
// },
|
// },
|
||||||
hodProperty := hod.Property()
|
hodProperty := hod.Property()
|
||||||
hodField := proptools.FieldNameForProperty(hodProperty)
|
hodField := proptools.FieldNameForProperty(hodProperty)
|
||||||
a.extendProperties(ctx, "target", hodProperty, generalPropsValue,
|
extendProperties(ctx, "arch_variant", "target."+hodProperty, generalPropsValue,
|
||||||
reflect.ValueOf(a.archProperties[i].Target).FieldByName(hodField).Elem().Elem())
|
reflect.ValueOf(a.archProperties[i].Target).FieldByName(hodField).Elem().Elem(), callback)
|
||||||
|
|
||||||
// Handle host target properties in the form:
|
// Handle host target properties in the form:
|
||||||
// target: {
|
// target: {
|
||||||
|
@ -538,15 +542,15 @@ func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext)
|
||||||
if hod.Host() {
|
if hod.Host() {
|
||||||
for _, v := range osList {
|
for _, v := range osList {
|
||||||
if v.goos == runtime.GOOS {
|
if v.goos == runtime.GOOS {
|
||||||
a.extendProperties(ctx, "target", v.goos, generalPropsValue,
|
extendProperties(ctx, "arch_variant", "target."+v.goos, generalPropsValue,
|
||||||
reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field).Elem().Elem())
|
reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field).Elem().Elem(), callback)
|
||||||
t := arch.ArchType
|
t := arch.ArchType
|
||||||
a.extendProperties(ctx, "target", v.goos+"_"+t.Name, generalPropsValue,
|
extendProperties(ctx, "arch_variant", "target."+v.goos+"_"+t.Name, generalPropsValue,
|
||||||
reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field+"_"+t.Name).Elem().Elem())
|
reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field+"_"+t.Name).Elem().Elem(), callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
a.extendProperties(ctx, "target", "not_windows", generalPropsValue,
|
extendProperties(ctx, "arch_variant", "target.not_windows", generalPropsValue,
|
||||||
reflect.ValueOf(a.archProperties[i].Target).FieldByName("Not_windows").Elem().Elem())
|
reflect.ValueOf(a.archProperties[i].Target).FieldByName("Not_windows").Elem().Elem(), callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle 64-bit device properties in the form:
|
// Handle 64-bit device properties in the form:
|
||||||
|
@ -564,11 +568,11 @@ func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext)
|
||||||
// debuggerd that need to know when they are a 32-bit process running on a 64-bit device
|
// debuggerd that need to know when they are a 32-bit process running on a 64-bit device
|
||||||
if hod.Device() {
|
if hod.Device() {
|
||||||
if true /* && target_is_64_bit */ {
|
if true /* && target_is_64_bit */ {
|
||||||
a.extendProperties(ctx, "target", "android64", generalPropsValue,
|
extendProperties(ctx, "arch_variant", "target.android64", generalPropsValue,
|
||||||
reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android64").Elem().Elem())
|
reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android64").Elem().Elem(), callback)
|
||||||
} else {
|
} else {
|
||||||
a.extendProperties(ctx, "target", "android32", generalPropsValue,
|
extendProperties(ctx, "arch_variant", "target.android32", generalPropsValue,
|
||||||
reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android32").Elem().Elem())
|
reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android32").Elem().Elem(), callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -583,8 +587,8 @@ func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext)
|
||||||
// },
|
// },
|
||||||
if hod.Device() {
|
if hod.Device() {
|
||||||
t := arch.ArchType
|
t := arch.ArchType
|
||||||
a.extendProperties(ctx, "target", "android_"+t.Name, generalPropsValue,
|
extendProperties(ctx, "arch_variant", "target.android_"+t.Name, generalPropsValue,
|
||||||
reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android_"+t.Name).Elem().Elem())
|
reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android_"+t.Name).Elem().Elem(), callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Failed() {
|
if ctx.Failed() {
|
||||||
|
@ -607,93 +611,3 @@ func forEachInterface(v reflect.Value, f func(reflect.Value)) {
|
||||||
panic(fmt.Errorf("Unsupported kind %s", v.Kind()))
|
panic(fmt.Errorf("Unsupported kind %s", v.Kind()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move this to proptools
|
|
||||||
func (a *AndroidModuleBase) extendProperties(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
|
|
||||||
dstValue, srcValue reflect.Value) {
|
|
||||||
a.extendPropertiesRecursive(ctx, variationType, variationName, dstValue, srcValue, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AndroidModuleBase) extendPropertiesRecursive(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
|
|
||||||
dstValue, srcValue reflect.Value, recursePrefix string) {
|
|
||||||
|
|
||||||
typ := dstValue.Type()
|
|
||||||
if srcValue.Type() != typ {
|
|
||||||
panic(fmt.Errorf("can't extend mismatching types (%s <- %s)",
|
|
||||||
dstValue.Kind(), srcValue.Kind()))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < srcValue.NumField(); i++ {
|
|
||||||
field := typ.Field(i)
|
|
||||||
if field.PkgPath != "" {
|
|
||||||
// The field is not exported so just skip it.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
srcFieldValue := srcValue.Field(i)
|
|
||||||
dstFieldValue := dstValue.Field(i)
|
|
||||||
|
|
||||||
localPropertyName := proptools.PropertyNameForField(field.Name)
|
|
||||||
propertyName := fmt.Sprintf("%s.%s.%s%s", variationType, variationName,
|
|
||||||
recursePrefix, localPropertyName)
|
|
||||||
propertyPresentInVariation := ctx.ContainsProperty(propertyName)
|
|
||||||
|
|
||||||
if !propertyPresentInVariation {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
tag := field.Tag.Get("android")
|
|
||||||
tags := map[string]bool{}
|
|
||||||
for _, entry := range strings.Split(tag, ",") {
|
|
||||||
if entry != "" {
|
|
||||||
tags[entry] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tags["arch_variant"] {
|
|
||||||
ctx.PropertyErrorf(propertyName, "property %q can't be specific to a build variant",
|
|
||||||
recursePrefix+proptools.PropertyNameForField(field.Name))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ctx.ContainsProperty(propertyName) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
a.extendedProperties[localPropertyName] = struct{}{}
|
|
||||||
|
|
||||||
switch srcFieldValue.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
// Replace the original value.
|
|
||||||
dstFieldValue.Set(srcFieldValue)
|
|
||||||
case reflect.String:
|
|
||||||
// Append the extension string.
|
|
||||||
dstFieldValue.SetString(dstFieldValue.String() +
|
|
||||||
srcFieldValue.String())
|
|
||||||
case reflect.Struct:
|
|
||||||
// Recursively extend the struct's fields.
|
|
||||||
newRecursePrefix := fmt.Sprintf("%s%s.", recursePrefix, strings.ToLower(field.Name))
|
|
||||||
a.extendPropertiesRecursive(ctx, variationType, variationName,
|
|
||||||
dstFieldValue, srcFieldValue,
|
|
||||||
newRecursePrefix)
|
|
||||||
case reflect.Slice:
|
|
||||||
dstFieldValue.Set(reflect.AppendSlice(dstFieldValue, srcFieldValue))
|
|
||||||
case reflect.Ptr, reflect.Interface:
|
|
||||||
// Recursively extend the pointed-to struct's fields.
|
|
||||||
if dstFieldValue.IsNil() != srcFieldValue.IsNil() {
|
|
||||||
panic(fmt.Errorf("can't extend field %q: nilitude mismatch"))
|
|
||||||
}
|
|
||||||
if dstFieldValue.Type() != srcFieldValue.Type() {
|
|
||||||
panic(fmt.Errorf("can't extend field %q: type mismatch"))
|
|
||||||
}
|
|
||||||
if !dstFieldValue.IsNil() {
|
|
||||||
newRecursePrefix := fmt.Sprintf("%s.%s", recursePrefix, field.Name)
|
|
||||||
a.extendPropertiesRecursive(ctx, variationType, variationName,
|
|
||||||
dstFieldValue.Elem(), srcFieldValue.Elem(),
|
|
||||||
newRecursePrefix)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
|
|
||||||
field.Name, srcFieldValue.Kind()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
152
common/extend.go
Normal file
152
common/extend.go
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
// Copyright 2015 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 common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/blueprint"
|
||||||
|
"github.com/google/blueprint/proptools"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: move this to proptools
|
||||||
|
func extendProperties(ctx blueprint.EarlyMutatorContext,
|
||||||
|
requiredTag, srcPrefix string, dstValues []reflect.Value, srcValue reflect.Value,
|
||||||
|
callback func(string, string)) {
|
||||||
|
if srcPrefix != "" {
|
||||||
|
srcPrefix += "."
|
||||||
|
}
|
||||||
|
extendPropertiesRecursive(ctx, requiredTag, srcValue, dstValues, srcPrefix, "", callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extendPropertiesRecursive(ctx blueprint.EarlyMutatorContext, requiredTag string,
|
||||||
|
srcValue reflect.Value, dstValues []reflect.Value, srcPrefix, dstPrefix string,
|
||||||
|
callback func(string, string)) {
|
||||||
|
|
||||||
|
typ := srcValue.Type()
|
||||||
|
for i := 0; i < srcValue.NumField(); i++ {
|
||||||
|
srcField := typ.Field(i)
|
||||||
|
if srcField.PkgPath != "" {
|
||||||
|
// The field is not exported so just skip it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
localPropertyName := proptools.PropertyNameForField(srcField.Name)
|
||||||
|
srcPropertyName := srcPrefix + localPropertyName
|
||||||
|
srcFieldValue := srcValue.Field(i)
|
||||||
|
|
||||||
|
if !ctx.ContainsProperty(srcPropertyName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, dstValue := range dstValues {
|
||||||
|
dstField, ok := dstValue.Type().FieldByName(srcField.Name)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dstFieldValue := dstValue.FieldByIndex(dstField.Index)
|
||||||
|
|
||||||
|
if srcFieldValue.Type() != dstFieldValue.Type() {
|
||||||
|
panic(fmt.Errorf("can't extend mismatching types for %q (%s <- %s)",
|
||||||
|
srcPropertyName, dstFieldValue.Type(), srcFieldValue.Type()))
|
||||||
|
}
|
||||||
|
|
||||||
|
dstPropertyName := dstPrefix + localPropertyName
|
||||||
|
|
||||||
|
if requiredTag != "" {
|
||||||
|
tag := dstField.Tag.Get("android")
|
||||||
|
tags := map[string]bool{}
|
||||||
|
for _, entry := range strings.Split(tag, ",") {
|
||||||
|
if entry != "" {
|
||||||
|
tags[entry] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tags[requiredTag] {
|
||||||
|
ctx.PropertyErrorf(srcPropertyName, "property can't be specific to a build variant")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if callback != nil {
|
||||||
|
callback(srcPropertyName, dstPropertyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
found = true
|
||||||
|
|
||||||
|
switch srcFieldValue.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
// Replace the original value.
|
||||||
|
dstFieldValue.Set(srcFieldValue)
|
||||||
|
case reflect.String:
|
||||||
|
// Append the extension string.
|
||||||
|
dstFieldValue.SetString(dstFieldValue.String() +
|
||||||
|
srcFieldValue.String())
|
||||||
|
case reflect.Slice:
|
||||||
|
dstFieldValue.Set(reflect.AppendSlice(dstFieldValue, srcFieldValue))
|
||||||
|
case reflect.Interface:
|
||||||
|
if dstFieldValue.IsNil() != srcFieldValue.IsNil() {
|
||||||
|
panic(fmt.Errorf("can't extend field %q: nilitude mismatch", srcPropertyName))
|
||||||
|
}
|
||||||
|
if dstFieldValue.IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dstFieldValue = dstFieldValue.Elem()
|
||||||
|
srcFieldValue = srcFieldValue.Elem()
|
||||||
|
|
||||||
|
if dstFieldValue.Type() != srcFieldValue.Type() {
|
||||||
|
panic(fmt.Errorf("can't extend field %q: type mismatch", srcPropertyName))
|
||||||
|
}
|
||||||
|
if srcFieldValue.Kind() != reflect.Ptr {
|
||||||
|
panic(fmt.Errorf("can't extend field %q: interface not a pointer", srcPropertyName))
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case reflect.Ptr:
|
||||||
|
if dstFieldValue.IsNil() != srcFieldValue.IsNil() {
|
||||||
|
panic(fmt.Errorf("can't extend field %q: nilitude mismatch", srcPropertyName))
|
||||||
|
}
|
||||||
|
if dstFieldValue.IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dstFieldValue = dstFieldValue.Elem()
|
||||||
|
srcFieldValue = srcFieldValue.Elem()
|
||||||
|
|
||||||
|
if dstFieldValue.Type() != srcFieldValue.Type() {
|
||||||
|
panic(fmt.Errorf("can't extend field %q: type mismatch", srcPropertyName))
|
||||||
|
}
|
||||||
|
if srcFieldValue.Kind() != reflect.Struct {
|
||||||
|
panic(fmt.Errorf("can't extend field %q: pointer not to a struct", srcPropertyName))
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case reflect.Struct:
|
||||||
|
// Recursively extend the struct's fields.
|
||||||
|
extendPropertiesRecursive(ctx, requiredTag, srcFieldValue, []reflect.Value{dstFieldValue},
|
||||||
|
srcPropertyName+".", srcPropertyName+".", callback)
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
|
||||||
|
srcPropertyName, srcFieldValue.Kind()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
ctx.PropertyErrorf(srcPropertyName, "failed to find property to extend")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -125,7 +125,7 @@ func InitAndroidModule(m AndroidModule,
|
||||||
base.module = m
|
base.module = m
|
||||||
base.extendedProperties = make(map[string]struct{})
|
base.extendedProperties = make(map[string]struct{})
|
||||||
|
|
||||||
propertyStructs = append(propertyStructs, &base.commonProperties)
|
propertyStructs = append(propertyStructs, &base.commonProperties, &base.variableProperties)
|
||||||
|
|
||||||
return m, propertyStructs
|
return m, propertyStructs
|
||||||
}
|
}
|
||||||
|
@ -194,6 +194,7 @@ type AndroidModuleBase struct {
|
||||||
module AndroidModule
|
module AndroidModule
|
||||||
|
|
||||||
commonProperties commonProperties
|
commonProperties commonProperties
|
||||||
|
variableProperties variableProperties
|
||||||
hostAndDeviceProperties hostAndDeviceProperties
|
hostAndDeviceProperties hostAndDeviceProperties
|
||||||
generalProperties []interface{}
|
generalProperties []interface{}
|
||||||
archProperties []*archProperties
|
archProperties []*archProperties
|
||||||
|
|
141
common/variable.go
Normal file
141
common/variable.go
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
// Copyright 2015 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 common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"android/soong"
|
||||||
|
|
||||||
|
"github.com/google/blueprint"
|
||||||
|
"github.com/google/blueprint/proptools"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
soong.RegisterEarlyMutator("variable", VariableMutator)
|
||||||
|
}
|
||||||
|
|
||||||
|
type variableProperties struct {
|
||||||
|
Product_variables struct {
|
||||||
|
Device_uses_logd struct {
|
||||||
|
Cflags []string
|
||||||
|
Srcs []string
|
||||||
|
}
|
||||||
|
Device_uses_dlmalloc struct {
|
||||||
|
Cflags []string
|
||||||
|
Srcs []string
|
||||||
|
}
|
||||||
|
Device_uses_jemalloc struct {
|
||||||
|
Cflags []string
|
||||||
|
Srcs []string
|
||||||
|
Whole_static_libs []string
|
||||||
|
Include_dirs []string
|
||||||
|
}
|
||||||
|
Dlmalloc_alignment struct {
|
||||||
|
Cflags []string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var zeroProductVariables variableProperties
|
||||||
|
|
||||||
|
// TODO: replace hardcoded test values with per-product values
|
||||||
|
var productVariables = map[string]interface{}{
|
||||||
|
"device_uses_logd": true,
|
||||||
|
"device_uses_jemalloc": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
func VariableMutator(mctx blueprint.EarlyMutatorContext) {
|
||||||
|
var module AndroidModule
|
||||||
|
var ok bool
|
||||||
|
if module, ok = mctx.Module().(AndroidModule); !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: depend on config variable, create variants, propagate variants up tree
|
||||||
|
a := module.base()
|
||||||
|
variableValues := reflect.ValueOf(a.variableProperties.Product_variables)
|
||||||
|
zeroValues := reflect.ValueOf(zeroProductVariables.Product_variables)
|
||||||
|
|
||||||
|
for i := 0; i < variableValues.NumField(); i++ {
|
||||||
|
variableValue := variableValues.Field(i)
|
||||||
|
zeroValue := zeroValues.Field(i)
|
||||||
|
if reflect.DeepEqual(variableValue, zeroValue) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
name := proptools.PropertyNameForField(variableValues.Type().Field(i).Name)
|
||||||
|
property := "product_variables." + name
|
||||||
|
val := productVariables[name]
|
||||||
|
|
||||||
|
if mctx.ContainsProperty(property) && val != nil {
|
||||||
|
a.setVariableProperties(mctx, property, variableValue, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AndroidModuleBase) setVariableProperties(ctx blueprint.EarlyMutatorContext,
|
||||||
|
prefix string, productVariablePropertyValue reflect.Value, variableValue interface{}) {
|
||||||
|
|
||||||
|
generalPropertyValues := make([]reflect.Value, len(a.generalProperties))
|
||||||
|
for i := range a.generalProperties {
|
||||||
|
generalPropertyValues[i] = reflect.ValueOf(a.generalProperties[i]).Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if variableValue != nil {
|
||||||
|
printfIntoProperties(productVariablePropertyValue, variableValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
extendProperties(ctx, "", prefix, generalPropertyValues, productVariablePropertyValue, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printfIntoProperties(productVariablePropertyValue reflect.Value, variableValue interface{}) {
|
||||||
|
for i := 0; i < productVariablePropertyValue.NumField(); i++ {
|
||||||
|
propertyValue := productVariablePropertyValue.Field(i)
|
||||||
|
switch propertyValue.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
printfIntoProperty(propertyValue, variableValue)
|
||||||
|
case reflect.Slice:
|
||||||
|
for j := 0; j < propertyValue.Len(); j++ {
|
||||||
|
printfIntoProperty(propertyValue.Index(j), variableValue)
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
printfIntoProperties(propertyValue, variableValue)
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unsupported field kind %q", propertyValue.Kind()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printfIntoProperty(propertyValue reflect.Value, variableValue interface{}) {
|
||||||
|
s := propertyValue.String()
|
||||||
|
// For now, we only support int formats
|
||||||
|
var i int
|
||||||
|
if strings.Contains(s, "%d") {
|
||||||
|
switch v := variableValue.(type) {
|
||||||
|
case int:
|
||||||
|
i = v
|
||||||
|
case bool:
|
||||||
|
if v {
|
||||||
|
i = 1
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unsupported type %T", variableValue))
|
||||||
|
}
|
||||||
|
propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, i)))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue