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