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 21 "github.com/google/blueprint" 22) 23 24// PackagingSpec abstracts a request to place a built artifact at a certain path in a package. A 25// package can be the traditional <partition>.img, but isn't limited to those. Other examples could 26// be a new filesystem image that is a subset of system.img (e.g. for an Android-like mini OS 27// running on a VM), or a zip archive for some of the host tools. 28type PackagingSpec struct { 29 // Path relative to the root of the package 30 relPathInPackage string 31 32 // The path to the built artifact 33 srcPath Path 34 35 // If this is not empty, then relPathInPackage should be a symlink to this target. (Then 36 // srcPath is of course ignored.) 37 symlinkTarget string 38 39 // Whether relPathInPackage should be marked as executable or not 40 executable bool 41 42 effectiveLicenseFiles *Paths 43 44 partition string 45} 46 47// Get file name of installed package 48func (p *PackagingSpec) FileName() string { 49 if p.relPathInPackage != "" { 50 return filepath.Base(p.relPathInPackage) 51 } 52 53 return "" 54} 55 56// Path relative to the root of the package 57func (p *PackagingSpec) RelPathInPackage() string { 58 return p.relPathInPackage 59} 60 61func (p *PackagingSpec) SetRelPathInPackage(relPathInPackage string) { 62 p.relPathInPackage = relPathInPackage 63} 64 65func (p *PackagingSpec) EffectiveLicenseFiles() Paths { 66 if p.effectiveLicenseFiles == nil { 67 return Paths{} 68 } 69 return *p.effectiveLicenseFiles 70} 71 72func (p *PackagingSpec) Partition() string { 73 return p.partition 74} 75 76type PackageModule interface { 77 Module 78 packagingBase() *PackagingBase 79 80 // AddDeps adds dependencies to the `deps` modules. This should be called in DepsMutator. 81 // When adding the dependencies, depTag is used as the tag. If `deps` modules are meant to 82 // be copied to a zip in CopyDepsToZip, `depTag` should implement PackagingItem marker interface. 83 AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) 84 85 // GatherPackagingSpecs gathers PackagingSpecs of transitive dependencies. 86 GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec 87 88 // CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and 89 // returns zip entries in it. This is expected to be called in GenerateAndroidBuildActions, 90 // followed by a build rule that unzips it and creates the final output (img, zip, tar.gz, 91 // etc.) from the extracted files 92 CopyDepsToZip(ctx ModuleContext, specs map[string]PackagingSpec, zipOut WritablePath) []string 93} 94 95// PackagingBase provides basic functionality for packaging dependencies. A module is expected to 96// include this struct and call InitPackageModule. 97type PackagingBase struct { 98 properties PackagingProperties 99 100 // Allows this module to skip missing dependencies. In most cases, this is not required, but 101 // for rare cases like when there's a dependency to a module which exists in certain repo 102 // checkouts, this is needed. 103 IgnoreMissingDependencies bool 104} 105 106type depsProperty struct { 107 // Modules to include in this package 108 Deps []string `android:"arch_variant"` 109} 110 111type packagingMultilibProperties struct { 112 First depsProperty `android:"arch_variant"` 113 Common depsProperty `android:"arch_variant"` 114 Lib32 depsProperty `android:"arch_variant"` 115 Lib64 depsProperty `android:"arch_variant"` 116} 117 118type packagingArchProperties struct { 119 Arm64 depsProperty 120 Arm depsProperty 121 X86_64 depsProperty 122 X86 depsProperty 123} 124 125type PackagingProperties struct { 126 Deps []string `android:"arch_variant"` 127 Multilib packagingMultilibProperties `android:"arch_variant"` 128 Arch packagingArchProperties 129} 130 131func InitPackageModule(p PackageModule) { 132 base := p.packagingBase() 133 p.AddProperties(&base.properties) 134} 135 136func (p *PackagingBase) packagingBase() *PackagingBase { 137 return p 138} 139 140// From deps and multilib.*.deps, select the dependencies that are for the given arch deps is for 141// the current archicture when this module is not configured for multi target. When configured for 142// multi target, deps is selected for each of the targets and is NOT selected for the current 143// architecture which would be Common. 144func (p *PackagingBase) getDepsForArch(ctx BaseModuleContext, arch ArchType) []string { 145 var ret []string 146 if arch == ctx.Target().Arch.ArchType && len(ctx.MultiTargets()) == 0 { 147 ret = append(ret, p.properties.Deps...) 148 } else if arch.Multilib == "lib32" { 149 ret = append(ret, p.properties.Multilib.Lib32.Deps...) 150 } else if arch.Multilib == "lib64" { 151 ret = append(ret, p.properties.Multilib.Lib64.Deps...) 152 } else if arch == Common { 153 ret = append(ret, p.properties.Multilib.Common.Deps...) 154 } 155 156 for i, t := range ctx.MultiTargets() { 157 if t.Arch.ArchType == arch { 158 ret = append(ret, p.properties.Deps...) 159 if i == 0 { 160 ret = append(ret, p.properties.Multilib.First.Deps...) 161 } 162 } 163 } 164 165 if ctx.Arch().ArchType == Common { 166 switch arch { 167 case Arm64: 168 ret = append(ret, p.properties.Arch.Arm64.Deps...) 169 case Arm: 170 ret = append(ret, p.properties.Arch.Arm.Deps...) 171 case X86_64: 172 ret = append(ret, p.properties.Arch.X86_64.Deps...) 173 case X86: 174 ret = append(ret, p.properties.Arch.X86.Deps...) 175 } 176 } 177 178 return FirstUniqueStrings(ret) 179} 180 181func (p *PackagingBase) getSupportedTargets(ctx BaseModuleContext) []Target { 182 var ret []Target 183 // The current and the common OS targets are always supported 184 ret = append(ret, ctx.Target()) 185 if ctx.Arch().ArchType != Common { 186 ret = append(ret, Target{Os: ctx.Os(), Arch: Arch{ArchType: Common}}) 187 } 188 // If this module is configured for multi targets, those should be supported as well 189 ret = append(ret, ctx.MultiTargets()...) 190 return ret 191} 192 193// PackagingItem is a marker interface for dependency tags. 194// Direct dependencies with a tag implementing PackagingItem are packaged in CopyDepsToZip(). 195type PackagingItem interface { 196 // IsPackagingItem returns true if the dep is to be packaged 197 IsPackagingItem() bool 198} 199 200// DepTag provides default implementation of PackagingItem interface. 201// PackagingBase-derived modules can define their own dependency tag by embedding this, which 202// can be passed to AddDeps() or AddDependencies(). 203type PackagingItemAlwaysDepTag struct { 204} 205 206// IsPackagingItem returns true if the dep is to be packaged 207func (PackagingItemAlwaysDepTag) IsPackagingItem() bool { 208 return true 209} 210 211// See PackageModule.AddDeps 212func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) { 213 for _, t := range p.getSupportedTargets(ctx) { 214 for _, dep := range p.getDepsForArch(ctx, t.Arch.ArchType) { 215 if p.IgnoreMissingDependencies && !ctx.OtherModuleExists(dep) { 216 continue 217 } 218 ctx.AddFarVariationDependencies(t.Variations(), depTag, dep) 219 } 220 } 221} 222 223// See PackageModule.GatherPackagingSpecs 224func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec { 225 m := make(map[string]PackagingSpec) 226 ctx.VisitDirectDeps(func(child Module) { 227 if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() { 228 return 229 } 230 for _, ps := range child.TransitivePackagingSpecs() { 231 if _, ok := m[ps.relPathInPackage]; !ok { 232 m[ps.relPathInPackage] = ps 233 } 234 } 235 }) 236 return m 237} 238 239// CopySpecsToDir is a helper that will add commands to the rule builder to copy the PackagingSpec 240// entries into the specified directory. 241func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir ModuleOutPath) (entries []string) { 242 seenDir := make(map[string]bool) 243 for _, k := range SortedStringKeys(specs) { 244 ps := specs[k] 245 destPath := dir.Join(ctx, ps.relPathInPackage).String() 246 destDir := filepath.Dir(destPath) 247 entries = append(entries, ps.relPathInPackage) 248 if _, ok := seenDir[destDir]; !ok { 249 seenDir[destDir] = true 250 builder.Command().Text("mkdir").Flag("-p").Text(destDir) 251 } 252 if ps.symlinkTarget == "" { 253 builder.Command().Text("cp").Input(ps.srcPath).Text(destPath) 254 } else { 255 builder.Command().Text("ln").Flag("-sf").Text(ps.symlinkTarget).Text(destPath) 256 } 257 if ps.executable { 258 builder.Command().Text("chmod").Flag("a+x").Text(destPath) 259 } 260 } 261 262 return entries 263} 264 265// See PackageModule.CopyDepsToZip 266func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, specs map[string]PackagingSpec, zipOut WritablePath) (entries []string) { 267 builder := NewRuleBuilder(pctx, ctx) 268 269 dir := PathForModuleOut(ctx, ".zip") 270 builder.Command().Text("rm").Flag("-rf").Text(dir.String()) 271 builder.Command().Text("mkdir").Flag("-p").Text(dir.String()) 272 entries = p.CopySpecsToDir(ctx, builder, specs, dir) 273 274 builder.Command(). 275 BuiltTool("soong_zip"). 276 FlagWithOutput("-o ", zipOut). 277 FlagWithArg("-C ", dir.String()). 278 Flag("-L 0"). // no compression because this will be unzipped soon 279 FlagWithArg("-D ", dir.String()) 280 builder.Command().Text("rm").Flag("-rf").Text(dir.String()) 281 282 builder.Build("zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName())) 283 return entries 284} 285 286// packagingSpecsDepSet is a thin type-safe wrapper around the generic depSet. It always uses 287// topological order. 288type packagingSpecsDepSet struct { 289 depSet 290} 291 292// newPackagingSpecsDepSet returns an immutable packagingSpecsDepSet with the given direct and 293// transitive contents. 294func newPackagingSpecsDepSet(direct []PackagingSpec, transitive []*packagingSpecsDepSet) *packagingSpecsDepSet { 295 return &packagingSpecsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)} 296} 297 298// ToList returns the packagingSpecsDepSet flattened to a list in topological order. 299func (d *packagingSpecsDepSet) ToList() []PackagingSpec { 300 if d == nil { 301 return nil 302 } 303 return d.depSet.ToList().([]PackagingSpec) 304} 305