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/arch.go b/common/arch.go
index 3d685e5..b9e3a5c 100644
--- a/common/arch.go
+++ b/common/arch.go
@@ -450,8 +450,12 @@
 		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 @@
 		// },
 		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 @@
 		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 @@
 		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 @@
 		//     },
 		// },
 		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 @@
 		// },
 		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 @@
 		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 @@
 		// 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 @@
 		// },
 		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 @@
 		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()))
-		}
-	}
-}