diff options
| author | Colin Cross <ccross@android.com> | 2015-07-09 13:57:48 -0700 |
|---|---|---|
| committer | Colin Cross <ccross@android.com> | 2015-08-24 16:20:08 -0700 |
| commit | 7f64b6de3156da72cb310c0d16083e5e36481e48 (patch) | |
| tree | ee9f23a377f80ff8f764bfcf32ca54e581f6782d /common | |
| parent | 8f301d583fb41374c3a3b9041e5189e2640ab2ee (diff) | |
| download | build_soong-7f64b6de3156da72cb310c0d16083e5e36481e48.tar.gz build_soong-7f64b6de3156da72cb310c0d16083e5e36481e48.tar.bz2 build_soong-7f64b6de3156da72cb310c0d16083e5e36481e48.zip | |
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
Diffstat (limited to 'common')
| -rw-r--r-- | common/arch.go | 140 | ||||
| -rw-r--r-- | common/extend.go | 152 | ||||
| -rw-r--r-- | common/module.go | 3 | ||||
| -rw-r--r-- | common/variable.go | 141 |
4 files changed, 322 insertions, 114 deletions
diff --git a/common/arch.go b/common/arch.go index 3d685e5b..b9e3a5c3 100644 --- a/common/arch.go +++ b/common/arch.go @@ -450,8 +450,12 @@ func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext) return } + callback := func(srcPropertyName, dstPropertyName string) { + a.extendedProperties[dstPropertyName] = struct{}{} + } + 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: // arch: { @@ -461,8 +465,8 @@ func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext) // }, t := arch.ArchType field := proptools.FieldNameForProperty(t.Name) - a.extendProperties(ctx, "arch", t.Name, generalPropsValue, - reflect.ValueOf(a.archProperties[i].Arch).FieldByName(field).Elem().Elem()) + extendProperties(ctx, "arch_variant", "arch."+t.Name, generalPropsValue, + reflect.ValueOf(a.archProperties[i].Arch).FieldByName(field).Elem().Elem(), callback) // Handle arch-variant-specific properties in the form: // arch: { @@ -473,8 +477,8 @@ func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext) v := dashToUnderscoreReplacer.Replace(arch.ArchVariant) if v != "" { field := proptools.FieldNameForProperty(v) - a.extendProperties(ctx, "arch", v, generalPropsValue, - reflect.ValueOf(a.archProperties[i].Arch).FieldByName(field).Elem().Elem()) + extendProperties(ctx, "arch_variant", "arch."+v, generalPropsValue, + reflect.ValueOf(a.archProperties[i].Arch).FieldByName(field).Elem().Elem(), callback) } // Handle cpu-variant-specific properties in the form: @@ -486,8 +490,8 @@ func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext) c := dashToUnderscoreReplacer.Replace(arch.CpuVariant) if c != "" { field := proptools.FieldNameForProperty(c) - a.extendProperties(ctx, "arch", c, generalPropsValue, - reflect.ValueOf(a.archProperties[i].Arch).FieldByName(field).Elem().Elem()) + extendProperties(ctx, "arch_variant", "arch."+c, generalPropsValue, + reflect.ValueOf(a.archProperties[i].Arch).FieldByName(field).Elem().Elem(), callback) } // Handle multilib-specific properties in the form: @@ -497,8 +501,8 @@ func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext) // }, // }, multilibField := proptools.FieldNameForProperty(t.Multilib) - a.extendProperties(ctx, "multilib", t.Multilib, generalPropsValue, - reflect.ValueOf(a.archProperties[i].Multilib).FieldByName(multilibField).Elem().Elem()) + extendProperties(ctx, "arch_variant", "multilib."+t.Multilib, generalPropsValue, + reflect.ValueOf(a.archProperties[i].Multilib).FieldByName(multilibField).Elem().Elem(), callback) // Handle host-or-device-specific properties in the form: // target: { @@ -508,8 +512,8 @@ func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext) // }, hodProperty := hod.Property() hodField := proptools.FieldNameForProperty(hodProperty) - a.extendProperties(ctx, "target", hodProperty, generalPropsValue, - reflect.ValueOf(a.archProperties[i].Target).FieldByName(hodField).Elem().Elem()) + extendProperties(ctx, "arch_variant", "target."+hodProperty, generalPropsValue, + reflect.ValueOf(a.archProperties[i].Target).FieldByName(hodField).Elem().Elem(), callback) // Handle host target properties in the form: // target: { @@ -538,15 +542,15 @@ func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext) if hod.Host() { for _, v := range osList { if v.goos == runtime.GOOS { - a.extendProperties(ctx, "target", v.goos, generalPropsValue, - reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field).Elem().Elem()) + extendProperties(ctx, "arch_variant", "target."+v.goos, generalPropsValue, + reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field).Elem().Elem(), callback) t := arch.ArchType - a.extendProperties(ctx, "target", v.goos+"_"+t.Name, generalPropsValue, - reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field+"_"+t.Name).Elem().Elem()) + extendProperties(ctx, "arch_variant", "target."+v.goos+"_"+t.Name, generalPropsValue, + reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field+"_"+t.Name).Elem().Elem(), callback) } } - a.extendProperties(ctx, "target", "not_windows", generalPropsValue, - reflect.ValueOf(a.archProperties[i].Target).FieldByName("Not_windows").Elem().Elem()) + extendProperties(ctx, "arch_variant", "target.not_windows", generalPropsValue, + reflect.ValueOf(a.archProperties[i].Target).FieldByName("Not_windows").Elem().Elem(), callback) } // 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 if hod.Device() { if true /* && target_is_64_bit */ { - a.extendProperties(ctx, "target", "android64", generalPropsValue, - reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android64").Elem().Elem()) + extendProperties(ctx, "arch_variant", "target.android64", generalPropsValue, + reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android64").Elem().Elem(), callback) } else { - a.extendProperties(ctx, "target", "android32", generalPropsValue, - reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android32").Elem().Elem()) + extendProperties(ctx, "arch_variant", "target.android32", generalPropsValue, + 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() { t := arch.ArchType - a.extendProperties(ctx, "target", "android_"+t.Name, generalPropsValue, - reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android_"+t.Name).Elem().Elem()) + extendProperties(ctx, "arch_variant", "target.android_"+t.Name, generalPropsValue, + reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android_"+t.Name).Elem().Elem(), callback) } if ctx.Failed() { @@ -607,93 +611,3 @@ func forEachInterface(v reflect.Value, f func(reflect.Value)) { 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())) - } - } -} diff --git a/common/extend.go b/common/extend.go new file mode 100644 index 00000000..f2d061bd --- /dev/null +++ b/common/extend.go @@ -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") + } + } +} diff --git a/common/module.go b/common/module.go index e8c4a87e..feaba83d 100644 --- a/common/module.go +++ b/common/module.go @@ -125,7 +125,7 @@ func InitAndroidModule(m AndroidModule, base.module = m base.extendedProperties = make(map[string]struct{}) - propertyStructs = append(propertyStructs, &base.commonProperties) + propertyStructs = append(propertyStructs, &base.commonProperties, &base.variableProperties) return m, propertyStructs } @@ -194,6 +194,7 @@ type AndroidModuleBase struct { module AndroidModule commonProperties commonProperties + variableProperties variableProperties hostAndDeviceProperties hostAndDeviceProperties generalProperties []interface{} archProperties []*archProperties diff --git a/common/variable.go b/common/variable.go new file mode 100644 index 00000000..19ec6251 --- /dev/null +++ b/common/variable.go @@ -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))) + } +} |
