blob: a5b2b4c3f70008cbe61aa0eaa9838e25efbf175d [file] [log] [blame]
Jamie Gennis1bc967e2014-05-27 16:34:41 -07001package blueprint
2
3import (
4 "fmt"
5 "path/filepath"
Jamie Gennis6a40c192014-07-02 16:40:31 -07006 "text/scanner"
Jamie Gennis1bc967e2014-05-27 16:34:41 -07007)
8
Jamie Gennisb9e87f62014-09-24 20:28:11 -07009// A Module handles generating all of the Ninja build actions needed to build a
10// single module that is defined in a Blueprints file. Module objects are
11// created during the parse phase of a Context using one of the registered
12// module types (and the associated ModuleFactory function). The Module's
13// properties struct is automatically filled in with the property values
14// specified in the Blueprints file (see Context.RegisterModuleType for more
15// information on this).
16//
17// The Module implementation can access the build configuration as well as any
18// modules on which on which it depends (as defined by the "deps" property
19// specified in the Blueprints file or dynamically added by implementing the
20// DynamicDependerModule interface) using the ModuleContext passed to
21// GenerateBuildActions. This ModuleContext is also used to create Ninja build
22// actions and to report errors to the user.
23//
24// In addition to implementing the GenerateBuildActions method, a Module should
25// implement methods that provide dependant modules and singletons information
26// they need to generate their build actions. These methods will only be called
27// after GenerateBuildActions is called because the Context calls
28// GenerateBuildActions in dependency-order (and singletons are invoked after
29// all the Modules). The set of methods a Module supports will determine how
30// dependant Modules interact with it.
31//
32// For example, consider a Module that is responsible for generating a library
33// that other modules can link against. The library Module might implement the
34// following interface:
35//
36// type LibraryProducer interface {
37// LibraryFileName() string
38// }
39//
40// func IsLibraryProducer(module blueprint.Module) {
41// _, ok := module.(LibraryProducer)
42// return ok
43// }
44//
45// A binary-producing Module that depends on the library Module could then do:
46//
47// func (m *myBinaryModule) GenerateBuildActions(ctx blueprint.ModuleContext) {
48// ...
49// var libraryFiles []string
50// ctx.VisitDepsDepthFirstIf(IsLibraryProducer,
51// func(module blueprint.Module) {
52// libProducer := module.(LibraryProducer)
53// libraryFiles = append(libraryFiles, libProducer.LibraryFileName())
54// })
55// ...
56// }
57//
58// to build the list of library file names that should be included in its link
59// command.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070060type Module interface {
Jamie Gennisb9e87f62014-09-24 20:28:11 -070061 // GenerateBuildActions is called by the Context that created the Module
62 // during its generate phase. This call should generate all Ninja build
63 // actions (rules, pools, and build statements) needed to build the module.
Jamie Gennis1bc967e2014-05-27 16:34:41 -070064 GenerateBuildActions(ModuleContext)
65}
66
Jamie Gennisb9e87f62014-09-24 20:28:11 -070067// A DynamicDependerModule is a Module that may add dependencies that do not
68// appear in its "deps" property. Any Module that implements this interface
69// will have its DynamicDependencies method called by the Context that created
70// it during generate phase.
71type DynamicDependerModule interface {
72 Module
73
74 // DynamicDependencies is called by the Context that created the
75 // DynamicDependerModule during its generate phase. This call should return
76 // the list of module names that the DynamicDependerModule depends on
77 // dynamically. Module names that already appear in the "deps" property may
78 // but do not need to be included in the returned list.
79 DynamicDependencies(DynamicDependerModuleContext) []string
80}
81
82type DynamicDependerModuleContext interface {
Jamie Gennis1bc967e2014-05-27 16:34:41 -070083 ModuleName() string
84 ModuleDir() string
Jamie Gennis6eb4d242014-06-11 18:31:16 -070085 Config() interface{}
Jamie Gennis1bc967e2014-05-27 16:34:41 -070086
Jamie Gennis6a40c192014-07-02 16:40:31 -070087 Errorf(pos scanner.Position, fmt string, args ...interface{})
Jamie Gennis1bc967e2014-05-27 16:34:41 -070088 ModuleErrorf(fmt string, args ...interface{})
89 PropertyErrorf(property, fmt string, args ...interface{})
Jamie Gennis6a40c192014-07-02 16:40:31 -070090 Failed() bool
Jamie Gennisb9e87f62014-09-24 20:28:11 -070091}
92
93type ModuleContext interface {
94 DynamicDependerModuleContext
95
96 OtherModuleName(m Module) string
97 OtherModuleErrorf(m Module, fmt string, args ...interface{})
Jamie Gennis1bc967e2014-05-27 16:34:41 -070098
99 Variable(name, value string)
Jamie Genniscbc6f862014-06-05 20:00:22 -0700100 Rule(name string, params RuleParams, argNames ...string) Rule
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700101 Build(params BuildParams)
102
103 VisitDepsDepthFirst(visit func(Module))
104 VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700105
106 AddNinjaFileDeps(deps ...string)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700107}
108
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700109var _ DynamicDependerModuleContext = (*dynamicDependerModuleContext)(nil)
110
111type dynamicDependerModuleContext struct {
112 context *Context
113 config interface{}
114 info *moduleInfo
115 errs []error
116}
117
118func (d *dynamicDependerModuleContext) ModuleName() string {
119 return d.info.properties.Name
120}
121
122func (d *dynamicDependerModuleContext) ModuleDir() string {
123 return filepath.Dir(d.info.relBlueprintsFile)
124}
125
126func (d *dynamicDependerModuleContext) Config() interface{} {
127 return d.config
128}
129
130func (d *dynamicDependerModuleContext) Errorf(pos scanner.Position,
131 format string, args ...interface{}) {
132
133 d.errs = append(d.errs, &Error{
134 Err: fmt.Errorf(format, args...),
135 Pos: pos,
136 })
137}
138
139func (d *dynamicDependerModuleContext) ModuleErrorf(format string,
140 args ...interface{}) {
141
142 d.errs = append(d.errs, &Error{
143 Err: fmt.Errorf(format, args...),
144 Pos: d.info.pos,
145 })
146}
147
148func (d *dynamicDependerModuleContext) PropertyErrorf(property, format string,
149 args ...interface{}) {
150
151 pos, ok := d.info.propertyPos[property]
152 if !ok {
153 panic(fmt.Errorf("property %q was not set for this module", property))
154 }
155
156 d.errs = append(d.errs, &Error{
157 Err: fmt.Errorf(format, args...),
158 Pos: pos,
159 })
160}
161
162func (d *dynamicDependerModuleContext) Failed() bool {
163 return len(d.errs) > 0
164}
165
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700166var _ ModuleContext = (*moduleContext)(nil)
167
168type moduleContext struct {
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700169 dynamicDependerModuleContext
170 module Module
171 scope *localScope
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700172 ninjaFileDeps []string
Jamie Gennisb9e87f62014-09-24 20:28:11 -0700173 actionDefs localBuildActions
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700174}
175
Jamie Gennisd4c53d82014-06-22 17:02:55 -0700176func (m *moduleContext) OtherModuleName(module Module) string {
177 info := m.context.moduleInfo[module]
178 return info.properties.Name
179}
180
Jamie Gennisd4c53d82014-06-22 17:02:55 -0700181func (m *moduleContext) OtherModuleErrorf(module Module, format string,
182 args ...interface{}) {
183
184 info := m.context.moduleInfo[module]
185 m.errs = append(m.errs, &Error{
186 Err: fmt.Errorf(format, args...),
187 Pos: info.pos,
188 })
189}
190
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700191func (m *moduleContext) Variable(name, value string) {
Jamie Gennis0ed63ef2014-06-30 18:07:17 -0700192 const skip = 2
193 m.scope.ReparentToCallerPackage(skip)
194
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700195 v, err := m.scope.AddLocalVariable(name, value)
196 if err != nil {
197 panic(err)
198 }
199
200 m.actionDefs.variables = append(m.actionDefs.variables, v)
201}
202
Jamie Genniscbc6f862014-06-05 20:00:22 -0700203func (m *moduleContext) Rule(name string, params RuleParams,
204 argNames ...string) Rule {
205
Jamie Gennis0ed63ef2014-06-30 18:07:17 -0700206 const skip = 2
207 m.scope.ReparentToCallerPackage(skip)
208
Jamie Genniscbc6f862014-06-05 20:00:22 -0700209 r, err := m.scope.AddLocalRule(name, &params, argNames...)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700210 if err != nil {
211 panic(err)
212 }
213
214 m.actionDefs.rules = append(m.actionDefs.rules, r)
215
216 return r
217}
218
219func (m *moduleContext) Build(params BuildParams) {
Jamie Gennis0ed63ef2014-06-30 18:07:17 -0700220 const skip = 2
221 m.scope.ReparentToCallerPackage(skip)
222
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700223 def, err := parseBuildParams(m.scope, &params)
224 if err != nil {
225 panic(err)
226 }
227
228 m.actionDefs.buildDefs = append(m.actionDefs.buildDefs, def)
229}
230
231func (m *moduleContext) VisitDepsDepthFirst(visit func(Module)) {
232 m.context.visitDepsDepthFirst(m.module, visit)
233}
234
235func (m *moduleContext) VisitDepsDepthFirstIf(pred func(Module) bool,
236 visit func(Module)) {
237
238 m.context.visitDepsDepthFirstIf(m.module, pred, visit)
239}
Mathias Agopian5b8477d2014-06-25 17:21:54 -0700240
241func (m *moduleContext) AddNinjaFileDeps(deps ...string) {
242 m.ninjaFileDeps = append(m.ninjaFileDeps, deps...)
243}