blob: 7a14479c26f1f1da1d33c15c719dfabd2c5b4871 [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 Parkdda8f692020-11-09 18:38:48 +090052type PackageModule interface {
53 Module
54 packagingBase() *PackagingBase
55
56 // AddDeps adds dependencies to the `deps` modules. This should be called in DepsMutator.
Jiyong Park65b62242020-11-25 12:44:59 +090057 // When adding the dependencies, depTag is used as the tag.
58 AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag)
Jiyong Parkdda8f692020-11-09 18:38:48 +090059
60 // CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and
Jiyong Parkcc1157c2020-11-25 11:31:13 +090061 // returns zip entries in it. This is expected to be called in GenerateAndroidBuildActions,
Jiyong Parkdda8f692020-11-09 18:38:48 +090062 // followed by a build rule that unzips it and creates the final output (img, zip, tar.gz,
63 // etc.) from the extracted files
64 CopyDepsToZip(ctx ModuleContext, zipOut OutputPath) []string
65}
66
67// PackagingBase provides basic functionality for packaging dependencies. A module is expected to
68// include this struct and call InitPackageModule.
69type PackagingBase struct {
70 properties PackagingProperties
71
Jiyong Parkcc1157c2020-11-25 11:31:13 +090072 // Allows this module to skip missing dependencies. In most cases, this is not required, but
73 // for rare cases like when there's a dependency to a module which exists in certain repo
74 // checkouts, this is needed.
Jiyong Parkdda8f692020-11-09 18:38:48 +090075 IgnoreMissingDependencies bool
76}
77
78type depsProperty struct {
79 // Modules to include in this package
80 Deps []string `android:"arch_variant"`
81}
82
83type packagingMultilibProperties struct {
84 First depsProperty `android:"arch_variant"`
85 Common depsProperty `android:"arch_variant"`
86 Lib32 depsProperty `android:"arch_variant"`
87 Lib64 depsProperty `android:"arch_variant"`
88}
89
Jiyong Park2136d152021-02-01 23:24:56 +090090type packagingArchProperties struct {
91 Arm64 depsProperty
92 Arm depsProperty
93 X86_64 depsProperty
94 X86 depsProperty
95}
96
Jiyong Parkdda8f692020-11-09 18:38:48 +090097type PackagingProperties struct {
98 Deps []string `android:"arch_variant"`
99 Multilib packagingMultilibProperties `android:"arch_variant"`
Jiyong Park2136d152021-02-01 23:24:56 +0900100 Arch packagingArchProperties
Jiyong Parkdda8f692020-11-09 18:38:48 +0900101}
102
Jiyong Parkdda8f692020-11-09 18:38:48 +0900103func InitPackageModule(p PackageModule) {
104 base := p.packagingBase()
105 p.AddProperties(&base.properties)
106}
107
108func (p *PackagingBase) packagingBase() *PackagingBase {
109 return p
110}
111
Jiyong Parkcc1157c2020-11-25 11:31:13 +0900112// From deps and multilib.*.deps, select the dependencies that are for the given arch deps is for
113// the current archicture when this module is not configured for multi target. When configured for
114// multi target, deps is selected for each of the targets and is NOT selected for the current
115// architecture which would be Common.
Jiyong Parkdda8f692020-11-09 18:38:48 +0900116func (p *PackagingBase) getDepsForArch(ctx BaseModuleContext, arch ArchType) []string {
117 var ret []string
118 if arch == ctx.Target().Arch.ArchType && len(ctx.MultiTargets()) == 0 {
119 ret = append(ret, p.properties.Deps...)
120 } else if arch.Multilib == "lib32" {
121 ret = append(ret, p.properties.Multilib.Lib32.Deps...)
122 } else if arch.Multilib == "lib64" {
123 ret = append(ret, p.properties.Multilib.Lib64.Deps...)
124 } else if arch == Common {
125 ret = append(ret, p.properties.Multilib.Common.Deps...)
126 }
Jiyong Park2136d152021-02-01 23:24:56 +0900127
Jiyong Parkdda8f692020-11-09 18:38:48 +0900128 for i, t := range ctx.MultiTargets() {
129 if t.Arch.ArchType == arch {
130 ret = append(ret, p.properties.Deps...)
131 if i == 0 {
132 ret = append(ret, p.properties.Multilib.First.Deps...)
133 }
134 }
135 }
Jiyong Park2136d152021-02-01 23:24:56 +0900136
137 if ctx.Arch().ArchType == Common {
138 switch arch {
139 case Arm64:
140 ret = append(ret, p.properties.Arch.Arm64.Deps...)
141 case Arm:
142 ret = append(ret, p.properties.Arch.Arm.Deps...)
143 case X86_64:
144 ret = append(ret, p.properties.Arch.X86_64.Deps...)
145 case X86:
146 ret = append(ret, p.properties.Arch.X86.Deps...)
147 }
148 }
149
Jiyong Parkdda8f692020-11-09 18:38:48 +0900150 return FirstUniqueStrings(ret)
151}
152
153func (p *PackagingBase) getSupportedTargets(ctx BaseModuleContext) []Target {
154 var ret []Target
155 // The current and the common OS targets are always supported
156 ret = append(ret, ctx.Target())
157 if ctx.Arch().ArchType != Common {
158 ret = append(ret, Target{Os: ctx.Os(), Arch: Arch{ArchType: Common}})
159 }
160 // If this module is configured for multi targets, those should be supported as well
161 ret = append(ret, ctx.MultiTargets()...)
162 return ret
163}
164
165// See PackageModule.AddDeps
Jiyong Park65b62242020-11-25 12:44:59 +0900166func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) {
Jiyong Parkdda8f692020-11-09 18:38:48 +0900167 for _, t := range p.getSupportedTargets(ctx) {
168 for _, dep := range p.getDepsForArch(ctx, t.Arch.ArchType) {
169 if p.IgnoreMissingDependencies && !ctx.OtherModuleExists(dep) {
170 continue
171 }
172 ctx.AddFarVariationDependencies(t.Variations(), depTag, dep)
173 }
174 }
175}
176
177// See PackageModule.CopyDepsToZip
178func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut OutputPath) (entries []string) {
Jiyong Parkdda8f692020-11-09 18:38:48 +0900179 m := make(map[string]PackagingSpec)
180 ctx.WalkDeps(func(child Module, parent Module) bool {
Jiyong Parkd630bdd2020-11-25 11:47:24 +0900181 if !IsInstallDepNeeded(ctx.OtherModuleDependencyTag(child)) {
Jiyong Parkdda8f692020-11-09 18:38:48 +0900182 return false
183 }
184 for _, ps := range child.PackagingSpecs() {
185 if _, ok := m[ps.relPathInPackage]; !ok {
186 m[ps.relPathInPackage] = ps
187 }
188 }
189 return true
190 })
191
Colin Crossf1a035e2020-11-16 17:32:30 -0800192 builder := NewRuleBuilder(pctx, ctx)
Jiyong Parkdda8f692020-11-09 18:38:48 +0900193
194 dir := PathForModuleOut(ctx, ".zip").OutputPath
195 builder.Command().Text("rm").Flag("-rf").Text(dir.String())
196 builder.Command().Text("mkdir").Flag("-p").Text(dir.String())
197
198 seenDir := make(map[string]bool)
199 for _, k := range SortedStringKeys(m) {
200 ps := m[k]
201 destPath := dir.Join(ctx, ps.relPathInPackage).String()
202 destDir := filepath.Dir(destPath)
203 entries = append(entries, ps.relPathInPackage)
204 if _, ok := seenDir[destDir]; !ok {
205 seenDir[destDir] = true
206 builder.Command().Text("mkdir").Flag("-p").Text(destDir)
207 }
208 if ps.symlinkTarget == "" {
209 builder.Command().Text("cp").Input(ps.srcPath).Text(destPath)
210 } else {
211 builder.Command().Text("ln").Flag("-sf").Text(ps.symlinkTarget).Text(destPath)
212 }
213 if ps.executable {
214 builder.Command().Text("chmod").Flag("a+x").Text(destPath)
215 }
216 }
217
218 builder.Command().
Colin Crossf1a035e2020-11-16 17:32:30 -0800219 BuiltTool("soong_zip").
Jiyong Parkdda8f692020-11-09 18:38:48 +0900220 FlagWithOutput("-o ", zipOut).
221 FlagWithArg("-C ", dir.String()).
222 Flag("-L 0"). // no compression because this will be unzipped soon
223 FlagWithArg("-D ", dir.String())
224 builder.Command().Text("rm").Flag("-rf").Text(dir.String())
225
Colin Crossf1a035e2020-11-16 17:32:30 -0800226 builder.Build("zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName()))
Jiyong Parkdda8f692020-11-09 18:38:48 +0900227 return entries
228}
Colin Crossffe6b9d2020-12-01 15:40:06 -0800229
230// packagingSpecsDepSet is a thin type-safe wrapper around the generic depSet. It always uses
231// topological order.
232type packagingSpecsDepSet struct {
233 depSet
234}
235
236// newPackagingSpecsDepSet returns an immutable packagingSpecsDepSet with the given direct and
237// transitive contents.
238func newPackagingSpecsDepSet(direct []PackagingSpec, transitive []*packagingSpecsDepSet) *packagingSpecsDepSet {
239 return &packagingSpecsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)}
240}
241
242// ToList returns the packagingSpecsDepSet flattened to a list in topological order.
243func (d *packagingSpecsDepSet) ToList() []PackagingSpec {
244 if d == nil {
245 return nil
246 }
247 return d.depSet.ToList().([]PackagingSpec)
248}