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 android
16
17import (
18	"github.com/google/blueprint"
19	"github.com/google/blueprint/proptools"
20)
21
22func init() {
23	RegisterApexContributionsBuildComponents(InitRegistrationContext)
24}
25
26func RegisterApexContributionsBuildComponents(ctx RegistrationContext) {
27	ctx.RegisterModuleType("apex_contributions", apexContributionsFactory)
28	ctx.RegisterModuleType("apex_contributions_defaults", apexContributionsDefaultsFactory)
29	ctx.RegisterSingletonModuleType("all_apex_contributions", allApexContributionsFactory)
30}
31
32type apexContributions struct {
33	ModuleBase
34	DefaultableModuleBase
35	properties contributionProps
36}
37
38type contributionProps struct {
39	// Name of the mainline module
40	Api_domain *string
41	// A list of module names that should be used when this contribution
42	// is selected via product_config
43	// The name should be explicit (foo or prebuilt_foo)
44	Contents []string
45}
46
47func (m *apexContributions) ApiDomain() string {
48	return proptools.String(m.properties.Api_domain)
49}
50
51func (m *apexContributions) Contents() []string {
52	return m.properties.Contents
53}
54
55// apex_contributions contains a list of module names (source or
56// prebuilt) belonging to the mainline module
57// An apex can have multiple apex_contributions modules
58// with different combinations of source or prebuilts, but only one can be
59// selected via product_config.
60func apexContributionsFactory() Module {
61	module := &apexContributions{}
62	module.AddProperties(&module.properties)
63	InitAndroidModule(module)
64	InitDefaultableModule(module)
65	return module
66}
67
68// This module type does not have any build actions.
69// It provides metadata that is used in post-deps mutator phase for source vs
70// prebuilts selection.
71func (m *apexContributions) GenerateAndroidBuildActions(ctx ModuleContext) {
72}
73
74type apexContributionsDefaults struct {
75	ModuleBase
76	DefaultsModuleBase
77}
78
79func apexContributionsDefaultsFactory() Module {
80	module := &apexContributionsDefaults{}
81	module.AddProperties(&contributionProps{})
82	InitDefaultsModule(module)
83	return module
84}
85
86// A container for apex_contributions.
87// Based on product_config, it will create a dependency on the selected
88// apex_contributions per mainline module
89type allApexContributions struct {
90	SingletonModuleBase
91}
92
93func allApexContributionsFactory() SingletonModule {
94	module := &allApexContributions{}
95	InitAndroidModule(module)
96	return module
97}
98
99type apexContributionsDepTag struct {
100	blueprint.BaseDependencyTag
101}
102
103var (
104	AcDepTag = apexContributionsDepTag{}
105)
106
107// Creates a dep to each selected apex_contributions
108func (a *allApexContributions) DepsMutator(ctx BottomUpMutatorContext) {
109	// Skip apex_contributions if BuildApexContributionContents is true
110	// This product config var allows some products in the same family to use mainline modules from source
111	// (e.g. shiba and shiba_fullmte)
112	// Eventually these product variants will have their own release config maps.
113	if !proptools.Bool(ctx.Config().BuildIgnoreApexContributionContents()) {
114		ctx.AddDependency(ctx.Module(), AcDepTag, ctx.Config().AllApexContributions()...)
115	}
116}
117
118// Set PrebuiltSelectionInfoProvider in post deps phase
119func (a *allApexContributions) SetPrebuiltSelectionInfoProvider(ctx BaseModuleContext) {
120	addContentsToProvider := func(p *PrebuiltSelectionInfoMap, m *apexContributions) {
121		for _, content := range m.Contents() {
122			// Verify that the module listed in contents exists in the tree
123			// Remove the prebuilt_ prefix to account for partner worksapces where the source module does not
124			// exist, and PrebuiltRenameMutator renames `prebuilt_foo` to `foo`
125			if !ctx.OtherModuleExists(content) && !ctx.OtherModuleExists(RemoveOptionalPrebuiltPrefix(content)) && !ctx.Config().AllowMissingDependencies() {
126				ctx.ModuleErrorf("%s listed in apex_contributions %s does not exist\n", content, m.Name())
127			}
128			pi := &PrebuiltSelectionInfo{
129				selectedModuleName: content,
130				metadataModuleName: m.Name(),
131				apiDomain:          m.ApiDomain(),
132			}
133			p.Add(ctx, pi)
134		}
135	}
136
137	p := PrebuiltSelectionInfoMap{}
138	ctx.VisitDirectDepsWithTag(AcDepTag, func(child Module) {
139		if m, ok := child.(*apexContributions); ok {
140			addContentsToProvider(&p, m)
141		} else {
142			ctx.ModuleErrorf("%s is not an apex_contributions module\n", child.Name())
143		}
144	})
145	SetProvider(ctx, PrebuiltSelectionInfoProvider, p)
146}
147
148// A provider containing metadata about whether source or prebuilt should be used
149// This provider will be used in prebuilt_select mutator to redirect deps
150var PrebuiltSelectionInfoProvider = blueprint.NewMutatorProvider[PrebuiltSelectionInfoMap]("prebuilt_select")
151
152// Map of selected module names to a metadata object
153// The metadata contains information about the api_domain of the selected module
154type PrebuiltSelectionInfoMap map[string]PrebuiltSelectionInfo
155
156// Add a new entry to the map with some validations
157func (pm *PrebuiltSelectionInfoMap) Add(ctx BaseModuleContext, p *PrebuiltSelectionInfo) {
158	if p == nil {
159		return
160	}
161	(*pm)[p.selectedModuleName] = *p
162}
163
164type PrebuiltSelectionInfo struct {
165	// e.g. (libc|prebuilt_libc)
166	selectedModuleName string
167	// Name of the apex_contributions module
168	metadataModuleName string
169	// e.g. com.android.runtime
170	apiDomain string
171}
172
173// Returns true if `name` is explicitly requested using one of the selected
174// apex_contributions metadata modules.
175func (p *PrebuiltSelectionInfoMap) IsSelected(name string) bool {
176	_, exists := (*p)[name]
177	return exists
178}
179
180// Return the list of soong modules selected for this api domain
181// In the case of apexes, it is the canonical name of the apex on device (/apex/<apex_name>)
182func (p *PrebuiltSelectionInfoMap) GetSelectedModulesForApiDomain(apiDomain string) []string {
183	selected := []string{}
184	for _, entry := range *p {
185		if entry.apiDomain == apiDomain {
186			selected = append(selected, entry.selectedModuleName)
187		}
188	}
189	return selected
190}
191
192// This module type does not have any build actions.
193func (a *allApexContributions) GenerateAndroidBuildActions(ctx ModuleContext) {
194}
195
196func (a *allApexContributions) GenerateSingletonBuildActions(ctx SingletonContext) {
197}
198