Add DeviceConfig and OncePer objects

Add DeviceConfig to store per-device configuration information.  Put a
OncePer object inside Config and DeviceConfig, which computes a value
once per key per object to allow build logic to store arbitrary
per-build or per-device computed values.

Change-Id: I1a38b426f29d223ef5e803e0d4d9604500de2fd2
diff --git a/android/config.go b/android/config.go
index ae04756..196df55 100644
--- a/android/config.go
+++ b/android/config.go
@@ -43,11 +43,18 @@
 	*f = FileConfigurableOptions{}
 }
 
+// A Config object represents the entire build configuration for Android.
 type Config struct {
 	*config
 }
 
-// A config object represents the entire build configuration for Android.
+// A DeviceConfig object represents the configuration for a particular device being built.  For
+// now there will only be one of these, but in the future there may be multiple devices being
+// built
+type DeviceConfig struct {
+	*deviceConfig
+}
+
 type config struct {
 	FileConfigurableOptions
 	ProductVariables productVariables
@@ -58,6 +65,8 @@
 	Targets        map[OsClass][]Target
 	BuildOsVariant string
 
+	deviceConfig *deviceConfig
+
 	srcDir   string // the path of the root source directory
 	buildDir string // the path of the build output directory
 
@@ -66,6 +75,13 @@
 	envFrozen bool
 
 	inMake bool
+	OncePer
+}
+
+type deviceConfig struct {
+	config  *config
+	targets []Arch
+	OncePer
 }
 
 type jsonConfigurable interface {
@@ -138,17 +154,23 @@
 // the root source directory. It also loads the config file, if found.
 func NewConfig(srcDir, buildDir string) (Config, error) {
 	// Make a config with default options
-	config := Config{
-		config: &config{
-			ConfigFileName:           filepath.Join(buildDir, configFileName),
-			ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
+	config := &config{
+		ConfigFileName:           filepath.Join(buildDir, configFileName),
+		ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
 
-			srcDir:   srcDir,
-			buildDir: buildDir,
-			envDeps:  make(map[string]string),
-		},
+		srcDir:   srcDir,
+		buildDir: buildDir,
+		envDeps:  make(map[string]string),
+
+		deviceConfig: &deviceConfig{},
 	}
 
+	deviceConfig := &deviceConfig{
+		config: config,
+	}
+
+	config.deviceConfig = deviceConfig
+
 	// Sanity check the build and source directories. This won't catch strange
 	// configurations with symlinks, but at least checks the obvious cases.
 	absBuildDir, err := filepath.Abs(buildDir)
@@ -166,7 +188,7 @@
 	}
 
 	// Load any configurable options from the configuration file
-	err = loadConfig(config.config)
+	err = loadConfig(config)
 	if err != nil {
 		return Config{}, err
 	}
@@ -192,7 +214,7 @@
 	config.Targets = targets
 	config.BuildOsVariant = targets[Host][0].String()
 
-	return config, nil
+	return Config{config}, nil
 }
 
 func (c *config) RemoveAbandonedFiles() bool {
@@ -337,3 +359,11 @@
 
 	return false
 }
+
+func (c *deviceConfig) Arches() []Arch {
+	var arches []Arch
+	for _, target := range c.config.Targets[Device] {
+		arches = append(arches, target.Arch)
+	}
+	return arches
+}