1# Copyright 2019 The Bazel Authors. 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 15"""Bazel ResourcesBusyBox Commands.""" 16 17load(":java.bzl", _java = "java") 18 19_ANDROID_RESOURCES_STRICT_DEPS = "android_resources_strict_deps" 20 21def _sanitize_assets_dir(assets_dir): 22 sanitized_assets_dir = "/".join( 23 [ 24 part 25 for part in assets_dir.split("/") 26 if part != "" and part != "." 27 ], 28 ) 29 30 return "/" + sanitized_assets_dir if assets_dir.startswith("/") else sanitized_assets_dir 31 32def _get_unique_assets_dirs(assets, assets_dir): 33 """Find the unique assets directories, partitioned by assets_dir. 34 35 Args: 36 assets: A list of Files. List of asset files to process. 37 assets_dir: String. String giving the path to the files in assets. 38 39 Returns: 40 A list of short_paths representing unique asset dirs. 41 """ 42 if not assets: 43 return [] 44 45 dirs = dict() 46 47 assets_dir = _sanitize_assets_dir(assets_dir) 48 if assets_dir: 49 partition_by = "/%s/" % assets_dir.strip("/") 50 for f in assets: 51 if f.is_directory and f.path.endswith(partition_by[:-1]): 52 # If f is a directory, check if its path ends with the assets_dir. 53 dirs[f.path] = True 54 elif f.is_directory and "_aar/unzipped" in f.path: 55 # Assets from an aar_import rule are extracted in a 56 # "assets" subdirectory of the given path 57 dirs["%s/assets" % f.path] = True 58 else: 59 # Partition to remove subdirectories beneath assets_dir 60 # Also removes the trailing / 61 dirs["".join(f.path.rpartition(partition_by)[:2])[:-1]] = True 62 else: 63 # Use the dirname of the generating target if no assets_dir. 64 for f in assets: 65 if f.is_source: 66 dirs[f.owner.package] = True 67 else: 68 # Prepend the root path for generated files. 69 dirs[f.root.path + "/" + f.owner.package] = True 70 return dirs.keys() 71 72def _get_unique_res_dirs(resource_files): 73 """Find the unique res dirs. 74 75 Args: 76 resource_files: A list of Files. A list of resource_files. 77 78 Returns: 79 A list of short_paths representing unique res dirs from the given resource files. 80 """ 81 dirs = dict() 82 for f in resource_files: 83 if f.is_directory: 84 dirs[f.path] = True 85 else: 86 dirs[f.dirname.rpartition("/" + f.dirname.split("/")[-1])[0]] = True 87 return dirs.keys() 88 89def _make_serialized_resources_flag( 90 assets = [], 91 assets_dir = None, 92 resource_files = [], 93 label = "", 94 symbols = None): 95 return ";".join( 96 [ 97 "#".join(_get_unique_res_dirs(resource_files)), 98 "#".join(_get_unique_assets_dirs(assets, assets_dir)), 99 label, 100 symbols.path if symbols else "", 101 ], 102 ).rstrip(":") 103 104def _make_resources_flag( 105 assets = [], 106 assets_dir = None, 107 resource_files = [], 108 manifest = None, 109 r_txt = None, 110 symbols = None): 111 return ":".join( 112 [ 113 "#".join(_get_unique_res_dirs(resource_files)), 114 "#".join(_get_unique_assets_dirs(assets, assets_dir)), 115 manifest.path if manifest else "", 116 r_txt.path if r_txt else "", 117 symbols.path if symbols else "", 118 ], 119 ) 120 121def _path(f): 122 return f.path 123 124def _make_package_resources_flags(resources_node): 125 if not (resources_node.manifest and resources_node.r_txt and resources_node.compiled_resources): 126 return None 127 flag = _make_resources_flag( 128 resource_files = resources_node.resource_files.to_list(), 129 assets = resources_node.assets.to_list(), 130 assets_dir = resources_node.assets_dir, 131 manifest = resources_node.manifest, 132 r_txt = resources_node.r_txt, 133 symbols = resources_node.compiled_resources, 134 ) 135 return flag 136 137def _make_package_assets_flags(resources_node): 138 assets = resources_node.assets.to_list() 139 if not assets: 140 return None 141 return _make_serialized_resources_flag( 142 assets = assets, 143 assets_dir = resources_node.assets_dir, 144 label = str(resources_node.label), 145 symbols = resources_node.compiled_assets, 146 ) 147 148def _extract_filters( 149 raw_list): 150 """Extract densities and resource_configuration filters from raw string lists. 151 152 In BUILD files, string lists can be represented as a list of strings, a single comma-separated 153 string, or a combination of both. This method outputs a single list of individual string values, 154 which can then be passed directly to resource processing actions. Empty strings are removed and 155 the final list is sorted. 156 157 Args: 158 raw_list: List of strings. The raw densities or resource configuration filters. 159 160 Returns: 161 List of strings extracted from the raw list. 162 """ 163 out_filters = [] 164 for item in raw_list: 165 if "," in item: 166 item_list = item.split(",") 167 for entry in item_list: 168 stripped_entry = entry.strip() 169 if stripped_entry: 170 out_filters.append(stripped_entry) 171 elif item: 172 out_filters.append(item) 173 return sorted(out_filters) 174 175def _package( 176 ctx, 177 out_r_src_jar = None, 178 out_r_txt = None, 179 out_symbols = None, 180 out_manifest = None, 181 out_proguard_cfg = None, 182 out_main_dex_proguard_cfg = None, 183 out_resource_files_zip = None, 184 out_file = None, 185 package_type = None, 186 java_package = None, 187 manifest = None, 188 assets = [], 189 assets_dir = None, 190 resource_files = [], 191 resource_configs = None, 192 densities = [], 193 application_id = None, 194 package_id = None, 195 direct_resources_nodes = [], 196 transitive_resources_nodes = [], 197 transitive_manifests = [], 198 transitive_assets = [], 199 transitive_compiled_assets = [], 200 transitive_resource_files = [], 201 transitive_compiled_resources = [], 202 transitive_r_txts = [], 203 additional_apks_to_link_against = [], 204 resource_apks = depset(), 205 nocompress_extensions = [], 206 proto_format = False, 207 shrink_resource_cycles = False, 208 version_name = None, 209 version_code = None, 210 android_jar = None, 211 aapt = None, 212 busybox = None, 213 host_javabase = None, 214 should_throw_on_conflict = True, # TODO: read this from allowlist at caller 215 debug = True): # TODO: we will set this to false in prod builds 216 """Packages the compiled Android Resources with AAPT. 217 218 Args: 219 ctx: The context. 220 out_r_src_jar: A File. The R.java outputted by linking resources in a srcjar. 221 out_r_txt: A File. The resource IDs outputted by linking resources in text. 222 out_symbols: A File. The output zip containing compiled resources. 223 out_manifest: A File. The output processed manifest. 224 out_proguard_cfg: A File. The proguard config to be generated. 225 out_main_dex_proguard_cfg: A File. The main dex proguard config to be generated. 226 out_resource_files_zip: A File. The resource files zipped by linking resources. 227 out_file: A File. The Resource APK outputted by linking resources. 228 package_type: A string. The configuration type to use when packaging. 229 java_package: A string. The Java package for the generated R.java. 230 manifest: A File. The AndroidManifest.xml. 231 assets: sequence of Files. A list of Android assets files to be processed. 232 assets_dir: String. The name of the assets directory. 233 resource_files: A list of Files. The resource files. 234 resource_configs: A list of strings. The list of resource configuration 235 filters. 236 densities: A list of strings. The list of screen densities to filter for when 237 building the apk. 238 application_id: An optional string. The applicationId set in manifest values. 239 package_id: An optional integer in [2,255]. This is the prefix byte for 240 all generated resource IDs, defaults to 0x7F (127). 1 is reserved by the 241 framework, and some builds are known to crash when given IDs > 127. 242 Shared libraries are also assigned monotonically increasing IDs in 243 [2,126], so care should be taken that there is room at the lower end. 244 direct_resources_nodes: Depset of ResourcesNodeInfo providers. The set of 245 ResourcesNodeInfo from direct dependencies. 246 transitive_resources_nodes: Depset of ResourcesNodeInfo providers. The set 247 of ResourcesNodeInfo from transitive dependencies (not including directs). 248 transitive_manifests: List of Depsets. Depsets contain all transitive manifests. 249 transitive_assets: List of Depsets. Depsets contain all transitive assets. 250 transitive_compiled_assets: List of Depsets. Depsets contain all transitive 251 compiled_assets. 252 transitive_resource_files: List of Depsets. Depsets contain all transitive 253 resource files. 254 transitive_compiled_resources: List of Depsets. Depsets contain all transitive 255 compiled_resources. 256 transitive_r_txts: List of Depsets. Depsets contain all transitive R txt files. 257 additional_apks_to_link_against: A list of Files. Additional APKs to link 258 against. Optional. 259 resource_apks: Depset of resource only apk files to link against. 260 nocompress_extensions: A list of strings. File extension to leave uncompressed 261 in the apk. 262 proto_format: Boolean, whether to generate the resource table in proto format. 263 shrink_resource_cycles: Boolean, flag that enables more shrinking of 264 code and resources by instructing AAPT2 to emit conditional Proguard keep rules. 265 version_name: A string. The version name to stamp the generated manifest with. Optional. 266 version_code: A string. The version code to stamp the generated manifest with. Optional. 267 android_jar: A File. The Android Jar. 268 aapt: A FilesToRunProvider. The AAPT executable. 269 busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. 270 host_javabase: Target. The host javabase. 271 should_throw_on_conflict: A boolean. Determines whether an error should be thrown 272 when a resource conflict occurs. 273 debug: A boolean. Determines whether to enable debugging. 274 """ 275 if not manifest: 276 fail("No manifest given, the manifest is mandatory.") 277 278 direct_data_flag = [] 279 direct_compiled_resources = [] 280 281 output_files = [] 282 input_files = [] 283 transitive_input_files = [] 284 285 args = ctx.actions.args() 286 args.use_param_file("@%s") 287 args.add("--tool", "AAPT2_PACKAGE") 288 args.add("--") 289 args.add("--aapt2", aapt.executable) 290 args.add_joined( 291 "--data", 292 transitive_resources_nodes, 293 map_each = _make_package_resources_flags, 294 join_with = ",", 295 ) 296 args.add_joined( 297 "--directData", 298 direct_resources_nodes, 299 map_each = _make_package_resources_flags, 300 join_with = ",", 301 ) 302 args.add_joined( 303 "--directAssets", 304 direct_resources_nodes, 305 map_each = _make_package_assets_flags, 306 join_with = "&", 307 omit_if_empty = True, 308 ) 309 args.add_joined( 310 "--assets", 311 transitive_resources_nodes, 312 map_each = _make_package_assets_flags, 313 join_with = "&", 314 omit_if_empty = True, 315 ) 316 transitive_input_files.extend(transitive_resource_files) 317 transitive_input_files.extend(transitive_assets) 318 transitive_input_files.extend(transitive_compiled_assets) 319 transitive_input_files.extend(transitive_compiled_resources) 320 transitive_input_files.extend(transitive_manifests) 321 transitive_input_files.extend(transitive_r_txts) 322 args.add( 323 "--primaryData", 324 _make_resources_flag( 325 manifest = manifest, 326 assets = assets, 327 assets_dir = assets_dir, 328 resource_files = resource_files, 329 ), 330 ) 331 input_files.append(manifest) 332 input_files.extend(resource_files) 333 input_files.extend(assets) 334 args.add("--androidJar", android_jar) 335 input_files.append(android_jar) 336 args.add("--rOutput", out_r_txt) 337 output_files.append(out_r_txt) 338 if out_symbols: 339 args.add("--symbolsOut", out_symbols) 340 output_files.append(out_symbols) 341 args.add("--srcJarOutput", out_r_src_jar) 342 output_files.append(out_r_src_jar) 343 if out_proguard_cfg: 344 args.add("--proguardOutput", out_proguard_cfg) 345 output_files.append(out_proguard_cfg) 346 if out_main_dex_proguard_cfg: 347 args.add("--mainDexProguardOutput", out_main_dex_proguard_cfg) 348 output_files.append(out_main_dex_proguard_cfg) 349 args.add("--manifestOutput", out_manifest) 350 output_files.append(out_manifest) 351 if out_resource_files_zip: 352 args.add("--resourcesOutput", out_resource_files_zip) 353 output_files.append(out_resource_files_zip) 354 if out_file: 355 args.add("--packagePath", out_file) 356 output_files.append(out_file) 357 args.add("--useAaptCruncher=no") # Unnecessary, used for AAPT1 only but added here to minimize diffs. 358 if package_type: 359 args.add("--packageType", package_type) 360 if debug: 361 args.add("--debug") 362 if should_throw_on_conflict: 363 args.add("--throwOnResourceConflict") 364 if resource_configs: 365 args.add_joined("--resourceConfigs", _extract_filters(resource_configs), join_with = ",") 366 if densities: 367 args.add_joined("--densities", _extract_filters(densities), join_with = ",") 368 if application_id: 369 args.add("--applicationId", application_id) 370 if package_id: 371 args.add("--packageId", package_id) 372 if additional_apks_to_link_against: 373 args.add_joined( 374 "--additionalApksToLinkAgainst", 375 additional_apks_to_link_against, 376 join_with = ":", 377 map_each = _path, 378 ) 379 input_files.extend(additional_apks_to_link_against) 380 if nocompress_extensions: 381 args.add_joined("--uncompressedExtensions", nocompress_extensions, join_with = ",") 382 if proto_format: 383 args.add("--resourceTableAsProto") 384 if shrink_resource_cycles: 385 args.add("--conditionalKeepRules=yes") 386 if version_name: 387 args.add("--versionName", version_name) 388 if version_code: 389 args.add("--versionCode", version_code) 390 if java_package: 391 args.add("--packageForR", java_package) 392 393 args.add_joined( 394 "--resourceApks", 395 resource_apks, 396 join_with = ":", 397 ) 398 transitive_input_files.append(resource_apks) 399 400 _java.run( 401 ctx = ctx, 402 host_javabase = host_javabase, 403 executable = busybox, 404 tools = [aapt], 405 arguments = [args], 406 inputs = depset(input_files, transitive = transitive_input_files), 407 outputs = output_files, 408 mnemonic = "PackageAndroidResources", 409 progress_message = "Packaging Android Resources in %s" % ctx.label, 410 ) 411 412def _parse( 413 ctx, 414 out_symbols = None, 415 assets = [], 416 assets_dir = None, 417 busybox = None, 418 host_javabase = None): 419 """Parses Android assets. 420 421 Args: 422 ctx: The context. 423 out_symbols: A File. The output bin containing parsed assets. 424 assets: sequence of Files. A list of Android assets files to be processed. 425 assets_dir: String. The name of the assets directory. 426 busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. 427 host_javabase: Target. The host javabase. 428 """ 429 args = ctx.actions.args() 430 args.use_param_file("@%s") 431 args.add("--tool", "PARSE") 432 args.add("--") 433 args.add( 434 "--primaryData", 435 _make_resources_flag( 436 assets = assets, 437 assets_dir = assets_dir, 438 ), 439 ) 440 args.add("--output", out_symbols) 441 442 _java.run( 443 ctx = ctx, 444 host_javabase = host_javabase, 445 executable = busybox, 446 arguments = [args], 447 inputs = assets, 448 outputs = [out_symbols], 449 mnemonic = "ParseAndroidResources", 450 progress_message = "Parsing Android Resources in %s" % out_symbols.short_path, 451 ) 452 453def _make_merge_assets_flags(resources_node): 454 assets = resources_node.assets.to_list() 455 if not (assets or resources_node.assets_dir): 456 return None 457 return _make_serialized_resources_flag( 458 assets = assets, 459 assets_dir = resources_node.assets_dir, 460 label = str(resources_node.label), 461 symbols = resources_node.assets_symbols, 462 ) 463 464def _merge_assets( 465 ctx, 466 out_assets_zip = None, 467 assets = [], 468 assets_dir = None, 469 symbols = None, 470 transitive_assets = [], 471 transitive_assets_symbols = [], 472 direct_resources_nodes = [], 473 transitive_resources_nodes = [], 474 busybox = None, 475 host_javabase = None): 476 """Merges Android assets. 477 478 Args: 479 ctx: The context. 480 out_assets_zip: A File. 481 assets: sequence of Files. A list of Android assets files to be processed. 482 assets_dir: String. The name of the assets directory. 483 symbols: A File. The parsed assets. 484 transitive_assets: Sequence of Depsets. The list of transitive 485 assets from deps. 486 transitive_assets_symbols: Sequence of Depsets. The list of 487 transitive assets_symbols files from deps. 488 direct_resources_nodes: Sequence of ResourcesNodeInfo providers. The list 489 of ResourcesNodeInfo providers that are direct depencies. 490 transitive_resources_nodes: Sequence of ResourcesNodeInfo providers. The 491 list of ResourcesNodeInfo providers that are transitive depencies. 492 busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. 493 host_javabase: Target. The host javabase. 494 """ 495 args = ctx.actions.args() 496 args.use_param_file("@%s") 497 args.add("--tool", "MERGE_ASSETS") 498 args.add("--") 499 args.add("--assetsOutput", out_assets_zip) 500 args.add( 501 "--primaryData", 502 _make_serialized_resources_flag( 503 assets = assets, 504 assets_dir = assets_dir, 505 label = str(ctx.label), 506 symbols = symbols, 507 ), 508 ) 509 args.add_joined( 510 "--directData", 511 direct_resources_nodes, 512 map_each = _make_merge_assets_flags, 513 join_with = "&", 514 ) 515 args.add_joined( 516 "--data", 517 transitive_resources_nodes, 518 map_each = _make_merge_assets_flags, 519 join_with = "&", 520 ) 521 522 _java.run( 523 ctx = ctx, 524 host_javabase = host_javabase, 525 executable = busybox, 526 arguments = [args], 527 inputs = depset( 528 assets + [symbols], 529 transitive = transitive_assets + transitive_assets_symbols, 530 ), 531 outputs = [out_assets_zip], 532 mnemonic = "MergeAndroidAssets", 533 progress_message = 534 "Merging Android Assets in %s" % out_assets_zip.short_path, 535 ) 536 537def _validate_and_link( 538 ctx, 539 out_r_src_jar = None, 540 out_r_txt = None, 541 out_file = None, 542 compiled_resources = None, 543 transitive_compiled_resources = depset(), 544 java_package = None, 545 manifest = None, 546 resource_apks = [], 547 android_jar = None, 548 busybox = None, 549 host_javabase = None, 550 aapt = None): 551 """Links compiled Android Resources with AAPT. 552 553 Args: 554 ctx: The context. 555 out_r_src_jar: A File. The R.java outputted by linking resources in a srcjar. 556 out_r_txt: A File. The resource IDs outputted by linking resources in text. 557 out_file: A File. The Resource APK outputted by linking resources. 558 compiled_resources: A File. The symbols.zip of compiled resources for 559 this target. 560 transitive_compiled_resources: Depset of Files. The symbols.zip of the 561 compiled resources from the transitive dependencies of this target. 562 java_package: A string. The Java package for the generated R.java. 563 manifest: A File. The AndroidManifest.xml. 564 resource_apks: List of direct resource only apk files. 565 android_jar: A File. The Android Jar. 566 busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. 567 host_javabase: Target. The host javabase. 568 aapt: A FilesToRunProvider. The AAPT executable. 569 """ 570 output_files = [] 571 input_files = [android_jar] 572 transitive_input_files = [] 573 574 # Retrieves the list of files at runtime when a directory is passed. 575 args = ctx.actions.args() 576 args.use_param_file("@%s") 577 args.add("--tool", "LINK_STATIC_LIBRARY") 578 args.add("--") 579 args.add("--aapt2", aapt.executable) 580 args.add("--libraries", android_jar) 581 if compiled_resources: 582 args.add("--compiled", compiled_resources) 583 input_files.append(compiled_resources) 584 args.add_joined( 585 "--compiledDep", 586 transitive_compiled_resources, 587 join_with = ":", 588 ) 589 transitive_input_files.append(transitive_compiled_resources) 590 args.add("--manifest", manifest) 591 input_files.append(manifest) 592 if java_package: 593 args.add("--packageForR", java_package) 594 args.add("--sourceJarOut", out_r_src_jar) 595 output_files.append(out_r_src_jar) 596 args.add("--rTxtOut", out_r_txt) 597 output_files.append(out_r_txt) 598 args.add("--staticLibraryOut", out_file) 599 output_files.append(out_file) 600 args.add_joined( 601 "--resourceApks", 602 resource_apks, 603 join_with = ":", 604 ) 605 input_files.extend(resource_apks) 606 607 _java.run( 608 ctx = ctx, 609 host_javabase = host_javabase, 610 executable = busybox, 611 tools = [aapt], 612 arguments = [args], 613 inputs = depset(input_files, transitive = transitive_input_files), 614 outputs = output_files, 615 mnemonic = "LinkAndroidResources", 616 progress_message = 617 "Linking Android Resources in " + out_file.short_path, 618 ) 619 620def _compile( 621 ctx, 622 out_file = None, 623 assets = [], 624 assets_dir = None, 625 resource_files = [], 626 busybox = None, 627 aapt = None, 628 host_javabase = None): 629 """Compile and store resources in a single archive. 630 631 Args: 632 ctx: The context. 633 out_file: File. The output zip containing compiled resources. 634 resource_files: A list of Files. The list of resource files or directories 635 assets: A list of Files. The list of assets files or directories 636 to process. 637 assets_dir: String. The name of the assets directory. 638 busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. 639 aapt: AAPT. Tool for compiling resources. 640 host_javabase: Target. The host javabase. 641 """ 642 if not out_file: 643 fail("No output directory specified.") 644 645 # Retrieves the list of files at runtime when a directory is passed. 646 args = ctx.actions.args() 647 args.use_param_file("@%s") 648 args.add("--tool", "COMPILE_LIBRARY_RESOURCES") 649 args.add("--") 650 args.add("--aapt2", aapt.executable) 651 args.add( 652 "--resources", 653 _make_resources_flag( 654 resource_files = resource_files, 655 assets = assets, 656 assets_dir = assets_dir, 657 ), 658 ) 659 args.add("--output", out_file) 660 661 _java.run( 662 ctx = ctx, 663 host_javabase = host_javabase, 664 executable = busybox, 665 tools = [aapt], 666 arguments = [args], 667 inputs = resource_files + assets, 668 outputs = [out_file], 669 mnemonic = "CompileAndroidResources", 670 progress_message = "Compiling Android Resources in %s" % out_file.short_path, 671 ) 672 673def _make_merge_compiled_flags(resources_node_info): 674 if not resources_node_info.compiled_resources: 675 return None 676 return _make_serialized_resources_flag( 677 label = str(resources_node_info.label), 678 symbols = resources_node_info.compiled_resources, 679 ) 680 681def _merge_compiled( 682 ctx, 683 out_class_jar = None, 684 out_manifest = None, 685 out_aapt2_r_txt = None, 686 java_package = None, 687 manifest = None, 688 compiled_resources = None, 689 direct_resources_nodes = [], 690 transitive_resources_nodes = [], 691 direct_compiled_resources = depset(), 692 transitive_compiled_resources = depset(), 693 android_jar = None, 694 busybox = None, 695 host_javabase = None): 696 """Merges the compile resources. 697 698 Args: 699 ctx: The context. 700 out_class_jar: A File. The compiled R.java outputted by linking resources. 701 out_manifest: A File. The list of resource files or directories 702 out_aapt2_r_txt: A File. The resource IDs outputted by linking resources in text. 703 java_package: A string. The Java package for the generated R.java. 704 manifest: A File. The AndroidManifest.xml. 705 compiled_resources: A File. The symbols.zip of compiled resources for this target. 706 direct_resources_nodes: Sequence of ResourcesNodeInfo providers. The list 707 of ResourcesNodeInfo providers that are direct depencies. 708 transitive_resources_nodes: Sequence of ResourcesNodeInfo providers. The 709 list of ResourcesNodeInfo providers that are transitive depencies. 710 direct_compiled_resources: Depset of Files. A depset of symbols.zip of 711 compiled resources from direct dependencies. 712 transitive_compiled_resources: Depset of Files. A depset of symbols.zip of 713 compiled resources from transitive dependencies. 714 android_jar: A File. The Android Jar. 715 busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. 716 host_javabase: Target. The host javabase. 717 """ 718 output_files = [] 719 input_files = [android_jar] 720 transitive_input_files = [] 721 722 args = ctx.actions.args() 723 args.use_param_file("@%s") 724 args.add("--tool", "MERGE_COMPILED") 725 args.add("--") 726 args.add("--classJarOutput", out_class_jar) 727 output_files.append(out_class_jar) 728 args.add("--targetLabel", ctx.label) 729 args.add("--manifestOutput", out_manifest) 730 output_files.append(out_manifest) 731 args.add("--rTxtOut", out_aapt2_r_txt) 732 output_files.append(out_aapt2_r_txt) 733 args.add("--androidJar", android_jar) 734 args.add("--primaryManifest", manifest) 735 input_files.append(manifest) 736 if java_package: 737 args.add("--packageForR", java_package) 738 args.add( 739 "--primaryData", 740 _make_serialized_resources_flag( 741 label = str(ctx.label), 742 symbols = compiled_resources, 743 ), 744 ) 745 input_files.append(compiled_resources) 746 args.add_joined( 747 "--directData", 748 direct_resources_nodes, 749 map_each = _make_merge_compiled_flags, 750 join_with = "&", 751 ) 752 transitive_input_files.append(direct_compiled_resources) 753 if _ANDROID_RESOURCES_STRICT_DEPS in ctx.disabled_features: 754 args.add_joined( 755 "--data", 756 transitive_resources_nodes, 757 map_each = _make_merge_compiled_flags, 758 join_with = "&", 759 ) 760 transitive_input_files.append(transitive_compiled_resources) 761 762 _java.run( 763 ctx = ctx, 764 host_javabase = host_javabase, 765 executable = busybox, 766 arguments = [args], 767 inputs = depset(input_files, transitive = transitive_input_files), 768 outputs = output_files, 769 mnemonic = "StarlarkMergeCompiledAndroidResources", 770 progress_message = 771 "Merging compiled Android Resources in " + out_class_jar.short_path, 772 ) 773 774def _escape_mv(s): 775 """Escapes `:` and `,` in manifest values so they can be used as a busybox flag.""" 776 return s.replace(":", "\\:").replace(",", "\\,") 777 778def _owner_label(file): 779 return "//" + file.owner.package + ":" + file.owner.name 780 781# We need to remove the "/_migrated/" path segment from file paths in order for sorting to 782# match the order of the native manifest merging action. 783def _manifest_short_path(manifest): 784 return manifest.short_path.replace("/_migrated/", "/") 785 786def _mergee_manifests_flag(manifests): 787 ordered_manifests = sorted(manifests.to_list(), key = _manifest_short_path) 788 entries = [] 789 for manifest in ordered_manifests: 790 label = _owner_label(manifest).replace(":", "\\:") 791 entries.append((manifest.path + ":" + label).replace(",", "\\,")) 792 flag_entry = ",".join(entries) 793 if not flag_entry: 794 return None 795 return flag_entry 796 797def _merge_manifests( 798 ctx, 799 out_file = None, 800 out_log_file = None, 801 merge_type = "APPLICATION", 802 manifest = None, 803 mergee_manifests = depset(), 804 manifest_values = None, 805 java_package = None, 806 busybox = None, 807 host_javabase = None): 808 """Merge multiple AndroidManifest.xml files into a single one. 809 810 Args: 811 ctx: The context. 812 out_file: A File. The output merged manifest. 813 out_log_file: A File. The output log from the merge tool. 814 merge_type: A string, either APPLICATION or LIBRARY. Type of merging. 815 manifest: A File. The primary AndroidManifest.xml. 816 mergee_manifests: A depset of Files. All transitive manifests to be merged. 817 manifest_values: A dictionary. Manifest values to substitute. 818 java_package: A string. Custom java package to insert in manifest package attribute. 819 busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. 820 host_javabase: Target. The host javabase. 821 """ 822 if merge_type not in ["APPLICATION", "LIBRARY"]: 823 fail("Unexpected manifest merge type: " + merge_type) 824 825 outputs = [out_file] 826 directs = [manifest] if manifest else [] 827 transitives = [mergee_manifests] 828 829 # Args for busybox 830 args = ctx.actions.args() 831 args.use_param_file("@%s", use_always = True) 832 args.add("--tool", "MERGE_MANIFEST") 833 args.add("--") 834 if manifest: 835 args.add("--manifest", manifest) 836 args.add_all( 837 "--mergeeManifests", 838 [mergee_manifests], 839 map_each = _mergee_manifests_flag, 840 ) 841 if manifest_values: 842 args.add( 843 "--manifestValues", 844 ",".join(["%s:%s" % (_escape_mv(k), _escape_mv(v)) for k, v in manifest_values.items()]), 845 ) 846 args.add("--mergeType", merge_type) 847 if java_package: 848 args.add("--customPackage", java_package) 849 args.add("--manifestOutput", out_file) 850 if out_log_file: 851 args.add("--log", out_log_file) 852 outputs.append(out_log_file) 853 854 _java.run( 855 ctx = ctx, 856 host_javabase = host_javabase, 857 executable = busybox, 858 arguments = [args], 859 inputs = depset(directs, transitive = transitives), 860 outputs = outputs, 861 mnemonic = "MergeManifests", 862 progress_message = "Merging Android Manifests in %s" % out_file.short_path, 863 ) 864 865def _process_databinding( 866 ctx, 867 out_databinding_info = None, 868 out_databinding_processed_resources = None, 869 databinding_resources_dirname = None, 870 resource_files = None, 871 java_package = None, 872 busybox = None, 873 host_javabase = None): 874 """Processes databinding for android_binary. 875 876 Processes databinding declarations over resources, populates the databinding layout 877 info file, and generates new resources with databinding expressions stripped out. 878 879 Args: 880 ctx: The context. 881 out_databinding_info: File. The output databinding layout info zip file. 882 out_databinding_processed_resources: List of Files. The generated databinding 883 processed resource files. 884 databinding_resources_dirname: String. The execution path to the directory where 885 the out_databinding_processed_resources are generated. 886 resource_files: List of Files. The resource files to be processed. 887 java_package: String. Java package for which java sources will be 888 generated. By default the package is inferred from the directory where 889 the BUILD file containing the rule is. 890 busybox: FilesToRunProvider. The ResourceBusyBox executable or 891 FilesToRunprovider 892 host_javabase: A Target. The host javabase. 893 """ 894 res_dirs = _get_unique_res_dirs(resource_files) 895 896 args = ctx.actions.args() 897 args.add("--tool", "PROCESS_DATABINDING") 898 args.add("--") 899 args.add("--output_resource_directory", databinding_resources_dirname) 900 args.add_all(res_dirs, before_each = "--resource_root") 901 args.add("--dataBindingInfoOut", out_databinding_info) 902 args.add("--appId", java_package) 903 904 _java.run( 905 ctx = ctx, 906 host_javabase = host_javabase, 907 executable = busybox, 908 arguments = [args], 909 inputs = resource_files, 910 outputs = [out_databinding_info] + out_databinding_processed_resources, 911 mnemonic = "StarlarkProcessDatabinding", 912 progress_message = "Processing data binding", 913 ) 914 915def _make_generate_binay_r_flags(resources_node): 916 if not (resources_node.r_txt or resources_node.manifest): 917 return None 918 return ",".join( 919 [ 920 resources_node.r_txt.path if resources_node.r_txt else "", 921 resources_node.manifest.path if resources_node.manifest else "", 922 ], 923 ) 924 925def _generate_binary_r( 926 ctx, 927 out_class_jar = None, 928 r_txt = None, 929 manifest = None, 930 package_for_r = None, 931 final_fields = None, 932 resources_nodes = depset(), 933 transitive_r_txts = [], 934 transitive_manifests = [], 935 busybox = None, 936 host_javabase = None): 937 """Generate compiled resources class jar. 938 939 Args: 940 ctx: The context. 941 out_class_jar: File. The output class jar file. 942 r_txt: File. The resource IDs outputted by linking resources in text. 943 manifest: File. The primary AndroidManifest.xml. 944 package_for_r: String. The Java package for the generated R class files. 945 final_fields: Bool. Whether fields get declared as final. 946 busybox: FilesToRunProvider. The ResourceBusyBox executable or 947 FilesToRunprovider 948 host_javabase: A Target. The host javabase. 949 """ 950 args = ctx.actions.args() 951 args.add("--tool", "GENERATE_BINARY_R") 952 args.add("--") 953 args.add("--primaryRTxt", r_txt) 954 args.add("--primaryManifest", manifest) 955 if package_for_r: 956 args.add("--packageForR", package_for_r) 957 args.add_all( 958 resources_nodes, 959 map_each = _make_generate_binay_r_flags, 960 before_each = "--library", 961 ) 962 if final_fields: 963 args.add("--finalFields") 964 else: 965 args.add("--nofinalFields") 966 967 # TODO(b/154003916): support transitive "--library transitive_r_txt_path,transitive_manifest_path" flags 968 args.add("--classJarOutput", out_class_jar) 969 args.add("--targetLabel", str(ctx.label)) 970 args.use_param_file("@%s") 971 972 _java.run( 973 ctx = ctx, 974 host_javabase = host_javabase, 975 executable = busybox, 976 arguments = [args], 977 inputs = depset([r_txt, manifest], transitive = transitive_r_txts + transitive_manifests), 978 outputs = [out_class_jar], 979 mnemonic = "StarlarkRClassGenerator", 980 progress_message = "Generating R classes", 981 ) 982 983def _make_aar( 984 ctx, 985 out_aar = None, 986 assets = [], 987 assets_dir = None, 988 resource_files = [], 989 class_jar = None, 990 r_txt = None, 991 manifest = None, 992 proguard_specs = [], 993 should_throw_on_conflict = False, 994 busybox = None, 995 host_javabase = None): 996 """Generate an android archive file. 997 998 Args: 999 ctx: The context. 1000 out_aar: File. The output AAR file. 1001 assets: sequence of Files. A list of Android assets files to be processed. 1002 assets_dir: String. The name of the assets directory. 1003 resource_files: A list of Files. The resource files. 1004 class_jar: File. The class jar file. 1005 r_txt: File. The resource IDs outputted by linking resources in text. 1006 manifest: File. The primary AndroidManifest.xml. 1007 proguard_specs: List of File. The proguard spec files. 1008 busybox: FilesToRunProvider. The ResourceBusyBox executable or 1009 FilesToRunprovider 1010 host_javabase: A Target. The host javabase. 1011 should_throw_on_conflict: A boolean. Determines whether an error should be thrown 1012 when a resource conflict occurs. 1013 """ 1014 args = ctx.actions.args() 1015 args.add("--tool", "GENERATE_AAR") 1016 args.add("--") 1017 args.add( 1018 "--mainData", 1019 _make_resources_flag( 1020 manifest = manifest, 1021 assets = assets, 1022 assets_dir = assets_dir, 1023 resource_files = resource_files, 1024 ), 1025 ) 1026 args.add("--manifest", manifest) 1027 args.add("--rtxt", r_txt) 1028 args.add("--classes", class_jar) 1029 args.add("--aarOutput", out_aar) 1030 args.add_all(proguard_specs, before_each = "--proguardSpec") 1031 if should_throw_on_conflict: 1032 args.add("--throwOnResourceConflict") 1033 1034 _java.run( 1035 ctx = ctx, 1036 host_javabase = host_javabase, 1037 executable = busybox, 1038 arguments = [args], 1039 inputs = ( 1040 resource_files + 1041 assets + 1042 proguard_specs + 1043 [r_txt, manifest, class_jar] 1044 ), 1045 outputs = [out_aar], 1046 mnemonic = "StarlarkAARGenerator", 1047 progress_message = "Generating AAR package for %s" % ctx.label, 1048 ) 1049 1050def _shrink( 1051 ctx, 1052 out_apk, 1053 out_zip, 1054 out_log, 1055 out_config = None, 1056 resources_zip = None, 1057 aapt = None, 1058 android_jar = None, 1059 r_txt = None, 1060 shrunk_jar = None, 1061 proguard_mapping = None, 1062 debug = True, 1063 busybox = None, 1064 host_javabase = None): 1065 """Shrinks the resource apk by removing the resources unused from the packaged app. 1066 1067 Args: 1068 ctx: The context. 1069 out_apk: File. The output shrunk resource ap_ package. 1070 out_zip: File. The output shrunk resources file zip. 1071 out_log: File. The output shrinker log. 1072 out_config: File. The output config for the optimizer. 1073 resources_zip: File. The input resources file zip. 1074 aapt: FilesToRunProvider. The AAPT executable. 1075 android_jar: File. The Android Jar. 1076 r_txt: File. The resource IDs outputted by linking resources in text. 1077 shrunk_jar: File. The proguarded jar. 1078 proguard_mapping: File. The Proguard Mapping file. 1079 debug: Boolean. Whether to enable debug mode. 1080 busybox: FilesToRunProvider. The ResourceBusyBox executable. 1081 host_javabase: Target. The host javabase. 1082 """ 1083 1084 args = ctx.actions.args() 1085 args.use_param_file("@%s") 1086 args.add("--tool", "SHRINK_AAPT2") 1087 args.add("--") 1088 args.add("--aapt2", aapt.executable) 1089 args.add("--androidJar", android_jar) 1090 args.add("--resources", resources_zip) 1091 args.add("--shrunkJar", shrunk_jar) 1092 args.add("--proguardMapping", proguard_mapping) 1093 args.add("--rTxt", r_txt) 1094 args.add("--shrunkResourceApk", out_apk) 1095 args.add("--shrunkResources", out_zip) 1096 args.add("--log", out_log) 1097 args.add("--useDataBindingAndroidX") 1098 if debug: 1099 args.add("--debug") 1100 1101 input_files = [ 1102 android_jar, 1103 resources_zip, 1104 shrunk_jar, 1105 proguard_mapping, 1106 r_txt, 1107 ] 1108 output_files = [ 1109 out_apk, 1110 out_zip, 1111 out_log, 1112 ] 1113 if out_config: 1114 args.add("--resourcesConfigOutput", out_config) 1115 output_files.append(out_config) 1116 1117 _java.run( 1118 ctx = ctx, 1119 executable = busybox, 1120 tools = [aapt], 1121 outputs = output_files, 1122 inputs = input_files, 1123 arguments = [args], 1124 mnemonic = "ResourceShrinker", 1125 progress_message = 1126 "Shrinking resources for " + str(ctx.label), 1127 host_javabase = host_javabase, 1128 use_default_shell_env = True, 1129 ) 1130 1131def _optimize( 1132 ctx, 1133 out_apk, 1134 in_apk, 1135 resource_path_shortening_map = None, 1136 resource_optimization_config = None, 1137 aapt = None, 1138 busybox = None, 1139 host_javabase = None): 1140 """Optimizes the resource apk including resource obfuscation, sparse encoding and path shortening. 1141 1142 Args: 1143 ctx: The context. 1144 out_apk: File. The output optimized resource ap_ package. 1145 in_apk: File. The resource ap_ package to be optimized. 1146 resource_path_shortening_map: File. The output path shortening map. Optional. 1147 resource_optimization_config: File. The input optimization config. Optional. 1148 aapt: FilesToRunProvider. The AAPT executable. 1149 busybox: FilesToRunProvider. The ResourceBusyBox executable. 1150 host_javabase: Target. The host javabase. 1151 """ 1152 1153 output_files = [out_apk] 1154 input_files = [in_apk] 1155 1156 args = ctx.actions.args() 1157 args.use_param_file("@%s") 1158 args.add("--tool", "AAPT2_OPTIMIZE") 1159 args.add("--") 1160 args.add("--aapt2", aapt.executable) 1161 args.add("--") 1162 if resource_path_shortening_map: 1163 args.add("--shorten-resource-paths") 1164 args.add("--resource-path-shortening-map", resource_path_shortening_map) 1165 output_files.append(resource_path_shortening_map) 1166 if resource_optimization_config: 1167 args.add("--collapse-resource-names") 1168 args.add("--resources-config-path", resource_optimization_config) 1169 input_files.append(resource_optimization_config) 1170 args.add("-o", out_apk) 1171 args.add(in_apk) 1172 1173 _java.run( 1174 ctx = ctx, 1175 host_javabase = host_javabase, 1176 executable = busybox, 1177 tools = [aapt], 1178 arguments = [args], 1179 inputs = input_files, 1180 outputs = output_files, 1181 mnemonic = "Aapt2Optimize", 1182 progress_message = 1183 "Optimizing Android resources for " + str(ctx.label), 1184 use_default_shell_env = True, 1185 ) 1186 1187busybox = struct( 1188 compile = _compile, 1189 merge_compiled = _merge_compiled, 1190 validate_and_link = _validate_and_link, 1191 merge_manifests = _merge_manifests, 1192 package = _package, 1193 parse = _parse, 1194 merge_assets = _merge_assets, 1195 make_resources_flag = _make_resources_flag, 1196 process_databinding = _process_databinding, 1197 generate_binary_r = _generate_binary_r, 1198 make_aar = _make_aar, 1199 shrink = _shrink, 1200 optimize = _optimize, 1201 1202 # Exposed for testing 1203 mergee_manifests_flag = _mergee_manifests_flag, 1204 get_unique_res_dirs = _get_unique_res_dirs, 1205 sanitize_assets_dir = _sanitize_assets_dir, 1206 extract_filters = _extract_filters, 1207) 1208