1// Copyright 2016 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 "bytes" 19 "fmt" 20 "strconv" 21 "strings" 22 23 "github.com/google/blueprint" 24 "github.com/google/blueprint/pathtools" 25 "github.com/google/blueprint/proptools" 26) 27 28func init() { 29 RegisterMakeVarsProvider(pctx, androidMakeVarsProvider) 30} 31 32func androidMakeVarsProvider(ctx MakeVarsContext) { 33 ctx.Strict("MIN_SUPPORTED_SDK_VERSION", strconv.Itoa(ctx.Config().MinSupportedSdkVersion())) 34} 35 36/////////////////////////////////////////////////////////////////////////////// 37// Interface for other packages to use to declare make variables 38type MakeVarsContext interface { 39 Config() Config 40 DeviceConfig() DeviceConfig 41 AddNinjaFileDeps(deps ...string) 42 43 ModuleName(module blueprint.Module) string 44 ModuleDir(module blueprint.Module) string 45 ModuleSubDir(module blueprint.Module) string 46 ModuleType(module blueprint.Module) string 47 BlueprintFile(module blueprint.Module) string 48 49 ModuleErrorf(module blueprint.Module, format string, args ...interface{}) 50 Errorf(format string, args ...interface{}) 51 Failed() bool 52 53 VisitAllModules(visit func(Module)) 54 VisitAllModulesIf(pred func(Module) bool, visit func(Module)) 55 56 // Verify the make variable matches the Soong version, fail the build 57 // if it does not. If the make variable is empty, just set it. 58 Strict(name, ninjaStr string) 59 // Check to see if the make variable matches the Soong version, warn if 60 // it does not. If the make variable is empty, just set it. 61 Check(name, ninjaStr string) 62 63 // These are equivalent to the above, but sort the make and soong 64 // variables before comparing them. They also show the unique entries 65 // in each list when displaying the difference, instead of the entire 66 // string. 67 StrictSorted(name, ninjaStr string) 68 CheckSorted(name, ninjaStr string) 69 70 // Evaluates a ninja string and returns the result. Used if more 71 // complicated modification needs to happen before giving it to Make. 72 Eval(ninjaStr string) (string, error) 73 74 // These are equivalent to Strict and Check, but do not attempt to 75 // evaluate the values before writing them to the Makefile. They can 76 // be used when all ninja variables have already been evaluated through 77 // Eval(). 78 StrictRaw(name, value string) 79 CheckRaw(name, value string) 80 81 // GlobWithDeps returns a list of files that match the specified pattern but do not match any 82 // of the patterns in excludes. It also adds efficient dependencies to rerun the primary 83 // builder whenever a file matching the pattern as added or removed, without rerunning if a 84 // file that does not match the pattern is added to a searched directory. 85 GlobWithDeps(pattern string, excludes []string) ([]string, error) 86 87 // Phony creates a phony rule in Make, which will allow additional DistForGoal 88 // dependencies to be added to it. Phony can be called on the same name multiple 89 // times to add additional dependencies. 90 Phony(names string, deps ...Path) 91 92 // DistForGoal creates a rule to copy one or more Paths to the artifacts 93 // directory on the build server when the specified goal is built. 94 DistForGoal(goal string, paths ...Path) 95 96 // DistForGoalWithFilename creates a rule to copy a Path to the artifacts 97 // directory on the build server with the given filename when the specified 98 // goal is built. 99 DistForGoalWithFilename(goal string, path Path, filename string) 100 101 // DistForGoals creates a rule to copy one or more Paths to the artifacts 102 // directory on the build server when any of the specified goals are built. 103 DistForGoals(goals []string, paths ...Path) 104 105 // DistForGoalsWithFilename creates a rule to copy a Path to the artifacts 106 // directory on the build server with the given filename when any of the 107 // specified goals are built. 108 DistForGoalsWithFilename(goals []string, path Path, filename string) 109} 110 111var _ PathContext = MakeVarsContext(nil) 112 113type MakeVarsProvider func(ctx MakeVarsContext) 114 115func RegisterMakeVarsProvider(pctx PackageContext, provider MakeVarsProvider) { 116 makeVarsProviders = append(makeVarsProviders, makeVarsProvider{pctx, provider}) 117} 118 119// SingletonMakeVarsProvider is a Singleton with an extra method to provide extra values to be exported to Make. 120type SingletonMakeVarsProvider interface { 121 Singleton 122 123 // MakeVars uses a MakeVarsContext to provide extra values to be exported to Make. 124 MakeVars(ctx MakeVarsContext) 125} 126 127// registerSingletonMakeVarsProvider adds a singleton that implements SingletonMakeVarsProvider to the list of 128// MakeVarsProviders to run. 129func registerSingletonMakeVarsProvider(singleton SingletonMakeVarsProvider) { 130 makeVarsProviders = append(makeVarsProviders, makeVarsProvider{pctx, SingletonmakeVarsProviderAdapter(singleton)}) 131} 132 133// SingletonmakeVarsProviderAdapter converts a SingletonMakeVarsProvider to a MakeVarsProvider. 134func SingletonmakeVarsProviderAdapter(singleton SingletonMakeVarsProvider) MakeVarsProvider { 135 return func(ctx MakeVarsContext) { singleton.MakeVars(ctx) } 136} 137 138/////////////////////////////////////////////////////////////////////////////// 139 140func makeVarsSingletonFunc() Singleton { 141 return &makeVarsSingleton{} 142} 143 144type makeVarsSingleton struct{} 145 146type makeVarsProvider struct { 147 pctx PackageContext 148 call MakeVarsProvider 149} 150 151var makeVarsProviders []makeVarsProvider 152 153type makeVarsContext struct { 154 SingletonContext 155 config Config 156 pctx PackageContext 157 vars []makeVarsVariable 158 phonies []phony 159 dists []dist 160} 161 162var _ MakeVarsContext = &makeVarsContext{} 163 164type makeVarsVariable struct { 165 name string 166 value string 167 sort bool 168 strict bool 169} 170 171type phony struct { 172 name string 173 deps []string 174} 175 176type dist struct { 177 goals []string 178 paths []string 179} 180 181func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) { 182 if !ctx.Config().EmbeddedInMake() { 183 return 184 } 185 186 outFile := absolutePath(PathForOutput(ctx, 187 "make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String()) 188 189 lateOutFile := absolutePath(PathForOutput(ctx, 190 "late"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String()) 191 192 if ctx.Failed() { 193 return 194 } 195 196 var vars []makeVarsVariable 197 var dists []dist 198 var phonies []phony 199 for _, provider := range makeVarsProviders { 200 mctx := &makeVarsContext{ 201 SingletonContext: ctx, 202 pctx: provider.pctx, 203 } 204 205 provider.call(mctx) 206 207 vars = append(vars, mctx.vars...) 208 phonies = append(phonies, mctx.phonies...) 209 dists = append(dists, mctx.dists...) 210 } 211 212 if ctx.Failed() { 213 return 214 } 215 216 outBytes := s.writeVars(vars) 217 218 if err := pathtools.WriteFileIfChanged(outFile, outBytes, 0666); err != nil { 219 ctx.Errorf(err.Error()) 220 } 221 222 lateOutBytes := s.writeLate(phonies, dists) 223 224 if err := pathtools.WriteFileIfChanged(lateOutFile, lateOutBytes, 0666); err != nil { 225 ctx.Errorf(err.Error()) 226 } 227 228} 229 230func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte { 231 buf := &bytes.Buffer{} 232 233 fmt.Fprint(buf, `# Autogenerated file 234 235# Compares SOONG_$(1) against $(1), and warns if they are not equal. 236# 237# If the original variable is empty, then just set it to the SOONG_ version. 238# 239# $(1): Name of the variable to check 240# $(2): If not-empty, sort the values before comparing 241# $(3): Extra snippet to run if it does not match 242define soong-compare-var 243ifneq ($$($(1)),) 244 my_val_make := $$(strip $(if $(2),$$(sort $$($(1))),$$($(1)))) 245 my_val_soong := $(if $(2),$$(sort $$(SOONG_$(1))),$$(SOONG_$(1))) 246 ifneq ($$(my_val_make),$$(my_val_soong)) 247 $$(warning $(1) does not match between Make and Soong:) 248 $(if $(2),$$(warning Make adds: $$(filter-out $$(my_val_soong),$$(my_val_make))),$$(warning Make : $$(my_val_make))) 249 $(if $(2),$$(warning Soong adds: $$(filter-out $$(my_val_make),$$(my_val_soong))),$$(warning Soong: $$(my_val_soong))) 250 $(3) 251 endif 252 my_val_make := 253 my_val_soong := 254else 255 $(1) := $$(SOONG_$(1)) 256endif 257.KATI_READONLY := $(1) SOONG_$(1) 258endef 259 260my_check_failed := false 261 262`) 263 264 // Write all the strict checks out first so that if one of them errors, 265 // we get all of the strict errors printed, but not the non-strict 266 // warnings. 267 for _, v := range vars { 268 if !v.strict { 269 continue 270 } 271 272 sort := "" 273 if v.sort { 274 sort = "true" 275 } 276 277 fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value) 278 fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s,my_check_failed := true))\n\n", v.name, sort) 279 } 280 281 fmt.Fprint(buf, ` 282ifneq ($(my_check_failed),false) 283 $(error Soong variable check failed) 284endif 285my_check_failed := 286 287 288`) 289 290 for _, v := range vars { 291 if v.strict { 292 continue 293 } 294 295 sort := "" 296 if v.sort { 297 sort = "true" 298 } 299 300 fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value) 301 fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s))\n\n", v.name, sort) 302 } 303 304 fmt.Fprintln(buf, "\nsoong-compare-var :=") 305 306 fmt.Fprintln(buf) 307 308 return buf.Bytes() 309} 310 311func (s *makeVarsSingleton) writeLate(phonies []phony, dists []dist) []byte { 312 buf := &bytes.Buffer{} 313 314 fmt.Fprint(buf, `# Autogenerated file 315 316# Values written by Soong read after parsing all Android.mk files. 317 318 319`) 320 321 for _, phony := range phonies { 322 fmt.Fprintf(buf, ".PHONY: %s\n", phony.name) 323 fmt.Fprintf(buf, "%s: %s\n", phony.name, strings.Join(phony.deps, "\\\n ")) 324 } 325 326 fmt.Fprintln(buf) 327 328 for _, dist := range dists { 329 fmt.Fprintf(buf, "$(call dist-for-goals,%s,%s)\n", 330 strings.Join(dist.goals, " "), strings.Join(dist.paths, " ")) 331 } 332 333 return buf.Bytes() 334} 335 336func (c *makeVarsContext) DeviceConfig() DeviceConfig { 337 return DeviceConfig{c.Config().deviceConfig} 338} 339 340var ninjaDescaper = strings.NewReplacer("$$", "$") 341 342func (c *makeVarsContext) Eval(ninjaStr string) (string, error) { 343 s, err := c.SingletonContext.Eval(c.pctx, ninjaStr) 344 if err != nil { 345 return "", err 346 } 347 // SingletonContext.Eval returns an exapnded string that is valid for a ninja file, de-escape $$ to $ for use 348 // in a Makefile 349 return ninjaDescaper.Replace(s), nil 350} 351 352func (c *makeVarsContext) addVariableRaw(name, value string, strict, sort bool) { 353 c.vars = append(c.vars, makeVarsVariable{ 354 name: name, 355 value: value, 356 strict: strict, 357 sort: sort, 358 }) 359} 360 361func (c *makeVarsContext) addVariable(name, ninjaStr string, strict, sort bool) { 362 value, err := c.Eval(ninjaStr) 363 if err != nil { 364 c.SingletonContext.Errorf(err.Error()) 365 } 366 c.addVariableRaw(name, value, strict, sort) 367} 368 369func (c *makeVarsContext) addPhony(name string, deps []string) { 370 c.phonies = append(c.phonies, phony{name, deps}) 371} 372 373func (c *makeVarsContext) addDist(goals []string, paths []string) { 374 c.dists = append(c.dists, dist{ 375 goals: goals, 376 paths: paths, 377 }) 378} 379 380func (c *makeVarsContext) Strict(name, ninjaStr string) { 381 c.addVariable(name, ninjaStr, true, false) 382} 383func (c *makeVarsContext) StrictSorted(name, ninjaStr string) { 384 c.addVariable(name, ninjaStr, true, true) 385} 386func (c *makeVarsContext) StrictRaw(name, value string) { 387 c.addVariableRaw(name, value, true, false) 388} 389 390func (c *makeVarsContext) Check(name, ninjaStr string) { 391 c.addVariable(name, ninjaStr, false, false) 392} 393func (c *makeVarsContext) CheckSorted(name, ninjaStr string) { 394 c.addVariable(name, ninjaStr, false, true) 395} 396func (c *makeVarsContext) CheckRaw(name, value string) { 397 c.addVariableRaw(name, value, false, false) 398} 399 400func (c *makeVarsContext) Phony(name string, deps ...Path) { 401 c.addPhony(name, Paths(deps).Strings()) 402} 403 404func (c *makeVarsContext) DistForGoal(goal string, paths ...Path) { 405 c.DistForGoals([]string{goal}, paths...) 406} 407 408func (c *makeVarsContext) DistForGoalWithFilename(goal string, path Path, filename string) { 409 c.DistForGoalsWithFilename([]string{goal}, path, filename) 410} 411 412func (c *makeVarsContext) DistForGoals(goals []string, paths ...Path) { 413 c.addDist(goals, Paths(paths).Strings()) 414} 415 416func (c *makeVarsContext) DistForGoalsWithFilename(goals []string, path Path, filename string) { 417 c.addDist(goals, []string{path.String() + ":" + filename}) 418} 419