blob: 455f6fc8eedd57b79bd4b8af7a2a9e4293c699b7 [file] [log] [blame]
Colin Cross8e0c5112015-01-23 14:15:10 -08001// 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
Jamie Gennis1bc967e2014-05-27 16:34:41 -070015package blueprint
16
17import (
18 "fmt"
Colin Crossc5816202017-12-11 14:39:10 -080019
Colin Crossb519a7e2017-02-01 13:21:35 -080020 "github.com/google/blueprint/pathtools"
Jamie Gennis1bc967e2014-05-27 16:34:41 -070021)
22
23type Singleton interface {
24 GenerateBuildActions(SingletonContext)
25}
26
27type SingletonContext interface {
Colin Cross7bcc2562019-05-20 14:51:55 -070028 // Config returns the config object that was passed to Context.PrepareBuildActions.
Jamie Gennis6eb4d242014-06-11 18:31:16 -070029 Config() interface{}
Jamie Gennis1bc967e2014-05-27 16:34:41 -070030
Colin Cross7bcc2562019-05-20 14:51:55 -070031 // Name returns the name of the current singleton passed to Context.RegisterSingletonType
Colin Cross9226d6c2019-02-25 18:07:44 -080032 Name() string
33
Colin Cross7bcc2562019-05-20 14:51:55 -070034 // ModuleName returns the name of the given Module. See BaseModuleContext.ModuleName for more information.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070035 ModuleName(module Module) string
Colin Cross7bcc2562019-05-20 14:51:55 -070036
37 // ModuleDir returns the directory of the given Module. See BaseModuleContext.ModuleDir for more information.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070038 ModuleDir(module Module) string
Colin Cross7bcc2562019-05-20 14:51:55 -070039
40 // ModuleSubDir returns the unique subdirectory name of the given Module. See ModuleContext.ModuleSubDir for
41 // more information.
Colin Cross8c602f72015-12-17 18:02:11 -080042 ModuleSubDir(module Module) string
Colin Cross7bcc2562019-05-20 14:51:55 -070043
44 // ModuleType returns the type of the given Module. See BaseModuleContext.ModuleType for more information.
Dan Willemsenc98e55b2016-07-25 15:51:50 -070045 ModuleType(module Module) string
Colin Cross7bcc2562019-05-20 14:51:55 -070046
47 // BlueprintFile returns the path of the Blueprint file that defined the given module.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070048 BlueprintFile(module Module) string
49
Colin Cross2da84922020-07-02 10:08:12 -070050 // 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 ProviderKey) interface{}
56
57 // ModuleHasProvider returns true if the provider for the given module has been set.
58 ModuleHasProvider(m Module, provider ProviderKey) bool
59
Colin Cross7bcc2562019-05-20 14:51:55 -070060 // ModuleErrorf reports an error at the line number of the module type in the module definition.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070061 ModuleErrorf(module Module, format string, args ...interface{})
Colin Cross7bcc2562019-05-20 14:51:55 -070062
63 // Errorf reports an error at the specified position of the module definition file.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070064 Errorf(format string, args ...interface{})
Colin Cross7bcc2562019-05-20 14:51:55 -070065
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.
Dan Willemsen265d92c2015-12-09 18:06:23 -080069 Failed() bool
Jamie Gennis1bc967e2014-05-27 16:34:41 -070070
Colin Cross7bcc2562019-05-20 14:51:55 -070071 // 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.
Dan Willemsenaeffbf72015-11-25 15:29:32 -080073 Variable(pctx PackageContext, name, value string)
Colin Cross7bcc2562019-05-20 14:51:55 -070074
75 // Rule creates a new ninja rule scoped to the singleton. It can be referenced by calls to Build in the same
76 // singleton.
Dan Willemsenaeffbf72015-11-25 15:29:32 -080077 Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule
Colin Cross7bcc2562019-05-20 14:51:55 -070078
79 // Build creates a new ninja build statement.
Dan Willemsenaeffbf72015-11-25 15:29:32 -080080 Build(pctx PackageContext, params BuildParams)
Colin Cross7bcc2562019-05-20 14:51:55 -070081
82 // RequireNinjaVersion sets the generated ninja manifest to require at least the specified version of ninja.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070083 RequireNinjaVersion(major, minor, micro int)
84
Lukacs T. Berki843db402021-08-19 09:35:56 +020085 // SetOutDir sets the value of the top-level "builddir" Ninja variable
Jamie Gennis1bc967e2014-05-27 16:34:41 -070086 // that controls where Ninja stores its build log files. This value can be
Colin Crossa2599452015-11-18 16:01:01 -080087 // set at most one time for a single build, later calls are ignored.
Lukacs T. Berki843db402021-08-19 09:35:56 +020088 SetOutDir(pctx PackageContext, value string)
Jamie Gennis1bc967e2014-05-27 16:34:41 -070089
Dan Willemsenab223a52018-07-05 21:56:59 -070090 // 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
Dan Willemsen4bb62762016-01-14 15:42:54 -080094 // 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
Colin Cross7bcc2562019-05-20 14:51:55 -070099 // VisitAllModules calls visit for each defined variant of each module in an unspecified order.
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700100 VisitAllModules(visit func(Module))
Colin Cross7bcc2562019-05-20 14:51:55 -0700101
102 // VisitAllModules calls pred for each defined variant of each module in an unspecified order, and if pred returns
103 // true calls visit.
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700104 VisitAllModulesIf(pred func(Module) bool, visit func(Module))
Colin Cross7bcc2562019-05-20 14:51:55 -0700105
Ulya Trafimovich811381a2019-09-20 12:01:01 +0100106 // VisitDirectDeps calls visit for each direct dependency of the Module. If there are
107 // multiple direct dependencies on the same module visit will be called multiple times on
108 // that module and OtherModuleDependencyTag will return a different tag for each.
109 //
110 // The Module passed to the visit function should not be retained outside of the visit
111 // function, it may be invalidated by future mutators.
112 VisitDirectDeps(module Module, visit func(Module))
113
114 // VisitDirectDepsIf calls pred for each direct dependency of the Module, and if pred
115 // returns true calls visit. If there are multiple direct dependencies on the same module
116 // pred and visit will be called multiple times on that module and OtherModuleDependencyTag
117 // will return a different tag for each.
118 //
119 // The Module passed to the visit function should not be retained outside of the visit
120 // function, it may be invalidated by future mutators.
121 VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module))
122
Colin Cross7bcc2562019-05-20 14:51:55 -0700123 // VisitDepsDepthFirst calls visit for each transitive dependency, traversing the dependency tree in depth first
124 // order. visit will only be called once for any given module, even if there are multiple paths through the
125 // dependency tree to the module or multiple direct dependencies with different tags.
Jamie Gennise98b8a92014-06-17 10:24:24 -0700126 VisitDepsDepthFirst(module Module, visit func(Module))
Colin Cross7bcc2562019-05-20 14:51:55 -0700127
128 // VisitDepsDepthFirst calls pred for each transitive dependency, and if pred returns true calls visit, traversing
129 // the dependency tree in depth first order. visit will only be called once for any given module, even if there are
130 // multiple paths through the dependency tree to the module or multiple direct dependencies with different tags.
Jamie Gennise98b8a92014-06-17 10:24:24 -0700131 VisitDepsDepthFirstIf(module Module, pred func(Module) bool,
132 visit func(Module))
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700133
Colin Cross7bcc2562019-05-20 14:51:55 -0700134 // VisitAllModuleVariants calls visit for each variant of the given module.
Colin Cross24ad5872015-11-17 16:22:29 -0800135 VisitAllModuleVariants(module Module, visit func(Module))
136
Colin Cross7bcc2562019-05-20 14:51:55 -0700137 // PrimaryModule returns the first variant of the given module. This can be used to perform
138 // // singleton actions that are only done once for all variants of a module.
Colin Cross24ad5872015-11-17 16:22:29 -0800139 PrimaryModule(module Module) Module
Colin Cross7bcc2562019-05-20 14:51:55 -0700140
141 // FinalModule returns the last variant of the given module. This can be used to perform
142 // singleton actions that are only done once for all variants of a module.
Colin Cross24ad5872015-11-17 16:22:29 -0800143 FinalModule(module Module) Module
144
Colin Cross7bcc2562019-05-20 14:51:55 -0700145 // AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest. The
146 // primary builder will be rerun whenever the specified files are modified.
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700147 AddNinjaFileDeps(deps ...string)
Colin Cross127d2ea2016-11-01 11:10:51 -0700148
Dan Willemsenb6c90232018-02-23 14:49:45 -0800149 // GlobWithDeps returns a list of files and directories that match the
150 // specified pattern but do not match any of the patterns in excludes.
151 // Any directories will have a '/' suffix. It also adds efficient
152 // dependencies to rerun the primary builder whenever a file matching
153 // the pattern as added or removed, without rerunning if a file that
154 // does not match the pattern is added to a searched directory.
Colin Cross127d2ea2016-11-01 11:10:51 -0700155 GlobWithDeps(pattern string, excludes []string) ([]string, error)
Colin Crossb519a7e2017-02-01 13:21:35 -0800156
Colin Cross7bcc2562019-05-20 14:51:55 -0700157 // Fs returns a pathtools.Filesystem that can be used to interact with files. Using the Filesystem interface allows
158 // the singleton to be used in build system tests that run against a mock filesystem.
Colin Crossb519a7e2017-02-01 13:21:35 -0800159 Fs() pathtools.FileSystem
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700160}
161
162var _ SingletonContext = (*singletonContext)(nil)
163
164type singletonContext struct {
Colin Cross9226d6c2019-02-25 18:07:44 -0800165 name string
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700166 context *Context
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700167 config interface{}
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700168 scope *localScope
Dan Willemsen4bb62762016-01-14 15:42:54 -0800169 globals *liveTracker
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700170
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700171 ninjaFileDeps []string
172 errs []error
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700173
174 actionDefs localBuildActions
175}
176
Jamie Gennis6eb4d242014-06-11 18:31:16 -0700177func (s *singletonContext) Config() interface{} {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700178 return s.config
179}
180
Colin Cross9226d6c2019-02-25 18:07:44 -0800181func (s *singletonContext) Name() string {
182 return s.name
183}
184
Colin Crossbbfa51a2014-12-17 16:12:41 -0800185func (s *singletonContext) ModuleName(logicModule Module) string {
Colin Cross4572edd2015-05-13 14:36:24 -0700186 return s.context.ModuleName(logicModule)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700187}
188
Colin Crossbbfa51a2014-12-17 16:12:41 -0800189func (s *singletonContext) ModuleDir(logicModule Module) string {
Colin Cross4572edd2015-05-13 14:36:24 -0700190 return s.context.ModuleDir(logicModule)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700191}
192
Colin Cross8c602f72015-12-17 18:02:11 -0800193func (s *singletonContext) ModuleSubDir(logicModule Module) string {
194 return s.context.ModuleSubDir(logicModule)
195}
196
Dan Willemsenc98e55b2016-07-25 15:51:50 -0700197func (s *singletonContext) ModuleType(logicModule Module) string {
198 return s.context.ModuleType(logicModule)
199}
200
Colin Cross2da84922020-07-02 10:08:12 -0700201func (s *singletonContext) ModuleProvider(logicModule Module, provider ProviderKey) interface{} {
202 return s.context.ModuleProvider(logicModule, provider)
203}
204
205// ModuleHasProvider returns true if the provider for the given module has been set.
206func (s *singletonContext) ModuleHasProvider(logicModule Module, provider ProviderKey) bool {
207 return s.context.ModuleHasProvider(logicModule, provider)
208}
209
Colin Crossbbfa51a2014-12-17 16:12:41 -0800210func (s *singletonContext) BlueprintFile(logicModule Module) string {
Colin Cross4572edd2015-05-13 14:36:24 -0700211 return s.context.BlueprintFile(logicModule)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700212}
213
Colin Cross0aa6a5f2016-01-07 13:43:09 -0800214func (s *singletonContext) error(err error) {
215 if err != nil {
216 s.errs = append(s.errs, err)
217 }
218}
219
Colin Crossbbfa51a2014-12-17 16:12:41 -0800220func (s *singletonContext) ModuleErrorf(logicModule Module, format string,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700221 args ...interface{}) {
222
Colin Cross0aa6a5f2016-01-07 13:43:09 -0800223 s.error(s.context.ModuleErrorf(logicModule, format, args...))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700224}
225
226func (s *singletonContext) Errorf(format string, args ...interface{}) {
227 // TODO: Make this not result in the error being printed as "internal error"
Colin Cross0aa6a5f2016-01-07 13:43:09 -0800228 s.error(fmt.Errorf(format, args...))
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700229}
230
Dan Willemsen265d92c2015-12-09 18:06:23 -0800231func (s *singletonContext) Failed() bool {
232 return len(s.errs) > 0
233}
234
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800235func (s *singletonContext) Variable(pctx PackageContext, name, value string) {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700236 s.scope.ReparentTo(pctx)
Jamie Gennis0ed63ef2014-06-30 18:07:17 -0700237
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700238 v, err := s.scope.AddLocalVariable(name, value)
239 if err != nil {
240 panic(err)
241 }
242
243 s.actionDefs.variables = append(s.actionDefs.variables, v)
244}
245
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800246func (s *singletonContext) Rule(pctx PackageContext, name string,
Jamie Gennis2fb20952014-10-03 02:49:58 -0700247 params RuleParams, argNames ...string) Rule {
Jamie Genniscbc6f862014-06-05 20:00:22 -0700248
Jamie Gennis2fb20952014-10-03 02:49:58 -0700249 s.scope.ReparentTo(pctx)
Jamie Gennis0ed63ef2014-06-30 18:07:17 -0700250
Jamie Genniscbc6f862014-06-05 20:00:22 -0700251 r, err := s.scope.AddLocalRule(name, &params, argNames...)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700252 if err != nil {
253 panic(err)
254 }
255
256 s.actionDefs.rules = append(s.actionDefs.rules, r)
257
258 return r
259}
260
Dan Willemsenaeffbf72015-11-25 15:29:32 -0800261func (s *singletonContext) Build(pctx PackageContext, params BuildParams) {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700262 s.scope.ReparentTo(pctx)
Jamie Gennis0ed63ef2014-06-30 18:07:17 -0700263
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700264 def, err := parseBuildParams(s.scope, &params)
265 if err != nil {
266 panic(err)
267 }
268
269 s.actionDefs.buildDefs = append(s.actionDefs.buildDefs, def)
270}
271
Dan Willemsen4bb62762016-01-14 15:42:54 -0800272func (s *singletonContext) Eval(pctx PackageContext, str string) (string, error) {
273 s.scope.ReparentTo(pctx)
274
275 ninjaStr, err := parseNinjaString(s.scope, str)
276 if err != nil {
277 return "", err
278 }
279
280 err = s.globals.addNinjaStringDeps(ninjaStr)
281 if err != nil {
282 return "", err
283 }
284
285 return ninjaStr.Eval(s.globals.variables)
286}
287
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700288func (s *singletonContext) RequireNinjaVersion(major, minor, micro int) {
289 s.context.requireNinjaVersion(major, minor, micro)
290}
291
Lukacs T. Berki843db402021-08-19 09:35:56 +0200292func (s *singletonContext) SetOutDir(pctx PackageContext, value string) {
Jamie Gennis2fb20952014-10-03 02:49:58 -0700293 s.scope.ReparentTo(pctx)
Jamie Gennis7d5b2f82014-09-24 17:51:52 -0700294
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700295 ninjaValue, err := parseNinjaString(s.scope, value)
296 if err != nil {
297 panic(err)
298 }
299
Lukacs T. Berki843db402021-08-19 09:35:56 +0200300 s.context.setOutDir(ninjaValue)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700301}
302
Dan Willemsenab223a52018-07-05 21:56:59 -0700303func (s *singletonContext) AddSubninja(file string) {
304 s.context.subninjas = append(s.context.subninjas, file)
305}
306
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700307func (s *singletonContext) VisitAllModules(visit func(Module)) {
Colin Cross7f90d172018-09-19 13:37:29 -0700308 var visitingModule Module
309 defer func() {
310 if r := recover(); r != nil {
311 panic(newPanicErrorf(r, "VisitAllModules(%s) for module %s",
Colin Cross818af3b2019-03-25 15:04:09 -0700312 funcName(visit), s.context.moduleInfo[visitingModule]))
Colin Cross7f90d172018-09-19 13:37:29 -0700313 }
314 }()
315
316 s.context.VisitAllModules(func(m Module) {
317 visitingModule = m
318 visit(m)
319 })
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700320}
321
322func (s *singletonContext) VisitAllModulesIf(pred func(Module) bool,
323 visit func(Module)) {
324
Colin Cross4572edd2015-05-13 14:36:24 -0700325 s.context.VisitAllModulesIf(pred, visit)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700326}
Jamie Gennise98b8a92014-06-17 10:24:24 -0700327
Ulya Trafimovich811381a2019-09-20 12:01:01 +0100328func (s *singletonContext) VisitDirectDeps(module Module, visit func(Module)) {
329 s.context.VisitDirectDeps(module, visit)
330}
331
332func (s *singletonContext) VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) {
333 s.context.VisitDirectDepsIf(module, pred, visit)
334}
335
Jamie Gennise98b8a92014-06-17 10:24:24 -0700336func (s *singletonContext) VisitDepsDepthFirst(module Module,
337 visit func(Module)) {
338
Colin Cross4572edd2015-05-13 14:36:24 -0700339 s.context.VisitDepsDepthFirst(module, visit)
Jamie Gennise98b8a92014-06-17 10:24:24 -0700340}
341
342func (s *singletonContext) VisitDepsDepthFirstIf(module Module,
343 pred func(Module) bool, visit func(Module)) {
344
Colin Cross4572edd2015-05-13 14:36:24 -0700345 s.context.VisitDepsDepthFirstIf(module, pred, visit)
Jamie Gennise98b8a92014-06-17 10:24:24 -0700346}
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700347
Colin Cross24ad5872015-11-17 16:22:29 -0800348func (s *singletonContext) PrimaryModule(module Module) Module {
349 return s.context.PrimaryModule(module)
350}
351
352func (s *singletonContext) FinalModule(module Module) Module {
353 return s.context.FinalModule(module)
354}
355
356func (s *singletonContext) VisitAllModuleVariants(module Module, visit func(Module)) {
357 s.context.VisitAllModuleVariants(module, visit)
358}
359
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700360func (s *singletonContext) AddNinjaFileDeps(deps ...string) {
361 s.ninjaFileDeps = append(s.ninjaFileDeps, deps...)
362}
Colin Cross127d2ea2016-11-01 11:10:51 -0700363
364func (s *singletonContext) GlobWithDeps(pattern string,
365 excludes []string) ([]string, error) {
366 return s.context.glob(pattern, excludes)
367}
Colin Crossb519a7e2017-02-01 13:21:35 -0800368
369func (s *singletonContext) Fs() pathtools.FileSystem {
370 return s.context.fs
371}