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