blob: e5a7fa4fdf5ed44463976dc6968fcab90f4b94ed [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
Dan Willemsen9fe14102021-07-13 21:52:04 -070041
42 effectiveLicenseFiles *Paths
Jooyung Han99c5fe62022-03-21 15:13:38 +090043
44 partition string
Jiyong Park073ea552020-11-09 14:08:34 +090045}
Jiyong Parkdda8f692020-11-09 18:38:48 +090046
Kiyoung Kim24dfc1f2020-11-16 10:48:44 +090047// 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
Jiyong Park6446b622021-02-01 20:08:28 +090056// Path relative to the root of the package
57func (p *PackagingSpec) RelPathInPackage() string {
58 return p.relPathInPackage
59}
60
Dan Willemsen9fe14102021-07-13 21:52:04 -070061func (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
Jooyung Han99c5fe62022-03-21 15:13:38 +090072func (p *PackagingSpec) Partition() string {
73 return p.partition
74}
75
Jiyong Parkdda8f692020-11-09 18:38:48 +090076type PackageModule interface {
77 Module
78 packagingBase() *PackagingBase
79
80 // AddDeps adds dependencies to the `deps` modules. This should be called in DepsMutator.
Jooyung Han092ef812021-03-10 15:40:34 +090081 // 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.
Jiyong Park65b62242020-11-25 12:44:59 +090083 AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag)
Jiyong Parkdda8f692020-11-09 18:38:48 +090084
85 // CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and
Jiyong Parkcc1157c2020-11-25 11:31:13 +090086 // returns zip entries in it. This is expected to be called in GenerateAndroidBuildActions,
Jiyong Parkdda8f692020-11-09 18:38:48 +090087 // followed by a build rule that unzips it and creates the final output (img, zip, tar.gz,
88 // etc.) from the extracted files
Paul Duffin4076a752021-02-02 10:59:54 +000089 CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) []string
Jiyong Parkdda8f692020-11-09 18:38:48 +090090}
91
92// PackagingBase provides basic functionality for packaging dependencies. A module is expected to
93// include this struct and call InitPackageModule.
94type PackagingBase struct {
95 properties PackagingProperties
96
Jiyong Parkcc1157c2020-11-25 11:31:13 +090097 // Allows this module to skip missing dependencies. In most cases, this is not required, but
98 // for rare cases like when there's a dependency to a module which exists in certain repo
99 // checkouts, this is needed.
Jiyong Parkdda8f692020-11-09 18:38:48 +0900100 IgnoreMissingDependencies bool
101}
102
103type depsProperty struct {
104 // Modules to include in this package
105 Deps []string `android:"arch_variant"`
106}
107
108type packagingMultilibProperties struct {
109 First depsProperty `android:"arch_variant"`
110 Common depsProperty `android:"arch_variant"`
111 Lib32 depsProperty `android:"arch_variant"`
112 Lib64 depsProperty `android:"arch_variant"`
113}
114
Jiyong Park2136d152021-02-01 23:24:56 +0900115type packagingArchProperties struct {
116 Arm64 depsProperty
117 Arm depsProperty
118 X86_64 depsProperty
119 X86 depsProperty
120}
121
Jiyong Parkdda8f692020-11-09 18:38:48 +0900122type PackagingProperties struct {
123 Deps []string `android:"arch_variant"`
124 Multilib packagingMultilibProperties `android:"arch_variant"`
Jiyong Park2136d152021-02-01 23:24:56 +0900125 Arch packagingArchProperties
Jiyong Parkdda8f692020-11-09 18:38:48 +0900126}
127
Jiyong Parkdda8f692020-11-09 18:38:48 +0900128func InitPackageModule(p PackageModule) {
129 base := p.packagingBase()
130 p.AddProperties(&base.properties)
131}
132
133func (p *PackagingBase) packagingBase() *PackagingBase {
134 return p
135}
136
Jiyong Parkcc1157c2020-11-25 11:31:13 +0900137// From deps and multilib.*.deps, select the dependencies that are for the given arch deps is for
138// the current archicture when this module is not configured for multi target. When configured for
139// multi target, deps is selected for each of the targets and is NOT selected for the current
140// architecture which would be Common.
Jiyong Parkdda8f692020-11-09 18:38:48 +0900141func (p *PackagingBase) getDepsForArch(ctx BaseModuleContext, arch ArchType) []string {
142 var ret []string
143 if arch == ctx.Target().Arch.ArchType && len(ctx.MultiTargets()) == 0 {
144 ret = append(ret, p.properties.Deps...)
145 } else if arch.Multilib == "lib32" {
146 ret = append(ret, p.properties.Multilib.Lib32.Deps...)
147 } else if arch.Multilib == "lib64" {
148 ret = append(ret, p.properties.Multilib.Lib64.Deps...)
149 } else if arch == Common {
150 ret = append(ret, p.properties.Multilib.Common.Deps...)
151 }
Jiyong Park2136d152021-02-01 23:24:56 +0900152
Jiyong Parkdda8f692020-11-09 18:38:48 +0900153 for i, t := range ctx.MultiTargets() {
154 if t.Arch.ArchType == arch {
155 ret = append(ret, p.properties.Deps...)
156 if i == 0 {
157 ret = append(ret, p.properties.Multilib.First.Deps...)
158 }
159 }
160 }
Jiyong Park2136d152021-02-01 23:24:56 +0900161
162 if ctx.Arch().ArchType == Common {
163 switch arch {
164 case Arm64:
165 ret = append(ret, p.properties.Arch.Arm64.Deps...)
166 case Arm:
167 ret = append(ret, p.properties.Arch.Arm.Deps...)
168 case X86_64:
169 ret = append(ret, p.properties.Arch.X86_64.Deps...)
170 case X86:
171 ret = append(ret, p.properties.Arch.X86.Deps...)
172 }
173 }
174
Jiyong Parkdda8f692020-11-09 18:38:48 +0900175 return FirstUniqueStrings(ret)
176}
177
178func (p *PackagingBase) getSupportedTargets(ctx BaseModuleContext) []Target {
179 var ret []Target
180 // The current and the common OS targets are always supported
181 ret = append(ret, ctx.Target())
182 if ctx.Arch().ArchType != Common {
183 ret = append(ret, Target{Os: ctx.Os(), Arch: Arch{ArchType: Common}})
184 }
185 // If this module is configured for multi targets, those should be supported as well
186 ret = append(ret, ctx.MultiTargets()...)
187 return ret
188}
189
Jooyung Han092ef812021-03-10 15:40:34 +0900190// PackagingItem is a marker interface for dependency tags.
191// Direct dependencies with a tag implementing PackagingItem are packaged in CopyDepsToZip().
192type PackagingItem interface {
193 // IsPackagingItem returns true if the dep is to be packaged
194 IsPackagingItem() bool
195}
196
197// DepTag provides default implementation of PackagingItem interface.
198// PackagingBase-derived modules can define their own dependency tag by embedding this, which
199// can be passed to AddDeps() or AddDependencies().
200type PackagingItemAlwaysDepTag struct {
201}
202
203// IsPackagingItem returns true if the dep is to be packaged
204func (PackagingItemAlwaysDepTag) IsPackagingItem() bool {
205 return true
206}
207
Jiyong Parkdda8f692020-11-09 18:38:48 +0900208// See PackageModule.AddDeps
Jiyong Park65b62242020-11-25 12:44:59 +0900209func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) {
Jiyong Parkdda8f692020-11-09 18:38:48 +0900210 for _, t := range p.getSupportedTargets(ctx) {
211 for _, dep := range p.getDepsForArch(ctx, t.Arch.ArchType) {
212 if p.IgnoreMissingDependencies && !ctx.OtherModuleExists(dep) {
213 continue
214 }
215 ctx.AddFarVariationDependencies(t.Variations(), depTag, dep)
216 }
217 }
218}
219
Jooyung Handf09d172021-05-11 11:13:30 +0900220// Returns transitive PackagingSpecs from deps
221func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec {
Jiyong Parkdda8f692020-11-09 18:38:48 +0900222 m := make(map[string]PackagingSpec)
Jooyung Han092ef812021-03-10 15:40:34 +0900223 ctx.VisitDirectDeps(func(child Module) {
224 if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() {
225 return
Jiyong Parkdda8f692020-11-09 18:38:48 +0900226 }
Jooyung Han092ef812021-03-10 15:40:34 +0900227 for _, ps := range child.TransitivePackagingSpecs() {
Jiyong Parkdda8f692020-11-09 18:38:48 +0900228 if _, ok := m[ps.relPathInPackage]; !ok {
229 m[ps.relPathInPackage] = ps
230 }
231 }
Jiyong Parkdda8f692020-11-09 18:38:48 +0900232 })
Jooyung Handf09d172021-05-11 11:13:30 +0900233 return m
234}
Jiyong Parkdda8f692020-11-09 18:38:48 +0900235
Dan Willemsen9fe14102021-07-13 21:52:04 -0700236// CopySpecsToDir is a helper that will add commands to the rule builder to copy the PackagingSpec
237// entries into the specified directory.
238func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, m map[string]PackagingSpec, dir ModuleOutPath) (entries []string) {
Jiyong Parkdda8f692020-11-09 18:38:48 +0900239 seenDir := make(map[string]bool)
240 for _, k := range SortedStringKeys(m) {
241 ps := m[k]
242 destPath := dir.Join(ctx, ps.relPathInPackage).String()
243 destDir := filepath.Dir(destPath)
244 entries = append(entries, ps.relPathInPackage)
245 if _, ok := seenDir[destDir]; !ok {
246 seenDir[destDir] = true
247 builder.Command().Text("mkdir").Flag("-p").Text(destDir)
248 }
249 if ps.symlinkTarget == "" {
250 builder.Command().Text("cp").Input(ps.srcPath).Text(destPath)
251 } else {
252 builder.Command().Text("ln").Flag("-sf").Text(ps.symlinkTarget).Text(destPath)
253 }
254 if ps.executable {
255 builder.Command().Text("chmod").Flag("a+x").Text(destPath)
256 }
257 }
258
Dan Willemsen9fe14102021-07-13 21:52:04 -0700259 return entries
260}
261
262// See PackageModule.CopyDepsToZip
263func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (entries []string) {
264 m := p.GatherPackagingSpecs(ctx)
265 builder := NewRuleBuilder(pctx, ctx)
266
267 dir := PathForModuleOut(ctx, ".zip")
268 builder.Command().Text("rm").Flag("-rf").Text(dir.String())
269 builder.Command().Text("mkdir").Flag("-p").Text(dir.String())
270 entries = p.CopySpecsToDir(ctx, builder, m, dir)
271
Jiyong Parkdda8f692020-11-09 18:38:48 +0900272 builder.Command().
Colin Crossf1a035e2020-11-16 17:32:30 -0800273 BuiltTool("soong_zip").
Jiyong Parkdda8f692020-11-09 18:38:48 +0900274 FlagWithOutput("-o ", zipOut).
275 FlagWithArg("-C ", dir.String()).
276 Flag("-L 0"). // no compression because this will be unzipped soon
277 FlagWithArg("-D ", dir.String())
278 builder.Command().Text("rm").Flag("-rf").Text(dir.String())
279
Colin Crossf1a035e2020-11-16 17:32:30 -0800280 builder.Build("zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName()))
Jiyong Parkdda8f692020-11-09 18:38:48 +0900281 return entries
282}
Colin Crossffe6b9d2020-12-01 15:40:06 -0800283
284// packagingSpecsDepSet is a thin type-safe wrapper around the generic depSet. It always uses
285// topological order.
286type packagingSpecsDepSet struct {
287 depSet
288}
289
290// newPackagingSpecsDepSet returns an immutable packagingSpecsDepSet with the given direct and
291// transitive contents.
292func newPackagingSpecsDepSet(direct []PackagingSpec, transitive []*packagingSpecsDepSet) *packagingSpecsDepSet {
293 return &packagingSpecsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)}
294}
295
296// ToList returns the packagingSpecsDepSet flattened to a list in topological order.
297func (d *packagingSpecsDepSet) ToList() []PackagingSpec {
298 if d == nil {
299 return nil
300 }
301 return d.depSet.ToList().([]PackagingSpec)
302}