blob: 9b901ce28189e3ef195d72c89f0bebe48d2278de [file] [log] [blame]
Jiyong Park073ea552020-11-09 14:08:34 +09001// 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
Jiyong Parkdda8f692020-11-09 18:38:48 +090017import (
18 "fmt"
19 "path/filepath"
20
21 "github.com/google/blueprint"
22)
23
Jiyong Parkcc1157c2020-11-25 11:31:13 +090024// 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.
Jiyong Park073ea552020-11-09 14:08:34 +090028type 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}
Jiyong Parkdda8f692020-11-09 18:38:48 +090042
Kiyoung Kim24dfc1f2020-11-16 10:48:44 +090043// 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
Jiyong Park6446b622021-02-01 20:08:28 +090052// Path relative to the root of the package
53func (p *PackagingSpec) RelPathInPackage() string {
54 return p.relPathInPackage
55}
56
Jiyong Parkdda8f692020-11-09 18:38:48 +090057type PackageModule interface {
58 Module
59 packagingBase() *PackagingBase
60
61 // AddDeps adds dependencies to the `deps` modules. This should be called in DepsMutator.
Jiyong Park65b62242020-11-25 12:44:59 +090062 // When adding the dependencies, depTag is used as the tag.
63 AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag)
Jiyong Parkdda8f692020-11-09 18:38:48 +090064
65 // CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and
Jiyong Parkcc1157c2020-11-25 11:31:13 +090066 // returns zip entries in it. This is expected to be called in GenerateAndroidBuildActions,
Jiyong Parkdda8f692020-11-09 18:38:48 +090067 // followed by a build rule that unzips it and creates the final output (img, zip, tar.gz,
68 // etc.) from the extracted files
Paul Duffin4076a752021-02-02 10:59:54 +000069 CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) []string
Jiyong Parkdda8f692020-11-09 18:38:48 +090070}
71
72// PackagingBase provides basic functionality for packaging dependencies. A module is expected to
73// include this struct and call InitPackageModule.
74type PackagingBase struct {
75 properties PackagingProperties
76
Jiyong Parkcc1157c2020-11-25 11:31:13 +090077 // Allows this module to skip missing dependencies. In most cases, this is not required, but
78 // for rare cases like when there's a dependency to a module which exists in certain repo
79 // checkouts, this is needed.
Jiyong Parkdda8f692020-11-09 18:38:48 +090080 IgnoreMissingDependencies bool
81}
82
83type depsProperty struct {
84 // Modules to include in this package
85 Deps []string `android:"arch_variant"`
86}
87
88type packagingMultilibProperties struct {
89 First depsProperty `android:"arch_variant"`
90 Common depsProperty `android:"arch_variant"`
91 Lib32 depsProperty `android:"arch_variant"`
92 Lib64 depsProperty `android:"arch_variant"`
93}
94
Jiyong Park2136d152021-02-01 23:24:56 +090095type packagingArchProperties struct {
96 Arm64 depsProperty
97 Arm depsProperty
98 X86_64 depsProperty
99 X86 depsProperty
100}
101
Jiyong Parkdda8f692020-11-09 18:38:48 +0900102type PackagingProperties struct {
103 Deps []string `android:"arch_variant"`
104 Multilib packagingMultilibProperties `android:"arch_variant"`
Jiyong Park2136d152021-02-01 23:24:56 +0900105 Arch packagingArchProperties
Jiyong Parkdda8f692020-11-09 18:38:48 +0900106}
107
Jiyong Parkdda8f692020-11-09 18:38:48 +0900108func InitPackageModule(p PackageModule) {
109 base := p.packagingBase()
110 p.AddProperties(&base.properties)
111}
112
113func (p *PackagingBase) packagingBase() *PackagingBase {
114 return p
115}
116
Jiyong Parkcc1157c2020-11-25 11:31:13 +0900117// From deps and multilib.*.deps, select the dependencies that are for the given arch deps is for
118// the current archicture when this module is not configured for multi target. When configured for
119// multi target, deps is selected for each of the targets and is NOT selected for the current
120// architecture which would be Common.
Jiyong Parkdda8f692020-11-09 18:38:48 +0900121func (p *PackagingBase) getDepsForArch(ctx BaseModuleContext, arch ArchType) []string {
122 var ret []string
123 if arch == ctx.Target().Arch.ArchType && len(ctx.MultiTargets()) == 0 {
124 ret = append(ret, p.properties.Deps...)
125 } else if arch.Multilib == "lib32" {
126 ret = append(ret, p.properties.Multilib.Lib32.Deps...)
127 } else if arch.Multilib == "lib64" {
128 ret = append(ret, p.properties.Multilib.Lib64.Deps...)
129 } else if arch == Common {
130 ret = append(ret, p.properties.Multilib.Common.Deps...)
131 }
Jiyong Park2136d152021-02-01 23:24:56 +0900132
Jiyong Parkdda8f692020-11-09 18:38:48 +0900133 for i, t := range ctx.MultiTargets() {
134 if t.Arch.ArchType == arch {
135 ret = append(ret, p.properties.Deps...)
136 if i == 0 {
137 ret = append(ret, p.properties.Multilib.First.Deps...)
138 }
139 }
140 }
Jiyong Park2136d152021-02-01 23:24:56 +0900141
142 if ctx.Arch().ArchType == Common {
143 switch arch {
144 case Arm64:
145 ret = append(ret, p.properties.Arch.Arm64.Deps...)
146 case Arm:
147 ret = append(ret, p.properties.Arch.Arm.Deps...)
148 case X86_64:
149 ret = append(ret, p.properties.Arch.X86_64.Deps...)
150 case X86:
151 ret = append(ret, p.properties.Arch.X86.Deps...)
152 }
153 }
154
Jiyong Parkdda8f692020-11-09 18:38:48 +0900155 return FirstUniqueStrings(ret)
156}
157
158func (p *PackagingBase) getSupportedTargets(ctx BaseModuleContext) []Target {
159 var ret []Target
160 // The current and the common OS targets are always supported
161 ret = append(ret, ctx.Target())
162 if ctx.Arch().ArchType != Common {
163 ret = append(ret, Target{Os: ctx.Os(), Arch: Arch{ArchType: Common}})
164 }
165 // If this module is configured for multi targets, those should be supported as well
166 ret = append(ret, ctx.MultiTargets()...)
167 return ret
168}
169
170// See PackageModule.AddDeps
Jiyong Park65b62242020-11-25 12:44:59 +0900171func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) {
Jiyong Parkdda8f692020-11-09 18:38:48 +0900172 for _, t := range p.getSupportedTargets(ctx) {
173 for _, dep := range p.getDepsForArch(ctx, t.Arch.ArchType) {
174 if p.IgnoreMissingDependencies && !ctx.OtherModuleExists(dep) {
175 continue
176 }
177 ctx.AddFarVariationDependencies(t.Variations(), depTag, dep)
178 }
179 }
180}
181
182// See PackageModule.CopyDepsToZip
Paul Duffin4076a752021-02-02 10:59:54 +0000183func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (entries []string) {
Jiyong Parkdda8f692020-11-09 18:38:48 +0900184 m := make(map[string]PackagingSpec)
185 ctx.WalkDeps(func(child Module, parent Module) bool {
Jiyong Parkd630bdd2020-11-25 11:47:24 +0900186 if !IsInstallDepNeeded(ctx.OtherModuleDependencyTag(child)) {
Jiyong Parkdda8f692020-11-09 18:38:48 +0900187 return false
188 }
189 for _, ps := range child.PackagingSpecs() {
190 if _, ok := m[ps.relPathInPackage]; !ok {
191 m[ps.relPathInPackage] = ps
192 }
193 }
194 return true
195 })
196
Colin Crossf1a035e2020-11-16 17:32:30 -0800197 builder := NewRuleBuilder(pctx, ctx)
Jiyong Parkdda8f692020-11-09 18:38:48 +0900198
Paul Duffin4076a752021-02-02 10:59:54 +0000199 dir := PathForModuleOut(ctx, ".zip")
Jiyong Parkdda8f692020-11-09 18:38:48 +0900200 builder.Command().Text("rm").Flag("-rf").Text(dir.String())
201 builder.Command().Text("mkdir").Flag("-p").Text(dir.String())
202
203 seenDir := make(map[string]bool)
204 for _, k := range SortedStringKeys(m) {
205 ps := m[k]
206 destPath := dir.Join(ctx, ps.relPathInPackage).String()
207 destDir := filepath.Dir(destPath)
208 entries = append(entries, ps.relPathInPackage)
209 if _, ok := seenDir[destDir]; !ok {
210 seenDir[destDir] = true
211 builder.Command().Text("mkdir").Flag("-p").Text(destDir)
212 }
213 if ps.symlinkTarget == "" {
214 builder.Command().Text("cp").Input(ps.srcPath).Text(destPath)
215 } else {
216 builder.Command().Text("ln").Flag("-sf").Text(ps.symlinkTarget).Text(destPath)
217 }
218 if ps.executable {
219 builder.Command().Text("chmod").Flag("a+x").Text(destPath)
220 }
221 }
222
223 builder.Command().
Colin Crossf1a035e2020-11-16 17:32:30 -0800224 BuiltTool("soong_zip").
Jiyong Parkdda8f692020-11-09 18:38:48 +0900225 FlagWithOutput("-o ", zipOut).
226 FlagWithArg("-C ", dir.String()).
227 Flag("-L 0"). // no compression because this will be unzipped soon
228 FlagWithArg("-D ", dir.String())
229 builder.Command().Text("rm").Flag("-rf").Text(dir.String())
230
Colin Crossf1a035e2020-11-16 17:32:30 -0800231 builder.Build("zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName()))
Jiyong Parkdda8f692020-11-09 18:38:48 +0900232 return entries
233}
Colin Crossffe6b9d2020-12-01 15:40:06 -0800234
235// packagingSpecsDepSet is a thin type-safe wrapper around the generic depSet. It always uses
236// topological order.
237type packagingSpecsDepSet struct {
238 depSet
239}
240
241// newPackagingSpecsDepSet returns an immutable packagingSpecsDepSet with the given direct and
242// transitive contents.
243func newPackagingSpecsDepSet(direct []PackagingSpec, transitive []*packagingSpecsDepSet) *packagingSpecsDepSet {
244 return &packagingSpecsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)}
245}
246
247// ToList returns the packagingSpecsDepSet flattened to a list in topological order.
248func (d *packagingSpecsDepSet) ToList() []PackagingSpec {
249 if d == nil {
250 return nil
251 }
252 return d.depSet.ToList().([]PackagingSpec)
253}