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