Aconfig rules
Test: m services_device_config (which runs sooong tests too)
Change-Id: I432e914d01d2bff77ba68de65ae5baea527090f5
diff --git a/android/config.go b/android/config.go
index 9e94e05..acadb3f 100644
--- a/android/config.go
+++ b/android/config.go
@@ -183,6 +183,16 @@
return String(c.config.productVariables.DeviceMaxPageSizeSupported)
}
+// The release version passed to aconfig, derived from RELEASE_VERSION
+func (c Config) ReleaseVersion() string {
+ return c.config.productVariables.ReleaseVersion
+}
+
+// The flag values files passed to aconfig, derived from RELEASE_VERSION
+func (c Config) ReleaseDeviceConfigValueSets() []string {
+ return c.config.productVariables.ReleaseDeviceConfigValueSets
+}
+
// 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.
diff --git a/android/variable.go b/android/variable.go
index bf66135..97171e7 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -473,6 +473,9 @@
ProductManufacturer string `json:",omitempty"`
ProductBrand string `json:",omitempty"`
BuildVersionTags []string `json:",omitempty"`
+
+ ReleaseVersion string `json:",omitempty"`
+ ReleaseDeviceConfigValueSets []string `json:",omitempty"`
}
func boolPtr(v bool) *bool {
diff --git a/device_config/Android.bp b/device_config/Android.bp
new file mode 100644
index 0000000..360b389
--- /dev/null
+++ b/device_config/Android.bp
@@ -0,0 +1,30 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-device_config",
+ pkgPath: "android/soong/device_config",
+ deps: [
+ "blueprint",
+ "blueprint-pathtools",
+ "sbox_proto",
+ "soong",
+ "soong-android",
+ "soong-bazel",
+ "soong-shared",
+ ],
+ srcs: [
+ "device_config_definitions.go",
+ "device_config_values.go",
+ "device_config_value_set.go",
+ "init.go",
+ //"testing.go"
+ ],
+ /*
+ testSrcs: [
+ "device_config_test.go",
+ ],
+ */
+ pluginFor: ["soong_build"],
+}
diff --git a/device_config/device_config_definitions.go b/device_config/device_config_definitions.go
new file mode 100644
index 0000000..c565766
--- /dev/null
+++ b/device_config/device_config_definitions.go
@@ -0,0 +1,150 @@
+// Copyright 2023 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 device_config
+
+import (
+ "android/soong/android"
+ "fmt"
+ "github.com/google/blueprint"
+ "strings"
+)
+
+type DefinitionsModule struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+
+ // Properties for "device_config_definitions"
+ properties struct {
+ // aconfig files, relative to this Android.bp file
+ Srcs []string `android:"path"`
+
+ // Release config flag namespace
+ Namespace string
+
+ // Values from TARGET_RELEASE / RELEASE_DEVICE_CONFIG_VALUE_SETS
+ Values []string `blueprint:"mutated"`
+ }
+
+ intermediatePath android.WritablePath
+ srcJarPath android.WritablePath
+}
+
+func DefinitionsFactory() android.Module {
+ module := &DefinitionsModule{}
+
+ android.InitAndroidModule(module)
+ android.InitDefaultableModule(module)
+ module.AddProperties(&module.properties)
+ // TODO: bp2build
+ //android.InitBazelModule(module)
+
+ return module
+}
+
+type implicitValuesTagType struct {
+ blueprint.BaseDependencyTag
+}
+
+var implicitValuesTag = implicitValuesTagType{}
+
+func (module *DefinitionsModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // Validate Properties
+ if len(module.properties.Srcs) == 0 {
+ ctx.PropertyErrorf("srcs", "missing source files")
+ return
+ }
+ if len(module.properties.Namespace) == 0 {
+ ctx.PropertyErrorf("namespace", "missing namespace property")
+ }
+
+ // Add a dependency on the device_config_value_sets defined in
+ // RELEASE_DEVICE_CONFIG_VALUE_SETS, and add any device_config_values that
+ // match our namespace.
+ valuesFromConfig := ctx.Config().ReleaseDeviceConfigValueSets()
+ ctx.AddDependency(ctx.Module(), implicitValuesTag, valuesFromConfig...)
+}
+
+func (module *DefinitionsModule) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case ".srcjar":
+ return []android.Path{module.srcJarPath}, nil
+ case "":
+ // The default output of this module is the intermediates format, which is
+ // not installable and in a private format that no other rules can handle
+ // correctly.
+ return []android.Path{module.intermediatePath}, nil
+ default:
+ return nil, fmt.Errorf("unsupported device_config_definitions module reference tag %q", tag)
+ }
+}
+
+func joinAndPrefix(prefix string, values []string) string {
+ var sb strings.Builder
+ for _, v := range values {
+ sb.WriteString(prefix)
+ sb.WriteString(v)
+ }
+ return sb.String()
+}
+
+func (module *DefinitionsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // Get the values that came from the global RELEASE_DEVICE_CONFIG_VALUE_SETS flag
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ if !ctx.OtherModuleHasProvider(dep, valueSetProviderKey) {
+ // Other modules get injected as dependencies too, for example the license modules
+ return
+ }
+ depData := ctx.OtherModuleProvider(dep, valueSetProviderKey).(valueSetProviderData)
+ valuesFiles, ok := depData.AvailableNamespaces[module.properties.Namespace]
+ if ok {
+ for _, path := range valuesFiles {
+ module.properties.Values = append(module.properties.Values, path.String())
+ }
+ }
+ })
+
+ // Intermediate format
+ inputFiles := android.PathsForModuleSrc(ctx, module.properties.Srcs)
+ module.intermediatePath = android.PathForModuleOut(ctx, "intermediate.json")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: aconfigRule,
+ Inputs: inputFiles,
+ Output: module.intermediatePath,
+ Description: "device_config_definitions",
+ Args: map[string]string{
+ "release_version": ctx.Config().ReleaseVersion(),
+ "namespace": module.properties.Namespace,
+ "values": joinAndPrefix(" --values ", module.properties.Values),
+ },
+ })
+
+ // Generated java inside a srcjar
+ module.srcJarPath = android.PathForModuleGen(ctx, ctx.ModuleName()+".srcjar")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: srcJarRule,
+ Input: module.intermediatePath,
+ Output: module.srcJarPath,
+ Description: "device_config.srcjar",
+ })
+
+ // TODO: C++
+
+ // Phony target for debugging convenience
+ ctx.Build(pctx, android.BuildParams{
+ Rule: blueprint.Phony,
+ Output: android.PathForPhony(ctx, ctx.ModuleName()),
+ Inputs: []android.Path{module.srcJarPath}, // TODO: C++
+ })
+}
diff --git a/device_config/device_config_test.go b/device_config/device_config_test.go
new file mode 100644
index 0000000..91a06a7
--- /dev/null
+++ b/device_config/device_config_test.go
@@ -0,0 +1,61 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// 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 device_config
+
+import (
+ "os"
+ "testing"
+
+ "android/soong/android"
+ "android/soong/java"
+)
+
+func TestMain(m *testing.M) {
+ os.Exit(m.Run())
+}
+
+func test(t *testing.T, bp string) *android.TestResult {
+ t.Helper()
+
+ mockFS := android.MockFS{
+ "config.aconfig": nil,
+ }
+
+ result := android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithSyspropBuildComponents,
+ // TODO: Consider values files, although maybe in its own test
+ // android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ // variables.ReleaseConfigValuesBasePaths = ...
+ //})
+ mockFS.AddToFixture(),
+ android.FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
+
+ return result
+}
+
+func TestOutputs(t *testing.T) {
+ /*result := */ test(t, `
+ device_config {
+ name: "my_device_config",
+ srcs: ["config.aconfig"],
+ }
+ `)
+
+ // TODO: Make sure it exports a .srcjar, which is used by java libraries
+ // TODO: Make sure it exports an intermediates file
+ // TODO: Make sure the intermediates file is propagated to the Android.mk file
+}
diff --git a/device_config/device_config_value_set.go b/device_config/device_config_value_set.go
new file mode 100644
index 0000000..e406d20
--- /dev/null
+++ b/device_config/device_config_value_set.go
@@ -0,0 +1,92 @@
+// Copyright 2023 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 device_config
+
+import (
+ "android/soong/android"
+ "github.com/google/blueprint"
+)
+
+// Properties for "device_config_value_set"
+type ValueSetModule struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+
+ properties struct {
+ // device_config_values modules
+ Values []string
+ }
+}
+
+func ValueSetFactory() android.Module {
+ module := &ValueSetModule{}
+
+ android.InitAndroidModule(module)
+ android.InitDefaultableModule(module)
+ module.AddProperties(&module.properties)
+ // TODO: bp2build
+ //android.InitBazelModule(module)
+
+ return module
+}
+
+// Dependency tag for values property
+type valueSetType struct {
+ blueprint.BaseDependencyTag
+}
+
+var valueSetTag = valueSetType{}
+
+// Provider published by device_config_value_set
+type valueSetProviderData struct {
+ // The namespace of each of the
+ // (map of namespace --> device_config_module)
+ AvailableNamespaces map[string]android.Paths
+}
+
+var valueSetProviderKey = blueprint.NewProvider(valueSetProviderData{})
+
+func (module *ValueSetModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+ deps := ctx.AddDependency(ctx.Module(), valueSetTag, module.properties.Values...)
+ for _, dep := range deps {
+ _, ok := dep.(*ValuesModule)
+ if !ok {
+ ctx.PropertyErrorf("values", "values must be a device_config_values module")
+ return
+ }
+ }
+}
+
+func (module *ValueSetModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // Accumulate the namespaces of the values modules listed, and set that as an
+ // valueSetProviderKey provider that device_config modules can read and use
+ // to append values to their aconfig actions.
+ namespaces := make(map[string]android.Paths)
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ if !ctx.OtherModuleHasProvider(dep, valuesProviderKey) {
+ // Other modules get injected as dependencies too, for example the license modules
+ return
+ }
+ depData := ctx.OtherModuleProvider(dep, valuesProviderKey).(valuesProviderData)
+
+ srcs := make([]android.Path, len(depData.Values))
+ copy(srcs, depData.Values)
+ namespaces[depData.Namespace] = srcs
+
+ })
+ ctx.SetProvider(valueSetProviderKey, valueSetProviderData{
+ AvailableNamespaces: namespaces,
+ })
+}
diff --git a/device_config/device_config_values.go b/device_config/device_config_values.go
new file mode 100644
index 0000000..110f12a
--- /dev/null
+++ b/device_config/device_config_values.go
@@ -0,0 +1,70 @@
+// Copyright 2023 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 device_config
+
+import (
+ "android/soong/android"
+ "github.com/google/blueprint"
+)
+
+// Properties for "device_config_value"
+type ValuesModule struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+
+ properties struct {
+ // aconfig files, relative to this Android.bp file
+ Srcs []string `android:"path"`
+
+ // Release config flag namespace
+ Namespace string
+ }
+}
+
+func ValuesFactory() android.Module {
+ module := &ValuesModule{}
+
+ android.InitAndroidModule(module)
+ android.InitDefaultableModule(module)
+ module.AddProperties(&module.properties)
+ // TODO: bp2build
+ //android.InitBazelModule(module)
+
+ return module
+}
+
+// Provider published by device_config_value_set
+type valuesProviderData struct {
+ // The namespace that this values module values
+ Namespace string
+
+ // The values aconfig files, relative to the root of the tree
+ Values android.Paths
+}
+
+var valuesProviderKey = blueprint.NewProvider(valuesProviderData{})
+
+func (module *ValuesModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ if len(module.properties.Namespace) == 0 {
+ ctx.PropertyErrorf("namespace", "missing namespace property")
+ }
+
+ // Provide the our source files list to the device_config_value_set as a list of files
+ providerData := valuesProviderData{
+ Namespace: module.properties.Namespace,
+ Values: android.PathsForModuleSrc(ctx, module.properties.Srcs),
+ }
+ ctx.SetProvider(valuesProviderKey, providerData)
+}
diff --git a/device_config/init.go b/device_config/init.go
new file mode 100644
index 0000000..0a4645b
--- /dev/null
+++ b/device_config/init.go
@@ -0,0 +1,70 @@
+// Copyright 2023 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 device_config
+
+import (
+ "android/soong/android"
+ "github.com/google/blueprint"
+)
+
+var (
+ pctx = android.NewPackageContext("android/soong/device_config")
+
+ // For device_config_definitions: Generate cache file
+ // TODO: Restat
+ aconfigRule = pctx.AndroidStaticRule("aconfig",
+ blueprint.RuleParams{
+ Command: `${aconfig} create-cache` +
+ ` --namespace ${namespace}` +
+ ` --declarations ${in}` +
+ ` ${values}` +
+ ` --cache ${out}.tmp` +
+ ` && ( if cmp -s ${out}.tmp ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
+ // ` --build-id ${release_version}` +
+ CommandDeps: []string{
+ "${aconfig}",
+ },
+ Restat: true,
+ }, "release_version", "namespace", "values")
+
+ // For device_config_definitions: Generate java file
+ srcJarRule = pctx.AndroidStaticRule("aconfig_srcjar",
+ blueprint.RuleParams{
+ Command: `rm -rf ${out}.tmp` +
+ ` && mkdir -p ${out}.tmp` +
+ ` && ${aconfig} create-java-lib` +
+ ` --cache ${in}` +
+ ` --out ${out}.tmp` +
+ ` && $soong_zip -write_if_changed -jar -o ${out} -C ${out}.tmp -D ${out}.tmp` +
+ ` && rm -rf ${out}.tmp`,
+ CommandDeps: []string{
+ "$aconfig",
+ "$soong_zip",
+ },
+ Restat: true,
+ })
+)
+
+func init() {
+ registerBuildComponents(android.InitRegistrationContext)
+}
+
+func registerBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("device_config_definitions", DefinitionsFactory)
+ ctx.RegisterModuleType("device_config_values", ValuesFactory)
+ ctx.RegisterModuleType("device_config_value_set", ValueSetFactory)
+ pctx.HostBinToolVariable("aconfig", "aconfig")
+ pctx.HostBinToolVariable("soong_zip", "soong_zip")
+}
diff --git a/device_config/testing.go b/device_config/testing.go
new file mode 100644
index 0000000..f054476
--- /dev/null
+++ b/device_config/testing.go
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// 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 device_config
+
+import "android/soong/android"
+
+var PrepareForTestWithSyspropBuildComponents = android.FixtureRegisterWithContext(registerBuildComponents)