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
diff --git a/common/variable.go b/common/variable.go
new file mode 100644
index 0000000..19ec625
--- /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)))
+	}
+}