xref: /aosp_15_r20/build/soong/android/packaging.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
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	"sort"
21
22	"github.com/google/blueprint"
23	"github.com/google/blueprint/gobtools"
24	"github.com/google/blueprint/proptools"
25)
26
27// PackagingSpec abstracts a request to place a built artifact at a certain path in a package. A
28// package can be the traditional <partition>.img, but isn't limited to those. Other examples could
29// be a new filesystem image that is a subset of system.img (e.g. for an Android-like mini OS
30// running on a VM), or a zip archive for some of the host tools.
31type PackagingSpec struct {
32	// Path relative to the root of the package
33	relPathInPackage string
34
35	// The path to the built artifact
36	srcPath Path
37
38	// If this is not empty, then relPathInPackage should be a symlink to this target. (Then
39	// srcPath is of course ignored.)
40	symlinkTarget string
41
42	// Whether relPathInPackage should be marked as executable or not
43	executable bool
44
45	effectiveLicenseFiles *Paths
46
47	partition string
48
49	// Whether this packaging spec represents an installation of the srcPath (i.e. this struct
50	// is created via InstallFile or InstallSymlink) or a simple packaging (i.e. created via
51	// PackageFile).
52	skipInstall bool
53
54	// Paths of aconfig files for the built artifact
55	aconfigPaths *Paths
56
57	// ArchType of the module which produced this packaging spec
58	archType ArchType
59
60	// List of module names that this packaging spec overrides
61	overrides *[]string
62
63	// Name of the module where this packaging spec is output of
64	owner string
65}
66
67type packagingSpecGob struct {
68	RelPathInPackage      string
69	SrcPath               Path
70	SymlinkTarget         string
71	Executable            bool
72	EffectiveLicenseFiles *Paths
73	Partition             string
74	SkipInstall           bool
75	AconfigPaths          *Paths
76	ArchType              ArchType
77	Overrides             *[]string
78	Owner                 string
79}
80
81func (p *PackagingSpec) ToGob() *packagingSpecGob {
82	return &packagingSpecGob{
83		RelPathInPackage:      p.relPathInPackage,
84		SrcPath:               p.srcPath,
85		SymlinkTarget:         p.symlinkTarget,
86		Executable:            p.executable,
87		EffectiveLicenseFiles: p.effectiveLicenseFiles,
88		Partition:             p.partition,
89		SkipInstall:           p.skipInstall,
90		AconfigPaths:          p.aconfigPaths,
91		ArchType:              p.archType,
92		Overrides:             p.overrides,
93		Owner:                 p.owner,
94	}
95}
96
97func (p *PackagingSpec) FromGob(data *packagingSpecGob) {
98	p.relPathInPackage = data.RelPathInPackage
99	p.srcPath = data.SrcPath
100	p.symlinkTarget = data.SymlinkTarget
101	p.executable = data.Executable
102	p.effectiveLicenseFiles = data.EffectiveLicenseFiles
103	p.partition = data.Partition
104	p.skipInstall = data.SkipInstall
105	p.aconfigPaths = data.AconfigPaths
106	p.archType = data.ArchType
107	p.overrides = data.Overrides
108	p.owner = data.Owner
109}
110
111func (p *PackagingSpec) GobEncode() ([]byte, error) {
112	return gobtools.CustomGobEncode[packagingSpecGob](p)
113}
114
115func (p *PackagingSpec) GobDecode(data []byte) error {
116	return gobtools.CustomGobDecode[packagingSpecGob](data, p)
117}
118
119func (p *PackagingSpec) Equals(other *PackagingSpec) bool {
120	if other == nil {
121		return false
122	}
123	if p.relPathInPackage != other.relPathInPackage {
124		return false
125	}
126	if p.srcPath != other.srcPath || p.symlinkTarget != other.symlinkTarget {
127		return false
128	}
129	if p.executable != other.executable {
130		return false
131	}
132	if p.partition != other.partition {
133		return false
134	}
135	return true
136}
137
138// Get file name of installed package
139func (p *PackagingSpec) FileName() string {
140	if p.relPathInPackage != "" {
141		return filepath.Base(p.relPathInPackage)
142	}
143
144	return ""
145}
146
147// Path relative to the root of the package
148func (p *PackagingSpec) RelPathInPackage() string {
149	return p.relPathInPackage
150}
151
152func (p *PackagingSpec) SetRelPathInPackage(relPathInPackage string) {
153	p.relPathInPackage = relPathInPackage
154}
155
156func (p *PackagingSpec) EffectiveLicenseFiles() Paths {
157	if p.effectiveLicenseFiles == nil {
158		return Paths{}
159	}
160	return *p.effectiveLicenseFiles
161}
162
163func (p *PackagingSpec) Partition() string {
164	return p.partition
165}
166
167func (p *PackagingSpec) SetPartition(partition string) {
168	p.partition = partition
169}
170
171func (p *PackagingSpec) SkipInstall() bool {
172	return p.skipInstall
173}
174
175// Paths of aconfig files for the built artifact
176func (p *PackagingSpec) GetAconfigPaths() Paths {
177	return *p.aconfigPaths
178}
179
180type PackageModule interface {
181	Module
182	packagingBase() *PackagingBase
183
184	// AddDeps adds dependencies to the `deps` modules. This should be called in DepsMutator.
185	// When adding the dependencies, depTag is used as the tag. If `deps` modules are meant to
186	// be copied to a zip in CopyDepsToZip, `depTag` should implement PackagingItem marker interface.
187	AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag)
188
189	// GatherPackagingSpecs gathers PackagingSpecs of transitive dependencies.
190	GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec
191	GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec
192	GatherPackagingSpecsWithFilterAndModifier(ctx ModuleContext, filter func(PackagingSpec) bool, modifier func(*PackagingSpec)) map[string]PackagingSpec
193
194	// CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and
195	// returns zip entries in it. This is expected to be called in GenerateAndroidBuildActions,
196	// followed by a build rule that unzips it and creates the final output (img, zip, tar.gz,
197	// etc.) from the extracted files
198	CopyDepsToZip(ctx ModuleContext, specs map[string]PackagingSpec, zipOut WritablePath) []string
199}
200
201// PackagingBase provides basic functionality for packaging dependencies. A module is expected to
202// include this struct and call InitPackageModule.
203type PackagingBase struct {
204	properties PackagingProperties
205
206	// Allows this module to skip missing dependencies. In most cases, this is not required, but
207	// for rare cases like when there's a dependency to a module which exists in certain repo
208	// checkouts, this is needed.
209	IgnoreMissingDependencies bool
210
211	// If this is set to true by a module type inheriting PackagingBase, the deps property
212	// collects the first target only even with compile_multilib: true.
213	DepsCollectFirstTargetOnly bool
214
215	// If this is set to try by a module type inheriting PackagingBase, the module type is
216	// allowed to utilize High_priority_deps.
217	AllowHighPriorityDeps bool
218}
219
220type DepsProperty struct {
221	// Deps that have higher priority in packaging when there is a packaging conflict.
222	// For example, if multiple files are being installed to same filepath, the install file
223	// of the module listed in this property will have a higher priority over those in other
224	// deps properties.
225	High_priority_deps []string `android:"arch_variant"`
226
227	// Modules to include in this package
228	Deps proptools.Configurable[[]string] `android:"arch_variant"`
229}
230
231type packagingMultilibProperties struct {
232	First    DepsProperty `android:"arch_variant"`
233	Common   DepsProperty `android:"arch_variant"`
234	Lib32    DepsProperty `android:"arch_variant"`
235	Lib64    DepsProperty `android:"arch_variant"`
236	Both     DepsProperty `android:"arch_variant"`
237	Prefer32 DepsProperty `android:"arch_variant"`
238}
239
240type packagingArchProperties struct {
241	Arm64  DepsProperty
242	Arm    DepsProperty
243	X86_64 DepsProperty
244	X86    DepsProperty
245}
246
247type PackagingProperties struct {
248	DepsProperty
249
250	Multilib packagingMultilibProperties `android:"arch_variant"`
251	Arch     packagingArchProperties
252}
253
254func InitPackageModule(p PackageModule) {
255	base := p.packagingBase()
256	p.AddProperties(&base.properties, &base.properties.DepsProperty)
257}
258
259func (p *PackagingBase) packagingBase() *PackagingBase {
260	return p
261}
262
263// From deps and multilib.*.deps, select the dependencies that are for the given arch deps is for
264// the current archicture when this module is not configured for multi target. When configured for
265// multi target, deps is selected for each of the targets and is NOT selected for the current
266// architecture which would be Common.
267// It returns two lists, the normal and high priority deps, respectively.
268func (p *PackagingBase) getDepsForArch(ctx BaseModuleContext, arch ArchType) ([]string, []string) {
269	var normalDeps []string
270	var highPriorityDeps []string
271
272	get := func(prop DepsProperty) {
273		normalDeps = append(normalDeps, prop.Deps.GetOrDefault(ctx, nil)...)
274		highPriorityDeps = append(highPriorityDeps, prop.High_priority_deps...)
275	}
276	has := func(prop DepsProperty) bool {
277		return len(prop.Deps.GetOrDefault(ctx, nil)) > 0 || len(prop.High_priority_deps) > 0
278	}
279
280	if arch == ctx.Target().Arch.ArchType && len(ctx.MultiTargets()) == 0 {
281		get(p.properties.DepsProperty)
282	} else if arch.Multilib == "lib32" {
283		get(p.properties.Multilib.Lib32)
284		// multilib.prefer32.deps are added for lib32 only when they support 32-bit arch
285		for _, dep := range p.properties.Multilib.Prefer32.Deps.GetOrDefault(ctx, nil) {
286			if checkIfOtherModuleSupportsLib32(ctx, dep) {
287				normalDeps = append(normalDeps, dep)
288			}
289		}
290		for _, dep := range p.properties.Multilib.Prefer32.High_priority_deps {
291			if checkIfOtherModuleSupportsLib32(ctx, dep) {
292				highPriorityDeps = append(highPriorityDeps, dep)
293			}
294		}
295	} else if arch.Multilib == "lib64" {
296		get(p.properties.Multilib.Lib64)
297		// multilib.prefer32.deps are added for lib64 only when they don't support 32-bit arch
298		for _, dep := range p.properties.Multilib.Prefer32.Deps.GetOrDefault(ctx, nil) {
299			if !checkIfOtherModuleSupportsLib32(ctx, dep) {
300				normalDeps = append(normalDeps, dep)
301			}
302		}
303		for _, dep := range p.properties.Multilib.Prefer32.High_priority_deps {
304			if !checkIfOtherModuleSupportsLib32(ctx, dep) {
305				highPriorityDeps = append(highPriorityDeps, dep)
306			}
307		}
308	} else if arch == Common {
309		get(p.properties.Multilib.Common)
310	}
311
312	if p.DepsCollectFirstTargetOnly {
313		if has(p.properties.Multilib.First) {
314			ctx.PropertyErrorf("multilib.first.deps", "not supported. use \"deps\" instead")
315		}
316		for i, t := range ctx.MultiTargets() {
317			if t.Arch.ArchType == arch {
318				get(p.properties.Multilib.Both)
319				if i == 0 {
320					get(p.properties.DepsProperty)
321				}
322			}
323		}
324	} else {
325		if has(p.properties.Multilib.Both) {
326			ctx.PropertyErrorf("multilib.both.deps", "not supported. use \"deps\" instead")
327		}
328		for i, t := range ctx.MultiTargets() {
329			if t.Arch.ArchType == arch {
330				get(p.properties.DepsProperty)
331				if i == 0 {
332					get(p.properties.Multilib.First)
333				}
334			}
335		}
336	}
337
338	if ctx.Arch().ArchType == Common {
339		switch arch {
340		case Arm64:
341			get(p.properties.Arch.Arm64)
342		case Arm:
343			get(p.properties.Arch.Arm)
344		case X86_64:
345			get(p.properties.Arch.X86_64)
346		case X86:
347			get(p.properties.Arch.X86)
348		}
349	}
350
351	if len(highPriorityDeps) > 0 && !p.AllowHighPriorityDeps {
352		ctx.ModuleErrorf("Usage of high_priority_deps is not allowed for %s module type", ctx.ModuleType())
353	}
354
355	return FirstUniqueStrings(normalDeps), FirstUniqueStrings(highPriorityDeps)
356}
357
358func getSupportedTargets(ctx BaseModuleContext) []Target {
359	var ret []Target
360	// The current and the common OS targets are always supported
361	ret = append(ret, ctx.Target())
362	if ctx.Arch().ArchType != Common {
363		ret = append(ret, Target{Os: ctx.Os(), Arch: Arch{ArchType: Common}})
364	}
365	// If this module is configured for multi targets, those should be supported as well
366	ret = append(ret, ctx.MultiTargets()...)
367	return ret
368}
369
370// getLib32Target returns the 32-bit target from the list of targets this module supports. If this
371// module doesn't support 32-bit target, nil is returned.
372func getLib32Target(ctx BaseModuleContext) *Target {
373	for _, t := range getSupportedTargets(ctx) {
374		if t.Arch.ArchType.Multilib == "lib32" {
375			return &t
376		}
377	}
378	return nil
379}
380
381// checkIfOtherModuleSUpportsLib32 returns true if 32-bit variant of dep exists.
382func checkIfOtherModuleSupportsLib32(ctx BaseModuleContext, dep string) bool {
383	t := getLib32Target(ctx)
384	if t == nil {
385		// This packaging module doesn't support 32bit. No point of checking if dep supports 32-bit
386		// or not.
387		return false
388	}
389	return ctx.OtherModuleFarDependencyVariantExists(t.Variations(), dep)
390}
391
392// PackagingItem is a marker interface for dependency tags.
393// Direct dependencies with a tag implementing PackagingItem are packaged in CopyDepsToZip().
394type PackagingItem interface {
395	// IsPackagingItem returns true if the dep is to be packaged
396	IsPackagingItem() bool
397}
398
399var _ PackagingItem = (*PackagingItemAlwaysDepTag)(nil)
400
401// DepTag provides default implementation of PackagingItem interface.
402// PackagingBase-derived modules can define their own dependency tag by embedding this, which
403// can be passed to AddDeps() or AddDependencies().
404type PackagingItemAlwaysDepTag struct {
405}
406
407// IsPackagingItem returns true if the dep is to be packaged
408func (PackagingItemAlwaysDepTag) IsPackagingItem() bool {
409	return true
410}
411
412type highPriorityDepTag struct {
413	blueprint.BaseDependencyTag
414	PackagingItemAlwaysDepTag
415}
416
417// See PackageModule.AddDeps
418func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) {
419	addDep := func(t Target, dep string, highPriority bool) {
420		if p.IgnoreMissingDependencies && !ctx.OtherModuleExists(dep) {
421			return
422		}
423		targetVariation := t.Variations()
424		sharedVariation := blueprint.Variation{
425			Mutator:   "link",
426			Variation: "shared",
427		}
428		// If a shared variation exists, use that. Static variants do not provide any standalone files
429		// for packaging.
430		if ctx.OtherModuleFarDependencyVariantExists([]blueprint.Variation{sharedVariation}, dep) {
431			targetVariation = append(targetVariation, sharedVariation)
432		}
433		depTagToUse := depTag
434		if highPriority {
435			depTagToUse = highPriorityDepTag{}
436		}
437
438		ctx.AddFarVariationDependencies(targetVariation, depTagToUse, dep)
439	}
440	for _, t := range getSupportedTargets(ctx) {
441		normalDeps, highPriorityDeps := p.getDepsForArch(ctx, t.Arch.ArchType)
442		for _, dep := range normalDeps {
443			addDep(t, dep, false)
444		}
445		for _, dep := range highPriorityDeps {
446			addDep(t, dep, true)
447		}
448	}
449}
450
451// See PackageModule.GatherPackagingSpecs
452func (p *PackagingBase) GatherPackagingSpecsWithFilterAndModifier(ctx ModuleContext, filter func(PackagingSpec) bool, modifier func(*PackagingSpec)) map[string]PackagingSpec {
453	// packaging specs gathered from the dep that are not high priorities.
454	var regularPriorities []PackagingSpec
455
456	// all packaging specs gathered from the high priority deps.
457	var highPriorities []PackagingSpec
458
459	// Name of the dependency which requested the packaging spec.
460	// If this dep is overridden, the packaging spec will not be installed via this dependency chain.
461	// (the packaging spec might still be installed if there are some other deps which depend on it).
462	var depNames []string
463
464	// list of module names overridden
465	var overridden []string
466
467	var arches []ArchType
468	for _, target := range getSupportedTargets(ctx) {
469		arches = append(arches, target.Arch.ArchType)
470	}
471
472	// filter out packaging specs for unsupported architecture
473	filterArch := func(ps PackagingSpec) bool {
474		for _, arch := range arches {
475			if arch == ps.archType {
476				return true
477			}
478		}
479		return false
480	}
481
482	ctx.VisitDirectDepsProxy(func(child ModuleProxy) {
483		depTag := ctx.OtherModuleDependencyTag(child)
484		if pi, ok := depTag.(PackagingItem); !ok || !pi.IsPackagingItem() {
485			return
486		}
487		for _, ps := range OtherModuleProviderOrDefault(
488			ctx, child, InstallFilesProvider).TransitivePackagingSpecs.ToList() {
489			if !filterArch(ps) {
490				continue
491			}
492
493			if filter != nil {
494				if !filter(ps) {
495					continue
496				}
497			}
498
499			if modifier != nil {
500				modifier(&ps)
501			}
502
503			if _, ok := depTag.(highPriorityDepTag); ok {
504				highPriorities = append(highPriorities, ps)
505			} else {
506				regularPriorities = append(regularPriorities, ps)
507			}
508
509			depNames = append(depNames, child.Name())
510			if ps.overrides != nil {
511				overridden = append(overridden, *ps.overrides...)
512			}
513		}
514	})
515
516	filterOverridden := func(input []PackagingSpec) []PackagingSpec {
517		// input minus packaging specs that are overridden
518		var filtered []PackagingSpec
519		for index, ps := range input {
520			if ps.owner != "" && InList(ps.owner, overridden) {
521				continue
522			}
523			// The dependency which requested this packaging spec has been overridden.
524			if InList(depNames[index], overridden) {
525				continue
526			}
527			filtered = append(filtered, ps)
528		}
529		return filtered
530	}
531
532	filteredRegularPriority := filterOverridden(regularPriorities)
533
534	m := make(map[string]PackagingSpec)
535	for _, ps := range filteredRegularPriority {
536		dstPath := ps.relPathInPackage
537		if existingPs, ok := m[dstPath]; ok {
538			if !existingPs.Equals(&ps) {
539				ctx.ModuleErrorf("packaging conflict at %v:\n%v\n%v", dstPath, existingPs, ps)
540			}
541			continue
542		}
543		m[dstPath] = ps
544	}
545
546	filteredHighPriority := filterOverridden(highPriorities)
547	highPriorityPs := make(map[string]PackagingSpec)
548	for _, ps := range filteredHighPriority {
549		dstPath := ps.relPathInPackage
550		if existingPs, ok := highPriorityPs[dstPath]; ok {
551			if !existingPs.Equals(&ps) {
552				ctx.ModuleErrorf("packaging conflict at %v:\n%v\n%v", dstPath, existingPs, ps)
553			}
554			continue
555		}
556		highPriorityPs[dstPath] = ps
557		m[dstPath] = ps
558	}
559
560	return m
561}
562
563// See PackageModule.GatherPackagingSpecs
564func (p *PackagingBase) GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec {
565	return p.GatherPackagingSpecsWithFilterAndModifier(ctx, filter, nil)
566}
567
568// See PackageModule.GatherPackagingSpecs
569func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec {
570	return p.GatherPackagingSpecsWithFilter(ctx, nil)
571}
572
573// CopySpecsToDir is a helper that will add commands to the rule builder to copy the PackagingSpec
574// entries into the specified directory.
575func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir WritablePath) (entries []string) {
576	dirsToSpecs := make(map[WritablePath]map[string]PackagingSpec)
577	dirsToSpecs[dir] = specs
578	return p.CopySpecsToDirs(ctx, builder, dirsToSpecs)
579}
580
581// CopySpecsToDirs is a helper that will add commands to the rule builder to copy the PackagingSpec
582// entries into corresponding directories.
583func (p *PackagingBase) CopySpecsToDirs(ctx ModuleContext, builder *RuleBuilder, dirsToSpecs map[WritablePath]map[string]PackagingSpec) (entries []string) {
584	empty := true
585	for _, specs := range dirsToSpecs {
586		if len(specs) > 0 {
587			empty = false
588			break
589		}
590	}
591	if empty {
592		return entries
593	}
594
595	seenDir := make(map[string]bool)
596
597	dirs := make([]WritablePath, 0, len(dirsToSpecs))
598	for dir, _ := range dirsToSpecs {
599		dirs = append(dirs, dir)
600	}
601	sort.Slice(dirs, func(i, j int) bool {
602		return dirs[i].String() < dirs[j].String()
603	})
604
605	for _, dir := range dirs {
606		specs := dirsToSpecs[dir]
607		for _, k := range SortedKeys(specs) {
608			ps := specs[k]
609			destPath := filepath.Join(dir.String(), ps.relPathInPackage)
610			destDir := filepath.Dir(destPath)
611			entries = append(entries, ps.relPathInPackage)
612			if _, ok := seenDir[destDir]; !ok {
613				seenDir[destDir] = true
614				builder.Command().Textf("mkdir -p %s", destDir)
615			}
616			if ps.symlinkTarget == "" {
617				builder.Command().Text("cp").Input(ps.srcPath).Text(destPath)
618			} else {
619				builder.Command().Textf("ln -sf %s %s", ps.symlinkTarget, destPath)
620			}
621			if ps.executable {
622				builder.Command().Textf("chmod a+x %s", destPath)
623			}
624		}
625	}
626
627	return entries
628}
629
630// See PackageModule.CopyDepsToZip
631func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, specs map[string]PackagingSpec, zipOut WritablePath) (entries []string) {
632	builder := NewRuleBuilder(pctx, ctx)
633
634	dir := PathForModuleOut(ctx, ".zip")
635	builder.Command().Text("rm").Flag("-rf").Text(dir.String())
636	builder.Command().Text("mkdir").Flag("-p").Text(dir.String())
637	entries = p.CopySpecsToDir(ctx, builder, specs, dir)
638
639	builder.Command().
640		BuiltTool("soong_zip").
641		FlagWithOutput("-o ", zipOut).
642		FlagWithArg("-C ", dir.String()).
643		Flag("-L 0"). // no compression because this will be unzipped soon
644		FlagWithArg("-D ", dir.String())
645	builder.Command().Text("rm").Flag("-rf").Text(dir.String())
646
647	builder.Build("zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName()))
648	return entries
649}
650