Generic configuration for generic system modules.
Config object includes a copy of itself for the generic configuration.
The generic configuration replaces any product-specific configurations
with the generic information.
When a module context gets Config() object, it returns the generic
configuration if the module sets use_generic_config to true. Otherwise,
Config() returns the original config object as before.
By adding `generic:"<value>"` annotation to the product variable, the
variables will be initialized with the <value> for the generic configs.
If the <value> is "unset", the variable will be unset.
The generic modules can be included in the shared system image to be
installed in multiple targets.
Bug: 361816274
Test: m nothing --no-skip-soong-tests
Change-Id: I15e4ade17ad1a8969f8e0e91d994b60545dc412f
diff --git a/android/config.go b/android/config.go
index d47f0d4..e026782 100644
--- a/android/config.go
+++ b/android/config.go
@@ -22,6 +22,7 @@
"fmt"
"os"
"path/filepath"
+ "reflect"
"runtime"
"strconv"
"strings"
@@ -108,6 +109,10 @@
const testKeyDir = "build/make/target/product/security"
+func (c Config) genericConfig() Config {
+ return Config{c.config.genericConfig}
+}
+
// SoongOutDir returns the build output directory for the configuration.
func (c Config) SoongOutDir() string {
return c.soongOutDir
@@ -372,7 +377,7 @@
// regenerate build.ninja.
ninjaFileDepsSet sync.Map
- OncePer
+ *OncePer
// If buildFromSourceStub is true then the Java API stubs are
// built from the source Java files, not the signature text files.
@@ -382,6 +387,17 @@
// modules that aren't mixed-built for at least one variant will cause a build
// failure
ensureAllowlistIntegrity bool
+
+ // If isGeneric is true, this config is the generic config.
+ isGeneric bool
+
+ // InstallPath requires the device name.
+ // This is only for the installPath.
+ deviceNameToInstall *string
+
+ // Copy of this config struct but some product-specific variables are
+ // replaced with the generic configuration values.
+ genericConfig *config
}
type partialCompileFlags struct {
@@ -639,11 +655,9 @@
}
}
-// NewConfig creates a new Config object. The srcDir argument specifies the path
-// to the root source directory. It also loads the config file, if found.
-func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) {
+func initConfig(cmdArgs CmdArgs, availableEnv map[string]string) (*config, error) {
// Make a config with default options.
- config := &config{
+ newConfig := &config{
ProductVariablesFileName: cmdArgs.SoongVariables,
env: availableEnv,
@@ -657,70 +671,72 @@
moduleListFile: cmdArgs.ModuleListFile,
fs: pathtools.NewOsFs(absSrcDir),
+ OncePer: &OncePer{},
+
buildFromSourceStub: cmdArgs.BuildFromSourceStub,
}
variant, ok := os.LookupEnv("TARGET_BUILD_VARIANT")
isEngBuild := !ok || variant == "eng"
- config.deviceConfig = &deviceConfig{
- config: config,
+ newConfig.deviceConfig = &deviceConfig{
+ config: newConfig,
}
// Soundness check of the build and source directories. This won't catch strange
// configurations with symlinks, but at least checks the obvious case.
absBuildDir, err := filepath.Abs(cmdArgs.SoongOutDir)
if err != nil {
- return Config{}, err
+ return &config{}, err
}
absSrcDir, err := filepath.Abs(".")
if err != nil {
- return Config{}, err
+ return &config{}, err
}
if strings.HasPrefix(absSrcDir, absBuildDir) {
- return Config{}, fmt.Errorf("Build dir must not contain source directory")
+ return &config{}, fmt.Errorf("Build dir must not contain source directory")
}
// Load any configurable options from the configuration file
- err = loadConfig(config)
+ err = loadConfig(newConfig)
if err != nil {
- return Config{}, err
+ return &config{}, err
}
KatiEnabledMarkerFile := filepath.Join(cmdArgs.SoongOutDir, ".soong.kati_enabled")
if _, err := os.Stat(absolutePath(KatiEnabledMarkerFile)); err == nil {
- config.katiEnabled = true
+ newConfig.katiEnabled = true
}
- determineBuildOS(config)
+ determineBuildOS(newConfig)
// Sets up the map of target OSes to the finer grained compilation targets
// that are configured from the product variables.
- targets, err := decodeTargetProductVariables(config)
+ targets, err := decodeTargetProductVariables(newConfig)
if err != nil {
- return Config{}, err
+ return &config{}, err
}
- config.partialCompileFlags, err = config.parsePartialCompileFlags(isEngBuild)
+ newConfig.partialCompileFlags, err = newConfig.parsePartialCompileFlags(isEngBuild)
if err != nil {
- return Config{}, err
+ return &config{}, err
}
// Make the CommonOS OsType available for all products.
targets[CommonOS] = []Target{commonTargetMap[CommonOS.Name]}
var archConfig []archConfig
- if config.NdkAbis() {
+ if newConfig.NdkAbis() {
archConfig = getNdkAbisConfig()
- } else if config.AmlAbis() {
+ } else if newConfig.AmlAbis() {
archConfig = getAmlAbisConfig()
}
if archConfig != nil {
androidTargets, err := decodeAndroidArchSettings(archConfig)
if err != nil {
- return Config{}, err
+ return &config{}, err
}
targets[Android] = androidTargets
}
@@ -728,37 +744,113 @@
multilib := make(map[string]bool)
for _, target := range targets[Android] {
if seen := multilib[target.Arch.ArchType.Multilib]; seen {
- config.multilibConflicts[target.Arch.ArchType] = true
+ newConfig.multilibConflicts[target.Arch.ArchType] = true
}
multilib[target.Arch.ArchType.Multilib] = true
}
// Map of OS to compilation targets.
- config.Targets = targets
+ newConfig.Targets = targets
// Compilation targets for host tools.
- config.BuildOSTarget = config.Targets[config.BuildOS][0]
- config.BuildOSCommonTarget = getCommonTargets(config.Targets[config.BuildOS])[0]
+ newConfig.BuildOSTarget = newConfig.Targets[newConfig.BuildOS][0]
+ newConfig.BuildOSCommonTarget = getCommonTargets(newConfig.Targets[newConfig.BuildOS])[0]
// Compilation targets for Android.
- if len(config.Targets[Android]) > 0 {
- config.AndroidCommonTarget = getCommonTargets(config.Targets[Android])[0]
- config.AndroidFirstDeviceTarget = FirstTarget(config.Targets[Android], "lib64", "lib32")[0]
+ if len(newConfig.Targets[Android]) > 0 {
+ newConfig.AndroidCommonTarget = getCommonTargets(newConfig.Targets[Android])[0]
+ newConfig.AndroidFirstDeviceTarget = FirstTarget(newConfig.Targets[Android], "lib64", "lib32")[0]
}
setBuildMode := func(arg string, mode SoongBuildMode) {
if arg != "" {
- if config.BuildMode != AnalysisNoBazel {
+ if newConfig.BuildMode != AnalysisNoBazel {
fmt.Fprintf(os.Stderr, "buildMode is already set, illegal argument: %s", arg)
os.Exit(1)
}
- config.BuildMode = mode
+ newConfig.BuildMode = mode
}
}
setBuildMode(cmdArgs.ModuleGraphFile, GenerateModuleGraph)
setBuildMode(cmdArgs.DocFile, GenerateDocFile)
- config.productVariables.Build_from_text_stub = boolPtr(config.BuildFromTextStub())
+ newConfig.productVariables.Build_from_text_stub = boolPtr(newConfig.BuildFromTextStub())
+
+ newConfig.deviceNameToInstall = newConfig.productVariables.DeviceName
+
+ return newConfig, err
+}
+
+// Replace variables in config.productVariables that have tags with "generic" key.
+// A generic tag may have a string or an int value for the generic configuration.
+// If the value is "unset", generic configuration will unset the variable.
+func overrideGenericConfig(config *config) {
+ config.genericConfig.isGeneric = true
+ type_pv := reflect.TypeOf(config.genericConfig.productVariables)
+ value_pv := reflect.ValueOf(&config.genericConfig.productVariables)
+ for i := range type_pv.NumField() {
+ type_pv_field := type_pv.Field(i)
+ generic_value := type_pv_field.Tag.Get("generic")
+ // If a product variable has an annotation of "generic" tag, use the
+ // value of the tag to set the generic variable.
+ if generic_value != "" {
+ value_pv_field := value_pv.Elem().Field(i)
+
+ if generic_value == "unset" {
+ // unset the product variable
+ value_pv_field.SetZero()
+ continue
+ }
+
+ kind_of_type_pv := type_pv_field.Type.Kind()
+ is_pointer := false
+ if kind_of_type_pv == reflect.Pointer {
+ is_pointer = true
+ kind_of_type_pv = type_pv_field.Type.Elem().Kind()
+ }
+
+ switch kind_of_type_pv {
+ case reflect.String:
+ if is_pointer {
+ value_pv_field.Set(reflect.ValueOf(stringPtr(generic_value)))
+ } else {
+ value_pv_field.Set(reflect.ValueOf(generic_value))
+ }
+ case reflect.Int:
+ generic_int, err := strconv.Atoi(generic_value)
+ if err != nil {
+ panic(fmt.Errorf("Only an int value can be assigned to int variable: %s", err))
+ }
+ if is_pointer {
+ value_pv_field.Set(reflect.ValueOf(intPtr(generic_int)))
+ } else {
+ value_pv_field.Set(reflect.ValueOf(generic_int))
+ }
+ default:
+ panic(fmt.Errorf("Unknown type to replace for generic variable: %s", &kind_of_type_pv))
+ }
+ }
+ }
+
+ // OncePer must be a singleton.
+ config.genericConfig.OncePer = config.OncePer
+ // keep the device name to get the install path.
+ config.genericConfig.deviceNameToInstall = config.deviceNameToInstall
+}
+
+// NewConfig creates a new Config object. It also loads the config file, if
+// found. The Config object includes a duplicated Config object in it for the
+// generic configuration that does not provide any product specific information.
+func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) {
+ config, err := initConfig(cmdArgs, availableEnv)
+ if err != nil {
+ return Config{}, err
+ }
+
+ // Initialize generic configuration.
+ config.genericConfig, err = initConfig(cmdArgs, availableEnv)
+ // Update product specific variables with the generic configuration.
+ overrideGenericConfig(config)
return Config{config}, err
}
@@ -946,7 +1038,7 @@
// require them to run and get the current build fingerprint. This ensures they
// don't rebuild on every incremental build when the build number changes.
func (c *config) BuildFingerprintFile(ctx PathContext) Path {
- return PathForArbitraryOutput(ctx, "target", "product", c.DeviceName(), String(c.productVariables.BuildFingerprintFile))
+ return PathForArbitraryOutput(ctx, "target", "product", *c.deviceNameToInstall, String(c.productVariables.BuildFingerprintFile))
}
// BuildNumberFile returns the path to a text file containing metadata
@@ -974,7 +1066,7 @@
// require them to run and get the current build thumbprint. This ensures they
// don't rebuild on every incremental build when the build thumbprint changes.
func (c *config) BuildThumbprintFile(ctx PathContext) Path {
- return PathForArbitraryOutput(ctx, "target", "product", c.DeviceName(), String(c.productVariables.BuildThumbprintFile))
+ return PathForArbitraryOutput(ctx, "target", "product", *c.deviceNameToInstall, String(c.productVariables.BuildThumbprintFile))
}
// DeviceName returns the name of the current device target.