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/arch.go b/android/arch.go
index 2d4ad2e..2d38daf 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -717,7 +717,7 @@
}
// Convert the arch product variables into a list of targets for each os class structs
-func decodeTargetProductVariables(config Config) (map[OsClass][]Target, error) {
+func decodeTargetProductVariables(config *config) (map[OsClass][]Target, error) {
variables := config.ProductVariables
targets := make(map[OsClass][]Target)
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
+}
diff --git a/android/module.go b/android/module.go
index bf9dfaa..03c06b4 100644
--- a/android/module.go
+++ b/android/module.go
@@ -56,6 +56,7 @@
Darwin() bool
Debug() bool
AConfig() Config
+ DeviceConfig() DeviceConfig
}
type BaseContext interface {
@@ -535,6 +536,10 @@
return a.config
}
+func (a *androidBaseContextImpl) DeviceConfig() DeviceConfig {
+ return DeviceConfig{a.config.deviceConfig}
+}
+
func (a *androidModuleContext) Proprietary() bool {
return a.module.base().commonProperties.Proprietary
}
diff --git a/android/onceper.go b/android/onceper.go
new file mode 100644
index 0000000..5f7a310
--- /dev/null
+++ b/android/onceper.go
@@ -0,0 +1,75 @@
+// Copyright 2016 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 android
+
+import (
+ "sync"
+ "sync/atomic"
+)
+
+type OncePer struct {
+ values atomic.Value
+ valuesLock sync.Mutex
+}
+
+type valueMap map[interface{}]interface{}
+
+// Once computes a value the first time it is called with a given key per OncePer, and returns the
+// value without recomputing when called with the same key. key must be hashable.
+func (once *OncePer) Once(key interface{}, value func() interface{}) interface{} {
+ // Atomically load the map without locking. If this is the first call Load() will return nil
+ // and the type assertion will fail, leaving a nil map in m, but that's OK since m is only used
+ // for reads.
+ m, _ := once.values.Load().(valueMap)
+ if v, ok := m[key]; ok {
+ return v
+ }
+
+ once.valuesLock.Lock()
+ defer once.valuesLock.Unlock()
+
+ // Check again with the lock held
+ m, _ = once.values.Load().(valueMap)
+ if v, ok := m[key]; ok {
+ return v
+ }
+
+ // Copy the existing map
+ newMap := make(valueMap, len(m))
+ for k, v := range m {
+ newMap[k] = v
+ }
+
+ v := value()
+
+ newMap[key] = v
+ once.values.Store(newMap)
+
+ return v
+}
+
+func (once *OncePer) OnceStringSlice(key interface{}, value func() []string) []string {
+ return once.Once(key, func() interface{} { return value() }).([]string)
+}
+
+func (once *OncePer) Once2StringSlice(key interface{}, value func() ([]string, []string)) ([]string, []string) {
+ type twoStringSlice [2][]string
+ s := once.Once(key, func() interface{} {
+ var s twoStringSlice
+ s[0], s[1] = value()
+ return s
+ }).(twoStringSlice)
+ return s[0], s[1]
+}