diff --git a/Android.bp b/Android.bp index f001246f6..404c47876 100644 --- a/Android.bp +++ b/Android.bp @@ -97,10 +97,12 @@ bootstrap_go_package { "common/config.go", "common/defs.go", "common/env.go", + "common/extend.go", "common/glob.go", "common/module.go", "common/paths.go", "common/util.go", + "common/variable.go", ], } diff --git a/androidbp/cmd/androidbp.go b/androidbp/cmd/androidbp.go index b1e364c96..3edd24346 100644 --- a/androidbp/cmd/androidbp.go +++ b/androidbp/cmd/androidbp.go @@ -175,6 +175,44 @@ func translateTargetConditionals(props []*bpparser.Property, 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_") func translateSuffixProperties(suffixProps []*bpparser.Property, @@ -303,6 +341,12 @@ func (w *androidMkWriter) parsePropsAndWriteModule(module *Module) error { return err } 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 { return fmt.Errorf("Unsupported property %q", prop.Name.Name) diff --git a/androidbp/cmd/soong.go b/androidbp/cmd/soong.go index 1dffe4a65..e9d421acd 100644 --- a/androidbp/cmd/soong.go +++ b/androidbp/cmd/soong.go @@ -172,3 +172,10 @@ var targetToHostModuleRule = map[string]string{ "BUILD_NATIVE_TEST": "BUILD_HOST_NATIVE_TEST", "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)"}, +} diff --git a/common/arch.go b/common/arch.go index 3d685e5ba..b9e3a5c34 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 000000000..f2d061bde --- /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 e8c4a87e0..feaba83d4 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 000000000..19ec6251a --- /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))) + } +}