Aconfig rules

Test: m services_device_config (which runs sooong tests too)
Change-Id: I432e914d01d2bff77ba68de65ae5baea527090f5
This commit is contained in:
Joe Onorato
2023-05-09 08:14:14 -07:00
parent 7699d15864
commit fee845a737
9 changed files with 505 additions and 0 deletions

View File

@@ -183,6 +183,16 @@ func (c Config) MaxPageSizeSupported() string {
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.

View File

@@ -473,6 +473,9 @@ type productVariables struct {
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 {

30
device_config/Android.bp Normal file
View File

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

View File

@@ -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++
})
}

View File

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

View File

@@ -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,
})
}

View File

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

70
device_config/init.go Normal file
View File

@@ -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")
}

19
device_config/testing.go Normal file
View File

@@ -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)