1// Copyright 2015 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 android
16
17import (
18	"fmt"
19	"strings"
20
21	"github.com/google/blueprint"
22)
23
24// PackageContext is a wrapper for blueprint.PackageContext that adds
25// some android-specific helper functions.
26type PackageContext struct {
27	blueprint.PackageContext
28}
29
30func NewPackageContext(pkgPath string) PackageContext {
31	return PackageContext{blueprint.NewPackageContext(pkgPath)}
32}
33
34// configErrorWrapper can be used with Path functions when a Context is not
35// available. A Config can be provided, and errors are stored as a list for
36// later retrieval.
37//
38// The most common use here will be with VariableFunc, where only a config is
39// provided, and an error should be returned.
40type configErrorWrapper struct {
41	pctx   PackageContext
42	config Config
43	errors []error
44}
45
46var _ PathContext = &configErrorWrapper{}
47var _ errorfContext = &configErrorWrapper{}
48var _ PackageVarContext = &configErrorWrapper{}
49var _ PackagePoolContext = &configErrorWrapper{}
50var _ PackageRuleContext = &configErrorWrapper{}
51
52func (e *configErrorWrapper) Config() Config {
53	return e.config
54}
55func (e *configErrorWrapper) Errorf(format string, args ...interface{}) {
56	e.errors = append(e.errors, fmt.Errorf(format, args...))
57}
58func (e *configErrorWrapper) AddNinjaFileDeps(deps ...string) {
59	e.pctx.AddNinjaFileDeps(deps...)
60}
61
62type PackageVarContext interface {
63	PathContext
64	errorfContext
65}
66
67type PackagePoolContext PackageVarContext
68type PackageRuleContext PackageVarContext
69
70// VariableFunc wraps blueprint.PackageContext.VariableFunc, converting the interface{} config
71// argument to a PackageVarContext.
72func (p PackageContext) VariableFunc(name string,
73	f func(PackageVarContext) string) blueprint.Variable {
74
75	return p.PackageContext.VariableFunc(name, func(config interface{}) (string, error) {
76		ctx := &configErrorWrapper{p, config.(Config), nil}
77		ret := f(ctx)
78		if len(ctx.errors) > 0 {
79			return "", ctx.errors[0]
80		}
81		return ret, nil
82	})
83}
84
85// PoolFunc wraps blueprint.PackageContext.PoolFunc, converting the interface{} config
86// argument to a Context that supports Config().
87func (p PackageContext) PoolFunc(name string,
88	f func(PackagePoolContext) blueprint.PoolParams) blueprint.Pool {
89
90	return p.PackageContext.PoolFunc(name, func(config interface{}) (blueprint.PoolParams, error) {
91		ctx := &configErrorWrapper{p, config.(Config), nil}
92		params := f(ctx)
93		if len(ctx.errors) > 0 {
94			return params, ctx.errors[0]
95		}
96		return params, nil
97	})
98}
99
100// RuleFunc wraps blueprint.PackageContext.RuleFunc, converting the interface{} config
101// argument to a Context that supports Config(), and provides a default Pool if none is
102// specified.
103func (p PackageContext) RuleFunc(name string,
104	f func(PackageRuleContext) blueprint.RuleParams, argNames ...string) blueprint.Rule {
105
106	return p.PackageContext.RuleFunc(name, func(config interface{}) (blueprint.RuleParams, error) {
107		ctx := &configErrorWrapper{p, config.(Config), nil}
108		params := f(ctx)
109		if len(ctx.errors) > 0 {
110			return params, ctx.errors[0]
111		}
112		if ctx.Config().UseRemoteBuild() && params.Pool == nil {
113			// When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by
114			// goma/RBE, restrict jobs to the local parallelism value
115			params.Pool = localPool
116		}
117		return params, nil
118	}, argNames...)
119}
120
121// SourcePathVariable returns a Variable whose value is the source directory
122// appended with the supplied path. It may only be called during a Go package's
123// initialization - either from the init() function or as part of a
124// package-scoped variable's initialization.
125func (p PackageContext) SourcePathVariable(name, path string) blueprint.Variable {
126	return p.VariableFunc(name, func(ctx PackageVarContext) string {
127		p, err := safePathForSource(ctx, path)
128		if err != nil {
129			ctx.Errorf("%s", err.Error())
130		}
131		return p.String()
132	})
133}
134
135// SourcePathsVariable returns a Variable whose value is the source directory
136// appended with the supplied paths, joined with separator. It may only be
137// called during a Go package's initialization - either from the init()
138// function or as part of a package-scoped variable's initialization.
139func (p PackageContext) SourcePathsVariable(name, separator string, paths ...string) blueprint.Variable {
140	return p.VariableFunc(name, func(ctx PackageVarContext) string {
141		var ret []string
142		for _, path := range paths {
143			p, err := safePathForSource(ctx, path)
144			if err != nil {
145				ctx.Errorf("%s", err.Error())
146			}
147			ret = append(ret, p.String())
148		}
149		return strings.Join(ret, separator)
150	})
151}
152
153// SourcePathVariableWithEnvOverride returns a Variable whose value is the source directory
154// appended with the supplied path, or the value of the given environment variable if it is set.
155// The environment variable is not required to point to a path inside the source tree.
156// It may only be called during a Go package's initialization - either from the init() function or
157// as part of a package-scoped variable's initialization.
158func (p PackageContext) SourcePathVariableWithEnvOverride(name, path, env string) blueprint.Variable {
159	return p.VariableFunc(name, func(ctx PackageVarContext) string {
160		p, err := safePathForSource(ctx, path)
161		if err != nil {
162			ctx.Errorf("%s", err.Error())
163		}
164		return ctx.Config().GetenvWithDefault(env, p.String())
165	})
166}
167
168// HostBinToolVariable returns a Variable whose value is the path to a host tool
169// in the bin directory for host targets. It may only be called during a Go
170// package's initialization - either from the init() function or as part of a
171// package-scoped variable's initialization.
172func (p PackageContext) HostBinToolVariable(name, path string) blueprint.Variable {
173	return p.VariableFunc(name, func(ctx PackageVarContext) string {
174		return ctx.Config().HostToolPath(ctx, path).String()
175	})
176}
177
178// HostJNIToolVariable returns a Variable whose value is the path to a host tool
179// in the lib directory for host targets. It may only be called during a Go
180// package's initialization - either from the init() function or as part of a
181// package-scoped variable's initialization.
182func (p PackageContext) HostJNIToolVariable(name, path string) blueprint.Variable {
183	return p.VariableFunc(name, func(ctx PackageVarContext) string {
184		return ctx.Config().HostJNIToolPath(ctx, path).String()
185	})
186}
187
188// HostJavaToolVariable returns a Variable whose value is the path to a host
189// tool in the frameworks directory for host targets. It may only be called
190// during a Go package's initialization - either from the init() function or as
191// part of a package-scoped variable's initialization.
192func (p PackageContext) HostJavaToolVariable(name, path string) blueprint.Variable {
193	return p.VariableFunc(name, func(ctx PackageVarContext) string {
194		return ctx.Config().HostJavaToolPath(ctx, path).String()
195	})
196}
197
198// IntermediatesPathVariable returns a Variable whose value is the intermediate
199// directory appended with the supplied path. It may only be called during a Go
200// package's initialization - either from the init() function or as part of a
201// package-scoped variable's initialization.
202func (p PackageContext) IntermediatesPathVariable(name, path string) blueprint.Variable {
203	return p.VariableFunc(name, func(ctx PackageVarContext) string {
204		return PathForIntermediates(ctx, path).String()
205	})
206}
207
208// PrefixedExistentPathsForSourcesVariable returns a Variable whose value is the
209// list of present source paths prefixed with the supplied prefix. It may only
210// be called during a Go package's initialization - either from the init()
211// function or as part of a package-scoped variable's initialization.
212func (p PackageContext) PrefixedExistentPathsForSourcesVariable(
213	name, prefix string, paths []string) blueprint.Variable {
214
215	return p.VariableFunc(name, func(ctx PackageVarContext) string {
216		paths := ExistentPathsForSources(ctx, paths)
217		return JoinWithPrefix(paths.Strings(), prefix)
218	})
219}
220
221// AndroidStaticRule is an alias for StaticRule.
222func (p PackageContext) AndroidStaticRule(name string, params blueprint.RuleParams,
223	argNames ...string) blueprint.Rule {
224	return p.StaticRule(name, params, argNames...)
225}
226
227// StaticRule wraps blueprint.StaticRule and provides a default Pool if none is specified.
228func (p PackageContext) StaticRule(name string, params blueprint.RuleParams,
229	argNames ...string) blueprint.Rule {
230	return p.RuleFunc(name, func(PackageRuleContext) blueprint.RuleParams {
231		return params
232	}, argNames...)
233}
234
235// RemoteRuleSupports configures rules with whether they have Goma and/or RBE support.
236type RemoteRuleSupports struct {
237	Goma bool
238	RBE  bool
239}
240
241// AndroidRemoteStaticRule wraps blueprint.StaticRule but uses goma or RBE's parallelism if goma or RBE are enabled
242// and the appropriate SUPPORTS_* flag is set.
243func (p PackageContext) AndroidRemoteStaticRule(name string, supports RemoteRuleSupports, params blueprint.RuleParams,
244	argNames ...string) blueprint.Rule {
245
246	return p.PackageContext.RuleFunc(name, func(config interface{}) (blueprint.RuleParams, error) {
247		ctx := &configErrorWrapper{p, config.(Config), nil}
248		if ctx.Config().UseGoma() && !supports.Goma {
249			// When USE_GOMA=true is set and the rule is not supported by goma, restrict jobs to the
250			// local parallelism value
251			params.Pool = localPool
252		}
253
254		if ctx.Config().UseRBE() && !supports.RBE {
255			// When USE_RBE=true is set and the rule is not supported by RBE, restrict jobs to the
256			// local parallelism value
257			params.Pool = localPool
258		}
259
260		return params, nil
261	}, argNames...)
262}
263