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/androidbp/cmd/androidbp.go b/androidbp/cmd/androidbp.go
index b1e364c..3edd243 100644
--- a/androidbp/cmd/androidbp.go
+++ b/androidbp/cmd/androidbp.go
@@ -175,6 +175,44 @@
 	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 @@
 				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 1dffe4a..e9d421a 100644
--- a/androidbp/cmd/soong.go
+++ b/androidbp/cmd/soong.go
@@ -172,3 +172,10 @@
 	"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)"},
+}