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
This commit is contained in:
Colin Cross
2016-08-17 15:24:12 -07:00
parent 389d2bb145
commit 9272ade7a8
5 changed files with 123 additions and 12 deletions

View File

@@ -100,6 +100,7 @@ bootstrap_go_package {
"android/makevars.go", "android/makevars.go",
"android/module.go", "android/module.go",
"android/mutator.go", "android/mutator.go",
"android/onceper.go",
"android/package_ctx.go", "android/package_ctx.go",
"android/paths.go", "android/paths.go",
"android/util.go", "android/util.go",

View File

@@ -717,7 +717,7 @@ func forEachInterface(v reflect.Value, f func(reflect.Value)) {
} }
// Convert the arch product variables into a list of targets for each os class structs // 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 variables := config.ProductVariables
targets := make(map[OsClass][]Target) targets := make(map[OsClass][]Target)

View File

@@ -43,11 +43,18 @@ func (f *FileConfigurableOptions) SetDefaultConfig() {
*f = FileConfigurableOptions{} *f = FileConfigurableOptions{}
} }
// A Config object represents the entire build configuration for Android.
type Config struct { type Config struct {
*config *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 { type config struct {
FileConfigurableOptions FileConfigurableOptions
ProductVariables productVariables ProductVariables productVariables
@@ -58,6 +65,8 @@ type config struct {
Targets map[OsClass][]Target Targets map[OsClass][]Target
BuildOsVariant string BuildOsVariant string
deviceConfig *deviceConfig
srcDir string // the path of the root source directory srcDir string // the path of the root source directory
buildDir string // the path of the build output directory buildDir string // the path of the build output directory
@@ -66,6 +75,13 @@ type config struct {
envFrozen bool envFrozen bool
inMake bool inMake bool
OncePer
}
type deviceConfig struct {
config *config
targets []Arch
OncePer
} }
type jsonConfigurable interface { type jsonConfigurable interface {
@@ -138,17 +154,23 @@ func saveToConfigFile(config jsonConfigurable, filename string) error {
// the root source directory. It also loads the config file, if found. // the root source directory. It also loads the config file, if found.
func NewConfig(srcDir, buildDir string) (Config, error) { func NewConfig(srcDir, buildDir string) (Config, error) {
// Make a config with default options // Make a config with default options
config := Config{ config := &config{
config: &config{ ConfigFileName: filepath.Join(buildDir, configFileName),
ConfigFileName: filepath.Join(buildDir, configFileName), ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
srcDir: srcDir, srcDir: srcDir,
buildDir: buildDir, buildDir: buildDir,
envDeps: make(map[string]string), 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 // Sanity check the build and source directories. This won't catch strange
// configurations with symlinks, but at least checks the obvious cases. // configurations with symlinks, but at least checks the obvious cases.
absBuildDir, err := filepath.Abs(buildDir) absBuildDir, err := filepath.Abs(buildDir)
@@ -166,7 +188,7 @@ func NewConfig(srcDir, buildDir string) (Config, error) {
} }
// Load any configurable options from the configuration file // Load any configurable options from the configuration file
err = loadConfig(config.config) err = loadConfig(config)
if err != nil { if err != nil {
return Config{}, err return Config{}, err
} }
@@ -192,7 +214,7 @@ func NewConfig(srcDir, buildDir string) (Config, error) {
config.Targets = targets config.Targets = targets
config.BuildOsVariant = targets[Host][0].String() config.BuildOsVariant = targets[Host][0].String()
return config, nil return Config{config}, nil
} }
func (c *config) RemoveAbandonedFiles() bool { func (c *config) RemoveAbandonedFiles() bool {
@@ -337,3 +359,11 @@ func (c *config) Android64() bool {
return false return false
} }
func (c *deviceConfig) Arches() []Arch {
var arches []Arch
for _, target := range c.config.Targets[Device] {
arches = append(arches, target.Arch)
}
return arches
}

View File

@@ -56,6 +56,7 @@ type androidBaseContext interface {
Darwin() bool Darwin() bool
Debug() bool Debug() bool
AConfig() Config AConfig() Config
DeviceConfig() DeviceConfig
} }
type BaseContext interface { type BaseContext interface {
@@ -535,6 +536,10 @@ func (a *androidBaseContextImpl) AConfig() Config {
return a.config return a.config
} }
func (a *androidBaseContextImpl) DeviceConfig() DeviceConfig {
return DeviceConfig{a.config.deviceConfig}
}
func (a *androidModuleContext) Proprietary() bool { func (a *androidModuleContext) Proprietary() bool {
return a.module.base().commonProperties.Proprietary return a.module.base().commonProperties.Proprietary
} }

75
android/onceper.go Normal file
View File

@@ -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]
}