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 "path/filepath" 21 "runtime" 22 "sort" 23 "strings" 24 25 "github.com/google/blueprint" 26 "github.com/google/blueprint/pathtools" 27 "github.com/google/blueprint/proptools" 28) 29 30func init() { 31 RegisterMakeVarsProvider(pctx, androidMakeVarsProvider) 32} 33 34func androidMakeVarsProvider(ctx MakeVarsContext) { 35 ctx.Strict("MIN_SUPPORTED_SDK_VERSION", ctx.Config().MinSupportedSdkVersion().String()) 36} 37 38// ///////////////////////////////////////////////////////////////////////////// 39 40// BaseMakeVarsContext contains the common functions for other packages to use 41// to declare make variables 42type BaseMakeVarsContext interface { 43 Config() Config 44 DeviceConfig() DeviceConfig 45 AddNinjaFileDeps(deps ...string) 46 47 Failed() bool 48 49 // These are equivalent to Strict and Check, but do not attempt to 50 // evaluate the values before writing them to the Makefile. They can 51 // be used when all ninja variables have already been evaluated through 52 // Eval(). 53 StrictRaw(name, value string) 54 CheckRaw(name, value string) 55 56 // GlobWithDeps returns a list of files that match the specified pattern but do not match any 57 // of the patterns in excludes. It also adds efficient dependencies to rerun the primary 58 // builder whenever a file matching the pattern as added or removed, without rerunning if a 59 // file that does not match the pattern is added to a searched directory. 60 GlobWithDeps(pattern string, excludes []string) ([]string, error) 61 62 // Phony creates a phony rule in Make, which will allow additional DistForGoal 63 // dependencies to be added to it. Phony can be called on the same name multiple 64 // times to add additional dependencies. 65 Phony(names string, deps ...Path) 66 67 // DistForGoal creates a rule to copy one or more Paths to the artifacts 68 // directory on the build server when the specified goal is built. 69 DistForGoal(goal string, paths ...Path) 70 71 // DistForGoalWithFilename creates a rule to copy a Path to the artifacts 72 // directory on the build server with the given filename when the specified 73 // goal is built. 74 DistForGoalWithFilename(goal string, path Path, filename string) 75 76 // DistForGoals creates a rule to copy one or more Paths to the artifacts 77 // directory on the build server when any of the specified goals are built. 78 DistForGoals(goals []string, paths ...Path) 79 80 // DistForGoalsWithFilename creates a rule to copy a Path to the artifacts 81 // directory on the build server with the given filename when any of the 82 // specified goals are built. 83 DistForGoalsWithFilename(goals []string, path Path, filename string) 84} 85 86// MakeVarsContext contains the set of functions available for MakeVarsProvider 87// and SingletonMakeVarsProvider implementations. 88type MakeVarsContext interface { 89 BaseMakeVarsContext 90 91 ModuleName(module blueprint.Module) string 92 ModuleDir(module blueprint.Module) string 93 ModuleSubDir(module blueprint.Module) string 94 ModuleType(module blueprint.Module) string 95 ModuleProvider(module blueprint.Module, key blueprint.ProviderKey) interface{} 96 BlueprintFile(module blueprint.Module) string 97 98 ModuleErrorf(module blueprint.Module, format string, args ...interface{}) 99 Errorf(format string, args ...interface{}) 100 101 VisitAllModules(visit func(Module)) 102 VisitAllModulesIf(pred func(Module) bool, visit func(Module)) 103 104 // Verify the make variable matches the Soong version, fail the build 105 // if it does not. If the make variable is empty, just set it. 106 Strict(name, ninjaStr string) 107 // Check to see if the make variable matches the Soong version, warn if 108 // it does not. If the make variable is empty, just set it. 109 Check(name, ninjaStr string) 110 111 // These are equivalent to the above, but sort the make and soong 112 // variables before comparing them. They also show the unique entries 113 // in each list when displaying the difference, instead of the entire 114 // string. 115 StrictSorted(name, ninjaStr string) 116 CheckSorted(name, ninjaStr string) 117 118 // Evaluates a ninja string and returns the result. Used if more 119 // complicated modification needs to happen before giving it to Make. 120 Eval(ninjaStr string) (string, error) 121} 122 123// MakeVarsModuleContext contains the set of functions available for modules 124// implementing the ModuleMakeVarsProvider interface. 125type MakeVarsModuleContext interface { 126 BaseMakeVarsContext 127} 128 129var _ PathContext = MakeVarsContext(nil) 130 131type MakeVarsProvider func(ctx MakeVarsContext) 132 133func RegisterMakeVarsProvider(pctx PackageContext, provider MakeVarsProvider) { 134 makeVarsInitProviders = append(makeVarsInitProviders, makeVarsProvider{pctx, provider}) 135} 136 137// SingletonMakeVarsProvider is a Singleton with an extra method to provide extra values to be exported to Make. 138type SingletonMakeVarsProvider interface { 139 // MakeVars uses a MakeVarsContext to provide extra values to be exported to Make. 140 MakeVars(ctx MakeVarsContext) 141} 142 143var singletonMakeVarsProvidersKey = NewOnceKey("singletonMakeVarsProvidersKey") 144 145func getSingletonMakevarsProviders(config Config) *[]makeVarsProvider { 146 return config.Once(singletonMakeVarsProvidersKey, func() interface{} { 147 return &[]makeVarsProvider{} 148 }).(*[]makeVarsProvider) 149} 150 151// registerSingletonMakeVarsProvider adds a singleton that implements SingletonMakeVarsProvider to 152// the list of MakeVarsProviders to run. 153func registerSingletonMakeVarsProvider(config Config, singleton SingletonMakeVarsProvider) { 154 // Singletons are registered on the Context and may be different between different Contexts, 155 // for example when running multiple tests. Store the SingletonMakeVarsProviders in the 156 // Config so they are attached to the Context. 157 singletonMakeVarsProviders := getSingletonMakevarsProviders(config) 158 159 *singletonMakeVarsProviders = append(*singletonMakeVarsProviders, 160 makeVarsProvider{pctx, singletonMakeVarsProviderAdapter(singleton)}) 161} 162 163// singletonMakeVarsProviderAdapter converts a SingletonMakeVarsProvider to a MakeVarsProvider. 164func singletonMakeVarsProviderAdapter(singleton SingletonMakeVarsProvider) MakeVarsProvider { 165 return func(ctx MakeVarsContext) { singleton.MakeVars(ctx) } 166} 167 168// ModuleMakeVarsProvider is a Module with an extra method to provide extra values to be exported to Make. 169type ModuleMakeVarsProvider interface { 170 Module 171 172 // MakeVars uses a MakeVarsModuleContext to provide extra values to be exported to Make. 173 MakeVars(ctx MakeVarsModuleContext) 174} 175 176// ///////////////////////////////////////////////////////////////////////////// 177 178func makeVarsSingletonFunc() Singleton { 179 return &makeVarsSingleton{} 180} 181 182type makeVarsSingleton struct { 183 varsForTesting []makeVarsVariable 184 installsForTesting []byte 185} 186 187type makeVarsProvider struct { 188 pctx PackageContext 189 call MakeVarsProvider 190} 191 192// Collection of makevars providers that are registered in init() methods. 193var makeVarsInitProviders []makeVarsProvider 194 195type makeVarsContext struct { 196 SingletonContext 197 config Config 198 pctx PackageContext 199 vars []makeVarsVariable 200 phonies []phony 201 dists []dist 202} 203 204var _ MakeVarsContext = &makeVarsContext{} 205 206type makeVarsVariable struct { 207 name string 208 value string 209 sort bool 210 strict bool 211} 212 213type phony struct { 214 name string 215 deps []string 216} 217 218type dist struct { 219 goals []string 220 paths []string 221} 222 223func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) { 224 if !ctx.Config().KatiEnabled() { 225 return 226 } 227 228 outFile := absolutePath(PathForOutput(ctx, 229 "make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String()) 230 231 lateOutFile := absolutePath(PathForOutput(ctx, 232 "late"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String()) 233 234 installsFile := absolutePath(PathForOutput(ctx, 235 "installs"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String()) 236 237 if ctx.Failed() { 238 return 239 } 240 241 var vars []makeVarsVariable 242 var dists []dist 243 var phonies []phony 244 var katiInstalls []katiInstall 245 var katiSymlinks []katiInstall 246 247 providers := append([]makeVarsProvider(nil), makeVarsInitProviders...) 248 providers = append(providers, *getSingletonMakevarsProviders(ctx.Config())...) 249 250 for _, provider := range providers { 251 mctx := &makeVarsContext{ 252 SingletonContext: ctx, 253 pctx: provider.pctx, 254 } 255 256 provider.call(mctx) 257 258 vars = append(vars, mctx.vars...) 259 phonies = append(phonies, mctx.phonies...) 260 dists = append(dists, mctx.dists...) 261 } 262 263 ctx.VisitAllModules(func(m Module) { 264 if provider, ok := m.(ModuleMakeVarsProvider); ok && m.Enabled() { 265 mctx := &makeVarsContext{ 266 SingletonContext: ctx, 267 } 268 269 provider.MakeVars(mctx) 270 271 vars = append(vars, mctx.vars...) 272 phonies = append(phonies, mctx.phonies...) 273 dists = append(dists, mctx.dists...) 274 } 275 276 if m.ExportedToMake() { 277 katiInstalls = append(katiInstalls, m.base().katiInstalls...) 278 katiSymlinks = append(katiSymlinks, m.base().katiSymlinks...) 279 } 280 }) 281 282 if ctx.Failed() { 283 return 284 } 285 286 sort.Slice(vars, func(i, j int) bool { 287 return vars[i].name < vars[j].name 288 }) 289 sort.Slice(phonies, func(i, j int) bool { 290 return phonies[i].name < phonies[j].name 291 }) 292 lessArr := func(a, b []string) bool { 293 if len(a) == len(b) { 294 for i := range a { 295 if a[i] < b[i] { 296 return true 297 } 298 } 299 return false 300 } 301 return len(a) < len(b) 302 } 303 sort.Slice(dists, func(i, j int) bool { 304 return lessArr(dists[i].goals, dists[j].goals) || lessArr(dists[i].paths, dists[j].paths) 305 }) 306 307 outBytes := s.writeVars(vars) 308 309 if err := pathtools.WriteFileIfChanged(outFile, outBytes, 0666); err != nil { 310 ctx.Errorf(err.Error()) 311 } 312 313 lateOutBytes := s.writeLate(phonies, dists) 314 315 if err := pathtools.WriteFileIfChanged(lateOutFile, lateOutBytes, 0666); err != nil { 316 ctx.Errorf(err.Error()) 317 } 318 319 installsBytes := s.writeInstalls(katiInstalls, katiSymlinks) 320 if err := pathtools.WriteFileIfChanged(installsFile, installsBytes, 0666); err != nil { 321 ctx.Errorf(err.Error()) 322 } 323 324 // Only save state for tests when testing. 325 if ctx.Config().RunningInsideUnitTest() { 326 s.varsForTesting = vars 327 s.installsForTesting = installsBytes 328 } 329} 330 331func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte { 332 buf := &bytes.Buffer{} 333 334 fmt.Fprint(buf, `# Autogenerated file 335 336# Compares SOONG_$(1) against $(1), and warns if they are not equal. 337# 338# If the original variable is empty, then just set it to the SOONG_ version. 339# 340# $(1): Name of the variable to check 341# $(2): If not-empty, sort the values before comparing 342# $(3): Extra snippet to run if it does not match 343define soong-compare-var 344ifneq ($$($(1)),) 345 my_val_make := $$(strip $(if $(2),$$(sort $$($(1))),$$($(1)))) 346 my_val_soong := $(if $(2),$$(sort $$(SOONG_$(1))),$$(SOONG_$(1))) 347 ifneq ($$(my_val_make),$$(my_val_soong)) 348 $$(warning $(1) does not match between Make and Soong:) 349 $(if $(2),$$(warning Make adds: $$(filter-out $$(my_val_soong),$$(my_val_make))),$$(warning Make : $$(my_val_make))) 350 $(if $(2),$$(warning Soong adds: $$(filter-out $$(my_val_make),$$(my_val_soong))),$$(warning Soong: $$(my_val_soong))) 351 $(3) 352 endif 353 my_val_make := 354 my_val_soong := 355else 356 $(1) := $$(SOONG_$(1)) 357endif 358.KATI_READONLY := $(1) SOONG_$(1) 359endef 360 361my_check_failed := false 362 363`) 364 365 // Write all the strict checks out first so that if one of them errors, 366 // we get all of the strict errors printed, but not the non-strict 367 // warnings. 368 for _, v := range vars { 369 if !v.strict { 370 continue 371 } 372 373 sort := "" 374 if v.sort { 375 sort = "true" 376 } 377 378 fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value) 379 fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s,my_check_failed := true))\n\n", v.name, sort) 380 } 381 382 fmt.Fprint(buf, ` 383ifneq ($(my_check_failed),false) 384 $(error Soong variable check failed) 385endif 386my_check_failed := 387 388 389`) 390 391 for _, v := range vars { 392 if v.strict { 393 continue 394 } 395 396 sort := "" 397 if v.sort { 398 sort = "true" 399 } 400 401 fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value) 402 fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s))\n\n", v.name, sort) 403 } 404 405 fmt.Fprintln(buf, "\nsoong-compare-var :=") 406 407 fmt.Fprintln(buf) 408 409 return buf.Bytes() 410} 411 412func (s *makeVarsSingleton) writeLate(phonies []phony, dists []dist) []byte { 413 buf := &bytes.Buffer{} 414 415 fmt.Fprint(buf, `# Autogenerated file 416 417# Values written by Soong read after parsing all Android.mk files. 418 419 420`) 421 422 for _, phony := range phonies { 423 fmt.Fprintf(buf, ".PHONY: %s\n", phony.name) 424 fmt.Fprintf(buf, "%s: %s\n", phony.name, strings.Join(phony.deps, "\\\n ")) 425 } 426 427 fmt.Fprintln(buf) 428 429 for _, dist := range dists { 430 fmt.Fprintf(buf, ".PHONY: %s\n", strings.Join(dist.goals, " ")) 431 fmt.Fprintf(buf, "$(call dist-for-goals,%s,%s)\n", 432 strings.Join(dist.goals, " "), strings.Join(dist.paths, " ")) 433 } 434 435 return buf.Bytes() 436} 437 438// writeInstalls writes the list of install rules generated by Soong to a makefile. The rules 439// are exported to Make instead of written directly to the ninja file so that main.mk can add 440// the dependencies from the `required` property that are hard to resolve in Soong. 441func (s *makeVarsSingleton) writeInstalls(installs, symlinks []katiInstall) []byte { 442 buf := &bytes.Buffer{} 443 444 fmt.Fprint(buf, `# Autogenerated file 445 446# Values written by Soong to generate install rules that can be amended by Kati. 447 448 449`) 450 451 preserveSymlinksFlag := "-d" 452 if runtime.GOOS == "darwin" { 453 preserveSymlinksFlag = "-R" 454 } 455 456 for _, install := range installs { 457 // Write a rule for each install request in the form: 458 // to: from [ deps ] [ | order only deps ] 459 // cp -f -d $< $@ [ && chmod +x $@ ] 460 fmt.Fprintf(buf, "%s: %s", install.to.String(), install.from.String()) 461 for _, dep := range install.implicitDeps { 462 fmt.Fprintf(buf, " %s", dep.String()) 463 } 464 if extraFiles := install.extraFiles; extraFiles != nil { 465 fmt.Fprintf(buf, " %s", extraFiles.zip.String()) 466 } 467 if len(install.orderOnlyDeps) > 0 { 468 fmt.Fprintf(buf, " |") 469 } 470 for _, dep := range install.orderOnlyDeps { 471 fmt.Fprintf(buf, " %s", dep.String()) 472 } 473 fmt.Fprintln(buf) 474 fmt.Fprintln(buf, "\t@echo \"Install: $@\"") 475 fmt.Fprintf(buf, "\trm -f $@ && cp -f %s $< $@\n", preserveSymlinksFlag) 476 if install.executable { 477 fmt.Fprintf(buf, "\tchmod +x $@\n") 478 } 479 if extraFiles := install.extraFiles; extraFiles != nil { 480 fmt.Fprintf(buf, "\t( unzip -qDD -d '%s' '%s' 2>&1 | grep -v \"zipfile is empty\"; exit $${PIPESTATUS[0]} ) || \\\n", extraFiles.dir.String(), extraFiles.zip.String()) 481 fmt.Fprintf(buf, "\t ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi )\n") 482 } 483 fmt.Fprintln(buf) 484 } 485 486 for _, symlink := range symlinks { 487 fmt.Fprintf(buf, "%s:", symlink.to.String()) 488 if symlink.from != nil { 489 // The symlink doesn't need updating when the target is modified, but we sometimes 490 // have a dependency on a symlink to a binary instead of to the binary directly, and 491 // the mtime of the symlink must be updated when the binary is modified, so use a 492 // normal dependency here instead of an order-only dependency. 493 fmt.Fprintf(buf, " %s", symlink.from.String()) 494 } 495 for _, dep := range symlink.implicitDeps { 496 fmt.Fprintf(buf, " %s", dep.String()) 497 } 498 if len(symlink.orderOnlyDeps) > 0 { 499 fmt.Fprintf(buf, " |") 500 } 501 for _, dep := range symlink.orderOnlyDeps { 502 fmt.Fprintf(buf, " %s", dep.String()) 503 } 504 fmt.Fprintln(buf) 505 506 fromStr := "" 507 if symlink.from != nil { 508 rel, err := filepath.Rel(filepath.Dir(symlink.to.String()), symlink.from.String()) 509 if err != nil { 510 panic(fmt.Errorf("failed to find relative path for symlink from %q to %q: %w", 511 symlink.from.String(), symlink.to.String(), err)) 512 } 513 fromStr = rel 514 } else { 515 fromStr = symlink.absFrom 516 } 517 518 fmt.Fprintln(buf, "\t@echo \"Symlink: $@\"") 519 fmt.Fprintf(buf, "\trm -f $@ && ln -sfn %s $@", fromStr) 520 fmt.Fprintln(buf) 521 fmt.Fprintln(buf) 522 } 523 524 return buf.Bytes() 525} 526 527func (c *makeVarsContext) DeviceConfig() DeviceConfig { 528 return DeviceConfig{c.Config().deviceConfig} 529} 530 531var ninjaDescaper = strings.NewReplacer("$$", "$") 532 533func (c *makeVarsContext) Eval(ninjaStr string) (string, error) { 534 s, err := c.SingletonContext.Eval(c.pctx, ninjaStr) 535 if err != nil { 536 return "", err 537 } 538 // SingletonContext.Eval returns an exapnded string that is valid for a ninja file, de-escape $$ to $ for use 539 // in a Makefile 540 return ninjaDescaper.Replace(s), nil 541} 542 543func (c *makeVarsContext) addVariableRaw(name, value string, strict, sort bool) { 544 c.vars = append(c.vars, makeVarsVariable{ 545 name: name, 546 value: value, 547 strict: strict, 548 sort: sort, 549 }) 550} 551 552func (c *makeVarsContext) addVariable(name, ninjaStr string, strict, sort bool) { 553 value, err := c.Eval(ninjaStr) 554 if err != nil { 555 c.SingletonContext.Errorf(err.Error()) 556 } 557 c.addVariableRaw(name, value, strict, sort) 558} 559 560func (c *makeVarsContext) addPhony(name string, deps []string) { 561 c.phonies = append(c.phonies, phony{name, deps}) 562} 563 564func (c *makeVarsContext) addDist(goals []string, paths []string) { 565 c.dists = append(c.dists, dist{ 566 goals: goals, 567 paths: paths, 568 }) 569} 570 571func (c *makeVarsContext) Strict(name, ninjaStr string) { 572 c.addVariable(name, ninjaStr, true, false) 573} 574func (c *makeVarsContext) StrictSorted(name, ninjaStr string) { 575 c.addVariable(name, ninjaStr, true, true) 576} 577func (c *makeVarsContext) StrictRaw(name, value string) { 578 c.addVariableRaw(name, value, true, false) 579} 580 581func (c *makeVarsContext) Check(name, ninjaStr string) { 582 c.addVariable(name, ninjaStr, false, false) 583} 584func (c *makeVarsContext) CheckSorted(name, ninjaStr string) { 585 c.addVariable(name, ninjaStr, false, true) 586} 587func (c *makeVarsContext) CheckRaw(name, value string) { 588 c.addVariableRaw(name, value, false, false) 589} 590 591func (c *makeVarsContext) Phony(name string, deps ...Path) { 592 c.addPhony(name, Paths(deps).Strings()) 593} 594 595func (c *makeVarsContext) DistForGoal(goal string, paths ...Path) { 596 c.DistForGoals([]string{goal}, paths...) 597} 598 599func (c *makeVarsContext) DistForGoalWithFilename(goal string, path Path, filename string) { 600 c.DistForGoalsWithFilename([]string{goal}, path, filename) 601} 602 603func (c *makeVarsContext) DistForGoals(goals []string, paths ...Path) { 604 c.addDist(goals, Paths(paths).Strings()) 605} 606 607func (c *makeVarsContext) DistForGoalsWithFilename(goals []string, path Path, filename string) { 608 c.addDist(goals, []string{path.String() + ":" + filename}) 609} 610