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() interface{}
29
30	ModuleName(module Module) string
31	ModuleDir(module Module) string
32	ModuleSubDir(module Module) string
33	ModuleType(module Module) string
34	BlueprintFile(module Module) string
35
36	ModuleErrorf(module Module, format string, args ...interface{})
37	Errorf(format string, args ...interface{})
38	Failed() bool
39
40	Variable(pctx PackageContext, name, value string)
41	Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule
42	Build(pctx PackageContext, params BuildParams)
43	RequireNinjaVersion(major, minor, micro int)
44
45	// SetNinjaBuildDir sets the value of the top-level "builddir" Ninja variable
46	// that controls where Ninja stores its build log files.  This value can be
47	// set at most one time for a single build, later calls are ignored.
48	SetNinjaBuildDir(pctx PackageContext, value string)
49
50	// Eval takes a string with embedded ninja variables, and returns a string
51	// with all of the variables recursively expanded. Any variables references
52	// are expanded in the scope of the PackageContext.
53	Eval(pctx PackageContext, ninjaStr string) (string, error)
54
55	VisitAllModules(visit func(Module))
56	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
57	VisitDepsDepthFirst(module Module, visit func(Module))
58	VisitDepsDepthFirstIf(module Module, pred func(Module) bool,
59		visit func(Module))
60
61	VisitAllModuleVariants(module Module, visit func(Module))
62
63	PrimaryModule(module Module) Module
64	FinalModule(module Module) Module
65
66	AddNinjaFileDeps(deps ...string)
67
68	// GlobWithDeps returns a list of files and directories that match the
69	// specified pattern but do not match any of the patterns in excludes.
70	// Any directories will have a '/' suffix. It also adds efficient
71	// dependencies to rerun the primary builder whenever a file matching
72	// the pattern as added or removed, without rerunning if a file that
73	// does not match the pattern is added to a searched directory.
74	GlobWithDeps(pattern string, excludes []string) ([]string, error)
75
76	Fs() pathtools.FileSystem
77}
78
79var _ SingletonContext = (*singletonContext)(nil)
80
81type singletonContext struct {
82	context *Context
83	config  interface{}
84	scope   *localScope
85	globals *liveTracker
86
87	ninjaFileDeps []string
88	errs          []error
89
90	actionDefs localBuildActions
91}
92
93func (s *singletonContext) Config() interface{} {
94	return s.config
95}
96
97func (s *singletonContext) ModuleName(logicModule Module) string {
98	return s.context.ModuleName(logicModule)
99}
100
101func (s *singletonContext) ModuleDir(logicModule Module) string {
102	return s.context.ModuleDir(logicModule)
103}
104
105func (s *singletonContext) ModuleSubDir(logicModule Module) string {
106	return s.context.ModuleSubDir(logicModule)
107}
108
109func (s *singletonContext) ModuleType(logicModule Module) string {
110	return s.context.ModuleType(logicModule)
111}
112
113func (s *singletonContext) BlueprintFile(logicModule Module) string {
114	return s.context.BlueprintFile(logicModule)
115}
116
117func (s *singletonContext) error(err error) {
118	if err != nil {
119		s.errs = append(s.errs, err)
120	}
121}
122
123func (s *singletonContext) ModuleErrorf(logicModule Module, format string,
124	args ...interface{}) {
125
126	s.error(s.context.ModuleErrorf(logicModule, format, args...))
127}
128
129func (s *singletonContext) Errorf(format string, args ...interface{}) {
130	// TODO: Make this not result in the error being printed as "internal error"
131	s.error(fmt.Errorf(format, args...))
132}
133
134func (s *singletonContext) Failed() bool {
135	return len(s.errs) > 0
136}
137
138func (s *singletonContext) Variable(pctx PackageContext, name, value string) {
139	s.scope.ReparentTo(pctx)
140
141	v, err := s.scope.AddLocalVariable(name, value)
142	if err != nil {
143		panic(err)
144	}
145
146	s.actionDefs.variables = append(s.actionDefs.variables, v)
147}
148
149func (s *singletonContext) Rule(pctx PackageContext, name string,
150	params RuleParams, argNames ...string) Rule {
151
152	s.scope.ReparentTo(pctx)
153
154	r, err := s.scope.AddLocalRule(name, &params, argNames...)
155	if err != nil {
156		panic(err)
157	}
158
159	s.actionDefs.rules = append(s.actionDefs.rules, r)
160
161	return r
162}
163
164func (s *singletonContext) Build(pctx PackageContext, params BuildParams) {
165	s.scope.ReparentTo(pctx)
166
167	def, err := parseBuildParams(s.scope, &params)
168	if err != nil {
169		panic(err)
170	}
171
172	s.actionDefs.buildDefs = append(s.actionDefs.buildDefs, def)
173}
174
175func (s *singletonContext) Eval(pctx PackageContext, str string) (string, error) {
176	s.scope.ReparentTo(pctx)
177
178	ninjaStr, err := parseNinjaString(s.scope, str)
179	if err != nil {
180		return "", err
181	}
182
183	err = s.globals.addNinjaStringDeps(ninjaStr)
184	if err != nil {
185		return "", err
186	}
187
188	return ninjaStr.Eval(s.globals.variables)
189}
190
191func (s *singletonContext) RequireNinjaVersion(major, minor, micro int) {
192	s.context.requireNinjaVersion(major, minor, micro)
193}
194
195func (s *singletonContext) SetNinjaBuildDir(pctx PackageContext, value string) {
196	s.scope.ReparentTo(pctx)
197
198	ninjaValue, err := parseNinjaString(s.scope, value)
199	if err != nil {
200		panic(err)
201	}
202
203	s.context.setNinjaBuildDir(ninjaValue)
204}
205
206func (s *singletonContext) VisitAllModules(visit func(Module)) {
207	s.context.VisitAllModules(visit)
208}
209
210func (s *singletonContext) VisitAllModulesIf(pred func(Module) bool,
211	visit func(Module)) {
212
213	s.context.VisitAllModulesIf(pred, visit)
214}
215
216func (s *singletonContext) VisitDepsDepthFirst(module Module,
217	visit func(Module)) {
218
219	s.context.VisitDepsDepthFirst(module, visit)
220}
221
222func (s *singletonContext) VisitDepsDepthFirstIf(module Module,
223	pred func(Module) bool, visit func(Module)) {
224
225	s.context.VisitDepsDepthFirstIf(module, pred, visit)
226}
227
228func (s *singletonContext) PrimaryModule(module Module) Module {
229	return s.context.PrimaryModule(module)
230}
231
232func (s *singletonContext) FinalModule(module Module) Module {
233	return s.context.FinalModule(module)
234}
235
236func (s *singletonContext) VisitAllModuleVariants(module Module, visit func(Module)) {
237	s.context.VisitAllModuleVariants(module, visit)
238}
239
240func (s *singletonContext) AddNinjaFileDeps(deps ...string) {
241	s.ninjaFileDeps = append(s.ninjaFileDeps, deps...)
242}
243
244func (s *singletonContext) GlobWithDeps(pattern string,
245	excludes []string) ([]string, error) {
246	return s.context.glob(pattern, excludes)
247}
248
249func (s *singletonContext) Fs() pathtools.FileSystem {
250	return s.context.fs
251}
252