aapt2.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. // Copyright 2017 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 java
  15. import (
  16. "path/filepath"
  17. "sort"
  18. "strconv"
  19. "strings"
  20. "github.com/google/blueprint"
  21. "android/soong/android"
  22. )
  23. // Convert input resource file path to output file path.
  24. // values-[config]/<file>.xml -> values-[config]_<file>.arsc.flat;
  25. // For other resource file, just replace the last "/" with "_" and add .flat extension.
  26. func pathToAapt2Path(ctx android.ModuleContext, res android.Path) android.WritablePath {
  27. name := res.Base()
  28. subDir := filepath.Dir(res.String())
  29. subDir, lastDir := filepath.Split(subDir)
  30. if strings.HasPrefix(lastDir, "values") {
  31. name = strings.TrimSuffix(name, ".xml") + ".arsc"
  32. }
  33. name = lastDir + "_" + name + ".flat"
  34. return android.PathForModuleOut(ctx, "aapt2", subDir, name)
  35. }
  36. // pathsToAapt2Paths Calls pathToAapt2Path on each entry of the given Paths, i.e. []Path.
  37. func pathsToAapt2Paths(ctx android.ModuleContext, resPaths android.Paths) android.WritablePaths {
  38. outPaths := make(android.WritablePaths, len(resPaths))
  39. for i, res := range resPaths {
  40. outPaths[i] = pathToAapt2Path(ctx, res)
  41. }
  42. return outPaths
  43. }
  44. // Shard resource files for efficiency. See aapt2Compile for details.
  45. const AAPT2_SHARD_SIZE = 100
  46. var aapt2CompileRule = pctx.AndroidStaticRule("aapt2Compile",
  47. blueprint.RuleParams{
  48. Command: `${config.Aapt2Cmd} compile -o $outDir $cFlags $in`,
  49. CommandDeps: []string{"${config.Aapt2Cmd}"},
  50. },
  51. "outDir", "cFlags")
  52. // aapt2Compile compiles resources and puts the results in the requested directory.
  53. func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Paths,
  54. flags []string) android.WritablePaths {
  55. // Shard the input paths so that they can be processed in parallel. If we shard them into too
  56. // small chunks, the additional cost of spinning up aapt2 outweighs the performance gain. The
  57. // current shard size, 100, seems to be a good balance between the added cost and the gain.
  58. // The aapt2 compile actions are trivially short, but each action in ninja takes on the order of
  59. // ~10 ms to run. frameworks/base/core/res/res has >10k resource files, so compiling each one
  60. // with an individual action could take 100 CPU seconds. Sharding them reduces the overhead of
  61. // starting actions by a factor of 100, at the expense of recompiling more files when one
  62. // changes. Since the individual compiles are trivial it's a good tradeoff.
  63. shards := android.ShardPaths(paths, AAPT2_SHARD_SIZE)
  64. ret := make(android.WritablePaths, 0, len(paths))
  65. for i, shard := range shards {
  66. // This should be kept in sync with pathToAapt2Path. The aapt2 compile command takes an
  67. // output directory path, but not output file paths. So, outPaths is just where we expect
  68. // the output files will be located.
  69. outPaths := pathsToAapt2Paths(ctx, shard)
  70. ret = append(ret, outPaths...)
  71. shardDesc := ""
  72. if i != 0 {
  73. shardDesc = " " + strconv.Itoa(i+1)
  74. }
  75. ctx.Build(pctx, android.BuildParams{
  76. Rule: aapt2CompileRule,
  77. Description: "aapt2 compile " + dir.String() + shardDesc,
  78. Inputs: shard,
  79. Outputs: outPaths,
  80. Args: map[string]string{
  81. // The aapt2 compile command takes an output directory path, but not output file paths.
  82. // outPaths specified above is only used for dependency management purposes. In order for
  83. // the outPaths values to match the actual outputs from aapt2, the dir parameter value
  84. // must be a common prefix path of the paths values, and the top-level path segment used
  85. // below, "aapt2", must always be kept in sync with the one in pathToAapt2Path.
  86. // TODO(b/174505750): Make this easier and robust to use.
  87. "outDir": android.PathForModuleOut(ctx, "aapt2", dir.String()).String(),
  88. "cFlags": strings.Join(flags, " "),
  89. },
  90. })
  91. }
  92. sort.Slice(ret, func(i, j int) bool {
  93. return ret[i].String() < ret[j].String()
  94. })
  95. return ret
  96. }
  97. var aapt2CompileZipRule = pctx.AndroidStaticRule("aapt2CompileZip",
  98. blueprint.RuleParams{
  99. Command: `${config.ZipSyncCmd} -d $resZipDir $zipSyncFlags $in && ` +
  100. `${config.Aapt2Cmd} compile -o $out $cFlags --dir $resZipDir`,
  101. CommandDeps: []string{
  102. "${config.Aapt2Cmd}",
  103. "${config.ZipSyncCmd}",
  104. },
  105. }, "cFlags", "resZipDir", "zipSyncFlags")
  106. // Unzips the given compressed file and compiles the resource source files in it. The zipPrefix
  107. // parameter points to the subdirectory in the zip file where the resource files are located.
  108. func aapt2CompileZip(ctx android.ModuleContext, flata android.WritablePath, zip android.Path, zipPrefix string,
  109. flags []string) {
  110. if zipPrefix != "" {
  111. zipPrefix = "--zip-prefix " + zipPrefix
  112. }
  113. ctx.Build(pctx, android.BuildParams{
  114. Rule: aapt2CompileZipRule,
  115. Description: "aapt2 compile zip",
  116. Input: zip,
  117. Output: flata,
  118. Args: map[string]string{
  119. "cFlags": strings.Join(flags, " "),
  120. "resZipDir": android.PathForModuleOut(ctx, "aapt2", "reszip", flata.Base()).String(),
  121. "zipSyncFlags": zipPrefix,
  122. },
  123. })
  124. }
  125. var aapt2LinkRule = pctx.AndroidStaticRule("aapt2Link",
  126. blueprint.RuleParams{
  127. Command: `rm -rf $genDir && ` +
  128. `${config.Aapt2Cmd} link -o $out $flags --java $genDir --proguard $proguardOptions ` +
  129. `--output-text-symbols ${rTxt} $inFlags && ` +
  130. `${config.SoongZipCmd} -write_if_changed -jar -o $genJar -C $genDir -D $genDir &&` +
  131. `${config.ExtractJarPackagesCmd} -i $genJar -o $extraPackages --prefix '--extra-packages '`,
  132. CommandDeps: []string{
  133. "${config.Aapt2Cmd}",
  134. "${config.SoongZipCmd}",
  135. "${config.ExtractJarPackagesCmd}",
  136. },
  137. Restat: true,
  138. },
  139. "flags", "inFlags", "proguardOptions", "genDir", "genJar", "rTxt", "extraPackages")
  140. var fileListToFileRule = pctx.AndroidStaticRule("fileListToFile",
  141. blueprint.RuleParams{
  142. Command: `cp $out.rsp $out`,
  143. Rspfile: "$out.rsp",
  144. RspfileContent: "$in",
  145. })
  146. var mergeAssetsRule = pctx.AndroidStaticRule("mergeAssets",
  147. blueprint.RuleParams{
  148. Command: `${config.MergeZipsCmd} ${out} ${in}`,
  149. CommandDeps: []string{"${config.MergeZipsCmd}"},
  150. })
  151. func aapt2Link(ctx android.ModuleContext,
  152. packageRes, genJar, proguardOptions, rTxt, extraPackages android.WritablePath,
  153. flags []string, deps android.Paths,
  154. compiledRes, compiledOverlay, assetPackages android.Paths, splitPackages android.WritablePaths) {
  155. genDir := android.PathForModuleGen(ctx, "aapt2", "R")
  156. var inFlags []string
  157. if len(compiledRes) > 0 {
  158. // Create a file that contains the list of all compiled resource file paths.
  159. resFileList := android.PathForModuleOut(ctx, "aapt2", "res.list")
  160. // Write out file lists to files
  161. ctx.Build(pctx, android.BuildParams{
  162. Rule: fileListToFileRule,
  163. Description: "resource file list",
  164. Inputs: compiledRes,
  165. Output: resFileList,
  166. })
  167. deps = append(deps, compiledRes...)
  168. deps = append(deps, resFileList)
  169. // aapt2 filepath arguments that start with "@" mean file-list files.
  170. inFlags = append(inFlags, "@"+resFileList.String())
  171. }
  172. if len(compiledOverlay) > 0 {
  173. // Compiled overlay files are processed the same way as compiled resources.
  174. overlayFileList := android.PathForModuleOut(ctx, "aapt2", "overlay.list")
  175. ctx.Build(pctx, android.BuildParams{
  176. Rule: fileListToFileRule,
  177. Description: "overlay resource file list",
  178. Inputs: compiledOverlay,
  179. Output: overlayFileList,
  180. })
  181. deps = append(deps, compiledOverlay...)
  182. deps = append(deps, overlayFileList)
  183. // Compiled overlay files are passed over to aapt2 using -R option.
  184. inFlags = append(inFlags, "-R", "@"+overlayFileList.String())
  185. }
  186. // Set auxiliary outputs as implicit outputs to establish correct dependency chains.
  187. implicitOutputs := append(splitPackages, proguardOptions, genJar, rTxt, extraPackages)
  188. linkOutput := packageRes
  189. // AAPT2 ignores assets in overlays. Merge them after linking.
  190. if len(assetPackages) > 0 {
  191. linkOutput = android.PathForModuleOut(ctx, "aapt2", "package-res.apk")
  192. inputZips := append(android.Paths{linkOutput}, assetPackages...)
  193. ctx.Build(pctx, android.BuildParams{
  194. Rule: mergeAssetsRule,
  195. Inputs: inputZips,
  196. Output: packageRes,
  197. Description: "merge assets from dependencies",
  198. })
  199. }
  200. ctx.Build(pctx, android.BuildParams{
  201. Rule: aapt2LinkRule,
  202. Description: "aapt2 link",
  203. Implicits: deps,
  204. Output: linkOutput,
  205. ImplicitOutputs: implicitOutputs,
  206. // Note the absence of splitPackages. The caller is supposed to compose and provide --split flag
  207. // values via the flags parameter when it wants to split outputs.
  208. // TODO(b/174509108): Perhaps we can process it in this func while keeping the code reasonably
  209. // tidy.
  210. Args: map[string]string{
  211. "flags": strings.Join(flags, " "),
  212. "inFlags": strings.Join(inFlags, " "),
  213. "proguardOptions": proguardOptions.String(),
  214. "genDir": genDir.String(),
  215. "genJar": genJar.String(),
  216. "rTxt": rTxt.String(),
  217. "extraPackages": extraPackages.String(),
  218. },
  219. })
  220. }
  221. var aapt2ConvertRule = pctx.AndroidStaticRule("aapt2Convert",
  222. blueprint.RuleParams{
  223. Command: `${config.Aapt2Cmd} convert --output-format $format $in -o $out`,
  224. CommandDeps: []string{"${config.Aapt2Cmd}"},
  225. }, "format",
  226. )
  227. // Converts xml files and resource tables (resources.arsc) in the given jar/apk file to a proto
  228. // format. The proto definition is available at frameworks/base/tools/aapt2/Resources.proto.
  229. func aapt2Convert(ctx android.ModuleContext, out android.WritablePath, in android.Path, format string) {
  230. ctx.Build(pctx, android.BuildParams{
  231. Rule: aapt2ConvertRule,
  232. Input: in,
  233. Output: out,
  234. Description: "convert to " + format,
  235. Args: map[string]string{
  236. "format": format,
  237. },
  238. })
  239. }