• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2014 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 blueprint
16
17import (
18	"fmt"
19
20	"github.com/google/blueprint/pathtools"
21)
22
23type Singleton interface {
24	GenerateBuildActions(SingletonContext)
25}
26
27type SingletonContext interface {
28	// Config returns the config object that was passed to Context.PrepareBuildActions.
29	Config() interface{}
30
31	// Name returns the name of the current singleton passed to Context.RegisterSingletonType
32	Name() string
33
34	// ModuleName returns the name of the given Module.  See BaseModuleContext.ModuleName for more information.
35	ModuleName(module Module) string
36
37	// ModuleDir returns the directory of the given Module.  See BaseModuleContext.ModuleDir for more information.
38	ModuleDir(module Module) string
39
40	// ModuleSubDir returns the unique subdirectory name of the given Module.  See ModuleContext.ModuleSubDir for
41	// more information.
42	ModuleSubDir(module Module) string
43
44	// ModuleType returns the type of the given Module.  See BaseModuleContext.ModuleType for more information.
45	ModuleType(module Module) string
46
47	// BlueprintFile returns the path of the Blueprint file that defined the given module.
48	BlueprintFile(module Module) string
49
50	// ModuleProvider returns the value, if any, for the provider for a module.  If the value for the
51	// provider was not set it returns the zero value of the type of the provider, which means the
52	// return value can always be type-asserted to the type of the provider.  The return value should
53	// always be considered read-only.  It panics if called before the appropriate mutator or
54	// GenerateBuildActions pass for the provider on the module.
55	ModuleProvider(module Module, provider AnyProviderKey) (any, bool)
56
57	// ModuleErrorf reports an error at the line number of the module type in the module definition.
58	ModuleErrorf(module Module, format string, args ...interface{})
59
60	// Errorf reports an error at the specified position of the module definition file.
61	Errorf(format string, args ...interface{})
62
63	// OtherModulePropertyErrorf reports an error on the line number of the given property of the given module
64	OtherModulePropertyErrorf(module Module, property string, format string, args ...interface{})
65
66	// Failed returns true if any errors have been reported.  In most cases the singleton can continue with generating
67	// build rules after an error, allowing it to report additional errors in a single run, but in cases where the error
68	// has prevented the singleton from creating necessary data it can return early when Failed returns true.
69	Failed() bool
70
71	// Variable creates a new ninja variable scoped to the singleton.  It can be referenced by calls to Rule and Build
72	// in the same singleton.
73	Variable(pctx PackageContext, name, value string)
74
75	// Rule creates a new ninja rule scoped to the singleton.  It can be referenced by calls to Build in the same
76	// singleton.
77	Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule
78
79	// Build creates a new ninja build statement.
80	Build(pctx PackageContext, params BuildParams)
81
82	// RequireNinjaVersion sets the generated ninja manifest to require at least the specified version of ninja.
83	RequireNinjaVersion(major, minor, micro int)
84
85	// SetOutDir sets the value of the top-level "builddir" Ninja variable
86	// that controls where Ninja stores its build log files.  This value can be
87	// set at most one time for a single build, later calls are ignored.
88	SetOutDir(pctx PackageContext, value string)
89
90	// AddSubninja adds a ninja file to include with subninja. This should likely
91	// only ever be used inside bootstrap to handle glob rules.
92	AddSubninja(file string)
93
94	// Eval takes a string with embedded ninja variables, and returns a string
95	// with all of the variables recursively expanded. Any variables references
96	// are expanded in the scope of the PackageContext.
97	Eval(pctx PackageContext, ninjaStr string) (string, error)
98
99	// VisitAllModules calls visit for each defined variant of each module in an unspecified order.
100	VisitAllModules(visit func(Module))
101
102	// VisitAllModuleProxies calls visit for each defined variant of each module in an unspecified order.
103	VisitAllModuleProxies(visit func(proxy ModuleProxy))
104
105	// VisitAllModules calls pred for each defined variant of each module in an unspecified order, and if pred returns
106	// true calls visit.
107	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
108
109	// VisitDirectDeps calls visit for each direct dependency of the Module.  If there are
110	// multiple direct dependencies on the same module visit will be called multiple times on
111	// that module and OtherModuleDependencyTag will return a different tag for each.
112	//
113	// The Module passed to the visit function should not be retained outside of the visit
114	// function, it may be invalidated by future mutators.
115	VisitDirectDeps(module Module, visit func(Module))
116
117	// VisitDirectDepsIf calls pred for each direct dependency of the Module, and if pred
118	// returns true calls visit.  If there are multiple direct dependencies on the same module
119	// pred and visit will be called multiple times on that module and OtherModuleDependencyTag
120	// will return a different tag for each.
121	//
122	// The Module passed to the visit function should not be retained outside of the visit
123	// function, it may be invalidated by future mutators.
124	VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module))
125
126	// VisitDepsDepthFirst calls visit for each transitive dependency, traversing the dependency tree in depth first
127	// order. visit will only be called once for any given module, even if there are multiple paths through the
128	// dependency tree to the module or multiple direct dependencies with different tags.
129	VisitDepsDepthFirst(module Module, visit func(Module))
130
131	// VisitDepsDepthFirst calls pred for each transitive dependency, and if pred returns true calls visit, traversing
132	// the dependency tree in depth first order.  visit will only be called once for any given module, even if there are
133	// multiple paths through the dependency tree to the module or multiple direct dependencies with different tags.
134	VisitDepsDepthFirstIf(module Module, pred func(Module) bool,
135		visit func(Module))
136
137	// VisitAllModuleVariants calls visit for each variant of the given module.
138	VisitAllModuleVariants(module Module, visit func(Module))
139
140	// VisitAllModuleVariantProxies calls visit for each variant of the given module.
141	VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy))
142
143	// PrimaryModule returns the first variant of the given module.  This can be used to perform
144	// singleton actions that are only done once for all variants of a module.
145	PrimaryModule(module Module) Module
146
147	// PrimaryModuleProxy returns the proxy of the first variant of the given module.
148	// This can be used to perform singleton actions that are only done once for
149	// all variants of a module.
150	PrimaryModuleProxy(module ModuleProxy) ModuleProxy
151
152	// IsFinalModule returns if the given module is the last variant. This can be used to perform
153	// singleton actions that are only done once for all variants of a module.
154	IsFinalModule(module Module) bool
155
156	// AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest.  The
157	// primary builder will be rerun whenever the specified files are modified.
158	AddNinjaFileDeps(deps ...string)
159
160	// GlobWithDeps returns a list of files and directories that match the
161	// specified pattern but do not match any of the patterns in excludes.
162	// Any directories will have a '/' suffix. It also adds efficient
163	// dependencies to rerun the primary builder whenever a file matching
164	// the pattern as added or removed, without rerunning if a file that
165	// does not match the pattern is added to a searched directory.
166	GlobWithDeps(pattern string, excludes []string) ([]string, error)
167
168	// Fs returns a pathtools.Filesystem that can be used to interact with files.  Using the Filesystem interface allows
169	// the singleton to be used in build system tests that run against a mock filesystem.
170	Fs() pathtools.FileSystem
171
172	// ModuleVariantsFromName returns the list of module variants named `name` in the same namespace as `referer`.
173	// Allows generating build actions for `referer` based on the metadata for `name` deferred until the singleton context.
174	ModuleVariantsFromName(referer ModuleProxy, name string) []ModuleProxy
175
176	// HasMutatorFinished returns true if the given mutator has finished running.
177	// It will panic if given an invalid mutator name.
178	HasMutatorFinished(mutatorName string) bool
179}
180
181var _ SingletonContext = (*singletonContext)(nil)
182
183type singletonContext struct {
184	name    string
185	context *Context
186	config  interface{}
187	scope   *localScope
188	globals *liveTracker
189
190	ninjaFileDeps []string
191	errs          []error
192
193	actionDefs localBuildActions
194}
195
196func (s *singletonContext) Config() interface{} {
197	return s.config
198}
199
200func (s *singletonContext) Name() string {
201	return s.name
202}
203
204func (s *singletonContext) ModuleName(logicModule Module) string {
205	return s.context.ModuleName(getWrappedModule(logicModule))
206}
207
208func (s *singletonContext) ModuleDir(logicModule Module) string {
209	return s.context.ModuleDir(getWrappedModule(logicModule))
210}
211
212func (s *singletonContext) ModuleSubDir(logicModule Module) string {
213	return s.context.ModuleSubDir(getWrappedModule(logicModule))
214}
215
216func (s *singletonContext) ModuleType(logicModule Module) string {
217	return s.context.ModuleType(getWrappedModule(logicModule))
218}
219
220func (s *singletonContext) ModuleProvider(logicModule Module, provider AnyProviderKey) (any, bool) {
221	return s.context.ModuleProvider(getWrappedModule(logicModule), provider)
222}
223
224func (s *singletonContext) BlueprintFile(logicModule Module) string {
225	return s.context.BlueprintFile(getWrappedModule(logicModule))
226}
227
228func (s *singletonContext) error(err error) {
229	if err != nil {
230		s.errs = append(s.errs, err)
231	}
232}
233
234func (s *singletonContext) ModuleErrorf(logicModule Module, format string,
235	args ...interface{}) {
236
237	s.error(s.context.ModuleErrorf(logicModule, format, args...))
238}
239
240func (s *singletonContext) Errorf(format string, args ...interface{}) {
241	// TODO: Make this not result in the error being printed as "internal error"
242	s.error(fmt.Errorf(format, args...))
243}
244
245func (s *singletonContext) OtherModulePropertyErrorf(logicModule Module, property string, format string,
246	args ...interface{}) {
247
248	s.error(s.context.PropertyErrorf(logicModule, property, format, args...))
249}
250
251func (s *singletonContext) Failed() bool {
252	return len(s.errs) > 0
253}
254
255func (s *singletonContext) Variable(pctx PackageContext, name, value string) {
256	s.scope.ReparentTo(pctx)
257
258	v, err := s.scope.AddLocalVariable(name, value)
259	if err != nil {
260		panic(err)
261	}
262
263	s.actionDefs.variables = append(s.actionDefs.variables, v)
264}
265
266func (s *singletonContext) Rule(pctx PackageContext, name string,
267	params RuleParams, argNames ...string) Rule {
268
269	s.scope.ReparentTo(pctx)
270
271	r, err := s.scope.AddLocalRule(name, &params, argNames...)
272	if err != nil {
273		panic(err)
274	}
275
276	s.actionDefs.rules = append(s.actionDefs.rules, r)
277
278	return r
279}
280
281func (s *singletonContext) Build(pctx PackageContext, params BuildParams) {
282	s.scope.ReparentTo(pctx)
283
284	def, err := parseBuildParams(s.scope, &params, map[string]string{
285		"module_name": s.name,
286		"module_type": "singleton",
287	})
288	if err != nil {
289		panic(err)
290	}
291
292	s.actionDefs.buildDefs = append(s.actionDefs.buildDefs, def)
293}
294
295func (s *singletonContext) Eval(pctx PackageContext, str string) (string, error) {
296	s.scope.ReparentTo(pctx)
297
298	ninjaStr, err := parseNinjaString(s.scope, str)
299	if err != nil {
300		return "", err
301	}
302
303	err = s.globals.addNinjaStringDeps(ninjaStr)
304	if err != nil {
305		return "", err
306	}
307
308	return s.globals.Eval(ninjaStr)
309}
310
311func (s *singletonContext) RequireNinjaVersion(major, minor, micro int) {
312	s.context.requireNinjaVersion(major, minor, micro)
313}
314
315func (s *singletonContext) SetOutDir(pctx PackageContext, value string) {
316	s.scope.ReparentTo(pctx)
317
318	ninjaValue, err := parseNinjaString(s.scope, value)
319	if err != nil {
320		panic(err)
321	}
322
323	s.context.setOutDir(ninjaValue)
324}
325
326func (s *singletonContext) AddSubninja(file string) {
327	s.context.subninjas = append(s.context.subninjas, file)
328}
329
330func (s *singletonContext) VisitAllModules(visit func(Module)) {
331	var visitingModule Module
332	defer func() {
333		if r := recover(); r != nil {
334			panic(newPanicErrorf(r, "VisitAllModules(%s) for module %s",
335				funcName(visit), s.context.moduleInfo[visitingModule]))
336		}
337	}()
338
339	s.context.VisitAllModules(func(m Module) {
340		visitingModule = m
341		visit(m)
342	})
343}
344
345func (s *singletonContext) VisitAllModuleProxies(visit func(proxy ModuleProxy)) {
346	s.VisitAllModules(visitProxyAdaptor(visit))
347}
348
349func (s *singletonContext) VisitAllModulesIf(pred func(Module) bool,
350	visit func(Module)) {
351
352	s.context.VisitAllModulesIf(pred, visit)
353}
354
355func (s *singletonContext) VisitDirectDeps(module Module, visit func(Module)) {
356	s.context.VisitDirectDeps(module, visit)
357}
358
359func (s *singletonContext) VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) {
360	s.context.VisitDirectDepsIf(module, pred, visit)
361}
362
363func (s *singletonContext) VisitDepsDepthFirst(module Module,
364	visit func(Module)) {
365
366	s.context.VisitDepsDepthFirst(module, visit)
367}
368
369func (s *singletonContext) VisitDepsDepthFirstIf(module Module,
370	pred func(Module) bool, visit func(Module)) {
371
372	s.context.VisitDepsDepthFirstIf(module, pred, visit)
373}
374
375func (s *singletonContext) PrimaryModule(module Module) Module {
376	return s.context.PrimaryModule(module)
377}
378
379func (s *singletonContext) PrimaryModuleProxy(module ModuleProxy) ModuleProxy {
380	return ModuleProxy{s.context.PrimaryModule(module.module)}
381}
382
383func (s *singletonContext) IsFinalModule(module Module) bool {
384	return s.context.IsFinalModule(getWrappedModule(module))
385}
386
387func (s *singletonContext) VisitAllModuleVariants(module Module, visit func(Module)) {
388	s.context.VisitAllModuleVariants(module, visit)
389}
390
391func (s *singletonContext) VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy)) {
392	s.context.VisitAllModuleVariants(getWrappedModule(module), visitProxyAdaptor(visit))
393}
394
395func (s *singletonContext) AddNinjaFileDeps(deps ...string) {
396	s.ninjaFileDeps = append(s.ninjaFileDeps, deps...)
397}
398
399func (s *singletonContext) GlobWithDeps(pattern string,
400	excludes []string) ([]string, error) {
401	return s.context.glob(pattern, excludes)
402}
403
404func (s *singletonContext) Fs() pathtools.FileSystem {
405	return s.context.fs
406}
407
408func (s *singletonContext) ModuleVariantsFromName(referer ModuleProxy, name string) []ModuleProxy {
409	c := s.context
410
411	refererInfo := c.moduleInfo[referer.module]
412	if refererInfo == nil {
413		s.ModuleErrorf(referer, "could not find module %q", referer.Name())
414		return nil
415	}
416
417	moduleGroup, exists := c.nameInterface.ModuleFromName(name, refererInfo.namespace())
418	if !exists {
419		return nil
420	}
421	result := make([]ModuleProxy, 0, len(moduleGroup.modules))
422	for _, moduleInfo := range moduleGroup.modules {
423		if moduleInfo.logicModule != nil {
424			result = append(result, ModuleProxy{moduleInfo.logicModule})
425		}
426	}
427	return result
428}
429
430func (s *singletonContext) HasMutatorFinished(mutatorName string) bool {
431	return s.context.HasMutatorFinished(mutatorName)
432}
433
434func visitProxyAdaptor(visit func(proxy ModuleProxy)) func(module Module) {
435	return func(module Module) {
436		visit(ModuleProxy{
437			module: module,
438		})
439	}
440}
441