• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2017 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	"slices"
19	"sync"
20
21	"github.com/google/blueprint"
22)
23
24// SingletonContext
25type SingletonContext interface {
26	blueprintSingletonContext() blueprint.SingletonContext
27
28	Config() Config
29	DeviceConfig() DeviceConfig
30
31	ModuleName(module blueprint.Module) string
32	ModuleDir(module blueprint.Module) string
33	ModuleSubDir(module blueprint.Module) string
34	ModuleType(module blueprint.Module) string
35	BlueprintFile(module blueprint.Module) string
36
37	// ModuleVariantsFromName returns the list of module variants named `name` in the same namespace as `referer` enforcing visibility rules.
38	// Allows generating build actions for `referer` based on the metadata for `name` deferred until the singleton context.
39	ModuleVariantsFromName(referer ModuleProxy, name string) []ModuleProxy
40
41	otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
42
43	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
44	Errorf(format string, args ...interface{})
45	Failed() bool
46
47	Variable(pctx PackageContext, name, value string)
48	Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule
49	Build(pctx PackageContext, params BuildParams)
50
51	// Phony creates a Make-style phony rule, a rule with no commands that can depend on other
52	// phony rules or real files.  Phony can be called on the same name multiple times to add
53	// additional dependencies.
54	Phony(name string, deps ...Path)
55
56	RequireNinjaVersion(major, minor, micro int)
57
58	// SetOutDir sets the value of the top-level "builddir" Ninja variable
59	// that controls where Ninja stores its build log files.  This value can be
60	// set at most one time for a single build, later calls are ignored.
61	SetOutDir(pctx PackageContext, value string)
62
63	// Eval takes a string with embedded ninja variables, and returns a string
64	// with all of the variables recursively expanded. Any variables references
65	// are expanded in the scope of the PackageContext.
66	Eval(pctx PackageContext, ninjaStr string) (string, error)
67
68	VisitAllModulesBlueprint(visit func(blueprint.Module))
69	VisitAllModules(visit func(Module))
70	VisitAllModuleProxies(visit func(proxy ModuleProxy))
71	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
72
73	VisitDirectDeps(module Module, visit func(Module))
74	VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module))
75
76	// Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
77	VisitDepsDepthFirst(module Module, visit func(Module))
78	// Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
79	VisitDepsDepthFirstIf(module Module, pred func(Module) bool,
80		visit func(Module))
81
82	VisitAllModuleVariants(module Module, visit func(Module))
83
84	VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy))
85
86	PrimaryModule(module Module) Module
87
88	PrimaryModuleProxy(module ModuleProxy) ModuleProxy
89
90	IsFinalModule(module Module) bool
91
92	AddNinjaFileDeps(deps ...string)
93
94	// GlobWithDeps returns a list of files that match the specified pattern but do not match any
95	// of the patterns in excludes.  It also adds efficient dependencies to rerun the primary
96	// builder whenever a file matching the pattern as added or removed, without rerunning if a
97	// file that does not match the pattern is added to a searched directory.
98	GlobWithDeps(pattern string, excludes []string) ([]string, error)
99
100	// OtherModulePropertyErrorf reports an error on the line number of the given property of the given module
101	OtherModulePropertyErrorf(module Module, property string, format string, args ...interface{})
102
103	// HasMutatorFinished returns true if the given mutator has finished running.
104	// It will panic if given an invalid mutator name.
105	HasMutatorFinished(mutatorName string) bool
106
107	// DistForGoals creates a rule to copy one or more Paths to the artifacts
108	// directory on the build server when any of the specified goals are built.
109	DistForGoal(goal string, paths ...Path)
110
111	// DistForGoalWithFilename creates a rule to copy a Path to the artifacts
112	// directory on the build server with the given filename when the specified
113	// goal is built.
114	DistForGoalWithFilename(goal string, path Path, filename string)
115
116	// DistForGoals creates a rule to copy one or more Paths to the artifacts
117	// directory on the build server when any of the specified goals are built.
118	DistForGoals(goals []string, paths ...Path)
119
120	// DistForGoalsWithFilename creates a rule to copy a Path to the artifacts
121	// directory on the build server with the given filename when any of the
122	// specified goals are built.
123	DistForGoalsWithFilename(goals []string, path Path, filename string)
124}
125
126type singletonAdaptor struct {
127	Singleton
128
129	buildParams []BuildParams
130	ruleParams  map[blueprint.Rule]blueprint.RuleParams
131}
132
133var _ testBuildProvider = (*singletonAdaptor)(nil)
134
135func (s *singletonAdaptor) GenerateBuildActions(ctx blueprint.SingletonContext) {
136	sctx := &singletonContextAdaptor{SingletonContext: ctx}
137	if sctx.Config().captureBuild {
138		sctx.ruleParams = make(map[blueprint.Rule]blueprint.RuleParams)
139	}
140
141	s.Singleton.GenerateBuildActions(sctx)
142
143	s.buildParams = sctx.buildParams
144	s.ruleParams = sctx.ruleParams
145
146	if len(sctx.dists) > 0 {
147		dists := getSingletonDists(sctx.Config())
148		dists.lock.Lock()
149		defer dists.lock.Unlock()
150		dists.dists = append(dists.dists, sctx.dists...)
151	}
152}
153
154func (s *singletonAdaptor) BuildParamsForTests() []BuildParams {
155	return s.buildParams
156}
157
158func (s *singletonAdaptor) RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams {
159	return s.ruleParams
160}
161
162var singletonDistsKey = NewOnceKey("singletonDistsKey")
163
164type singletonDistsAndLock struct {
165	dists []dist
166	lock  sync.Mutex
167}
168
169func getSingletonDists(config Config) *singletonDistsAndLock {
170	return config.Once(singletonDistsKey, func() interface{} {
171		return &singletonDistsAndLock{}
172	}).(*singletonDistsAndLock)
173}
174
175type Singleton interface {
176	GenerateBuildActions(SingletonContext)
177}
178
179type singletonContextAdaptor struct {
180	blueprint.SingletonContext
181
182	buildParams []BuildParams
183	ruleParams  map[blueprint.Rule]blueprint.RuleParams
184	dists       []dist
185}
186
187func (s *singletonContextAdaptor) blueprintSingletonContext() blueprint.SingletonContext {
188	return s.SingletonContext
189}
190
191func (s *singletonContextAdaptor) Config() Config {
192	return s.SingletonContext.Config().(Config)
193}
194
195func (s *singletonContextAdaptor) DeviceConfig() DeviceConfig {
196	return DeviceConfig{s.Config().deviceConfig}
197}
198
199func (s *singletonContextAdaptor) Variable(pctx PackageContext, name, value string) {
200	s.SingletonContext.Variable(pctx.PackageContext, name, value)
201}
202
203func (s *singletonContextAdaptor) Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule {
204	if s.Config().UseRemoteBuild() {
205		if params.Pool == nil {
206			// When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by goma/RBE, restrict
207			// jobs to the local parallelism value
208			params.Pool = localPool
209		} else if params.Pool == remotePool {
210			// remotePool is a fake pool used to identify rule that are supported for remoting. If the rule's
211			// pool is the remotePool, replace with nil so that ninja runs it at NINJA_REMOTE_NUM_JOBS
212			// parallelism.
213			params.Pool = nil
214		}
215	}
216	rule := s.SingletonContext.Rule(pctx.PackageContext, name, params, argNames...)
217	if s.Config().captureBuild {
218		s.ruleParams[rule] = params
219	}
220	return rule
221}
222
223func (s *singletonContextAdaptor) Build(pctx PackageContext, params BuildParams) {
224	if s.Config().captureBuild {
225		s.buildParams = append(s.buildParams, params)
226	}
227	bparams := convertBuildParams(params)
228	s.SingletonContext.Build(pctx.PackageContext, bparams)
229}
230
231func (s *singletonContextAdaptor) Phony(name string, deps ...Path) {
232	addSingletonPhony(s.Config(), name, deps...)
233}
234
235func (s *singletonContextAdaptor) SetOutDir(pctx PackageContext, value string) {
236	s.SingletonContext.SetOutDir(pctx.PackageContext, value)
237}
238
239func (s *singletonContextAdaptor) Eval(pctx PackageContext, ninjaStr string) (string, error) {
240	return s.SingletonContext.Eval(pctx.PackageContext, ninjaStr)
241}
242
243// visitAdaptor wraps a visit function that takes an android.Module parameter into
244// a function that takes a blueprint.Module parameter and only calls the visit function if the
245// blueprint.Module is an android.Module.
246func visitAdaptor(visit func(Module)) func(blueprint.Module) {
247	return func(module blueprint.Module) {
248		if aModule, ok := module.(Module); ok {
249			visit(aModule)
250		}
251	}
252}
253
254// visitProxyAdaptor wraps a visit function that takes an android.ModuleProxy parameter into
255// a function that takes a blueprint.ModuleProxy parameter.
256func visitProxyAdaptor(visit func(proxy ModuleProxy)) func(proxy blueprint.ModuleProxy) {
257	return func(module blueprint.ModuleProxy) {
258		visit(ModuleProxy{
259			module: module,
260		})
261	}
262}
263
264// predAdaptor wraps a pred function that takes an android.Module parameter
265// into a function that takes an blueprint.Module parameter and only calls the visit function if the
266// blueprint.Module is an android.Module, otherwise returns false.
267func predAdaptor(pred func(Module) bool) func(blueprint.Module) bool {
268	return func(module blueprint.Module) bool {
269		if aModule, ok := module.(Module); ok {
270			return pred(aModule)
271		} else {
272			return false
273		}
274	}
275}
276
277func (s *singletonContextAdaptor) ModuleName(module blueprint.Module) string {
278	return s.SingletonContext.ModuleName(getWrappedModule(module))
279}
280
281func (s *singletonContextAdaptor) ModuleDir(module blueprint.Module) string {
282	return s.SingletonContext.ModuleDir(getWrappedModule(module))
283}
284
285func (s *singletonContextAdaptor) ModuleSubDir(module blueprint.Module) string {
286	return s.SingletonContext.ModuleSubDir(getWrappedModule(module))
287}
288
289func (s *singletonContextAdaptor) ModuleType(module blueprint.Module) string {
290	return s.SingletonContext.ModuleType(getWrappedModule(module))
291}
292
293func (s *singletonContextAdaptor) BlueprintFile(module blueprint.Module) string {
294	return s.SingletonContext.BlueprintFile(getWrappedModule(module))
295}
296
297func (s *singletonContextAdaptor) VisitAllModulesBlueprint(visit func(blueprint.Module)) {
298	s.SingletonContext.VisitAllModules(visit)
299}
300
301func (s *singletonContextAdaptor) VisitAllModules(visit func(Module)) {
302	s.SingletonContext.VisitAllModules(visitAdaptor(visit))
303}
304
305func (s *singletonContextAdaptor) VisitAllModuleProxies(visit func(proxy ModuleProxy)) {
306	s.SingletonContext.VisitAllModuleProxies(visitProxyAdaptor(visit))
307}
308
309func (s *singletonContextAdaptor) VisitAllModulesIf(pred func(Module) bool, visit func(Module)) {
310	s.SingletonContext.VisitAllModulesIf(predAdaptor(pred), visitAdaptor(visit))
311}
312
313func (s *singletonContextAdaptor) VisitDirectDeps(module Module, visit func(Module)) {
314	s.SingletonContext.VisitDirectDeps(module, visitAdaptor(visit))
315}
316
317func (s *singletonContextAdaptor) VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) {
318	s.SingletonContext.VisitDirectDepsIf(module, predAdaptor(pred), visitAdaptor(visit))
319}
320
321func (s *singletonContextAdaptor) VisitDepsDepthFirst(module Module, visit func(Module)) {
322	s.SingletonContext.VisitDepsDepthFirst(module, visitAdaptor(visit))
323}
324
325func (s *singletonContextAdaptor) VisitDepsDepthFirstIf(module Module, pred func(Module) bool, visit func(Module)) {
326	s.SingletonContext.VisitDepsDepthFirstIf(module, predAdaptor(pred), visitAdaptor(visit))
327}
328
329func (s *singletonContextAdaptor) VisitAllModuleVariants(module Module, visit func(Module)) {
330	s.SingletonContext.VisitAllModuleVariants(module, visitAdaptor(visit))
331}
332
333func (s *singletonContextAdaptor) VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy)) {
334	s.SingletonContext.VisitAllModuleVariantProxies(getWrappedModule(module), visitProxyAdaptor(visit))
335}
336
337func (s *singletonContextAdaptor) PrimaryModule(module Module) Module {
338	return s.SingletonContext.PrimaryModule(module).(Module)
339}
340
341func (s *singletonContextAdaptor) PrimaryModuleProxy(module ModuleProxy) ModuleProxy {
342	return ModuleProxy{s.SingletonContext.PrimaryModuleProxy(module.module)}
343}
344
345func (s *singletonContextAdaptor) IsFinalModule(module Module) bool {
346	return s.SingletonContext.IsFinalModule(getWrappedModule(module))
347}
348
349func (s *singletonContextAdaptor) ModuleVariantsFromName(referer ModuleProxy, name string) []ModuleProxy {
350	// get module reference for visibility enforcement
351	qualified := createVisibilityModuleProxyReference(s, s.ModuleName(referer), s.ModuleDir(referer), referer)
352
353	modules := s.SingletonContext.ModuleVariantsFromName(referer.module, name)
354	result := make([]ModuleProxy, 0, len(modules))
355	for _, module := range modules {
356		// enforce visibility
357		depName := s.ModuleName(module)
358		depDir := s.ModuleDir(module)
359		depQualified := qualifiedModuleName{depDir, depName}
360		// Targets are always visible to other targets in their own package.
361		if depQualified.pkg != qualified.name.pkg {
362			rule := effectiveVisibilityRules(s.Config(), depQualified)
363			if !rule.matches(qualified) {
364				s.ModuleErrorf(referer, "module %q references %q which is not visible to this module\nYou may need to add %q to its visibility",
365					referer.Name(), depQualified, "//"+s.ModuleDir(referer))
366				continue
367			}
368		}
369		result = append(result, ModuleProxy{module})
370	}
371	return result
372}
373
374func (s *singletonContextAdaptor) otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
375	return s.SingletonContext.ModuleProvider(module, provider)
376}
377
378func (s *singletonContextAdaptor) OtherModulePropertyErrorf(module Module, property string, format string, args ...interface{}) {
379	s.blueprintSingletonContext().OtherModulePropertyErrorf(module, property, format, args...)
380}
381
382func (s *singletonContextAdaptor) HasMutatorFinished(mutatorName string) bool {
383	return s.blueprintSingletonContext().HasMutatorFinished(mutatorName)
384}
385func (s *singletonContextAdaptor) DistForGoal(goal string, paths ...Path) {
386	s.DistForGoals([]string{goal}, paths...)
387}
388
389func (s *singletonContextAdaptor) DistForGoalWithFilename(goal string, path Path, filename string) {
390	s.DistForGoalsWithFilename([]string{goal}, path, filename)
391}
392
393func (s *singletonContextAdaptor) DistForGoals(goals []string, paths ...Path) {
394	var copies distCopies
395	for _, path := range paths {
396		copies = append(copies, distCopy{
397			from: path,
398			dest: path.Base(),
399		})
400	}
401	s.dists = append(s.dists, dist{
402		goals: slices.Clone(goals),
403		paths: copies,
404	})
405}
406
407func (s *singletonContextAdaptor) DistForGoalsWithFilename(goals []string, path Path, filename string) {
408	s.dists = append(s.dists, dist{
409		goals: slices.Clone(goals),
410		paths: distCopies{{from: path, dest: filename}},
411	})
412}
413