packaging.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  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. package android
  15. import (
  16. "fmt"
  17. "path/filepath"
  18. "github.com/google/blueprint"
  19. )
  20. // PackagingSpec abstracts a request to place a built artifact at a certain path in a package. A
  21. // package can be the traditional <partition>.img, but isn't limited to those. Other examples could
  22. // be a new filesystem image that is a subset of system.img (e.g. for an Android-like mini OS
  23. // running on a VM), or a zip archive for some of the host tools.
  24. type PackagingSpec struct {
  25. // Path relative to the root of the package
  26. relPathInPackage string
  27. // The path to the built artifact
  28. srcPath Path
  29. // If this is not empty, then relPathInPackage should be a symlink to this target. (Then
  30. // srcPath is of course ignored.)
  31. symlinkTarget string
  32. // Whether relPathInPackage should be marked as executable or not
  33. executable bool
  34. effectiveLicenseFiles *Paths
  35. partition string
  36. }
  37. // Get file name of installed package
  38. func (p *PackagingSpec) FileName() string {
  39. if p.relPathInPackage != "" {
  40. return filepath.Base(p.relPathInPackage)
  41. }
  42. return ""
  43. }
  44. // Path relative to the root of the package
  45. func (p *PackagingSpec) RelPathInPackage() string {
  46. return p.relPathInPackage
  47. }
  48. func (p *PackagingSpec) SetRelPathInPackage(relPathInPackage string) {
  49. p.relPathInPackage = relPathInPackage
  50. }
  51. func (p *PackagingSpec) EffectiveLicenseFiles() Paths {
  52. if p.effectiveLicenseFiles == nil {
  53. return Paths{}
  54. }
  55. return *p.effectiveLicenseFiles
  56. }
  57. func (p *PackagingSpec) Partition() string {
  58. return p.partition
  59. }
  60. type PackageModule interface {
  61. Module
  62. packagingBase() *PackagingBase
  63. // AddDeps adds dependencies to the `deps` modules. This should be called in DepsMutator.
  64. // When adding the dependencies, depTag is used as the tag. If `deps` modules are meant to
  65. // be copied to a zip in CopyDepsToZip, `depTag` should implement PackagingItem marker interface.
  66. AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag)
  67. // GatherPackagingSpecs gathers PackagingSpecs of transitive dependencies.
  68. GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec
  69. // CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and
  70. // returns zip entries in it. This is expected to be called in GenerateAndroidBuildActions,
  71. // followed by a build rule that unzips it and creates the final output (img, zip, tar.gz,
  72. // etc.) from the extracted files
  73. CopyDepsToZip(ctx ModuleContext, specs map[string]PackagingSpec, zipOut WritablePath) []string
  74. }
  75. // PackagingBase provides basic functionality for packaging dependencies. A module is expected to
  76. // include this struct and call InitPackageModule.
  77. type PackagingBase struct {
  78. properties PackagingProperties
  79. // Allows this module to skip missing dependencies. In most cases, this is not required, but
  80. // for rare cases like when there's a dependency to a module which exists in certain repo
  81. // checkouts, this is needed.
  82. IgnoreMissingDependencies bool
  83. }
  84. type depsProperty struct {
  85. // Modules to include in this package
  86. Deps []string `android:"arch_variant"`
  87. }
  88. type 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. type packagingArchProperties struct {
  95. Arm64 depsProperty
  96. Arm depsProperty
  97. X86_64 depsProperty
  98. X86 depsProperty
  99. }
  100. type PackagingProperties struct {
  101. Deps []string `android:"arch_variant"`
  102. Multilib packagingMultilibProperties `android:"arch_variant"`
  103. Arch packagingArchProperties
  104. }
  105. func InitPackageModule(p PackageModule) {
  106. base := p.packagingBase()
  107. p.AddProperties(&base.properties)
  108. }
  109. func (p *PackagingBase) packagingBase() *PackagingBase {
  110. return p
  111. }
  112. // 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.
  116. func (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. }
  127. for i, t := range ctx.MultiTargets() {
  128. if t.Arch.ArchType == arch {
  129. ret = append(ret, p.properties.Deps...)
  130. if i == 0 {
  131. ret = append(ret, p.properties.Multilib.First.Deps...)
  132. }
  133. }
  134. }
  135. if ctx.Arch().ArchType == Common {
  136. switch arch {
  137. case Arm64:
  138. ret = append(ret, p.properties.Arch.Arm64.Deps...)
  139. case Arm:
  140. ret = append(ret, p.properties.Arch.Arm.Deps...)
  141. case X86_64:
  142. ret = append(ret, p.properties.Arch.X86_64.Deps...)
  143. case X86:
  144. ret = append(ret, p.properties.Arch.X86.Deps...)
  145. }
  146. }
  147. return FirstUniqueStrings(ret)
  148. }
  149. func (p *PackagingBase) getSupportedTargets(ctx BaseModuleContext) []Target {
  150. var ret []Target
  151. // The current and the common OS targets are always supported
  152. ret = append(ret, ctx.Target())
  153. if ctx.Arch().ArchType != Common {
  154. ret = append(ret, Target{Os: ctx.Os(), Arch: Arch{ArchType: Common}})
  155. }
  156. // If this module is configured for multi targets, those should be supported as well
  157. ret = append(ret, ctx.MultiTargets()...)
  158. return ret
  159. }
  160. // PackagingItem is a marker interface for dependency tags.
  161. // Direct dependencies with a tag implementing PackagingItem are packaged in CopyDepsToZip().
  162. type PackagingItem interface {
  163. // IsPackagingItem returns true if the dep is to be packaged
  164. IsPackagingItem() bool
  165. }
  166. // DepTag provides default implementation of PackagingItem interface.
  167. // PackagingBase-derived modules can define their own dependency tag by embedding this, which
  168. // can be passed to AddDeps() or AddDependencies().
  169. type PackagingItemAlwaysDepTag struct {
  170. }
  171. // IsPackagingItem returns true if the dep is to be packaged
  172. func (PackagingItemAlwaysDepTag) IsPackagingItem() bool {
  173. return true
  174. }
  175. // See PackageModule.AddDeps
  176. func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) {
  177. for _, t := range p.getSupportedTargets(ctx) {
  178. for _, dep := range p.getDepsForArch(ctx, t.Arch.ArchType) {
  179. if p.IgnoreMissingDependencies && !ctx.OtherModuleExists(dep) {
  180. continue
  181. }
  182. ctx.AddFarVariationDependencies(t.Variations(), depTag, dep)
  183. }
  184. }
  185. }
  186. // See PackageModule.GatherPackagingSpecs
  187. func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec {
  188. m := make(map[string]PackagingSpec)
  189. ctx.VisitDirectDeps(func(child Module) {
  190. if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() {
  191. return
  192. }
  193. for _, ps := range child.TransitivePackagingSpecs() {
  194. if _, ok := m[ps.relPathInPackage]; !ok {
  195. m[ps.relPathInPackage] = ps
  196. }
  197. }
  198. })
  199. return m
  200. }
  201. // CopySpecsToDir is a helper that will add commands to the rule builder to copy the PackagingSpec
  202. // entries into the specified directory.
  203. func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir WritablePath) (entries []string) {
  204. seenDir := make(map[string]bool)
  205. for _, k := range SortedKeys(specs) {
  206. ps := specs[k]
  207. destPath := filepath.Join(dir.String(), ps.relPathInPackage)
  208. destDir := filepath.Dir(destPath)
  209. entries = append(entries, ps.relPathInPackage)
  210. if _, ok := seenDir[destDir]; !ok {
  211. seenDir[destDir] = true
  212. builder.Command().Text("mkdir").Flag("-p").Text(destDir)
  213. }
  214. if ps.symlinkTarget == "" {
  215. builder.Command().Text("cp").Input(ps.srcPath).Text(destPath)
  216. } else {
  217. builder.Command().Text("ln").Flag("-sf").Text(ps.symlinkTarget).Text(destPath)
  218. }
  219. if ps.executable {
  220. builder.Command().Text("chmod").Flag("a+x").Text(destPath)
  221. }
  222. }
  223. return entries
  224. }
  225. // See PackageModule.CopyDepsToZip
  226. func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, specs map[string]PackagingSpec, zipOut WritablePath) (entries []string) {
  227. builder := NewRuleBuilder(pctx, ctx)
  228. dir := PathForModuleOut(ctx, ".zip")
  229. builder.Command().Text("rm").Flag("-rf").Text(dir.String())
  230. builder.Command().Text("mkdir").Flag("-p").Text(dir.String())
  231. entries = p.CopySpecsToDir(ctx, builder, specs, dir)
  232. builder.Command().
  233. BuiltTool("soong_zip").
  234. FlagWithOutput("-o ", zipOut).
  235. FlagWithArg("-C ", dir.String()).
  236. Flag("-L 0"). // no compression because this will be unzipped soon
  237. FlagWithArg("-D ", dir.String())
  238. builder.Command().Text("rm").Flag("-rf").Text(dir.String())
  239. builder.Build("zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName()))
  240. return entries
  241. }