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	// ModuleErrorf reports an error at the line number of the module type in the module definition.
51	ModuleErrorf(module Module, format string, args ...interface{})
52
53	// Errorf reports an error at the specified position of the module definition file.
54	Errorf(format string, args ...interface{})
55
56	// Failed returns true if any errors have been reported.  In most cases the singleton can continue with generating
57	// build rules after an error, allowing it to report additional errors in a single run, but in cases where the error
58	// has prevented the singleton from creating necessary data it can return early when Failed returns true.
59	Failed() bool
60
61	// Variable creates a new ninja variable scoped to the singleton.  It can be referenced by calls to Rule and Build
62	// in the same singleton.
63	Variable(pctx PackageContext, name, value string)
64
65	// Rule creates a new ninja rule scoped to the singleton.  It can be referenced by calls to Build in the same
66	// singleton.
67	Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule
68
69	// Build creates a new ninja build statement.
70	Build(pctx PackageContext, params BuildParams)
71
72	// RequireNinjaVersion sets the generated ninja manifest to require at least the specified version of ninja.
73	RequireNinjaVersion(major, minor, micro int)
74
75	// SetNinjaBuildDir sets the value of the top-level "builddir" Ninja variable
76	// that controls where Ninja stores its build log files.  This value can be
77	// set at most one time for a single build, later calls are ignored.
78	SetNinjaBuildDir(pctx PackageContext, value string)
79
80	// AddSubninja adds a ninja file to include with subninja. This should likely
81	// only ever be used inside bootstrap to handle glob rules.
82	AddSubninja(file string)
83
84	// Eval takes a string with embedded ninja variables, and returns a string
85	// with all of the variables recursively expanded. Any variables references
86	// are expanded in the scope of the PackageContext.
87	Eval(pctx PackageContext, ninjaStr string) (string, error)
88
89	// VisitAllModules calls visit for each defined variant of each module in an unspecified order.
90	VisitAllModules(visit func(Module))
91
92	// VisitAllModules calls pred for each defined variant of each module in an unspecified order, and if pred returns
93	// true calls visit.
94	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
95
96	// VisitDirectDeps calls visit for each direct dependency of the Module.  If there are
97	// multiple direct dependencies on the same module visit will be called multiple times on
98	// that module and OtherModuleDependencyTag will return a different tag for each.
99	//
100	// The Module passed to the visit function should not be retained outside of the visit
101	// function, it may be invalidated by future mutators.
102	VisitDirectDeps(module Module, visit func(Module))
103
104	// VisitDirectDepsIf calls pred for each direct dependency of the Module, and if pred
105	// returns true calls visit.  If there are multiple direct dependencies on the same module
106	// pred and visit will be called multiple times on that module and OtherModuleDependencyTag
107	// will return a different tag for each.
108	//
109	// The Module passed to the visit function should not be retained outside of the visit
110	// function, it may be invalidated by future mutators.
111	VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module))
112
113	// VisitDepsDepthFirst calls visit for each transitive dependency, traversing the dependency tree in depth first
114	// order. visit will only be called once for any given module, even if there are multiple paths through the
115	// dependency tree to the module or multiple direct dependencies with different tags.
116	VisitDepsDepthFirst(module Module, visit func(Module))
117
118	// VisitDepsDepthFirst calls pred for each transitive dependency, and if pred returns true calls visit, traversing
119	// the dependency tree in depth first order.  visit will only be called once for any given module, even if there are
120	// multiple paths through the dependency tree to the module or multiple direct dependencies with different tags.
121	VisitDepsDepthFirstIf(module Module, pred func(Module) bool,
122		visit func(Module))
123
124	// VisitAllModuleVariants calls visit for each variant of the given module.
125	VisitAllModuleVariants(module Module, visit func(Module))
126
127	// PrimaryModule returns the first variant of the given module.  This can be used to perform
128	//	// singleton actions that are only done once for all variants of a module.
129	PrimaryModule(module Module) Module
130
131	// FinalModule returns the last variant of the given module.  This can be used to perform
132	// singleton actions that are only done once for all variants of a module.
133	FinalModule(module Module) Module
134
135	// AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest.  The
136	// primary builder will be rerun whenever the specified files are modified.
137	AddNinjaFileDeps(deps ...string)
138
139	// GlobWithDeps returns a list of files and directories that match the
140	// specified pattern but do not match any of the patterns in excludes.
141	// Any directories will have a '/' suffix. It also adds efficient
142	// dependencies to rerun the primary builder whenever a file matching
143	// the pattern as added or removed, without rerunning if a file that
144	// does not match the pattern is added to a searched directory.
145	GlobWithDeps(pattern string, excludes []string) ([]string, error)
146
147	// Fs returns a pathtools.Filesystem that can be used to interact with files.  Using the Filesystem interface allows
148	// the singleton to be used in build system tests that run against a mock filesystem.
149	Fs() pathtools.FileSystem
150}
151
152var _ SingletonContext = (*singletonContext)(nil)
153
154type singletonContext struct {
155	name    string
156	context *Context
157	config  interface{}
158	scope   *localScope
159	globals *liveTracker
160
161	ninjaFileDeps []string
162	errs          []error
163
164	actionDefs localBuildActions
165}
166
167func (s *singletonContext) Config() interface{} {
168	return s.config
169}
170
171func (s *singletonContext) Name() string {
172	return s.name
173}
174
175func (s *singletonContext) ModuleName(logicModule Module) string {
176	return s.context.ModuleName(logicModule)
177}
178
179func (s *singletonContext) ModuleDir(logicModule Module) string {
180	return s.context.ModuleDir(logicModule)
181}
182
183func (s *singletonContext) ModuleSubDir(logicModule Module) string {
184	return s.context.ModuleSubDir(logicModule)
185}
186
187func (s *singletonContext) ModuleType(logicModule Module) string {
188	return s.context.ModuleType(logicModule)
189}
190
191func (s *singletonContext) BlueprintFile(logicModule Module) string {
192	return s.context.BlueprintFile(logicModule)
193}
194
195func (s *singletonContext) error(err error) {
196	if err != nil {
197		s.errs = append(s.errs, err)
198	}
199}
200
201func (s *singletonContext) ModuleErrorf(logicModule Module, format string,
202	args ...interface{}) {
203
204	s.error(s.context.ModuleErrorf(logicModule, format, args...))
205}
206
207func (s *singletonContext) Errorf(format string, args ...interface{}) {
208	// TODO: Make this not result in the error being printed as "internal error"
209	s.error(fmt.Errorf(format, args...))
210}
211
212func (s *singletonContext) Failed() bool {
213	return len(s.errs) > 0
214}
215
216func (s *singletonContext) Variable(pctx PackageContext, name, value string) {
217	s.scope.ReparentTo(pctx)
218
219	v, err := s.scope.AddLocalVariable(name, value)
220	if err != nil {
221		panic(err)
222	}
223
224	s.actionDefs.variables = append(s.actionDefs.variables, v)
225}
226
227func (s *singletonContext) Rule(pctx PackageContext, name string,
228	params RuleParams, argNames ...string) Rule {
229
230	s.scope.ReparentTo(pctx)
231
232	r, err := s.scope.AddLocalRule(name, &params, argNames...)
233	if err != nil {
234		panic(err)
235	}
236
237	s.actionDefs.rules = append(s.actionDefs.rules, r)
238
239	return r
240}
241
242func (s *singletonContext) Build(pctx PackageContext, params BuildParams) {
243	s.scope.ReparentTo(pctx)
244
245	def, err := parseBuildParams(s.scope, &params)
246	if err != nil {
247		panic(err)
248	}
249
250	s.actionDefs.buildDefs = append(s.actionDefs.buildDefs, def)
251}
252
253func (s *singletonContext) Eval(pctx PackageContext, str string) (string, error) {
254	s.scope.ReparentTo(pctx)
255
256	ninjaStr, err := parseNinjaString(s.scope, str)
257	if err != nil {
258		return "", err
259	}
260
261	err = s.globals.addNinjaStringDeps(ninjaStr)
262	if err != nil {
263		return "", err
264	}
265
266	return ninjaStr.Eval(s.globals.variables)
267}
268
269func (s *singletonContext) RequireNinjaVersion(major, minor, micro int) {
270	s.context.requireNinjaVersion(major, minor, micro)
271}
272
273func (s *singletonContext) SetNinjaBuildDir(pctx PackageContext, value string) {
274	s.scope.ReparentTo(pctx)
275
276	ninjaValue, err := parseNinjaString(s.scope, value)
277	if err != nil {
278		panic(err)
279	}
280
281	s.context.setNinjaBuildDir(ninjaValue)
282}
283
284func (s *singletonContext) AddSubninja(file string) {
285	s.context.subninjas = append(s.context.subninjas, file)
286}
287
288func (s *singletonContext) VisitAllModules(visit func(Module)) {
289	var visitingModule Module
290	defer func() {
291		if r := recover(); r != nil {
292			panic(newPanicErrorf(r, "VisitAllModules(%s) for module %s",
293				funcName(visit), s.context.moduleInfo[visitingModule]))
294		}
295	}()
296
297	s.context.VisitAllModules(func(m Module) {
298		visitingModule = m
299		visit(m)
300	})
301}
302
303func (s *singletonContext) VisitAllModulesIf(pred func(Module) bool,
304	visit func(Module)) {
305
306	s.context.VisitAllModulesIf(pred, visit)
307}
308
309func (s *singletonContext) VisitDirectDeps(module Module, visit func(Module)) {
310	s.context.VisitDirectDeps(module, visit)
311}
312
313func (s *singletonContext) VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) {
314	s.context.VisitDirectDepsIf(module, pred, visit)
315}
316
317func (s *singletonContext) VisitDepsDepthFirst(module Module,
318	visit func(Module)) {
319
320	s.context.VisitDepsDepthFirst(module, visit)
321}
322
323func (s *singletonContext) VisitDepsDepthFirstIf(module Module,
324	pred func(Module) bool, visit func(Module)) {
325
326	s.context.VisitDepsDepthFirstIf(module, pred, visit)
327}
328
329func (s *singletonContext) PrimaryModule(module Module) Module {
330	return s.context.PrimaryModule(module)
331}
332
333func (s *singletonContext) FinalModule(module Module) Module {
334	return s.context.FinalModule(module)
335}
336
337func (s *singletonContext) VisitAllModuleVariants(module Module, visit func(Module)) {
338	s.context.VisitAllModuleVariants(module, visit)
339}
340
341func (s *singletonContext) AddNinjaFileDeps(deps ...string) {
342	s.ninjaFileDeps = append(s.ninjaFileDeps, deps...)
343}
344
345func (s *singletonContext) GlobWithDeps(pattern string,
346	excludes []string) ([]string, error) {
347	return s.context.glob(pattern, excludes)
348}
349
350func (s *singletonContext) Fs() pathtools.FileSystem {
351	return s.context.fs
352}
353