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