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 15package java 16 17import ( 18 "path/filepath" 19 "sort" 20 "strconv" 21 "strings" 22 23 "github.com/google/blueprint" 24 25 "android/soong/android" 26) 27 28const AAPT2_SHARD_SIZE = 100 29 30// Convert input resource file path to output file path. 31// values-[config]/<file>.xml -> values-[config]_<file>.arsc.flat; 32// For other resource file, just replace the last "/" with "_" and 33// add .flat extension. 34func pathToAapt2Path(ctx android.ModuleContext, res android.Path) android.WritablePath { 35 36 name := res.Base() 37 subDir := filepath.Dir(res.String()) 38 subDir, lastDir := filepath.Split(subDir) 39 if strings.HasPrefix(lastDir, "values") { 40 name = strings.TrimSuffix(name, ".xml") + ".arsc" 41 } 42 name = lastDir + "_" + name + ".flat" 43 return android.PathForModuleOut(ctx, "aapt2", subDir, name) 44} 45 46func pathsToAapt2Paths(ctx android.ModuleContext, resPaths android.Paths) android.WritablePaths { 47 outPaths := make(android.WritablePaths, len(resPaths)) 48 49 for i, res := range resPaths { 50 outPaths[i] = pathToAapt2Path(ctx, res) 51 } 52 53 return outPaths 54} 55 56var aapt2CompileRule = pctx.AndroidStaticRule("aapt2Compile", 57 blueprint.RuleParams{ 58 Command: `${config.Aapt2Cmd} compile -o $outDir $cFlags $in`, 59 CommandDeps: []string{"${config.Aapt2Cmd}"}, 60 }, 61 "outDir", "cFlags") 62 63func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Paths, 64 flags []string) android.WritablePaths { 65 66 shards := android.ShardPaths(paths, AAPT2_SHARD_SIZE) 67 68 ret := make(android.WritablePaths, 0, len(paths)) 69 70 for i, shard := range shards { 71 outPaths := pathsToAapt2Paths(ctx, shard) 72 ret = append(ret, outPaths...) 73 74 shardDesc := "" 75 if i != 0 { 76 shardDesc = " " + strconv.Itoa(i+1) 77 } 78 79 ctx.Build(pctx, android.BuildParams{ 80 Rule: aapt2CompileRule, 81 Description: "aapt2 compile " + dir.String() + shardDesc, 82 Inputs: shard, 83 Outputs: outPaths, 84 Args: map[string]string{ 85 "outDir": android.PathForModuleOut(ctx, "aapt2", dir.String()).String(), 86 "cFlags": strings.Join(flags, " "), 87 }, 88 }) 89 } 90 91 sort.Slice(ret, func(i, j int) bool { 92 return ret[i].String() < ret[j].String() 93 }) 94 return ret 95} 96 97var 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 107func aapt2CompileZip(ctx android.ModuleContext, flata android.WritablePath, zip android.Path, zipPrefix string, 108 flags []string) { 109 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 126var aapt2LinkRule = pctx.AndroidStaticRule("aapt2Link", 127 blueprint.RuleParams{ 128 Command: `rm -rf $genDir && ` + 129 `${config.Aapt2Cmd} link -o $out $flags --java $genDir --proguard $proguardOptions ` + 130 `--output-text-symbols ${rTxt} $inFlags && ` + 131 `${config.SoongZipCmd} -write_if_changed -jar -o $genJar -C $genDir -D $genDir &&` + 132 `${config.ExtractJarPackagesCmd} -i $genJar -o $extraPackages --prefix '--extra-packages '`, 133 134 CommandDeps: []string{ 135 "${config.Aapt2Cmd}", 136 "${config.SoongZipCmd}", 137 "${config.ExtractJarPackagesCmd}", 138 }, 139 Restat: true, 140 }, 141 "flags", "inFlags", "proguardOptions", "genDir", "genJar", "rTxt", "extraPackages") 142 143var fileListToFileRule = pctx.AndroidStaticRule("fileListToFile", 144 blueprint.RuleParams{ 145 Command: `cp $out.rsp $out`, 146 Rspfile: "$out.rsp", 147 RspfileContent: "$in", 148 }) 149 150var mergeAssetsRule = pctx.AndroidStaticRule("mergeAssets", 151 blueprint.RuleParams{ 152 Command: `${config.MergeZipsCmd} ${out} ${in}`, 153 CommandDeps: []string{"${config.MergeZipsCmd}"}, 154 }) 155 156func aapt2Link(ctx android.ModuleContext, 157 packageRes, genJar, proguardOptions, rTxt, extraPackages android.WritablePath, 158 flags []string, deps android.Paths, 159 compiledRes, compiledOverlay, assetPackages android.Paths, splitPackages android.WritablePaths) { 160 161 genDir := android.PathForModuleGen(ctx, "aapt2", "R") 162 163 var inFlags []string 164 165 if len(compiledRes) > 0 { 166 resFileList := android.PathForModuleOut(ctx, "aapt2", "res.list") 167 // Write out file lists to files 168 ctx.Build(pctx, android.BuildParams{ 169 Rule: fileListToFileRule, 170 Description: "resource file list", 171 Inputs: compiledRes, 172 Output: resFileList, 173 }) 174 175 deps = append(deps, compiledRes...) 176 deps = append(deps, resFileList) 177 inFlags = append(inFlags, "@"+resFileList.String()) 178 } 179 180 if len(compiledOverlay) > 0 { 181 overlayFileList := android.PathForModuleOut(ctx, "aapt2", "overlay.list") 182 ctx.Build(pctx, android.BuildParams{ 183 Rule: fileListToFileRule, 184 Description: "overlay resource file list", 185 Inputs: compiledOverlay, 186 Output: overlayFileList, 187 }) 188 189 deps = append(deps, compiledOverlay...) 190 deps = append(deps, overlayFileList) 191 inFlags = append(inFlags, "-R", "@"+overlayFileList.String()) 192 } 193 194 implicitOutputs := append(splitPackages, proguardOptions, genJar, rTxt, extraPackages) 195 linkOutput := packageRes 196 197 // AAPT2 ignores assets in overlays. Merge them after linking. 198 if len(assetPackages) > 0 { 199 linkOutput = android.PathForModuleOut(ctx, "aapt2", "package-res.apk") 200 inputZips := append(android.Paths{linkOutput}, assetPackages...) 201 ctx.Build(pctx, android.BuildParams{ 202 Rule: mergeAssetsRule, 203 Inputs: inputZips, 204 Output: packageRes, 205 Description: "merge assets from dependencies", 206 }) 207 } 208 209 ctx.Build(pctx, android.BuildParams{ 210 Rule: aapt2LinkRule, 211 Description: "aapt2 link", 212 Implicits: deps, 213 Output: linkOutput, 214 ImplicitOutputs: implicitOutputs, 215 Args: map[string]string{ 216 "flags": strings.Join(flags, " "), 217 "inFlags": strings.Join(inFlags, " "), 218 "proguardOptions": proguardOptions.String(), 219 "genDir": genDir.String(), 220 "genJar": genJar.String(), 221 "rTxt": rTxt.String(), 222 "extraPackages": extraPackages.String(), 223 }, 224 }) 225} 226 227var aapt2ConvertRule = pctx.AndroidStaticRule("aapt2Convert", 228 blueprint.RuleParams{ 229 Command: `${config.Aapt2Cmd} convert --output-format proto $in -o $out`, 230 CommandDeps: []string{"${config.Aapt2Cmd}"}, 231 }) 232 233func aapt2Convert(ctx android.ModuleContext, out android.WritablePath, in android.Path) { 234 ctx.Build(pctx, android.BuildParams{ 235 Rule: aapt2ConvertRule, 236 Input: in, 237 Output: out, 238 Description: "convert to proto", 239 }) 240} 241