diff options
-rw-r--r-- | android/config.go | 158 | ||||
-rw-r--r-- | android/config_test.go | 69 | ||||
-rw-r--r-- | android/early_module_context.go | 7 | ||||
-rw-r--r-- | android/module.go | 11 | ||||
-rw-r--r-- | android/module_proxy.go | 4 | ||||
-rw-r--r-- | android/paths.go | 2 | ||||
-rw-r--r-- | android/test_config.go | 17 | ||||
-rw-r--r-- | android/variable.go | 4 | ||||
-rw-r--r-- | dexpreopt/config.go | 5 |
9 files changed, 238 insertions, 39 deletions
diff --git a/android/config.go b/android/config.go index d47f0d44a..e02678220 100644 --- a/android/config.go +++ b/android/config.go @@ -22,6 +22,7 @@ import ( "fmt" "os" "path/filepath" + "reflect" "runtime" "strconv" "strings" @@ -108,6 +109,10 @@ const ( 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 @@ type config struct { // 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 @@ type config struct { // 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 @@ func NullConfig(outDir, soongOutDir string) Config { } } -// 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 @@ func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) 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 @@ func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) 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 @@ func (c *config) DisplayBuildNumber() bool { // 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 @@ func (c *config) BuildHostnameFile(ctx PathContext) Path { // 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. diff --git a/android/config_test.go b/android/config_test.go index d1b26c12a..81b7c3eb5 100644 --- a/android/config_test.go +++ b/android/config_test.go @@ -281,3 +281,72 @@ func TestPartialCompile(t *testing.T) { }) } } + +type configTestProperties struct { + Use_generic_config *bool +} + +type configTestModule struct { + ModuleBase + properties configTestProperties +} + +func (d *configTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { + deviceName := ctx.Config().DeviceName() + if ctx.ModuleName() == "foo" { + if ctx.Module().UseGenericConfig() { + ctx.PropertyErrorf("use_generic_config", "must not be set for this test") + } + } else if ctx.ModuleName() == "bar" { + if !ctx.Module().UseGenericConfig() { + ctx.ModuleErrorf("\"use_generic_config: true\" must be set for this test") + } + } + + if ctx.Module().UseGenericConfig() { + if deviceName != "generic" { + ctx.ModuleErrorf("Device name for this module must be \"generic\" but %q\n", deviceName) + } + } else { + if deviceName == "generic" { + ctx.ModuleErrorf("Device name for this module must not be \"generic\"\n") + } + } +} + +func configTestModuleFactory() Module { + module := &configTestModule{} + module.AddProperties(&module.properties) + InitAndroidModule(module) + return module +} + +var prepareForConfigTest = GroupFixturePreparers( + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("test", configTestModuleFactory) + }), +) + +func TestGenericConfig(t *testing.T) { + bp := ` + test { + name: "foo", + } + + test { + name: "bar", + use_generic_config: true, + } + ` + + result := GroupFixturePreparers( + prepareForConfigTest, + FixtureWithRootAndroidBp(bp), + ).RunTest(t) + + foo := result.Module("foo", "").(*configTestModule) + bar := result.Module("bar", "").(*configTestModule) + + AssertBoolEquals(t, "Do not use generic config", false, foo.UseGenericConfig()) + AssertBoolEquals(t, "Use generic config", true, bar.UseGenericConfig()) +} diff --git a/android/early_module_context.go b/android/early_module_context.go index 8d2828545..300edf194 100644 --- a/android/early_module_context.go +++ b/android/early_module_context.go @@ -146,6 +146,13 @@ func (e *earlyModuleContext) Module() Module { } func (e *earlyModuleContext) Config() Config { + // Only the system image may use the generic config. + // If a module builds multiple image variations, provide the generic config only for the core + // variant which is installed in the system partition. Other image variant may still read the + // original configurations. + if e.Module().base().UseGenericConfig() && e.Module().base().commonProperties.ImageVariation == "" { + return e.EarlyModuleContext.Config().(Config).genericConfig() + } return e.EarlyModuleContext.Config().(Config) } diff --git a/android/module.go b/android/module.go index a3fe837a5..87377cc75 100644 --- a/android/module.go +++ b/android/module.go @@ -128,6 +128,9 @@ type Module interface { // WARNING: This should not be used outside build/soong/fsgen // Overrides returns the list of modules which should not be installed if this module is installed. Overrides() []string + + // If this is true, the module must not read product-specific configurations. + UseGenericConfig() bool } // Qualified id for a module @@ -507,6 +510,10 @@ type commonProperties struct { // List of module names that are prevented from being installed when this module gets // installed. Overrides []string + + // Set to true if this module must be generic and does not require product-specific information. + // To be included in the system image, this property must be set to true. + Use_generic_config *bool } // Properties common to all modules inheriting from ModuleBase. Unlike commonProperties, these @@ -2588,6 +2595,10 @@ func (m *ModuleBase) Overrides() []string { return m.commonProperties.Overrides } +func (m *ModuleBase) UseGenericConfig() bool { + return proptools.Bool(m.commonProperties.Use_generic_config) +} + type ConfigContext interface { Config() Config } diff --git a/android/module_proxy.go b/android/module_proxy.go index 77abc11e6..561c4770c 100644 --- a/android/module_proxy.go +++ b/android/module_proxy.go @@ -231,3 +231,7 @@ func (m ModuleProxy) Overrides() []string { func (m ModuleProxy) VintfFragments(ctx ConfigurableEvaluatorContext) []string { panic("method is not implemented on ModuleProxy") } + +func (m ModuleProxy) UseGenericConfig() bool { + panic("method is not implemented on ModuleProxy") +} diff --git a/android/paths.go b/android/paths.go index 977473fbd..6612d37d3 100644 --- a/android/paths.go +++ b/android/paths.go @@ -2054,7 +2054,7 @@ func pathForInstall(ctx PathContext, os OsType, arch ArchType, partition string, var partitionPaths []string if os.Class == Device { - partitionPaths = []string{"target", "product", ctx.Config().DeviceName(), partition} + partitionPaths = []string{"target", "product", *ctx.Config().deviceNameToInstall, partition} } else { osName := os.String() if os == Linux { diff --git a/android/test_config.go b/android/test_config.go index 3609e6b78..5d79df099 100644 --- a/android/test_config.go +++ b/android/test_config.go @@ -23,8 +23,7 @@ import ( "github.com/google/blueprint/proptools" ) -// TestConfig returns a Config object for testing. -func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config { +func initTestConfig(buildDir string, env map[string]string) *config { envCopy := make(map[string]string) for k, v := range env { envCopy[k] = v @@ -58,6 +57,7 @@ func TestConfig(buildDir string, env map[string]string, bp string, fs map[string soongOutDir: filepath.Join(buildDir, "soong"), captureBuild: true, env: envCopy, + OncePer: &OncePer{}, // Set testAllowNonExistentPaths so that test contexts don't need to specify every path // passed to PathForSource or PathForModuleSrc. @@ -69,10 +69,21 @@ func TestConfig(buildDir string, env map[string]string, bp string, fs map[string config: config, } config.TestProductVariables = &config.productVariables + config.deviceNameToInstall = config.TestProductVariables.DeviceName + + determineBuildOS(config) + + return config +} + +// TestConfig returns a Config object for testing. +func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config { + config := initTestConfig(buildDir, env) config.mockFileSystem(bp, fs) - determineBuildOS(config) + config.genericConfig = initTestConfig(buildDir, env) + overrideGenericConfig(config) return Config{config} } diff --git a/android/variable.go b/android/variable.go index 3d5a6ae12..8c9a0b1a5 100644 --- a/android/variable.go +++ b/android/variable.go @@ -225,8 +225,8 @@ type ProductVariables struct { Platform_version_last_stable *string `json:",omitempty"` Platform_version_known_codenames *string `json:",omitempty"` - DeviceName *string `json:",omitempty"` - DeviceProduct *string `json:",omitempty"` + DeviceName *string `json:",omitempty" generic:"generic"` + DeviceProduct *string `json:",omitempty" generic:"generic"` DeviceArch *string `json:",omitempty"` DeviceArchVariant *string `json:",omitempty"` DeviceCpuVariant *string `json:",omitempty"` diff --git a/dexpreopt/config.go b/dexpreopt/config.go index e57384fd7..af09dbcca 100644 --- a/dexpreopt/config.go +++ b/dexpreopt/config.go @@ -139,6 +139,11 @@ var allSystemServerJarsKey = android.NewOnceKey("allSystemServerJars") // Returns all jars that system_server loads. func (g *GlobalConfig) AllSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList { + // dexpreopt does not initialize the soong config. + // Initialize the OncePer here. + if ctx.Config().OncePer == nil { + ctx.Config().OncePer = &android.OncePer{} + } return ctx.Config().Once(allSystemServerJarsKey, func() interface{} { res := g.AllPlatformSystemServerJars(ctx).AppendList(g.AllApexSystemServerJars(ctx)) return &res |