1// Copyright (C) 2019 The Android Open Source Project 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 apex 16 17import ( 18 "encoding/json" 19 "fmt" 20 "path/filepath" 21 "runtime" 22 "sort" 23 "strconv" 24 "strings" 25 26 "android/soong/android" 27 "android/soong/java" 28 29 "github.com/google/blueprint" 30 "github.com/google/blueprint/proptools" 31) 32 33var ( 34 pctx = android.NewPackageContext("android/apex") 35) 36 37func init() { 38 pctx.Import("android/soong/android") 39 pctx.Import("android/soong/cc/config") 40 pctx.Import("android/soong/java") 41 pctx.HostBinToolVariable("apexer", "apexer") 42 // ART minimal builds (using the master-art manifest) do not have the "frameworks/base" 43 // projects, and hence cannot build 'aapt2'. Use the SDK prebuilt instead. 44 hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) { 45 pctx.VariableFunc(name, func(ctx android.PackageVarContext) string { 46 if !ctx.Config().FrameworksBaseDirExists(ctx) { 47 return filepath.Join(prebuiltDir, runtime.GOOS, "bin", tool) 48 } else { 49 return ctx.Config().HostToolPath(ctx, tool).String() 50 } 51 }) 52 } 53 hostBinToolVariableWithPrebuilt("aapt2", "prebuilts/sdk/tools", "aapt2") 54 pctx.HostBinToolVariable("avbtool", "avbtool") 55 pctx.HostBinToolVariable("e2fsdroid", "e2fsdroid") 56 pctx.HostBinToolVariable("merge_zips", "merge_zips") 57 pctx.HostBinToolVariable("mke2fs", "mke2fs") 58 pctx.HostBinToolVariable("resize2fs", "resize2fs") 59 pctx.HostBinToolVariable("sefcontext_compile", "sefcontext_compile") 60 pctx.HostBinToolVariable("soong_zip", "soong_zip") 61 pctx.HostBinToolVariable("zip2zip", "zip2zip") 62 pctx.HostBinToolVariable("zipalign", "zipalign") 63 pctx.HostBinToolVariable("jsonmodify", "jsonmodify") 64 pctx.HostBinToolVariable("conv_apex_manifest", "conv_apex_manifest") 65 pctx.HostBinToolVariable("extract_apks", "extract_apks") 66 pctx.HostBinToolVariable("make_f2fs", "make_f2fs") 67 pctx.HostBinToolVariable("sload_f2fs", "sload_f2fs") 68 pctx.HostBinToolVariable("make_erofs", "make_erofs") 69 pctx.HostBinToolVariable("apex_compression_tool", "apex_compression_tool") 70 pctx.HostBinToolVariable("dexdeps", "dexdeps") 71 pctx.SourcePathVariable("genNdkUsedbyApexPath", "build/soong/scripts/gen_ndk_usedby_apex.sh") 72} 73 74var ( 75 apexManifestRule = pctx.StaticRule("apexManifestRule", blueprint.RuleParams{ 76 Command: `rm -f $out && ${jsonmodify} $in ` + 77 `-a provideNativeLibs ${provideNativeLibs} ` + 78 `-a requireNativeLibs ${requireNativeLibs} ` + 79 `${opt} ` + 80 `-o $out`, 81 CommandDeps: []string{"${jsonmodify}"}, 82 Description: "prepare ${out}", 83 }, "provideNativeLibs", "requireNativeLibs", "opt") 84 85 stripApexManifestRule = pctx.StaticRule("stripApexManifestRule", blueprint.RuleParams{ 86 Command: `rm -f $out && ${conv_apex_manifest} strip $in -o $out`, 87 CommandDeps: []string{"${conv_apex_manifest}"}, 88 Description: "strip ${in}=>${out}", 89 }) 90 91 pbApexManifestRule = pctx.StaticRule("pbApexManifestRule", blueprint.RuleParams{ 92 Command: `rm -f $out && ${conv_apex_manifest} proto $in -o $out`, 93 CommandDeps: []string{"${conv_apex_manifest}"}, 94 Description: "convert ${in}=>${out}", 95 }) 96 97 // TODO(b/113233103): make sure that file_contexts is as expected, i.e., validate 98 // against the binary policy using sefcontext_compiler -p <policy>. 99 100 // TODO(b/114327326): automate the generation of file_contexts 101 apexRule = pctx.StaticRule("apexRule", blueprint.RuleParams{ 102 Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` + 103 `(. ${out}.copy_commands) && ` + 104 `APEXER_TOOL_PATH=${tool_path} ` + 105 `${apexer} --force --manifest ${manifest} ` + 106 `--file_contexts ${file_contexts} ` + 107 `--canned_fs_config ${canned_fs_config} ` + 108 `--include_build_info ` + 109 `--payload_type image ` + 110 `--key ${key} ${opt_flags} ${image_dir} ${out} `, 111 CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}", 112 "${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}", "${sload_f2fs}", "${make_erofs}", 113 "${soong_zip}", "${zipalign}", "${aapt2}", "prebuilts/sdk/current/public/android.jar"}, 114 Rspfile: "${out}.copy_commands", 115 RspfileContent: "${copy_commands}", 116 Description: "APEX ${image_dir} => ${out}", 117 }, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", "opt_flags", "manifest", "payload_fs_type") 118 119 zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{ 120 Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` + 121 `(. ${out}.copy_commands) && ` + 122 `APEXER_TOOL_PATH=${tool_path} ` + 123 `${apexer} --force --manifest ${manifest} ` + 124 `--payload_type zip ` + 125 `${image_dir} ${out} `, 126 CommandDeps: []string{"${apexer}", "${merge_zips}", "${soong_zip}", "${zipalign}", "${aapt2}"}, 127 Rspfile: "${out}.copy_commands", 128 RspfileContent: "${copy_commands}", 129 Description: "ZipAPEX ${image_dir} => ${out}", 130 }, "tool_path", "image_dir", "copy_commands", "manifest") 131 132 apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule", 133 blueprint.RuleParams{ 134 Command: `${aapt2} convert --output-format proto $in -o $out`, 135 CommandDeps: []string{"${aapt2}"}, 136 }) 137 138 apexBundleRule = pctx.StaticRule("apexBundleRule", blueprint.RuleParams{ 139 Command: `${zip2zip} -i $in -o $out.base ` + 140 `apex_payload.img:apex/${abi}.img ` + 141 `apex_build_info.pb:apex/${abi}.build_info.pb ` + 142 `apex_manifest.json:root/apex_manifest.json ` + 143 `apex_manifest.pb:root/apex_manifest.pb ` + 144 `AndroidManifest.xml:manifest/AndroidManifest.xml ` + 145 `assets/NOTICE.html.gz:assets/NOTICE.html.gz &&` + 146 `${soong_zip} -o $out.config -C $$(dirname ${config}) -f ${config} && ` + 147 `${merge_zips} $out $out.base $out.config`, 148 CommandDeps: []string{"${zip2zip}", "${soong_zip}", "${merge_zips}"}, 149 Description: "app bundle", 150 }, "abi", "config") 151 152 emitApexContentRule = pctx.StaticRule("emitApexContentRule", blueprint.RuleParams{ 153 Command: `rm -f ${out} && touch ${out} && (. ${out}.emit_commands)`, 154 Rspfile: "${out}.emit_commands", 155 RspfileContent: "${emit_commands}", 156 Description: "Emit APEX image content", 157 }, "emit_commands") 158 159 diffApexContentRule = pctx.StaticRule("diffApexContentRule", blueprint.RuleParams{ 160 Command: `diff --unchanged-group-format='' \` + 161 `--changed-group-format='%<' \` + 162 `${image_content_file} ${allowed_files_file} || (` + 163 `echo -e "New unexpected files were added to ${apex_module_name}." ` + 164 ` "To fix the build run following command:" && ` + 165 `echo "system/apex/tools/update_allowed_list.sh ${allowed_files_file} ${image_content_file}" && ` + 166 `exit 1); touch ${out}`, 167 Description: "Diff ${image_content_file} and ${allowed_files_file}", 168 }, "image_content_file", "allowed_files_file", "apex_module_name") 169 170 generateAPIsUsedbyApexRule = pctx.StaticRule("generateAPIsUsedbyApexRule", blueprint.RuleParams{ 171 Command: "$genNdkUsedbyApexPath ${image_dir} ${readelf} ${out}", 172 CommandDeps: []string{"${genNdkUsedbyApexPath}"}, 173 Description: "Generate symbol list used by Apex", 174 }, "image_dir", "readelf") 175 176 // Don't add more rules here. Consider using android.NewRuleBuilder instead. 177) 178 179// buildManifest creates buile rules to modify the input apex_manifest.json to add information 180// gathered by the build system such as provided/required native libraries. Two output files having 181// different formats are generated. a.manifestJsonOut is JSON format for Q devices, and 182// a.manifest.PbOut is protobuf format for R+ devices. 183// TODO(jiyong): make this to return paths instead of directly storing the paths to apexBundle 184func (a *apexBundle) buildManifest(ctx android.ModuleContext, provideNativeLibs, requireNativeLibs []string) { 185 src := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json")) 186 187 // Put dependency({provide|require}NativeLibs) in apex_manifest.json 188 provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs) 189 requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs)) 190 191 // APEX name can be overridden 192 optCommands := []string{} 193 if a.properties.Apex_name != nil { 194 optCommands = append(optCommands, "-v name "+*a.properties.Apex_name) 195 } 196 197 // Collect jniLibs. Notice that a.filesInfo is already sorted 198 var jniLibs []string 199 for _, fi := range a.filesInfo { 200 if fi.isJniLib && !android.InList(fi.stem(), jniLibs) { 201 jniLibs = append(jniLibs, fi.stem()) 202 } 203 } 204 if len(jniLibs) > 0 { 205 optCommands = append(optCommands, "-a jniLibs "+strings.Join(jniLibs, " ")) 206 } 207 208 manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json") 209 ctx.Build(pctx, android.BuildParams{ 210 Rule: apexManifestRule, 211 Input: src, 212 Output: manifestJsonFullOut, 213 Args: map[string]string{ 214 "provideNativeLibs": strings.Join(provideNativeLibs, " "), 215 "requireNativeLibs": strings.Join(requireNativeLibs, " "), 216 "opt": strings.Join(optCommands, " "), 217 }, 218 }) 219 220 // b/143654022 Q apexd can't understand newly added keys in apex_manifest.json prepare 221 // stripped-down version so that APEX modules built from R+ can be installed to Q 222 minSdkVersion := a.minSdkVersion(ctx) 223 if minSdkVersion.EqualTo(android.SdkVersion_Android10) { 224 a.manifestJsonOut = android.PathForModuleOut(ctx, "apex_manifest.json") 225 ctx.Build(pctx, android.BuildParams{ 226 Rule: stripApexManifestRule, 227 Input: manifestJsonFullOut, 228 Output: a.manifestJsonOut, 229 }) 230 } 231 232 // From R+, protobuf binary format (.pb) is the standard format for apex_manifest 233 a.manifestPbOut = android.PathForModuleOut(ctx, "apex_manifest.pb") 234 ctx.Build(pctx, android.BuildParams{ 235 Rule: pbApexManifestRule, 236 Input: manifestJsonFullOut, 237 Output: a.manifestPbOut, 238 }) 239} 240 241// buildFileContexts create build rules to append an entry for apex_manifest.pb to the file_contexts 242// file for this APEX which is either from /systme/sepolicy/apex/<apexname>-file_contexts or from 243// the file_contexts property of this APEX. This is to make sure that the manifest file is correctly 244// labeled as system_file. 245func (a *apexBundle) buildFileContexts(ctx android.ModuleContext) android.OutputPath { 246 var fileContexts android.Path 247 var fileContextsDir string 248 if a.properties.File_contexts == nil { 249 fileContexts = android.PathForSource(ctx, "system/sepolicy/apex", ctx.ModuleName()+"-file_contexts") 250 } else { 251 if m, t := android.SrcIsModuleWithTag(*a.properties.File_contexts); m != "" { 252 otherModule := android.GetModuleFromPathDep(ctx, m, t) 253 fileContextsDir = ctx.OtherModuleDir(otherModule) 254 } 255 fileContexts = android.PathForModuleSrc(ctx, *a.properties.File_contexts) 256 } 257 if fileContextsDir == "" { 258 fileContextsDir = filepath.Dir(fileContexts.String()) 259 } 260 fileContextsDir += string(filepath.Separator) 261 262 if a.Platform() { 263 if !strings.HasPrefix(fileContextsDir, "system/sepolicy/") { 264 ctx.PropertyErrorf("file_contexts", "should be under system/sepolicy, but found in %q", fileContextsDir) 265 } 266 } 267 if !android.ExistentPathForSource(ctx, fileContexts.String()).Valid() { 268 ctx.PropertyErrorf("file_contexts", "cannot find file_contexts file: %q", fileContexts.String()) 269 } 270 271 output := android.PathForModuleOut(ctx, "file_contexts") 272 rule := android.NewRuleBuilder(pctx, ctx) 273 274 switch a.properties.ApexType { 275 case imageApex: 276 // remove old file 277 rule.Command().Text("rm").FlagWithOutput("-f ", output) 278 // copy file_contexts 279 rule.Command().Text("cat").Input(fileContexts).Text(">>").Output(output) 280 // new line 281 rule.Command().Text("echo").Text(">>").Output(output) 282 // force-label /apex_manifest.pb and / as system_file so that apexd can read them 283 rule.Command().Text("echo").Flag("/apex_manifest\\\\.pb u:object_r:system_file:s0").Text(">>").Output(output) 284 rule.Command().Text("echo").Flag("/ u:object_r:system_file:s0").Text(">>").Output(output) 285 case flattenedApex: 286 // For flattened apexes, install path should be prepended. 287 // File_contexts file should be emiited to make via LOCAL_FILE_CONTEXTS 288 // so that it can be merged into file_contexts.bin 289 apexPath := android.InstallPathToOnDevicePath(ctx, a.installDir.Join(ctx, a.Name())) 290 apexPath = strings.ReplaceAll(apexPath, ".", `\\.`) 291 // remove old file 292 rule.Command().Text("rm").FlagWithOutput("-f ", output) 293 // copy file_contexts 294 rule.Command().Text("awk").Text(`'/object_r/{printf("` + apexPath + `%s\n", $0)}'`).Input(fileContexts).Text(">").Output(output) 295 // new line 296 rule.Command().Text("echo").Text(">>").Output(output) 297 // force-label /apex_manifest.pb and / as system_file so that apexd can read them 298 rule.Command().Text("echo").Flag(apexPath + `/apex_manifest\\.pb u:object_r:system_file:s0`).Text(">>").Output(output) 299 rule.Command().Text("echo").Flag(apexPath + "/ u:object_r:system_file:s0").Text(">>").Output(output) 300 default: 301 panic(fmt.Errorf("unsupported type %v", a.properties.ApexType)) 302 } 303 304 rule.Build("file_contexts."+a.Name(), "Generate file_contexts") 305 return output.OutputPath 306} 307 308// buildInstalledFilesFile creates a build rule for the installed-files.txt file where the list of 309// files included in this APEX is shown. The text file is dist'ed so that people can see what's 310// included in the APEX without actually downloading and extracting it. 311func (a *apexBundle) buildInstalledFilesFile(ctx android.ModuleContext, builtApex android.Path, imageDir android.Path) android.OutputPath { 312 output := android.PathForModuleOut(ctx, "installed-files.txt") 313 rule := android.NewRuleBuilder(pctx, ctx) 314 rule.Command(). 315 Implicit(builtApex). 316 Text("(cd " + imageDir.String() + " ; "). 317 Text("find . \\( -type f -o -type l \\) -printf \"%s %p\\n\") "). 318 Text(" | sort -nr > "). 319 Output(output) 320 rule.Build("installed-files."+a.Name(), "Installed files") 321 return output.OutputPath 322} 323 324// buildBundleConfig creates a build rule for the bundle config file that will control the bundle 325// creation process. 326func (a *apexBundle) buildBundleConfig(ctx android.ModuleContext) android.OutputPath { 327 output := android.PathForModuleOut(ctx, "bundle_config.json") 328 329 type ApkConfig struct { 330 Package_name string `json:"package_name"` 331 Apk_path string `json:"path"` 332 } 333 config := struct { 334 Compression struct { 335 Uncompressed_glob []string `json:"uncompressed_glob"` 336 } `json:"compression"` 337 Apex_config struct { 338 Apex_embedded_apk_config []ApkConfig `json:"apex_embedded_apk_config,omitempty"` 339 } `json:"apex_config,omitempty"` 340 }{} 341 342 config.Compression.Uncompressed_glob = []string{ 343 "apex_payload.img", 344 "apex_manifest.*", 345 } 346 347 // Collect the manifest names and paths of android apps if their manifest names are 348 // overridden. 349 for _, fi := range a.filesInfo { 350 if fi.class != app && fi.class != appSet { 351 continue 352 } 353 packageName := fi.overriddenPackageName 354 if packageName != "" { 355 config.Apex_config.Apex_embedded_apk_config = append( 356 config.Apex_config.Apex_embedded_apk_config, 357 ApkConfig{ 358 Package_name: packageName, 359 Apk_path: fi.path(), 360 }) 361 } 362 } 363 364 j, err := json.Marshal(config) 365 if err != nil { 366 panic(fmt.Errorf("error while marshalling to %q: %#v", output, err)) 367 } 368 369 android.WriteFileRule(ctx, output, string(j)) 370 371 return output.OutputPath 372} 373 374func markManifestTestOnly(ctx android.ModuleContext, androidManifestFile android.Path) android.Path { 375 return java.ManifestFixer(ctx, androidManifestFile, java.ManifestFixerParams{ 376 TestOnly: true, 377 }) 378} 379 380// buildUnflattendApex creates build rules to build an APEX using apexer. 381func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { 382 apexType := a.properties.ApexType 383 suffix := apexType.suffix() 384 apexName := proptools.StringDefault(a.properties.Apex_name, a.BaseModuleName()) 385 386 //////////////////////////////////////////////////////////////////////////////////////////// 387 // Step 1: copy built files to appropriate directories under the image directory 388 389 imageDir := android.PathForModuleOut(ctx, "image"+suffix) 390 391 installSymbolFiles := (!ctx.Config().KatiEnabled() || a.ExportedToMake()) && a.installable() 392 393 // b/140136207. When there are overriding APEXes for a VNDK APEX, the symbols file for the overridden 394 // APEX and the overriding APEX will have the same installation paths at /apex/com.android.vndk.v<ver> 395 // as their apexName will be the same. To avoid the path conflicts, skip installing the symbol files 396 // for the overriding VNDK APEXes. 397 if a.vndkApex && len(a.overridableProperties.Overrides) > 0 { 398 installSymbolFiles = false 399 } 400 401 // Avoid creating duplicate build rules for multi-installed APEXes. 402 if proptools.BoolDefault(a.properties.Multi_install_skip_symbol_files, false) { 403 installSymbolFiles = false 404 405 } 406 // set of dependency module:location mappings 407 installMapSet := make(map[string]bool) 408 409 // TODO(jiyong): use the RuleBuilder 410 var copyCommands []string 411 var implicitInputs []android.Path 412 pathWhenActivated := android.PathForModuleInPartitionInstall(ctx, "apex", apexName) 413 for _, fi := range a.filesInfo { 414 destPath := imageDir.Join(ctx, fi.path()).String() 415 // Prepare the destination path 416 destPathDir := filepath.Dir(destPath) 417 if fi.class == appSet { 418 copyCommands = append(copyCommands, "rm -rf "+destPathDir) 419 } 420 copyCommands = append(copyCommands, "mkdir -p "+destPathDir) 421 422 installMapPath := fi.builtFile 423 424 // Copy the built file to the directory. But if the symlink optimization is turned 425 // on, place a symlink to the corresponding file in /system partition instead. 426 if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() { 427 // TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here 428 pathOnDevice := filepath.Join("/system", fi.path()) 429 copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath) 430 } else { 431 var installedPath android.InstallPath 432 if fi.class == appSet { 433 copyCommands = append(copyCommands, 434 fmt.Sprintf("unzip -qDD -d %s %s", destPathDir, 435 fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs().String())) 436 if installSymbolFiles { 437 installedPath = ctx.InstallFileWithExtraFilesZip(pathWhenActivated.Join(ctx, fi.installDir), 438 fi.stem(), fi.builtFile, fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs()) 439 } 440 } else { 441 copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath) 442 if installSymbolFiles { 443 installedPath = ctx.InstallFile(pathWhenActivated.Join(ctx, fi.installDir), fi.stem(), fi.builtFile) 444 } 445 } 446 implicitInputs = append(implicitInputs, fi.builtFile) 447 if installSymbolFiles { 448 implicitInputs = append(implicitInputs, installedPath) 449 } 450 451 // Create additional symlinks pointing the file inside the APEX (if any). Note that 452 // this is independent from the symlink optimization. 453 for _, symlinkPath := range fi.symlinkPaths() { 454 symlinkDest := imageDir.Join(ctx, symlinkPath).String() 455 copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest) 456 if installSymbolFiles { 457 installedSymlink := ctx.InstallSymlink(pathWhenActivated.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath) 458 implicitInputs = append(implicitInputs, installedSymlink) 459 } 460 } 461 462 installMapPath = installedPath 463 } 464 465 // Copy the test files (if any) 466 for _, d := range fi.dataPaths { 467 // TODO(eakammer): This is now the third repetition of ~this logic for test paths, refactoring should be possible 468 relPath := d.SrcPath.Rel() 469 dataPath := d.SrcPath.String() 470 if !strings.HasSuffix(dataPath, relPath) { 471 panic(fmt.Errorf("path %q does not end with %q", dataPath, relPath)) 472 } 473 474 dataDest := imageDir.Join(ctx, fi.apexRelativePath(relPath), d.RelativeInstallPath).String() 475 476 copyCommands = append(copyCommands, "cp -f "+d.SrcPath.String()+" "+dataDest) 477 implicitInputs = append(implicitInputs, d.SrcPath) 478 } 479 480 installMapSet[installMapPath.String()+":"+fi.installDir+"/"+fi.builtFile.Base()] = true 481 } 482 implicitInputs = append(implicitInputs, a.manifestPbOut) 483 if installSymbolFiles { 484 installedManifest := ctx.InstallFile(pathWhenActivated, "apex_manifest.pb", a.manifestPbOut) 485 installedKey := ctx.InstallFile(pathWhenActivated, "apex_pubkey", a.publicKeyFile) 486 implicitInputs = append(implicitInputs, installedManifest, installedKey) 487 } 488 489 if len(installMapSet) > 0 { 490 var installs []string 491 installs = append(installs, android.SortedStringKeys(installMapSet)...) 492 a.SetLicenseInstallMap(installs) 493 } 494 495 //////////////////////////////////////////////////////////////////////////////////////////// 496 // Step 1.a: Write the list of files in this APEX to a txt file and compare it against 497 // the allowed list given via the allowed_files property. Build fails when the two lists 498 // differ. 499 // 500 // TODO(jiyong): consider removing this. Nobody other than com.android.apex.cts.shim.* seems 501 // to be using this at this moment. Furthermore, this looks very similar to what 502 // buildInstalledFilesFile does. At least, move this to somewhere else so that this doesn't 503 // hurt readability. 504 // TODO(jiyong): use RuleBuilder 505 if a.overridableProperties.Allowed_files != nil { 506 // Build content.txt 507 var emitCommands []string 508 imageContentFile := android.PathForModuleOut(ctx, "content.txt") 509 emitCommands = append(emitCommands, "echo ./apex_manifest.pb >> "+imageContentFile.String()) 510 minSdkVersion := a.minSdkVersion(ctx) 511 if minSdkVersion.EqualTo(android.SdkVersion_Android10) { 512 emitCommands = append(emitCommands, "echo ./apex_manifest.json >> "+imageContentFile.String()) 513 } 514 for _, fi := range a.filesInfo { 515 emitCommands = append(emitCommands, "echo './"+fi.path()+"' >> "+imageContentFile.String()) 516 } 517 emitCommands = append(emitCommands, "sort -o "+imageContentFile.String()+" "+imageContentFile.String()) 518 ctx.Build(pctx, android.BuildParams{ 519 Rule: emitApexContentRule, 520 Implicits: implicitInputs, 521 Output: imageContentFile, 522 Description: "emit apex image content", 523 Args: map[string]string{ 524 "emit_commands": strings.Join(emitCommands, " && "), 525 }, 526 }) 527 implicitInputs = append(implicitInputs, imageContentFile) 528 529 // Compare content.txt against allowed_files. 530 allowedFilesFile := android.PathForModuleSrc(ctx, proptools.String(a.overridableProperties.Allowed_files)) 531 phonyOutput := android.PathForModuleOut(ctx, a.Name()+"-diff-phony-output") 532 ctx.Build(pctx, android.BuildParams{ 533 Rule: diffApexContentRule, 534 Implicits: implicitInputs, 535 Output: phonyOutput, 536 Description: "diff apex image content", 537 Args: map[string]string{ 538 "allowed_files_file": allowedFilesFile.String(), 539 "image_content_file": imageContentFile.String(), 540 "apex_module_name": a.Name(), 541 }, 542 }) 543 implicitInputs = append(implicitInputs, phonyOutput) 544 } 545 546 unsignedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix+".unsigned") 547 outHostBinDir := ctx.Config().HostToolPath(ctx, "").String() 548 prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin") 549 550 // Figure out if we need to compress the apex. 551 compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.overridableProperties.Compressible, false) && !a.testApex && !ctx.Config().UnbundledBuildApps() 552 if apexType == imageApex { 553 554 //////////////////////////////////////////////////////////////////////////////////// 555 // Step 2: create canned_fs_config which encodes filemode,uid,gid of each files 556 // in this APEX. The file will be used by apexer in later steps. 557 cannedFsConfig := a.buildCannedFsConfig(ctx) 558 implicitInputs = append(implicitInputs, cannedFsConfig) 559 560 //////////////////////////////////////////////////////////////////////////////////// 561 // Step 3: Prepare option flags for apexer and invoke it to create an unsigned APEX. 562 // TODO(jiyong): use the RuleBuilder 563 optFlags := []string{} 564 565 fileContexts := a.buildFileContexts(ctx) 566 implicitInputs = append(implicitInputs, fileContexts) 567 568 implicitInputs = append(implicitInputs, a.privateKeyFile, a.publicKeyFile) 569 optFlags = append(optFlags, "--pubkey "+a.publicKeyFile.String()) 570 571 manifestPackageName := a.getOverrideManifestPackageName(ctx) 572 if manifestPackageName != "" { 573 optFlags = append(optFlags, "--override_apk_package_name "+manifestPackageName) 574 } 575 576 if a.properties.AndroidManifest != nil { 577 androidManifestFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.AndroidManifest)) 578 579 if a.testApex { 580 androidManifestFile = markManifestTestOnly(ctx, androidManifestFile) 581 } 582 583 implicitInputs = append(implicitInputs, androidManifestFile) 584 optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String()) 585 } else if a.testApex { 586 optFlags = append(optFlags, "--test_only") 587 } 588 589 // Determine target/min sdk version from the context 590 // TODO(jiyong): make this as a function 591 moduleMinSdkVersion := a.minSdkVersion(ctx) 592 minSdkVersion := moduleMinSdkVersion.String() 593 594 // bundletool doesn't understand what "current" is. We need to transform it to 595 // codename 596 if moduleMinSdkVersion.IsCurrent() || moduleMinSdkVersion.IsNone() { 597 minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String() 598 599 if java.UseApiFingerprint(ctx) { 600 minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String()) 601 implicitInputs = append(implicitInputs, java.ApiFingerprintPath(ctx)) 602 } 603 } 604 // apex module doesn't have a concept of target_sdk_version, hence for the time 605 // being targetSdkVersion == default targetSdkVersion of the branch. 606 targetSdkVersion := strconv.Itoa(ctx.Config().DefaultAppTargetSdk(ctx).FinalOrFutureInt()) 607 608 if java.UseApiFingerprint(ctx) { 609 targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String()) 610 implicitInputs = append(implicitInputs, java.ApiFingerprintPath(ctx)) 611 } 612 optFlags = append(optFlags, "--target_sdk_version "+targetSdkVersion) 613 optFlags = append(optFlags, "--min_sdk_version "+minSdkVersion) 614 615 if a.overridableProperties.Logging_parent != "" { 616 optFlags = append(optFlags, "--logging_parent ", a.overridableProperties.Logging_parent) 617 } 618 619 // Create a NOTICE file, and embed it as an asset file in the APEX. 620 a.htmlGzNotice = android.PathForModuleOut(ctx, "NOTICE.html.gz") 621 android.BuildNoticeHtmlOutputFromLicenseMetadata( 622 ctx, a.htmlGzNotice, "", "", 623 []string{ 624 android.PathForModuleInstall(ctx).String() + "/", 625 android.PathForModuleInPartitionInstall(ctx, "apex").String() + "/", 626 }) 627 noticeAssetPath := android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz") 628 builder := android.NewRuleBuilder(pctx, ctx) 629 builder.Command().Text("cp"). 630 Input(a.htmlGzNotice). 631 Output(noticeAssetPath) 632 builder.Build("notice_dir", "Building notice dir") 633 implicitInputs = append(implicitInputs, noticeAssetPath) 634 optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeAssetPath.String())) 635 636 if (moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) && !a.shouldGenerateHashtree()) && !compressionEnabled { 637 // Apexes which are supposed to be installed in builtin dirs(/system, etc) 638 // don't need hashtree for activation. Therefore, by removing hashtree from 639 // apex bundle (filesystem image in it, to be specific), we can save storage. 640 optFlags = append(optFlags, "--no_hashtree") 641 } 642 643 if a.testOnlyShouldSkipPayloadSign() { 644 optFlags = append(optFlags, "--unsigned_payload") 645 } 646 647 if a.properties.Apex_name != nil { 648 // If apex_name is set, apexer can skip checking if key name matches with 649 // apex name. Note that apex_manifest is also mended. 650 optFlags = append(optFlags, "--do_not_check_keyname") 651 } 652 653 if moduleMinSdkVersion == android.SdkVersion_Android10 { 654 implicitInputs = append(implicitInputs, a.manifestJsonOut) 655 optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String()) 656 } 657 658 optFlags = append(optFlags, "--payload_fs_type "+a.payloadFsType.string()) 659 660 ctx.Build(pctx, android.BuildParams{ 661 Rule: apexRule, 662 Implicits: implicitInputs, 663 Output: unsignedOutputFile, 664 Description: "apex (" + apexType.name() + ")", 665 Args: map[string]string{ 666 "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, 667 "image_dir": imageDir.String(), 668 "copy_commands": strings.Join(copyCommands, " && "), 669 "manifest": a.manifestPbOut.String(), 670 "file_contexts": fileContexts.String(), 671 "canned_fs_config": cannedFsConfig.String(), 672 "key": a.privateKeyFile.String(), 673 "opt_flags": strings.Join(optFlags, " "), 674 }, 675 }) 676 677 // TODO(jiyong): make the two rules below as separate functions 678 apexProtoFile := android.PathForModuleOut(ctx, a.Name()+".pb"+suffix) 679 bundleModuleFile := android.PathForModuleOut(ctx, a.Name()+suffix+"-base.zip") 680 a.bundleModuleFile = bundleModuleFile 681 682 ctx.Build(pctx, android.BuildParams{ 683 Rule: apexProtoConvertRule, 684 Input: unsignedOutputFile, 685 Output: apexProtoFile, 686 Description: "apex proto convert", 687 }) 688 689 implicitInputs = append(implicitInputs, unsignedOutputFile) 690 691 // Run coverage analysis 692 apisUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_using.txt") 693 ctx.Build(pctx, android.BuildParams{ 694 Rule: generateAPIsUsedbyApexRule, 695 Implicits: implicitInputs, 696 Description: "coverage", 697 Output: apisUsedbyOutputFile, 698 Args: map[string]string{ 699 "image_dir": imageDir.String(), 700 "readelf": "${config.ClangBin}/llvm-readelf", 701 }, 702 }) 703 a.nativeApisUsedByModuleFile = apisUsedbyOutputFile 704 705 var nativeLibNames []string 706 for _, f := range a.filesInfo { 707 if f.class == nativeSharedLib { 708 nativeLibNames = append(nativeLibNames, f.stem()) 709 } 710 } 711 apisBackedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_backing.txt") 712 rule := android.NewRuleBuilder(pctx, ctx) 713 rule.Command(). 714 Tool(android.PathForSource(ctx, "build/soong/scripts/gen_ndk_backedby_apex.sh")). 715 Output(apisBackedbyOutputFile). 716 Flags(nativeLibNames) 717 rule.Build("ndk_backedby_list", "Generate API libraries backed by Apex") 718 a.nativeApisBackedByModuleFile = apisBackedbyOutputFile 719 720 var javaLibOrApkPath []android.Path 721 for _, f := range a.filesInfo { 722 if f.class == javaSharedLib || f.class == app { 723 javaLibOrApkPath = append(javaLibOrApkPath, f.builtFile) 724 } 725 } 726 javaApiUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_using.xml") 727 javaUsedByRule := android.NewRuleBuilder(pctx, ctx) 728 javaUsedByRule.Command(). 729 Tool(android.PathForSource(ctx, "build/soong/scripts/gen_java_usedby_apex.sh")). 730 BuiltTool("dexdeps"). 731 Output(javaApiUsedbyOutputFile). 732 Inputs(javaLibOrApkPath) 733 javaUsedByRule.Build("java_usedby_list", "Generate Java APIs used by Apex") 734 a.javaApisUsedByModuleFile = javaApiUsedbyOutputFile 735 736 bundleConfig := a.buildBundleConfig(ctx) 737 738 var abis []string 739 for _, target := range ctx.MultiTargets() { 740 if len(target.Arch.Abi) > 0 { 741 abis = append(abis, target.Arch.Abi[0]) 742 } 743 } 744 745 abis = android.FirstUniqueStrings(abis) 746 747 ctx.Build(pctx, android.BuildParams{ 748 Rule: apexBundleRule, 749 Input: apexProtoFile, 750 Implicit: bundleConfig, 751 Output: a.bundleModuleFile, 752 Description: "apex bundle module", 753 Args: map[string]string{ 754 "abi": strings.Join(abis, "."), 755 "config": bundleConfig.String(), 756 }, 757 }) 758 } else { // zipApex 759 ctx.Build(pctx, android.BuildParams{ 760 Rule: zipApexRule, 761 Implicits: implicitInputs, 762 Output: unsignedOutputFile, 763 Description: "apex (" + apexType.name() + ")", 764 Args: map[string]string{ 765 "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, 766 "image_dir": imageDir.String(), 767 "copy_commands": strings.Join(copyCommands, " && "), 768 "manifest": a.manifestPbOut.String(), 769 }, 770 }) 771 } 772 773 //////////////////////////////////////////////////////////////////////////////////// 774 // Step 4: Sign the APEX using signapk 775 signedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix) 776 777 pem, key := a.getCertificateAndPrivateKey(ctx) 778 rule := java.Signapk 779 args := map[string]string{ 780 "certificates": pem.String() + " " + key.String(), 781 "flags": "-a 4096 --align-file-size", //alignment 782 } 783 implicits := android.Paths{pem, key} 784 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_SIGNAPK") { 785 rule = java.SignapkRE 786 args["implicits"] = strings.Join(implicits.Strings(), ",") 787 args["outCommaList"] = signedOutputFile.String() 788 } 789 ctx.Build(pctx, android.BuildParams{ 790 Rule: rule, 791 Description: "signapk", 792 Output: signedOutputFile, 793 Input: unsignedOutputFile, 794 Implicits: implicits, 795 Args: args, 796 }) 797 if suffix == imageApexSuffix { 798 a.outputApexFile = signedOutputFile 799 } 800 a.outputFile = signedOutputFile 801 802 if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && a.testOnlyShouldForceCompression() { 803 ctx.PropertyErrorf("test_only_force_compression", "not available") 804 return 805 } 806 807 if apexType == imageApex && (compressionEnabled || a.testOnlyShouldForceCompression()) { 808 a.isCompressed = true 809 unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+imageCapexSuffix+".unsigned") 810 811 compressRule := android.NewRuleBuilder(pctx, ctx) 812 compressRule.Command(). 813 Text("rm"). 814 FlagWithOutput("-f ", unsignedCompressedOutputFile) 815 compressRule.Command(). 816 BuiltTool("apex_compression_tool"). 817 Flag("compress"). 818 FlagWithArg("--apex_compression_tool ", outHostBinDir+":"+prebuiltSdkToolsBinDir). 819 FlagWithInput("--input ", signedOutputFile). 820 FlagWithOutput("--output ", unsignedCompressedOutputFile) 821 compressRule.Build("compressRule", "Generate unsigned compressed APEX file") 822 823 signedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+imageCapexSuffix) 824 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_SIGNAPK") { 825 args["outCommaList"] = signedCompressedOutputFile.String() 826 } 827 ctx.Build(pctx, android.BuildParams{ 828 Rule: rule, 829 Description: "sign compressedApex", 830 Output: signedCompressedOutputFile, 831 Input: unsignedCompressedOutputFile, 832 Implicits: implicits, 833 Args: args, 834 }) 835 a.outputFile = signedCompressedOutputFile 836 } 837 838 installSuffix := suffix 839 if a.isCompressed { 840 installSuffix = imageCapexSuffix 841 } 842 843 if !a.installable() { 844 a.SkipInstall() 845 } 846 847 // Install to $OUT/soong/{target,host}/.../apex. 848 a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile, 849 a.compatSymlinks.Paths()...) 850 851 // installed-files.txt is dist'ed 852 a.installedFilesFile = a.buildInstalledFilesFile(ctx, a.outputFile, imageDir) 853} 854 855// buildFlattenedApex creates rules for a flattened APEX. Flattened APEX actually doesn't have a 856// single output file. It is a phony target for all the files under /system/apex/<name> directory. 857// This function creates the installation rules for the files. 858func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) { 859 bundleName := a.Name() 860 installedSymlinks := append(android.InstallPaths(nil), a.compatSymlinks...) 861 if a.installable() { 862 for _, fi := range a.filesInfo { 863 dir := filepath.Join("apex", bundleName, fi.installDir) 864 installDir := android.PathForModuleInstall(ctx, dir) 865 if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() { 866 // TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here 867 pathOnDevice := filepath.Join("/system", fi.path()) 868 installedSymlinks = append(installedSymlinks, 869 ctx.InstallAbsoluteSymlink(installDir, fi.stem(), pathOnDevice)) 870 } else { 871 target := ctx.InstallFile(installDir, fi.stem(), fi.builtFile) 872 for _, sym := range fi.symlinks { 873 installedSymlinks = append(installedSymlinks, 874 ctx.InstallSymlink(installDir, sym, target)) 875 } 876 } 877 } 878 879 // Create install rules for the files added in GenerateAndroidBuildActions after 880 // buildFlattenedApex is called. Add the links to system libs (if any) as dependencies 881 // of the apex_manifest.pb file since it is always present. 882 dir := filepath.Join("apex", bundleName) 883 installDir := android.PathForModuleInstall(ctx, dir) 884 ctx.InstallFile(installDir, "apex_manifest.pb", a.manifestPbOut, installedSymlinks.Paths()...) 885 ctx.InstallFile(installDir, "apex_pubkey", a.publicKeyFile) 886 } 887 888 a.fileContexts = a.buildFileContexts(ctx) 889 890 a.outputFile = android.PathForModuleInstall(ctx, "apex", bundleName) 891} 892 893// getCertificateAndPrivateKey retrieves the cert and the private key that will be used to sign 894// the zip container of this APEX. See the description of the 'certificate' property for how 895// the cert and the private key are found. 896func (a *apexBundle) getCertificateAndPrivateKey(ctx android.PathContext) (pem, key android.Path) { 897 if a.containerCertificateFile != nil { 898 return a.containerCertificateFile, a.containerPrivateKeyFile 899 } 900 901 cert := String(a.overridableProperties.Certificate) 902 if cert == "" { 903 return ctx.Config().DefaultAppCertificate(ctx) 904 } 905 906 defaultDir := ctx.Config().DefaultAppCertificateDir(ctx) 907 pem = defaultDir.Join(ctx, cert+".x509.pem") 908 key = defaultDir.Join(ctx, cert+".pk8") 909 return pem, key 910} 911 912func (a *apexBundle) getOverrideManifestPackageName(ctx android.ModuleContext) string { 913 // For VNDK APEXes, check "com.android.vndk" in PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES 914 // to see if it should be overridden because their <apex name> is dynamically generated 915 // according to its VNDK version. 916 if a.vndkApex { 917 overrideName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(vndkApexName) 918 if overridden { 919 return strings.Replace(*a.properties.Apex_name, vndkApexName, overrideName, 1) 920 } 921 return "" 922 } 923 if a.overridableProperties.Package_name != "" { 924 return a.overridableProperties.Package_name 925 } 926 manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName()) 927 if overridden { 928 return manifestPackageName 929 } 930 return "" 931} 932 933func (a *apexBundle) buildApexDependencyInfo(ctx android.ModuleContext) { 934 if !a.primaryApexType { 935 return 936 } 937 938 if a.properties.IsCoverageVariant { 939 // Otherwise, we will have duplicated rules for coverage and 940 // non-coverage variants of the same APEX 941 return 942 } 943 944 if ctx.Host() { 945 // No need to generate dependency info for host variant 946 return 947 } 948 949 depInfos := android.DepNameToDepInfoMap{} 950 a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool { 951 if from.Name() == to.Name() { 952 // This can happen for cc.reuseObjTag. We are not interested in tracking this. 953 // As soon as the dependency graph crosses the APEX boundary, don't go further. 954 return !externalDep 955 } 956 957 // Skip dependencies that are only available to APEXes; they are developed with updatability 958 // in mind and don't need manual approval. 959 if to.(android.ApexModule).NotAvailableForPlatform() { 960 return !externalDep 961 } 962 963 depTag := ctx.OtherModuleDependencyTag(to) 964 // Check to see if dependency been marked to skip the dependency check 965 if skipDepCheck, ok := depTag.(android.SkipApexAllowedDependenciesCheck); ok && skipDepCheck.SkipApexAllowedDependenciesCheck() { 966 return !externalDep 967 } 968 969 if info, exists := depInfos[to.Name()]; exists { 970 if !android.InList(from.Name(), info.From) { 971 info.From = append(info.From, from.Name()) 972 } 973 info.IsExternal = info.IsExternal && externalDep 974 depInfos[to.Name()] = info 975 } else { 976 toMinSdkVersion := "(no version)" 977 if m, ok := to.(interface { 978 MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec 979 }); ok { 980 if v := m.MinSdkVersion(ctx); !v.ApiLevel.IsNone() { 981 toMinSdkVersion = v.ApiLevel.String() 982 } 983 } else if m, ok := to.(interface{ MinSdkVersion() string }); ok { 984 // TODO(b/175678607) eliminate the use of MinSdkVersion returning 985 // string 986 if v := m.MinSdkVersion(); v != "" { 987 toMinSdkVersion = v 988 } 989 } 990 depInfos[to.Name()] = android.ApexModuleDepInfo{ 991 To: to.Name(), 992 From: []string{from.Name()}, 993 IsExternal: externalDep, 994 MinSdkVersion: toMinSdkVersion, 995 } 996 } 997 998 // As soon as the dependency graph crosses the APEX boundary, don't go further. 999 return !externalDep 1000 }) 1001 1002 a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(ctx).Raw, depInfos) 1003 1004 ctx.Build(pctx, android.BuildParams{ 1005 Rule: android.Phony, 1006 Output: android.PathForPhony(ctx, a.Name()+"-deps-info"), 1007 Inputs: []android.Path{ 1008 a.ApexBundleDepsInfo.FullListPath(), 1009 a.ApexBundleDepsInfo.FlatListPath(), 1010 }, 1011 }) 1012} 1013 1014func (a *apexBundle) buildLintReports(ctx android.ModuleContext) { 1015 depSetsBuilder := java.NewLintDepSetBuilder() 1016 for _, fi := range a.filesInfo { 1017 depSetsBuilder.Transitive(fi.lintDepSets) 1018 } 1019 1020 a.lintReports = java.BuildModuleLintReportZips(ctx, depSetsBuilder.Build()) 1021} 1022 1023func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext) android.OutputPath { 1024 var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"} 1025 var executablePaths []string // this also includes dirs 1026 var appSetDirs []string 1027 appSetFiles := make(map[string]android.Path) 1028 for _, f := range a.filesInfo { 1029 pathInApex := f.path() 1030 if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") { 1031 executablePaths = append(executablePaths, pathInApex) 1032 for _, d := range f.dataPaths { 1033 readOnlyPaths = append(readOnlyPaths, filepath.Join(f.installDir, d.RelativeInstallPath, d.SrcPath.Rel())) 1034 } 1035 for _, s := range f.symlinks { 1036 executablePaths = append(executablePaths, filepath.Join(f.installDir, s)) 1037 } 1038 } else if f.class == appSet { 1039 appSetDirs = append(appSetDirs, f.installDir) 1040 appSetFiles[f.installDir] = f.builtFile 1041 } else { 1042 readOnlyPaths = append(readOnlyPaths, pathInApex) 1043 } 1044 dir := f.installDir 1045 for !android.InList(dir, executablePaths) && dir != "" { 1046 executablePaths = append(executablePaths, dir) 1047 dir, _ = filepath.Split(dir) // move up to the parent 1048 if len(dir) > 0 { 1049 // remove trailing slash 1050 dir = dir[:len(dir)-1] 1051 } 1052 } 1053 } 1054 sort.Strings(readOnlyPaths) 1055 sort.Strings(executablePaths) 1056 sort.Strings(appSetDirs) 1057 1058 cannedFsConfig := android.PathForModuleOut(ctx, "canned_fs_config") 1059 builder := android.NewRuleBuilder(pctx, ctx) 1060 cmd := builder.Command() 1061 cmd.Text("(") 1062 cmd.Text("echo '/ 1000 1000 0755';") 1063 for _, p := range readOnlyPaths { 1064 cmd.Textf("echo '/%s 1000 1000 0644';", p) 1065 } 1066 for _, p := range executablePaths { 1067 cmd.Textf("echo '/%s 0 2000 0755';", p) 1068 } 1069 for _, dir := range appSetDirs { 1070 cmd.Textf("echo '/%s 0 2000 0755';", dir) 1071 file := appSetFiles[dir] 1072 cmd.Text("zipinfo -1").Input(file).Textf(`| sed "s:\(.*\):/%s/\1 1000 1000 0644:";`, dir) 1073 } 1074 // Custom fs_config is "appended" to the last so that entries from the file are preferred 1075 // over default ones set above. 1076 if a.properties.Canned_fs_config != nil { 1077 cmd.Text("cat").Input(android.PathForModuleSrc(ctx, *a.properties.Canned_fs_config)) 1078 } 1079 cmd.Text(")").FlagWithOutput("> ", cannedFsConfig) 1080 builder.Build("generateFsConfig", fmt.Sprintf("Generating canned fs config for %s", a.BaseModuleName())) 1081 1082 return cannedFsConfig.OutputPath 1083} 1084