74f3f30ae4
By setting aconfig_flags: true, soong filesystem module installs the aconfig_flags.pb file to its etc directory. We need to define aconfigFilePaths to ModuleBase to store the list of aconfig file paths that is generated from the GenerateBuildActions(). The aconfigFilePaths are collected when build the filesystem module to build the aconfig_flags.pb for the filesystem image. Bug: 335363964 Test: compare the cache list with those generated from make. Change-Id: Ia32b566bf43174e94b9e610b9503608c6b583899
233 lines
8.1 KiB
Go
233 lines
8.1 KiB
Go
// 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 android
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"maps"
|
|
"reflect"
|
|
|
|
"github.com/google/blueprint"
|
|
)
|
|
|
|
var (
|
|
mergeAconfigFilesRule = pctx.AndroidStaticRule("mergeAconfigFilesRule",
|
|
blueprint.RuleParams{
|
|
Command: `${aconfig} dump --dedup --format protobuf --out $out $flags`,
|
|
CommandDeps: []string{"${aconfig}"},
|
|
}, "flags")
|
|
_ = pctx.HostBinToolVariable("aconfig", "aconfig")
|
|
)
|
|
|
|
// Provider published by aconfig_value_set
|
|
type AconfigDeclarationsProviderData struct {
|
|
Package string
|
|
Container string
|
|
Exportable bool
|
|
IntermediateCacheOutputPath WritablePath
|
|
IntermediateDumpOutputPath WritablePath
|
|
}
|
|
|
|
var AconfigDeclarationsProviderKey = blueprint.NewProvider[AconfigDeclarationsProviderData]()
|
|
|
|
type ModeInfo struct {
|
|
Container string
|
|
Mode string
|
|
}
|
|
type CodegenInfo struct {
|
|
// AconfigDeclarations is the name of the aconfig_declarations modules that
|
|
// the codegen module is associated with
|
|
AconfigDeclarations []string
|
|
|
|
// Paths to the cache files of the associated aconfig_declaration modules
|
|
IntermediateCacheOutputPaths Paths
|
|
|
|
// Paths to the srcjar files generated from the java_aconfig_library modules
|
|
Srcjars Paths
|
|
|
|
ModeInfos map[string]ModeInfo
|
|
}
|
|
|
|
var CodegenInfoProvider = blueprint.NewProvider[CodegenInfo]()
|
|
|
|
func propagateModeInfos(ctx ModuleContext, module Module, to, from map[string]ModeInfo) {
|
|
if len(from) > 0 {
|
|
depTag := ctx.OtherModuleDependencyTag(module)
|
|
if tag, ok := depTag.(PropagateAconfigValidationDependencyTag); ok && tag.PropagateAconfigValidation() {
|
|
maps.Copy(to, from)
|
|
}
|
|
}
|
|
}
|
|
|
|
type aconfigPropagatingDeclarationsInfo struct {
|
|
AconfigFiles map[string]Paths
|
|
ModeInfos map[string]ModeInfo
|
|
}
|
|
|
|
var AconfigPropagatingProviderKey = blueprint.NewProvider[aconfigPropagatingDeclarationsInfo]()
|
|
|
|
func VerifyAconfigBuildMode(ctx ModuleContext, container string, module blueprint.Module, asError bool) {
|
|
if dep, ok := OtherModuleProvider(ctx, module, AconfigPropagatingProviderKey); ok {
|
|
for k, v := range dep.ModeInfos {
|
|
msg := fmt.Sprintf("%s/%s depends on %s/%s/%s across containers\n",
|
|
module.Name(), container, k, v.Container, v.Mode)
|
|
if v.Container != container && v.Mode != "exported" && v.Mode != "force-read-only" {
|
|
if asError {
|
|
ctx.ModuleErrorf(msg)
|
|
} else {
|
|
fmt.Printf("WARNING: " + msg)
|
|
}
|
|
} else {
|
|
if !asError {
|
|
fmt.Printf("PASSED: " + msg)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func aconfigUpdateAndroidBuildActions(ctx ModuleContext) {
|
|
mergedAconfigFiles := make(map[string]Paths)
|
|
mergedModeInfos := make(map[string]ModeInfo)
|
|
|
|
ctx.VisitDirectDepsIgnoreBlueprint(func(module Module) {
|
|
if aconfig_dep, ok := OtherModuleProvider(ctx, module, CodegenInfoProvider); ok && len(aconfig_dep.ModeInfos) > 0 {
|
|
maps.Copy(mergedModeInfos, aconfig_dep.ModeInfos)
|
|
}
|
|
|
|
// If any of our dependencies have aconfig declarations (directly or propagated), then merge those and provide them.
|
|
if dep, ok := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); ok {
|
|
mergedAconfigFiles[dep.Container] = append(mergedAconfigFiles[dep.Container], dep.IntermediateCacheOutputPath)
|
|
}
|
|
if dep, ok := OtherModuleProvider(ctx, module, AconfigPropagatingProviderKey); ok {
|
|
for container, v := range dep.AconfigFiles {
|
|
mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...)
|
|
}
|
|
propagateModeInfos(ctx, module, mergedModeInfos, dep.ModeInfos)
|
|
}
|
|
})
|
|
// We only need to set the provider if we have aconfig files.
|
|
if len(mergedAconfigFiles) > 0 {
|
|
for _, container := range SortedKeys(mergedAconfigFiles) {
|
|
aconfigFiles := mergedAconfigFiles[container]
|
|
mergedAconfigFiles[container] = mergeAconfigFiles(ctx, container, aconfigFiles, true)
|
|
}
|
|
|
|
SetProvider(ctx, AconfigPropagatingProviderKey, aconfigPropagatingDeclarationsInfo{
|
|
AconfigFiles: mergedAconfigFiles,
|
|
ModeInfos: mergedModeInfos,
|
|
})
|
|
ctx.Module().base().aconfigFilePaths = getAconfigFilePaths(ctx.Module().base(), mergedAconfigFiles)
|
|
}
|
|
}
|
|
|
|
func aconfigUpdateAndroidMkData(ctx fillInEntriesContext, mod Module, data *AndroidMkData) {
|
|
info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
|
|
// If there is no aconfigPropagatingProvider, or there are no AconfigFiles, then we are done.
|
|
if !ok || len(info.AconfigFiles) == 0 {
|
|
return
|
|
}
|
|
data.Extra = append(data.Extra, func(w io.Writer, outputFile Path) {
|
|
AndroidMkEmitAssignList(w, "LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles).Strings())
|
|
})
|
|
// If there is a Custom writer, it needs to support this provider.
|
|
if data.Custom != nil {
|
|
switch reflect.TypeOf(mod).String() {
|
|
case "*aidl.aidlApi": // writes non-custom before adding .phony
|
|
case "*android_sdk.sdkRepoHost": // doesn't go through base_rules
|
|
case "*apex.apexBundle": // aconfig_file properties written
|
|
case "*bpf.bpf": // properties written (both for module and objs)
|
|
case "*genrule.Module": // writes non-custom before adding .phony
|
|
case "*java.SystemModules": // doesn't go through base_rules
|
|
case "*phony.phony": // properties written
|
|
case "*phony.PhonyRule": // writes phony deps and acts like `.PHONY`
|
|
case "*sysprop.syspropLibrary": // properties written
|
|
default:
|
|
panic(fmt.Errorf("custom make rules do not handle aconfig files for %q (%q) module %q", ctx.ModuleType(mod), reflect.TypeOf(mod), mod))
|
|
}
|
|
}
|
|
}
|
|
|
|
func aconfigUpdateAndroidMkEntries(ctx fillInEntriesContext, mod Module, entries *[]AndroidMkEntries) {
|
|
// If there are no entries, then we can ignore this module, even if it has aconfig files.
|
|
if len(*entries) == 0 {
|
|
return
|
|
}
|
|
info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
|
|
if !ok || len(info.AconfigFiles) == 0 {
|
|
return
|
|
}
|
|
// All of the files in the module potentially depend on the aconfig flag values.
|
|
for idx, _ := range *entries {
|
|
(*entries)[idx].ExtraEntries = append((*entries)[idx].ExtraEntries,
|
|
func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
|
|
entries.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles))
|
|
},
|
|
)
|
|
|
|
}
|
|
}
|
|
|
|
func mergeAconfigFiles(ctx ModuleContext, container string, inputs Paths, generateRule bool) Paths {
|
|
inputs = SortedUniquePaths(inputs)
|
|
if len(inputs) == 1 {
|
|
return Paths{inputs[0]}
|
|
}
|
|
|
|
output := PathForModuleOut(ctx, container, "aconfig_merged.pb")
|
|
|
|
if generateRule {
|
|
ctx.Build(pctx, BuildParams{
|
|
Rule: mergeAconfigFilesRule,
|
|
Description: "merge aconfig files",
|
|
Inputs: inputs,
|
|
Output: output,
|
|
Args: map[string]string{
|
|
"flags": JoinWithPrefix(inputs.Strings(), "--cache "),
|
|
},
|
|
})
|
|
}
|
|
|
|
return Paths{output}
|
|
}
|
|
|
|
func getAconfigFilePaths(m *ModuleBase, aconfigFiles map[string]Paths) (paths Paths) {
|
|
// TODO(b/311155208): The default container here should be system.
|
|
container := "system"
|
|
|
|
if m.SocSpecific() {
|
|
container = "vendor"
|
|
} else if m.ProductSpecific() {
|
|
container = "product"
|
|
} else if m.SystemExtSpecific() {
|
|
container = "system_ext"
|
|
}
|
|
|
|
paths = append(paths, aconfigFiles[container]...)
|
|
if container == "system" {
|
|
// TODO(b/311155208): Once the default container is system, we can drop this.
|
|
paths = append(paths, aconfigFiles[""]...)
|
|
}
|
|
if container != "system" {
|
|
if len(aconfigFiles[container]) == 0 && len(aconfigFiles[""]) > 0 {
|
|
// TODO(b/308625757): Either we guessed the container wrong, or the flag is misdeclared.
|
|
// For now, just include the system (aka "") container if we get here.
|
|
//fmt.Printf("container_mismatch: module=%v container=%v files=%v\n", m, container, aconfigFiles)
|
|
}
|
|
paths = append(paths, aconfigFiles[""]...)
|
|
}
|
|
return
|
|
}
|