blob: 080dcfea062cd595c339a891ee7ecf8b8f4f9406 [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"
Jeongik Cha76e677f2023-12-21 16:39:15 +090020 "strings"
Jiyong Parkdda8f692020-11-09 18:38:48 +090021
22 "github.com/google/blueprint"
23)
24
Jiyong Parkcc1157c2020-11-25 11:31:13 +090025// PackagingSpec abstracts a request to place a built artifact at a certain path in a package. A
26// package can be the traditional <partition>.img, but isn't limited to those. Other examples could
27// be a new filesystem image that is a subset of system.img (e.g. for an Android-like mini OS
28// running on a VM), or a zip archive for some of the host tools.
Jiyong Park073ea552020-11-09 14:08:34 +090029type PackagingSpec struct {
30 // Path relative to the root of the package
31 relPathInPackage string
32
33 // The path to the built artifact
34 srcPath Path
35
36 // If this is not empty, then relPathInPackage should be a symlink to this target. (Then
37 // srcPath is of course ignored.)
38 symlinkTarget string
39
40 // Whether relPathInPackage should be marked as executable or not
41 executable bool
Dan Willemsen9fe14102021-07-13 21:52:04 -070042
43 effectiveLicenseFiles *Paths
Jooyung Han99c5fe62022-03-21 15:13:38 +090044
45 partition string
Jiyong Park4152b192024-04-30 21:24:21 +090046
47 // Whether this packaging spec represents an installation of the srcPath (i.e. this struct
48 // is created via InstallFile or InstallSymlink) or a simple packaging (i.e. created via
49 // PackageFile).
50 skipInstall bool
Justin Yun74f3f302024-05-07 14:32:14 +090051
52 // Paths of aconfig files for the built artifact
53 aconfigPaths *Paths
Jiyong Parkc6a773d2024-05-14 21:49:11 +090054
55 // ArchType of the module which produced this packaging spec
56 archType ArchType
Jiyong Park073ea552020-11-09 14:08:34 +090057}
Jiyong Parkdda8f692020-11-09 18:38:48 +090058
Jiyong Park16ef7ac2024-05-01 12:36:10 +000059func (p *PackagingSpec) Equals(other *PackagingSpec) bool {
60 if other == nil {
61 return false
62 }
63 if p.relPathInPackage != other.relPathInPackage {
64 return false
65 }
66 if p.srcPath != other.srcPath || p.symlinkTarget != other.symlinkTarget {
67 return false
68 }
69 if p.executable != other.executable {
70 return false
71 }
72 if p.partition != other.partition {
73 return false
74 }
75 return true
76}
77
Kiyoung Kim24dfc1f2020-11-16 10:48:44 +090078// Get file name of installed package
79func (p *PackagingSpec) FileName() string {
80 if p.relPathInPackage != "" {
81 return filepath.Base(p.relPathInPackage)
82 }
83
84 return ""
85}
86
Jiyong Park6446b622021-02-01 20:08:28 +090087// Path relative to the root of the package
88func (p *PackagingSpec) RelPathInPackage() string {
89 return p.relPathInPackage
90}
91
Dan Willemsen9fe14102021-07-13 21:52:04 -070092func (p *PackagingSpec) SetRelPathInPackage(relPathInPackage string) {
93 p.relPathInPackage = relPathInPackage
94}
95
96func (p *PackagingSpec) EffectiveLicenseFiles() Paths {
97 if p.effectiveLicenseFiles == nil {
98 return Paths{}
99 }
100 return *p.effectiveLicenseFiles
101}
102
Jooyung Han99c5fe62022-03-21 15:13:38 +0900103func (p *PackagingSpec) Partition() string {
104 return p.partition
105}
106
Jiyong Park4152b192024-04-30 21:24:21 +0900107func (p *PackagingSpec) SkipInstall() bool {
108 return p.skipInstall
109}
110
Justin Yun74f3f302024-05-07 14:32:14 +0900111// Paths of aconfig files for the built artifact
112func (p *PackagingSpec) GetAconfigPaths() Paths {
113 return *p.aconfigPaths
114}
115
Jiyong Parkdda8f692020-11-09 18:38:48 +0900116type PackageModule interface {
117 Module
118 packagingBase() *PackagingBase
119
120 // AddDeps adds dependencies to the `deps` modules. This should be called in DepsMutator.
Jooyung Han092ef812021-03-10 15:40:34 +0900121 // When adding the dependencies, depTag is used as the tag. If `deps` modules are meant to
122 // be copied to a zip in CopyDepsToZip, `depTag` should implement PackagingItem marker interface.
Jiyong Park65b62242020-11-25 12:44:59 +0900123 AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag)
Jiyong Parkdda8f692020-11-09 18:38:48 +0900124
Jooyung Hana8834282022-03-25 11:40:12 +0900125 // GatherPackagingSpecs gathers PackagingSpecs of transitive dependencies.
126 GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec
Jeongik Cha54bf8752024-02-08 10:44:37 +0900127 GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec
Jooyung Hana8834282022-03-25 11:40:12 +0900128
Jiyong Parkdda8f692020-11-09 18:38:48 +0900129 // CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and
Jiyong Parkcc1157c2020-11-25 11:31:13 +0900130 // returns zip entries in it. This is expected to be called in GenerateAndroidBuildActions,
Jiyong Parkdda8f692020-11-09 18:38:48 +0900131 // followed by a build rule that unzips it and creates the final output (img, zip, tar.gz,
132 // etc.) from the extracted files
Jooyung Hana8834282022-03-25 11:40:12 +0900133 CopyDepsToZip(ctx ModuleContext, specs map[string]PackagingSpec, zipOut WritablePath) []string
Jiyong Parkdda8f692020-11-09 18:38:48 +0900134}
135
136// PackagingBase provides basic functionality for packaging dependencies. A module is expected to
137// include this struct and call InitPackageModule.
138type PackagingBase struct {
139 properties PackagingProperties
140
Jiyong Parkcc1157c2020-11-25 11:31:13 +0900141 // Allows this module to skip missing dependencies. In most cases, this is not required, but
142 // for rare cases like when there's a dependency to a module which exists in certain repo
143 // checkouts, this is needed.
Jiyong Parkdda8f692020-11-09 18:38:48 +0900144 IgnoreMissingDependencies bool
Jiyong Park3ea9b652024-05-15 23:01:54 +0900145
146 // If this is set to true by a module type inheriting PackagingBase, the deps property
147 // collects the first target only even with compile_multilib: true.
148 DepsCollectFirstTargetOnly bool
Jiyong Parkdda8f692020-11-09 18:38:48 +0900149}
150
151type depsProperty struct {
152 // Modules to include in this package
153 Deps []string `android:"arch_variant"`
154}
155
156type packagingMultilibProperties struct {
157 First depsProperty `android:"arch_variant"`
158 Common depsProperty `android:"arch_variant"`
159 Lib32 depsProperty `android:"arch_variant"`
160 Lib64 depsProperty `android:"arch_variant"`
Jiyong Park3ea9b652024-05-15 23:01:54 +0900161 Both depsProperty `android:"arch_variant"`
Jiyong Parkdda8f692020-11-09 18:38:48 +0900162}
163
Jiyong Park2136d152021-02-01 23:24:56 +0900164type packagingArchProperties struct {
165 Arm64 depsProperty
166 Arm depsProperty
167 X86_64 depsProperty
168 X86 depsProperty
169}
170
Jiyong Parkdda8f692020-11-09 18:38:48 +0900171type PackagingProperties struct {
172 Deps []string `android:"arch_variant"`
173 Multilib packagingMultilibProperties `android:"arch_variant"`
Jiyong Park2136d152021-02-01 23:24:56 +0900174 Arch packagingArchProperties
Jiyong Parkdda8f692020-11-09 18:38:48 +0900175}
176
Jiyong Parkdda8f692020-11-09 18:38:48 +0900177func InitPackageModule(p PackageModule) {
178 base := p.packagingBase()
179 p.AddProperties(&base.properties)
180}
181
182func (p *PackagingBase) packagingBase() *PackagingBase {
183 return p
184}
185
Jiyong Parkcc1157c2020-11-25 11:31:13 +0900186// From deps and multilib.*.deps, select the dependencies that are for the given arch deps is for
187// the current archicture when this module is not configured for multi target. When configured for
188// multi target, deps is selected for each of the targets and is NOT selected for the current
189// architecture which would be Common.
Jiyong Parkdda8f692020-11-09 18:38:48 +0900190func (p *PackagingBase) getDepsForArch(ctx BaseModuleContext, arch ArchType) []string {
191 var ret []string
192 if arch == ctx.Target().Arch.ArchType && len(ctx.MultiTargets()) == 0 {
193 ret = append(ret, p.properties.Deps...)
194 } else if arch.Multilib == "lib32" {
195 ret = append(ret, p.properties.Multilib.Lib32.Deps...)
196 } else if arch.Multilib == "lib64" {
197 ret = append(ret, p.properties.Multilib.Lib64.Deps...)
198 } else if arch == Common {
199 ret = append(ret, p.properties.Multilib.Common.Deps...)
200 }
Jiyong Park2136d152021-02-01 23:24:56 +0900201
Jiyong Park3ea9b652024-05-15 23:01:54 +0900202 if p.DepsCollectFirstTargetOnly {
203 if len(p.properties.Multilib.First.Deps) > 0 {
204 ctx.PropertyErrorf("multilib.first.deps", "not supported. use \"deps\" instead")
205 }
206 for i, t := range ctx.MultiTargets() {
207 if t.Arch.ArchType == arch {
208 ret = append(ret, p.properties.Multilib.Both.Deps...)
209 if i == 0 {
210 ret = append(ret, p.properties.Deps...)
211 }
212 }
213 }
214 } else {
215 if len(p.properties.Multilib.Both.Deps) > 0 {
216 ctx.PropertyErrorf("multilib.both.deps", "not supported. use \"deps\" instead")
217 }
218 for i, t := range ctx.MultiTargets() {
219 if t.Arch.ArchType == arch {
220 ret = append(ret, p.properties.Deps...)
221 if i == 0 {
222 ret = append(ret, p.properties.Multilib.First.Deps...)
223 }
Jiyong Parkdda8f692020-11-09 18:38:48 +0900224 }
225 }
226 }
Jiyong Park2136d152021-02-01 23:24:56 +0900227
228 if ctx.Arch().ArchType == Common {
229 switch arch {
230 case Arm64:
231 ret = append(ret, p.properties.Arch.Arm64.Deps...)
232 case Arm:
233 ret = append(ret, p.properties.Arch.Arm.Deps...)
234 case X86_64:
235 ret = append(ret, p.properties.Arch.X86_64.Deps...)
236 case X86:
237 ret = append(ret, p.properties.Arch.X86.Deps...)
238 }
239 }
240
Jiyong Parkdda8f692020-11-09 18:38:48 +0900241 return FirstUniqueStrings(ret)
242}
243
244func (p *PackagingBase) getSupportedTargets(ctx BaseModuleContext) []Target {
245 var ret []Target
246 // The current and the common OS targets are always supported
247 ret = append(ret, ctx.Target())
248 if ctx.Arch().ArchType != Common {
249 ret = append(ret, Target{Os: ctx.Os(), Arch: Arch{ArchType: Common}})
250 }
251 // If this module is configured for multi targets, those should be supported as well
252 ret = append(ret, ctx.MultiTargets()...)
253 return ret
254}
255
Jooyung Han092ef812021-03-10 15:40:34 +0900256// PackagingItem is a marker interface for dependency tags.
257// Direct dependencies with a tag implementing PackagingItem are packaged in CopyDepsToZip().
258type PackagingItem interface {
259 // IsPackagingItem returns true if the dep is to be packaged
260 IsPackagingItem() bool
261}
262
263// DepTag provides default implementation of PackagingItem interface.
264// PackagingBase-derived modules can define their own dependency tag by embedding this, which
265// can be passed to AddDeps() or AddDependencies().
266type PackagingItemAlwaysDepTag struct {
267}
268
269// IsPackagingItem returns true if the dep is to be packaged
270func (PackagingItemAlwaysDepTag) IsPackagingItem() bool {
271 return true
272}
273
Jiyong Parkdda8f692020-11-09 18:38:48 +0900274// See PackageModule.AddDeps
Jiyong Park65b62242020-11-25 12:44:59 +0900275func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) {
Jiyong Parkdda8f692020-11-09 18:38:48 +0900276 for _, t := range p.getSupportedTargets(ctx) {
277 for _, dep := range p.getDepsForArch(ctx, t.Arch.ArchType) {
278 if p.IgnoreMissingDependencies && !ctx.OtherModuleExists(dep) {
279 continue
280 }
281 ctx.AddFarVariationDependencies(t.Variations(), depTag, dep)
282 }
283 }
284}
285
Jeongik Cha54bf8752024-02-08 10:44:37 +0900286func (p *PackagingBase) GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec {
Jiyong Parkdda8f692020-11-09 18:38:48 +0900287 m := make(map[string]PackagingSpec)
Jiyong Parkc6a773d2024-05-14 21:49:11 +0900288
289 var arches []ArchType
290 for _, target := range p.getSupportedTargets(ctx) {
291 arches = append(arches, target.Arch.ArchType)
292 }
293
294 // filter out packaging specs for unsupported architecture
295 filterArch := func(ps PackagingSpec) bool {
296 for _, arch := range arches {
297 if arch == ps.archType {
298 return true
299 }
300 }
301 return false
302 }
303
Jooyung Han092ef812021-03-10 15:40:34 +0900304 ctx.VisitDirectDeps(func(child Module) {
305 if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() {
306 return
Jiyong Parkdda8f692020-11-09 18:38:48 +0900307 }
Jooyung Han092ef812021-03-10 15:40:34 +0900308 for _, ps := range child.TransitivePackagingSpecs() {
Jiyong Parkc6a773d2024-05-14 21:49:11 +0900309 if !filterArch(ps) {
310 continue
311 }
312
Jeongik Cha54bf8752024-02-08 10:44:37 +0900313 if filter != nil {
314 if !filter(ps) {
315 continue
316 }
317 }
Jiyong Park16ef7ac2024-05-01 12:36:10 +0000318 dstPath := ps.relPathInPackage
319 if existingPs, ok := m[dstPath]; ok {
320 if !existingPs.Equals(&ps) {
321 ctx.ModuleErrorf("packaging conflict at %v:\n%v\n%v", dstPath, existingPs, ps)
322 }
323 continue
Jiyong Parkdda8f692020-11-09 18:38:48 +0900324 }
Jiyong Park16ef7ac2024-05-01 12:36:10 +0000325
326 m[dstPath] = ps
Jiyong Parkdda8f692020-11-09 18:38:48 +0900327 }
Jiyong Parkdda8f692020-11-09 18:38:48 +0900328 })
Jooyung Handf09d172021-05-11 11:13:30 +0900329 return m
330}
Jiyong Parkdda8f692020-11-09 18:38:48 +0900331
Jeongik Cha54bf8752024-02-08 10:44:37 +0900332// See PackageModule.GatherPackagingSpecs
333func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec {
334 return p.GatherPackagingSpecsWithFilter(ctx, nil)
335}
336
Dan Willemsen9fe14102021-07-13 21:52:04 -0700337// CopySpecsToDir is a helper that will add commands to the rule builder to copy the PackagingSpec
338// entries into the specified directory.
Peter Collingbourneff56c012023-03-15 22:24:03 -0700339func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir WritablePath) (entries []string) {
Cole Faust3b3a0112024-01-03 15:16:55 -0800340 if len(specs) == 0 {
341 return entries
342 }
Jiyong Parkdda8f692020-11-09 18:38:48 +0900343 seenDir := make(map[string]bool)
Jeongik Cha76e677f2023-12-21 16:39:15 +0900344 preparerPath := PathForModuleOut(ctx, "preparer.sh")
345 cmd := builder.Command().Tool(preparerPath)
346 var sb strings.Builder
Cole Faust3b3a0112024-01-03 15:16:55 -0800347 sb.WriteString("set -e\n")
Cole Faust18994c72023-02-28 16:02:16 -0800348 for _, k := range SortedKeys(specs) {
Jooyung Hana8834282022-03-25 11:40:12 +0900349 ps := specs[k]
Peter Collingbourneff56c012023-03-15 22:24:03 -0700350 destPath := filepath.Join(dir.String(), ps.relPathInPackage)
Jiyong Parkdda8f692020-11-09 18:38:48 +0900351 destDir := filepath.Dir(destPath)
352 entries = append(entries, ps.relPathInPackage)
353 if _, ok := seenDir[destDir]; !ok {
354 seenDir[destDir] = true
Jeongik Cha76e677f2023-12-21 16:39:15 +0900355 sb.WriteString(fmt.Sprintf("mkdir -p %s\n", destDir))
Jiyong Parkdda8f692020-11-09 18:38:48 +0900356 }
357 if ps.symlinkTarget == "" {
Jeongik Cha76e677f2023-12-21 16:39:15 +0900358 cmd.Implicit(ps.srcPath)
359 sb.WriteString(fmt.Sprintf("cp %s %s\n", ps.srcPath, destPath))
Jiyong Parkdda8f692020-11-09 18:38:48 +0900360 } else {
Jeongik Cha76e677f2023-12-21 16:39:15 +0900361 sb.WriteString(fmt.Sprintf("ln -sf %s %s\n", ps.symlinkTarget, destPath))
Jiyong Parkdda8f692020-11-09 18:38:48 +0900362 }
363 if ps.executable {
Jeongik Cha76e677f2023-12-21 16:39:15 +0900364 sb.WriteString(fmt.Sprintf("chmod a+x %s\n", destPath))
Jiyong Parkdda8f692020-11-09 18:38:48 +0900365 }
366 }
367
Jeongik Cha76e677f2023-12-21 16:39:15 +0900368 WriteExecutableFileRuleVerbatim(ctx, preparerPath, sb.String())
369
Dan Willemsen9fe14102021-07-13 21:52:04 -0700370 return entries
371}
372
373// See PackageModule.CopyDepsToZip
Jooyung Hana8834282022-03-25 11:40:12 +0900374func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, specs map[string]PackagingSpec, zipOut WritablePath) (entries []string) {
Dan Willemsen9fe14102021-07-13 21:52:04 -0700375 builder := NewRuleBuilder(pctx, ctx)
376
377 dir := PathForModuleOut(ctx, ".zip")
378 builder.Command().Text("rm").Flag("-rf").Text(dir.String())
379 builder.Command().Text("mkdir").Flag("-p").Text(dir.String())
Jooyung Hana8834282022-03-25 11:40:12 +0900380 entries = p.CopySpecsToDir(ctx, builder, specs, dir)
Dan Willemsen9fe14102021-07-13 21:52:04 -0700381
Jiyong Parkdda8f692020-11-09 18:38:48 +0900382 builder.Command().
Colin Crossf1a035e2020-11-16 17:32:30 -0800383 BuiltTool("soong_zip").
Jiyong Parkdda8f692020-11-09 18:38:48 +0900384 FlagWithOutput("-o ", zipOut).
385 FlagWithArg("-C ", dir.String()).
386 Flag("-L 0"). // no compression because this will be unzipped soon
387 FlagWithArg("-D ", dir.String())
388 builder.Command().Text("rm").Flag("-rf").Text(dir.String())
389
Colin Crossf1a035e2020-11-16 17:32:30 -0800390 builder.Build("zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName()))
Jiyong Parkdda8f692020-11-09 18:38:48 +0900391 return entries
392}