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