1// Copyright 2020 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 "path/filepath" 20 "sort" 21 22 "github.com/google/blueprint" 23 "github.com/google/blueprint/gobtools" 24 "github.com/google/blueprint/proptools" 25) 26 27// PackagingSpec abstracts a request to place a built artifact at a certain path in a package. A 28// package can be the traditional <partition>.img, but isn't limited to those. Other examples could 29// be a new filesystem image that is a subset of system.img (e.g. for an Android-like mini OS 30// running on a VM), or a zip archive for some of the host tools. 31type PackagingSpec struct { 32 // Path relative to the root of the package 33 relPathInPackage string 34 35 // The path to the built artifact 36 srcPath Path 37 38 // If this is not empty, then relPathInPackage should be a symlink to this target. (Then 39 // srcPath is of course ignored.) 40 symlinkTarget string 41 42 // Whether relPathInPackage should be marked as executable or not 43 executable bool 44 45 effectiveLicenseFiles *Paths 46 47 partition string 48 49 // Whether this packaging spec represents an installation of the srcPath (i.e. this struct 50 // is created via InstallFile or InstallSymlink) or a simple packaging (i.e. created via 51 // PackageFile). 52 skipInstall bool 53 54 // Paths of aconfig files for the built artifact 55 aconfigPaths *Paths 56 57 // ArchType of the module which produced this packaging spec 58 archType ArchType 59 60 // List of module names that this packaging spec overrides 61 overrides *[]string 62 63 // Name of the module where this packaging spec is output of 64 owner string 65} 66 67type packagingSpecGob struct { 68 RelPathInPackage string 69 SrcPath Path 70 SymlinkTarget string 71 Executable bool 72 EffectiveLicenseFiles *Paths 73 Partition string 74 SkipInstall bool 75 AconfigPaths *Paths 76 ArchType ArchType 77 Overrides *[]string 78 Owner string 79} 80 81func (p *PackagingSpec) ToGob() *packagingSpecGob { 82 return &packagingSpecGob{ 83 RelPathInPackage: p.relPathInPackage, 84 SrcPath: p.srcPath, 85 SymlinkTarget: p.symlinkTarget, 86 Executable: p.executable, 87 EffectiveLicenseFiles: p.effectiveLicenseFiles, 88 Partition: p.partition, 89 SkipInstall: p.skipInstall, 90 AconfigPaths: p.aconfigPaths, 91 ArchType: p.archType, 92 Overrides: p.overrides, 93 Owner: p.owner, 94 } 95} 96 97func (p *PackagingSpec) FromGob(data *packagingSpecGob) { 98 p.relPathInPackage = data.RelPathInPackage 99 p.srcPath = data.SrcPath 100 p.symlinkTarget = data.SymlinkTarget 101 p.executable = data.Executable 102 p.effectiveLicenseFiles = data.EffectiveLicenseFiles 103 p.partition = data.Partition 104 p.skipInstall = data.SkipInstall 105 p.aconfigPaths = data.AconfigPaths 106 p.archType = data.ArchType 107 p.overrides = data.Overrides 108 p.owner = data.Owner 109} 110 111func (p *PackagingSpec) GobEncode() ([]byte, error) { 112 return gobtools.CustomGobEncode[packagingSpecGob](p) 113} 114 115func (p *PackagingSpec) GobDecode(data []byte) error { 116 return gobtools.CustomGobDecode[packagingSpecGob](data, p) 117} 118 119func (p *PackagingSpec) Equals(other *PackagingSpec) bool { 120 if other == nil { 121 return false 122 } 123 if p.relPathInPackage != other.relPathInPackage { 124 return false 125 } 126 if p.srcPath != other.srcPath || p.symlinkTarget != other.symlinkTarget { 127 return false 128 } 129 if p.executable != other.executable { 130 return false 131 } 132 if p.partition != other.partition { 133 return false 134 } 135 return true 136} 137 138// Get file name of installed package 139func (p *PackagingSpec) FileName() string { 140 if p.relPathInPackage != "" { 141 return filepath.Base(p.relPathInPackage) 142 } 143 144 return "" 145} 146 147// Path relative to the root of the package 148func (p *PackagingSpec) RelPathInPackage() string { 149 return p.relPathInPackage 150} 151 152func (p *PackagingSpec) SetRelPathInPackage(relPathInPackage string) { 153 p.relPathInPackage = relPathInPackage 154} 155 156func (p *PackagingSpec) EffectiveLicenseFiles() Paths { 157 if p.effectiveLicenseFiles == nil { 158 return Paths{} 159 } 160 return *p.effectiveLicenseFiles 161} 162 163func (p *PackagingSpec) Partition() string { 164 return p.partition 165} 166 167func (p *PackagingSpec) SetPartition(partition string) { 168 p.partition = partition 169} 170 171func (p *PackagingSpec) SkipInstall() bool { 172 return p.skipInstall 173} 174 175// Paths of aconfig files for the built artifact 176func (p *PackagingSpec) GetAconfigPaths() Paths { 177 return *p.aconfigPaths 178} 179 180type PackageModule interface { 181 Module 182 packagingBase() *PackagingBase 183 184 // AddDeps adds dependencies to the `deps` modules. This should be called in DepsMutator. 185 // When adding the dependencies, depTag is used as the tag. If `deps` modules are meant to 186 // be copied to a zip in CopyDepsToZip, `depTag` should implement PackagingItem marker interface. 187 AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) 188 189 // GatherPackagingSpecs gathers PackagingSpecs of transitive dependencies. 190 GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec 191 GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec 192 GatherPackagingSpecsWithFilterAndModifier(ctx ModuleContext, filter func(PackagingSpec) bool, modifier func(*PackagingSpec)) map[string]PackagingSpec 193 194 // CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and 195 // returns zip entries in it. This is expected to be called in GenerateAndroidBuildActions, 196 // followed by a build rule that unzips it and creates the final output (img, zip, tar.gz, 197 // etc.) from the extracted files 198 CopyDepsToZip(ctx ModuleContext, specs map[string]PackagingSpec, zipOut WritablePath) []string 199} 200 201// PackagingBase provides basic functionality for packaging dependencies. A module is expected to 202// include this struct and call InitPackageModule. 203type PackagingBase struct { 204 properties PackagingProperties 205 206 // Allows this module to skip missing dependencies. In most cases, this is not required, but 207 // for rare cases like when there's a dependency to a module which exists in certain repo 208 // checkouts, this is needed. 209 IgnoreMissingDependencies bool 210 211 // If this is set to true by a module type inheriting PackagingBase, the deps property 212 // collects the first target only even with compile_multilib: true. 213 DepsCollectFirstTargetOnly bool 214 215 // If this is set to try by a module type inheriting PackagingBase, the module type is 216 // allowed to utilize High_priority_deps. 217 AllowHighPriorityDeps bool 218} 219 220type DepsProperty struct { 221 // Deps that have higher priority in packaging when there is a packaging conflict. 222 // For example, if multiple files are being installed to same filepath, the install file 223 // of the module listed in this property will have a higher priority over those in other 224 // deps properties. 225 High_priority_deps []string `android:"arch_variant"` 226 227 // Modules to include in this package 228 Deps proptools.Configurable[[]string] `android:"arch_variant"` 229} 230 231type packagingMultilibProperties struct { 232 First DepsProperty `android:"arch_variant"` 233 Common DepsProperty `android:"arch_variant"` 234 Lib32 DepsProperty `android:"arch_variant"` 235 Lib64 DepsProperty `android:"arch_variant"` 236 Both DepsProperty `android:"arch_variant"` 237 Prefer32 DepsProperty `android:"arch_variant"` 238} 239 240type packagingArchProperties struct { 241 Arm64 DepsProperty 242 Arm DepsProperty 243 X86_64 DepsProperty 244 X86 DepsProperty 245} 246 247type PackagingProperties struct { 248 DepsProperty 249 250 Multilib packagingMultilibProperties `android:"arch_variant"` 251 Arch packagingArchProperties 252} 253 254func InitPackageModule(p PackageModule) { 255 base := p.packagingBase() 256 p.AddProperties(&base.properties, &base.properties.DepsProperty) 257} 258 259func (p *PackagingBase) packagingBase() *PackagingBase { 260 return p 261} 262 263// From deps and multilib.*.deps, select the dependencies that are for the given arch deps is for 264// the current archicture when this module is not configured for multi target. When configured for 265// multi target, deps is selected for each of the targets and is NOT selected for the current 266// architecture which would be Common. 267// It returns two lists, the normal and high priority deps, respectively. 268func (p *PackagingBase) getDepsForArch(ctx BaseModuleContext, arch ArchType) ([]string, []string) { 269 var normalDeps []string 270 var highPriorityDeps []string 271 272 get := func(prop DepsProperty) { 273 normalDeps = append(normalDeps, prop.Deps.GetOrDefault(ctx, nil)...) 274 highPriorityDeps = append(highPriorityDeps, prop.High_priority_deps...) 275 } 276 has := func(prop DepsProperty) bool { 277 return len(prop.Deps.GetOrDefault(ctx, nil)) > 0 || len(prop.High_priority_deps) > 0 278 } 279 280 if arch == ctx.Target().Arch.ArchType && len(ctx.MultiTargets()) == 0 { 281 get(p.properties.DepsProperty) 282 } else if arch.Multilib == "lib32" { 283 get(p.properties.Multilib.Lib32) 284 // multilib.prefer32.deps are added for lib32 only when they support 32-bit arch 285 for _, dep := range p.properties.Multilib.Prefer32.Deps.GetOrDefault(ctx, nil) { 286 if checkIfOtherModuleSupportsLib32(ctx, dep) { 287 normalDeps = append(normalDeps, dep) 288 } 289 } 290 for _, dep := range p.properties.Multilib.Prefer32.High_priority_deps { 291 if checkIfOtherModuleSupportsLib32(ctx, dep) { 292 highPriorityDeps = append(highPriorityDeps, dep) 293 } 294 } 295 } else if arch.Multilib == "lib64" { 296 get(p.properties.Multilib.Lib64) 297 // multilib.prefer32.deps are added for lib64 only when they don't support 32-bit arch 298 for _, dep := range p.properties.Multilib.Prefer32.Deps.GetOrDefault(ctx, nil) { 299 if !checkIfOtherModuleSupportsLib32(ctx, dep) { 300 normalDeps = append(normalDeps, dep) 301 } 302 } 303 for _, dep := range p.properties.Multilib.Prefer32.High_priority_deps { 304 if !checkIfOtherModuleSupportsLib32(ctx, dep) { 305 highPriorityDeps = append(highPriorityDeps, dep) 306 } 307 } 308 } else if arch == Common { 309 get(p.properties.Multilib.Common) 310 } 311 312 if p.DepsCollectFirstTargetOnly { 313 if has(p.properties.Multilib.First) { 314 ctx.PropertyErrorf("multilib.first.deps", "not supported. use \"deps\" instead") 315 } 316 for i, t := range ctx.MultiTargets() { 317 if t.Arch.ArchType == arch { 318 get(p.properties.Multilib.Both) 319 if i == 0 { 320 get(p.properties.DepsProperty) 321 } 322 } 323 } 324 } else { 325 if has(p.properties.Multilib.Both) { 326 ctx.PropertyErrorf("multilib.both.deps", "not supported. use \"deps\" instead") 327 } 328 for i, t := range ctx.MultiTargets() { 329 if t.Arch.ArchType == arch { 330 get(p.properties.DepsProperty) 331 if i == 0 { 332 get(p.properties.Multilib.First) 333 } 334 } 335 } 336 } 337 338 if ctx.Arch().ArchType == Common { 339 switch arch { 340 case Arm64: 341 get(p.properties.Arch.Arm64) 342 case Arm: 343 get(p.properties.Arch.Arm) 344 case X86_64: 345 get(p.properties.Arch.X86_64) 346 case X86: 347 get(p.properties.Arch.X86) 348 } 349 } 350 351 if len(highPriorityDeps) > 0 && !p.AllowHighPriorityDeps { 352 ctx.ModuleErrorf("Usage of high_priority_deps is not allowed for %s module type", ctx.ModuleType()) 353 } 354 355 return FirstUniqueStrings(normalDeps), FirstUniqueStrings(highPriorityDeps) 356} 357 358func getSupportedTargets(ctx BaseModuleContext) []Target { 359 var ret []Target 360 // The current and the common OS targets are always supported 361 ret = append(ret, ctx.Target()) 362 if ctx.Arch().ArchType != Common { 363 ret = append(ret, Target{Os: ctx.Os(), Arch: Arch{ArchType: Common}}) 364 } 365 // If this module is configured for multi targets, those should be supported as well 366 ret = append(ret, ctx.MultiTargets()...) 367 return ret 368} 369 370// getLib32Target returns the 32-bit target from the list of targets this module supports. If this 371// module doesn't support 32-bit target, nil is returned. 372func getLib32Target(ctx BaseModuleContext) *Target { 373 for _, t := range getSupportedTargets(ctx) { 374 if t.Arch.ArchType.Multilib == "lib32" { 375 return &t 376 } 377 } 378 return nil 379} 380 381// checkIfOtherModuleSUpportsLib32 returns true if 32-bit variant of dep exists. 382func checkIfOtherModuleSupportsLib32(ctx BaseModuleContext, dep string) bool { 383 t := getLib32Target(ctx) 384 if t == nil { 385 // This packaging module doesn't support 32bit. No point of checking if dep supports 32-bit 386 // or not. 387 return false 388 } 389 return ctx.OtherModuleFarDependencyVariantExists(t.Variations(), dep) 390} 391 392// PackagingItem is a marker interface for dependency tags. 393// Direct dependencies with a tag implementing PackagingItem are packaged in CopyDepsToZip(). 394type PackagingItem interface { 395 // IsPackagingItem returns true if the dep is to be packaged 396 IsPackagingItem() bool 397} 398 399var _ PackagingItem = (*PackagingItemAlwaysDepTag)(nil) 400 401// DepTag provides default implementation of PackagingItem interface. 402// PackagingBase-derived modules can define their own dependency tag by embedding this, which 403// can be passed to AddDeps() or AddDependencies(). 404type PackagingItemAlwaysDepTag struct { 405} 406 407// IsPackagingItem returns true if the dep is to be packaged 408func (PackagingItemAlwaysDepTag) IsPackagingItem() bool { 409 return true 410} 411 412type highPriorityDepTag struct { 413 blueprint.BaseDependencyTag 414 PackagingItemAlwaysDepTag 415} 416 417// See PackageModule.AddDeps 418func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) { 419 addDep := func(t Target, dep string, highPriority bool) { 420 if p.IgnoreMissingDependencies && !ctx.OtherModuleExists(dep) { 421 return 422 } 423 targetVariation := t.Variations() 424 sharedVariation := blueprint.Variation{ 425 Mutator: "link", 426 Variation: "shared", 427 } 428 // If a shared variation exists, use that. Static variants do not provide any standalone files 429 // for packaging. 430 if ctx.OtherModuleFarDependencyVariantExists([]blueprint.Variation{sharedVariation}, dep) { 431 targetVariation = append(targetVariation, sharedVariation) 432 } 433 depTagToUse := depTag 434 if highPriority { 435 depTagToUse = highPriorityDepTag{} 436 } 437 438 ctx.AddFarVariationDependencies(targetVariation, depTagToUse, dep) 439 } 440 for _, t := range getSupportedTargets(ctx) { 441 normalDeps, highPriorityDeps := p.getDepsForArch(ctx, t.Arch.ArchType) 442 for _, dep := range normalDeps { 443 addDep(t, dep, false) 444 } 445 for _, dep := range highPriorityDeps { 446 addDep(t, dep, true) 447 } 448 } 449} 450 451// See PackageModule.GatherPackagingSpecs 452func (p *PackagingBase) GatherPackagingSpecsWithFilterAndModifier(ctx ModuleContext, filter func(PackagingSpec) bool, modifier func(*PackagingSpec)) map[string]PackagingSpec { 453 // packaging specs gathered from the dep that are not high priorities. 454 var regularPriorities []PackagingSpec 455 456 // all packaging specs gathered from the high priority deps. 457 var highPriorities []PackagingSpec 458 459 // Name of the dependency which requested the packaging spec. 460 // If this dep is overridden, the packaging spec will not be installed via this dependency chain. 461 // (the packaging spec might still be installed if there are some other deps which depend on it). 462 var depNames []string 463 464 // list of module names overridden 465 var overridden []string 466 467 var arches []ArchType 468 for _, target := range getSupportedTargets(ctx) { 469 arches = append(arches, target.Arch.ArchType) 470 } 471 472 // filter out packaging specs for unsupported architecture 473 filterArch := func(ps PackagingSpec) bool { 474 for _, arch := range arches { 475 if arch == ps.archType { 476 return true 477 } 478 } 479 return false 480 } 481 482 ctx.VisitDirectDepsProxy(func(child ModuleProxy) { 483 depTag := ctx.OtherModuleDependencyTag(child) 484 if pi, ok := depTag.(PackagingItem); !ok || !pi.IsPackagingItem() { 485 return 486 } 487 for _, ps := range OtherModuleProviderOrDefault( 488 ctx, child, InstallFilesProvider).TransitivePackagingSpecs.ToList() { 489 if !filterArch(ps) { 490 continue 491 } 492 493 if filter != nil { 494 if !filter(ps) { 495 continue 496 } 497 } 498 499 if modifier != nil { 500 modifier(&ps) 501 } 502 503 if _, ok := depTag.(highPriorityDepTag); ok { 504 highPriorities = append(highPriorities, ps) 505 } else { 506 regularPriorities = append(regularPriorities, ps) 507 } 508 509 depNames = append(depNames, child.Name()) 510 if ps.overrides != nil { 511 overridden = append(overridden, *ps.overrides...) 512 } 513 } 514 }) 515 516 filterOverridden := func(input []PackagingSpec) []PackagingSpec { 517 // input minus packaging specs that are overridden 518 var filtered []PackagingSpec 519 for index, ps := range input { 520 if ps.owner != "" && InList(ps.owner, overridden) { 521 continue 522 } 523 // The dependency which requested this packaging spec has been overridden. 524 if InList(depNames[index], overridden) { 525 continue 526 } 527 filtered = append(filtered, ps) 528 } 529 return filtered 530 } 531 532 filteredRegularPriority := filterOverridden(regularPriorities) 533 534 m := make(map[string]PackagingSpec) 535 for _, ps := range filteredRegularPriority { 536 dstPath := ps.relPathInPackage 537 if existingPs, ok := m[dstPath]; ok { 538 if !existingPs.Equals(&ps) { 539 ctx.ModuleErrorf("packaging conflict at %v:\n%v\n%v", dstPath, existingPs, ps) 540 } 541 continue 542 } 543 m[dstPath] = ps 544 } 545 546 filteredHighPriority := filterOverridden(highPriorities) 547 highPriorityPs := make(map[string]PackagingSpec) 548 for _, ps := range filteredHighPriority { 549 dstPath := ps.relPathInPackage 550 if existingPs, ok := highPriorityPs[dstPath]; ok { 551 if !existingPs.Equals(&ps) { 552 ctx.ModuleErrorf("packaging conflict at %v:\n%v\n%v", dstPath, existingPs, ps) 553 } 554 continue 555 } 556 highPriorityPs[dstPath] = ps 557 m[dstPath] = ps 558 } 559 560 return m 561} 562 563// See PackageModule.GatherPackagingSpecs 564func (p *PackagingBase) GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec { 565 return p.GatherPackagingSpecsWithFilterAndModifier(ctx, filter, nil) 566} 567 568// See PackageModule.GatherPackagingSpecs 569func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec { 570 return p.GatherPackagingSpecsWithFilter(ctx, nil) 571} 572 573// CopySpecsToDir is a helper that will add commands to the rule builder to copy the PackagingSpec 574// entries into the specified directory. 575func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir WritablePath) (entries []string) { 576 dirsToSpecs := make(map[WritablePath]map[string]PackagingSpec) 577 dirsToSpecs[dir] = specs 578 return p.CopySpecsToDirs(ctx, builder, dirsToSpecs) 579} 580 581// CopySpecsToDirs is a helper that will add commands to the rule builder to copy the PackagingSpec 582// entries into corresponding directories. 583func (p *PackagingBase) CopySpecsToDirs(ctx ModuleContext, builder *RuleBuilder, dirsToSpecs map[WritablePath]map[string]PackagingSpec) (entries []string) { 584 empty := true 585 for _, specs := range dirsToSpecs { 586 if len(specs) > 0 { 587 empty = false 588 break 589 } 590 } 591 if empty { 592 return entries 593 } 594 595 seenDir := make(map[string]bool) 596 597 dirs := make([]WritablePath, 0, len(dirsToSpecs)) 598 for dir, _ := range dirsToSpecs { 599 dirs = append(dirs, dir) 600 } 601 sort.Slice(dirs, func(i, j int) bool { 602 return dirs[i].String() < dirs[j].String() 603 }) 604 605 for _, dir := range dirs { 606 specs := dirsToSpecs[dir] 607 for _, k := range SortedKeys(specs) { 608 ps := specs[k] 609 destPath := filepath.Join(dir.String(), ps.relPathInPackage) 610 destDir := filepath.Dir(destPath) 611 entries = append(entries, ps.relPathInPackage) 612 if _, ok := seenDir[destDir]; !ok { 613 seenDir[destDir] = true 614 builder.Command().Textf("mkdir -p %s", destDir) 615 } 616 if ps.symlinkTarget == "" { 617 builder.Command().Text("cp").Input(ps.srcPath).Text(destPath) 618 } else { 619 builder.Command().Textf("ln -sf %s %s", ps.symlinkTarget, destPath) 620 } 621 if ps.executable { 622 builder.Command().Textf("chmod a+x %s", destPath) 623 } 624 } 625 } 626 627 return entries 628} 629 630// See PackageModule.CopyDepsToZip 631func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, specs map[string]PackagingSpec, zipOut WritablePath) (entries []string) { 632 builder := NewRuleBuilder(pctx, ctx) 633 634 dir := PathForModuleOut(ctx, ".zip") 635 builder.Command().Text("rm").Flag("-rf").Text(dir.String()) 636 builder.Command().Text("mkdir").Flag("-p").Text(dir.String()) 637 entries = p.CopySpecsToDir(ctx, builder, specs, dir) 638 639 builder.Command(). 640 BuiltTool("soong_zip"). 641 FlagWithOutput("-o ", zipOut). 642 FlagWithArg("-C ", dir.String()). 643 Flag("-L 0"). // no compression because this will be unzipped soon 644 FlagWithArg("-D ", dir.String()) 645 builder.Command().Text("rm").Flag("-rf").Text(dir.String()) 646 647 builder.Build("zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName())) 648 return entries 649} 650