• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2023 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package aconfig
16
17import (
18	"fmt"
19	"slices"
20
21	"android/soong/android"
22
23	"github.com/google/blueprint"
24	"github.com/google/blueprint/proptools"
25)
26
27// A singleton module that collects all of the aconfig flags declared in the
28// tree into a single combined file for export to the external flag setting
29// server (inside Google it's Gantry).
30//
31// Note that this is ALL aconfig_declarations modules present in the tree, not just
32// ones that are relevant to the product currently being built, so that that infra
33// doesn't need to pull from multiple builds and merge them.
34func AllAconfigDeclarationsFactory() android.SingletonModule {
35	module := &allAconfigDeclarationsSingleton{releaseMap: make(map[string]allAconfigReleaseDeclarationsSingleton)}
36	module.AddProperties(&module.properties)
37	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
38	return module
39}
40
41type allAconfigDeclarationsInfo struct {
42	parsedFlagsFile android.Path
43}
44
45var allAconfigDeclarationsInfoProvider = blueprint.NewProvider[allAconfigDeclarationsInfo]()
46
47type allAconfigReleaseDeclarationsSingleton struct {
48	intermediateBinaryProtoPath android.OutputPath
49	intermediateTextProtoPath   android.OutputPath
50}
51
52type ApiSurfaceContributorProperties struct {
53	Api_signature_files  proptools.Configurable[[]string] `android:"arch_variant,path"`
54	Finalized_flags_file string                           `android:"arch_variant,path"`
55}
56
57type allAconfigDeclarationsSingleton struct {
58	android.SingletonModuleBase
59
60	releaseMap map[string]allAconfigReleaseDeclarationsSingleton
61	properties ApiSurfaceContributorProperties
62
63	finalizedFlags android.OutputPath
64}
65
66func (this *allAconfigDeclarationsSingleton) sortedConfigNames() []string {
67	var names []string
68	for k := range this.releaseMap {
69		names = append(names, k)
70	}
71	slices.Sort(names)
72	return names
73}
74
75func GenerateFinalizedFlagsForApiSurface(ctx android.ModuleContext, outputPath android.WritablePath,
76	parsedFlagsFile android.Path, apiSurface ApiSurfaceContributorProperties) {
77
78	apiSignatureFiles := android.Paths{}
79	for _, apiSignatureFile := range apiSurface.Api_signature_files.GetOrDefault(ctx, nil) {
80		if path := android.PathForModuleSrc(ctx, apiSignatureFile); path != nil {
81			apiSignatureFiles = append(apiSignatureFiles, path)
82		}
83	}
84	finalizedFlagsFile := android.PathForModuleSrc(ctx, apiSurface.Finalized_flags_file)
85
86	ctx.Build(pctx, android.BuildParams{
87		Rule:   RecordFinalizedFlagsRule,
88		Inputs: append(apiSignatureFiles, finalizedFlagsFile, parsedFlagsFile),
89		Output: outputPath,
90		Args: map[string]string{
91			"api_signature_files":  android.JoinPathsWithPrefix(apiSignatureFiles, "--api-signature-file "),
92			"finalized_flags_file": "--finalized-flags-file " + finalizedFlagsFile.String(),
93			"parsed_flags_file":    "--parsed-flags-file " + parsedFlagsFile.String(),
94		},
95	})
96}
97
98func GenerateExportedFlagCheck(ctx android.ModuleContext, outputPath android.WritablePath,
99	parsedFlagsFile android.Path, apiSurface ApiSurfaceContributorProperties) {
100
101	apiSignatureFiles := android.Paths{}
102	for _, apiSignatureFile := range apiSurface.Api_signature_files.GetOrDefault(ctx, nil) {
103		if path := android.PathForModuleSrc(ctx, apiSignatureFile); path != nil {
104			apiSignatureFiles = append(apiSignatureFiles, path)
105		}
106	}
107	finalizedFlagsFile := android.PathForModuleSrc(ctx, apiSurface.Finalized_flags_file)
108
109	ctx.Build(pctx, android.BuildParams{
110		Rule:   ExportedFlagCheckRule,
111		Inputs: append(apiSignatureFiles, finalizedFlagsFile, parsedFlagsFile),
112		Output: outputPath,
113		Args: map[string]string{
114			"api_signature_files":  android.JoinPathsWithPrefix(apiSignatureFiles, "--api-signature-file "),
115			"finalized_flags_file": "--finalized-flags-file " + finalizedFlagsFile.String(),
116			"parsed_flags_file":    "--parsed-flags-file " + parsedFlagsFile.String(),
117		},
118	})
119}
120
121func (this *allAconfigDeclarationsSingleton) GenerateAndroidBuildActions(ctx android.ModuleContext) {
122	parsedFlagsFile := android.PathForIntermediates(ctx, "all_aconfig_declarations.pb")
123	this.finalizedFlags = android.PathForIntermediates(ctx, "finalized-flags.txt")
124	GenerateFinalizedFlagsForApiSurface(ctx, this.finalizedFlags, parsedFlagsFile, this.properties)
125
126	depsFiles := android.Paths{this.finalizedFlags}
127	if checkExportedFlag, ok := ctx.Config().GetBuildFlag("RELEASE_EXPORTED_FLAG_CHECK"); ok {
128		if checkExportedFlag == "true" {
129			invalidExportedFlags := android.PathForIntermediates(ctx, "invalid_exported_flags.txt")
130			GenerateExportedFlagCheck(ctx, invalidExportedFlags, parsedFlagsFile, this.properties)
131			depsFiles = append(depsFiles, invalidExportedFlags)
132			ctx.Phony("droidcore", invalidExportedFlags)
133		}
134	}
135
136	ctx.Phony("all_aconfig_declarations", depsFiles...)
137
138	android.SetProvider(ctx, allAconfigDeclarationsInfoProvider, allAconfigDeclarationsInfo{
139		parsedFlagsFile: parsedFlagsFile,
140	})
141}
142
143func (this *allAconfigDeclarationsSingleton) GenerateSingletonBuildActions(ctx android.SingletonContext) {
144	for _, rcName := range append([]string{""}, ctx.Config().ReleaseAconfigExtraReleaseConfigs()...) {
145		// Find all of the aconfig_declarations modules
146		var packages = make(map[string]int)
147		var cacheFiles android.Paths
148		ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
149			decl, ok := android.OtherModuleProvider(ctx, module, android.AconfigReleaseDeclarationsProviderKey)
150			if !ok {
151				return
152			}
153			cacheFiles = append(cacheFiles, decl[rcName].IntermediateCacheOutputPath)
154			packages[decl[rcName].Package]++
155		})
156
157		var numOffendingPkg = 0
158		offendingPkgsMessage := ""
159		for pkg, cnt := range packages {
160			if cnt > 1 {
161				offendingPkgsMessage += fmt.Sprintf("%d aconfig_declarations found for package %s\n", cnt, pkg)
162				numOffendingPkg++
163			}
164		}
165
166		if numOffendingPkg > 0 {
167			panic("Only one aconfig_declarations allowed for each package.\n" + offendingPkgsMessage)
168		}
169
170		// Generate build action for aconfig (binary proto output)
171		paths := allAconfigReleaseDeclarationsSingleton{
172			intermediateBinaryProtoPath: android.PathForIntermediates(ctx, assembleFileName(rcName, "all_aconfig_declarations.pb")),
173			intermediateTextProtoPath:   android.PathForIntermediates(ctx, assembleFileName(rcName, "all_aconfig_declarations.textproto")),
174		}
175		this.releaseMap[rcName] = paths
176		ctx.Build(pctx, android.BuildParams{
177			Rule:        AllDeclarationsRule,
178			Inputs:      cacheFiles,
179			Output:      this.releaseMap[rcName].intermediateBinaryProtoPath,
180			Description: "all_aconfig_declarations",
181			Args: map[string]string{
182				"cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "),
183			},
184		})
185		ctx.Phony("all_aconfig_declarations", this.releaseMap[rcName].intermediateBinaryProtoPath)
186
187		// Generate build action for aconfig (text proto output)
188		ctx.Build(pctx, android.BuildParams{
189			Rule:        AllDeclarationsRuleTextProto,
190			Inputs:      cacheFiles,
191			Output:      this.releaseMap[rcName].intermediateTextProtoPath,
192			Description: "all_aconfig_declarations_textproto",
193			Args: map[string]string{
194				"cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "),
195			},
196		})
197		ctx.Phony("all_aconfig_declarations_textproto", this.releaseMap[rcName].intermediateTextProtoPath)
198	}
199
200	for _, rcName := range this.sortedConfigNames() {
201		ctx.DistForGoal("droid", this.releaseMap[rcName].intermediateBinaryProtoPath)
202		for _, goal := range []string{"docs", "droid", "sdk"} {
203			ctx.DistForGoalWithFilename(goal, this.releaseMap[rcName].intermediateBinaryProtoPath, assembleFileName(rcName, "flags.pb"))
204			ctx.DistForGoalWithFilename(goal, this.releaseMap[rcName].intermediateTextProtoPath, assembleFileName(rcName, "flags.textproto"))
205		}
206	}
207	ctx.DistForGoalWithFilename("sdk", this.finalizedFlags, "finalized-flags.txt")
208}
209