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 nocompress_extensions = [], 205 proto_format = False, 206 shrink_resource_cycles = False, 207 version_name = None, 208 version_code = None, 209 android_jar = None, 210 aapt = None, 211 busybox = None, 212 host_javabase = None, 213 should_throw_on_conflict = True, # TODO: read this from allowlist at caller 214 debug = True): # TODO: we will set this to false in prod builds 215 """Packages the compiled Android Resources with AAPT. 216 217 Args: 218 ctx: The context. 219 out_r_src_jar: A File. The R.java outputted by linking resources in a srcjar. 220 out_r_txt: A File. The resource IDs outputted by linking resources in text. 221 out_symbols: A File. The output zip containing compiled resources. 222 out_manifest: A File. The output processed manifest. 223 out_proguard_cfg: A File. The proguard config to be generated. 224 out_main_dex_proguard_cfg: A File. The main dex proguard config to be generated. 225 out_resource_files_zip: A File. The resource files zipped by linking resources. 226 out_file: A File. The Resource APK outputted by linking resources. 227 package_type: A string. The configuration type to use when packaging. 228 java_package: A string. The Java package for the generated R.java. 229 manifest: A File. The AndroidManifest.xml. 230 assets: sequence of Files. A list of Android assets files to be processed. 231 assets_dir: String. The name of the assets directory. 232 resource_files: A list of Files. The resource files. 233 resource_configs: A list of strings. The list of resource configuration 234 filters. 235 densities: A list of strings. The list of screen densities to filter for when 236 building the apk. 237 application_id: An optional string. The applicationId set in manifest values. 238 package_id: An optional integer in [2,255]. This is the prefix byte for 239 all generated resource IDs, defaults to 0x7F (127). 1 is reserved by the 240 framework, and some builds are known to crash when given IDs > 127. 241 Shared libraries are also assigned monotonically increasing IDs in 242 [2,126], so care should be taken that there is room at the lower end. 243 direct_resources_nodes: Depset of ResourcesNodeInfo providers. The set of 244 ResourcesNodeInfo from direct dependencies. 245 transitive_resources_nodes: Depset of ResourcesNodeInfo providers. The set 246 of ResourcesNodeInfo from transitive dependencies (not including directs). 247 transitive_manifests: List of Depsets. Depsets contain all transitive manifests. 248 transitive_assets: List of Depsets. Depsets contain all transitive assets. 249 transitive_compiled_assets: List of Depsets. Depsets contain all transitive 250 compiled_assets. 251 transitive_resource_files: List of Depsets. Depsets contain all transitive 252 resource files. 253 transitive_compiled_resources: List of Depsets. Depsets contain all transitive 254 compiled_resources. 255 transitive_r_txts: List of Depsets. Depsets contain all transitive R txt files. 256 additional_apks_to_link_against: A list of Files. Additional APKs to link 257 against. Optional. 258 nocompress_extensions: A list of strings. File extension to leave uncompressed 259 in the apk. 260 proto_format: Boolean, whether to generate the resource table in proto format. 261 shrink_resource_cycles: Boolean, flag that enables more shrinking of 262 code and resources by instructing AAPT2 to emit conditional Proguard keep rules. 263 version_name: A string. The version name to stamp the generated manifest with. Optional. 264 version_code: A string. The version code to stamp the generated manifest with. Optional. 265 android_jar: A File. The Android Jar. 266 aapt: A FilesToRunProvider. The AAPT executable. 267 busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. 268 host_javabase: Target. The host javabase. 269 should_throw_on_conflict: A boolean. Determines whether an error should be thrown 270 when a resource conflict occurs. 271 debug: A boolean. Determines whether to enable debugging. 272 """ 273 if not manifest: 274 fail("No manifest given, the manifest is mandatory.") 275 276 direct_data_flag = [] 277 direct_compiled_resources = [] 278 279 output_files = [] 280 input_files = [] 281 transitive_input_files = [] 282 283 args = ctx.actions.args() 284 args.use_param_file("@%s") 285 args.add("--tool", "AAPT2_PACKAGE") 286 args.add("--") 287 args.add("--aapt2", aapt.executable) 288 args.add_joined( 289 "--data", 290 transitive_resources_nodes, 291 map_each = _make_package_resources_flags, 292 join_with = ",", 293 ) 294 args.add_joined( 295 "--directData", 296 direct_resources_nodes, 297 map_each = _make_package_resources_flags, 298 join_with = ",", 299 ) 300 args.add_joined( 301 "--directAssets", 302 direct_resources_nodes, 303 map_each = _make_package_assets_flags, 304 join_with = "&", 305 omit_if_empty = True, 306 ) 307 args.add_joined( 308 "--assets", 309 transitive_resources_nodes, 310 map_each = _make_package_assets_flags, 311 join_with = "&", 312 omit_if_empty = True, 313 ) 314 transitive_input_files.extend(transitive_resource_files) 315 transitive_input_files.extend(transitive_assets) 316 transitive_input_files.extend(transitive_compiled_assets) 317 transitive_input_files.extend(transitive_compiled_resources) 318 transitive_input_files.extend(transitive_manifests) 319 transitive_input_files.extend(transitive_r_txts) 320 args.add( 321 "--primaryData", 322 _make_resources_flag( 323 manifest = manifest, 324 assets = assets, 325 assets_dir = assets_dir, 326 resource_files = resource_files, 327 ), 328 ) 329 input_files.append(manifest) 330 input_files.extend(resource_files) 331 input_files.extend(assets) 332 args.add("--androidJar", android_jar) 333 input_files.append(android_jar) 334 args.add("--rOutput", out_r_txt) 335 output_files.append(out_r_txt) 336 if out_symbols: 337 args.add("--symbolsOut", out_symbols) 338 output_files.append(out_symbols) 339 args.add("--srcJarOutput", out_r_src_jar) 340 output_files.append(out_r_src_jar) 341 if out_proguard_cfg: 342 args.add("--proguardOutput", out_proguard_cfg) 343 output_files.append(out_proguard_cfg) 344 if out_main_dex_proguard_cfg: 345 args.add("--mainDexProguardOutput", out_main_dex_proguard_cfg) 346 output_files.append(out_main_dex_proguard_cfg) 347 args.add("--manifestOutput", out_manifest) 348 output_files.append(out_manifest) 349 if out_resource_files_zip: 350 args.add("--resourcesOutput", out_resource_files_zip) 351 output_files.append(out_resource_files_zip) 352 if out_file: 353 args.add("--packagePath", out_file) 354 output_files.append(out_file) 355 args.add("--useAaptCruncher=no") # Unnecessary, used for AAPT1 only but added here to minimize diffs. 356 if package_type: 357 args.add("--packageType", package_type) 358 if debug: 359 args.add("--debug") 360 if should_throw_on_conflict: 361 args.add("--throwOnResourceConflict") 362 if resource_configs: 363 args.add_joined("--resourceConfigs", _extract_filters(resource_configs), join_with = ",") 364 if densities: 365 args.add_joined("--densities", _extract_filters(densities), join_with = ",") 366 if application_id: 367 args.add("--applicationId", application_id) 368 if package_id: 369 args.add("--packageId", package_id) 370 if additional_apks_to_link_against: 371 args.add_joined( 372 "--additionalApksToLinkAgainst", 373 additional_apks_to_link_against, 374 join_with = ",", 375 map_each = _path, 376 ) 377 input_files.extend(additional_apks_to_link_against) 378 if nocompress_extensions: 379 args.add_joined("--uncompressedExtensions", nocompress_extensions, join_with = ",") 380 if proto_format: 381 args.add("--resourceTableAsProto") 382 if shrink_resource_cycles: 383 args.add("--conditionalKeepRules=yes") 384 if version_name: 385 args.add("--versionName", version_name) 386 if version_code: 387 args.add("--versionCode", version_code) 388 if java_package: 389 args.add("--packageForR", java_package) 390 391 _java.run( 392 ctx = ctx, 393 host_javabase = host_javabase, 394 executable = busybox, 395 tools = [aapt], 396 arguments = [args], 397 inputs = depset(input_files, transitive = transitive_input_files), 398 outputs = output_files, 399 mnemonic = "PackageAndroidResources", 400 progress_message = "Packaging Android Resources in %s" % ctx.label, 401 ) 402 403def _parse( 404 ctx, 405 out_symbols = None, 406 assets = [], 407 assets_dir = None, 408 busybox = None, 409 host_javabase = None): 410 """Parses Android assets. 411 412 Args: 413 ctx: The context. 414 out_symbols: A File. The output bin containing parsed assets. 415 assets: sequence of Files. A list of Android assets files to be processed. 416 assets_dir: String. The name of the assets directory. 417 busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. 418 host_javabase: Target. The host javabase. 419 """ 420 args = ctx.actions.args() 421 args.use_param_file("@%s") 422 args.add("--tool", "PARSE") 423 args.add("--") 424 args.add( 425 "--primaryData", 426 _make_resources_flag( 427 assets = assets, 428 assets_dir = assets_dir, 429 ), 430 ) 431 args.add("--output", out_symbols) 432 433 _java.run( 434 ctx = ctx, 435 host_javabase = host_javabase, 436 executable = busybox, 437 arguments = [args], 438 inputs = assets, 439 outputs = [out_symbols], 440 mnemonic = "ParseAndroidResources", 441 progress_message = "Parsing Android Resources in %s" % out_symbols.short_path, 442 ) 443 444def _make_merge_assets_flags(resources_node): 445 assets = resources_node.assets.to_list() 446 if not (assets or resources_node.assets_dir): 447 return None 448 return _make_serialized_resources_flag( 449 assets = assets, 450 assets_dir = resources_node.assets_dir, 451 label = str(resources_node.label), 452 symbols = resources_node.assets_symbols, 453 ) 454 455def _merge_assets( 456 ctx, 457 out_assets_zip = None, 458 assets = [], 459 assets_dir = None, 460 symbols = None, 461 transitive_assets = [], 462 transitive_assets_symbols = [], 463 direct_resources_nodes = [], 464 transitive_resources_nodes = [], 465 busybox = None, 466 host_javabase = None): 467 """Merges Android assets. 468 469 Args: 470 ctx: The context. 471 out_assets_zip: A File. 472 assets: sequence of Files. A list of Android assets files to be processed. 473 assets_dir: String. The name of the assets directory. 474 symbols: A File. The parsed assets. 475 transitive_assets: Sequence of Depsets. The list of transitive 476 assets from deps. 477 transitive_assets_symbols: Sequence of Depsets. The list of 478 transitive assets_symbols files from deps. 479 direct_resources_nodes: Sequence of ResourcesNodeInfo providers. The list 480 of ResourcesNodeInfo providers that are direct depencies. 481 transitive_resources_nodes: Sequence of ResourcesNodeInfo providers. The 482 list of ResourcesNodeInfo providers that are transitive depencies. 483 busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. 484 host_javabase: Target. The host javabase. 485 """ 486 args = ctx.actions.args() 487 args.use_param_file("@%s") 488 args.add("--tool", "MERGE_ASSETS") 489 args.add("--") 490 args.add("--assetsOutput", out_assets_zip) 491 args.add( 492 "--primaryData", 493 _make_serialized_resources_flag( 494 assets = assets, 495 assets_dir = assets_dir, 496 label = str(ctx.label), 497 symbols = symbols, 498 ), 499 ) 500 args.add_joined( 501 "--directData", 502 direct_resources_nodes, 503 map_each = _make_merge_assets_flags, 504 join_with = "&", 505 ) 506 args.add_joined( 507 "--data", 508 transitive_resources_nodes, 509 map_each = _make_merge_assets_flags, 510 join_with = "&", 511 ) 512 513 _java.run( 514 ctx = ctx, 515 host_javabase = host_javabase, 516 executable = busybox, 517 arguments = [args], 518 inputs = depset( 519 assets + [symbols], 520 transitive = transitive_assets + transitive_assets_symbols, 521 ), 522 outputs = [out_assets_zip], 523 mnemonic = "MergeAndroidAssets", 524 progress_message = 525 "Merging Android Assets in %s" % out_assets_zip.short_path, 526 ) 527 528def _validate_and_link( 529 ctx, 530 out_r_src_jar = None, 531 out_r_txt = None, 532 out_file = None, 533 compiled_resources = None, 534 transitive_compiled_resources = depset(), 535 java_package = None, 536 manifest = None, 537 android_jar = None, 538 busybox = None, 539 host_javabase = None, 540 aapt = None): 541 """Links compiled Android Resources with AAPT. 542 543 Args: 544 ctx: The context. 545 out_r_src_jar: A File. The R.java outputted by linking resources in a srcjar. 546 out_r_txt: A File. The resource IDs outputted by linking resources in text. 547 out_file: A File. The Resource APK outputted by linking resources. 548 compiled_resources: A File. The symbols.zip of compiled resources for 549 this target. 550 transitive_compiled_resources: Depset of Files. The symbols.zip of the 551 compiled resources from the transitive dependencies of this target. 552 java_package: A string. The Java package for the generated R.java. 553 manifest: A File. The AndroidManifest.xml. 554 android_jar: A File. The Android Jar. 555 busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. 556 host_javabase: Target. The host javabase. 557 aapt: A FilesToRunProvider. The AAPT executable. 558 """ 559 output_files = [] 560 input_files = [android_jar] 561 transitive_input_files = [] 562 563 # Retrieves the list of files at runtime when a directory is passed. 564 args = ctx.actions.args() 565 args.use_param_file("@%s") 566 args.add("--tool", "LINK_STATIC_LIBRARY") 567 args.add("--") 568 args.add("--aapt2", aapt.executable) 569 args.add("--libraries", android_jar) 570 if compiled_resources: 571 args.add("--compiled", compiled_resources) 572 input_files.append(compiled_resources) 573 args.add_joined( 574 "--compiledDep", 575 transitive_compiled_resources, 576 join_with = ":", 577 ) 578 transitive_input_files.append(transitive_compiled_resources) 579 args.add("--manifest", manifest) 580 input_files.append(manifest) 581 if java_package: 582 args.add("--packageForR", java_package) 583 args.add("--sourceJarOut", out_r_src_jar) 584 output_files.append(out_r_src_jar) 585 args.add("--rTxtOut", out_r_txt) 586 output_files.append(out_r_txt) 587 args.add("--staticLibraryOut", out_file) 588 output_files.append(out_file) 589 590 _java.run( 591 ctx = ctx, 592 host_javabase = host_javabase, 593 executable = busybox, 594 tools = [aapt], 595 arguments = [args], 596 inputs = depset(input_files, transitive = transitive_input_files), 597 outputs = output_files, 598 mnemonic = "LinkAndroidResources", 599 progress_message = 600 "Linking Android Resources in " + out_file.short_path, 601 ) 602 603def _compile( 604 ctx, 605 out_file = None, 606 assets = [], 607 assets_dir = None, 608 resource_files = [], 609 busybox = None, 610 aapt = None, 611 host_javabase = None): 612 """Compile and store resources in a single archive. 613 614 Args: 615 ctx: The context. 616 out_file: File. The output zip containing compiled resources. 617 resource_files: A list of Files. The list of resource files or directories 618 assets: A list of Files. The list of assets files or directories 619 to process. 620 assets_dir: String. The name of the assets directory. 621 busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. 622 aapt: AAPT. Tool for compiling resources. 623 host_javabase: Target. The host javabase. 624 """ 625 if not out_file: 626 fail("No output directory specified.") 627 628 # Retrieves the list of files at runtime when a directory is passed. 629 args = ctx.actions.args() 630 args.use_param_file("@%s") 631 args.add("--tool", "COMPILE_LIBRARY_RESOURCES") 632 args.add("--") 633 args.add("--aapt2", aapt.executable) 634 args.add( 635 "--resources", 636 _make_resources_flag( 637 resource_files = resource_files, 638 assets = assets, 639 assets_dir = assets_dir, 640 ), 641 ) 642 args.add("--output", out_file) 643 644 _java.run( 645 ctx = ctx, 646 host_javabase = host_javabase, 647 executable = busybox, 648 tools = [aapt], 649 arguments = [args], 650 inputs = resource_files + assets, 651 outputs = [out_file], 652 mnemonic = "CompileAndroidResources", 653 progress_message = "Compiling Android Resources in %s" % out_file.short_path, 654 ) 655 656def _make_merge_compiled_flags(resources_node_info): 657 if not resources_node_info.compiled_resources: 658 return None 659 return _make_serialized_resources_flag( 660 label = str(resources_node_info.label), 661 symbols = resources_node_info.compiled_resources, 662 ) 663 664def _merge_compiled( 665 ctx, 666 out_class_jar = None, 667 out_manifest = None, 668 out_aapt2_r_txt = None, 669 java_package = None, 670 manifest = None, 671 compiled_resources = None, 672 direct_resources_nodes = [], 673 transitive_resources_nodes = [], 674 direct_compiled_resources = depset(), 675 transitive_compiled_resources = depset(), 676 android_jar = None, 677 busybox = None, 678 host_javabase = None): 679 """Merges the compile resources. 680 681 Args: 682 ctx: The context. 683 out_class_jar: A File. The compiled R.java outputted by linking resources. 684 out_manifest: A File. The list of resource files or directories 685 out_aapt2_r_txt: A File. The resource IDs outputted by linking resources in text. 686 java_package: A string. The Java package for the generated R.java. 687 manifest: A File. The AndroidManifest.xml. 688 compiled_resources: A File. The symbols.zip of compiled resources for this target. 689 direct_resources_nodes: Sequence of ResourcesNodeInfo providers. The list 690 of ResourcesNodeInfo providers that are direct depencies. 691 transitive_resources_nodes: Sequence of ResourcesNodeInfo providers. The 692 list of ResourcesNodeInfo providers that are transitive depencies. 693 direct_compiled_resources: Depset of Files. A depset of symbols.zip of 694 compiled resources from direct dependencies. 695 transitive_compiled_resources: Depset of Files. A depset of symbols.zip of 696 compiled resources from transitive dependencies. 697 android_jar: A File. The Android Jar. 698 busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. 699 host_javabase: Target. The host javabase. 700 """ 701 output_files = [] 702 input_files = [android_jar] 703 transitive_input_files = [] 704 705 args = ctx.actions.args() 706 args.use_param_file("@%s") 707 args.add("--tool", "MERGE_COMPILED") 708 args.add("--") 709 args.add("--classJarOutput", out_class_jar) 710 output_files.append(out_class_jar) 711 args.add("--targetLabel", ctx.label) 712 args.add("--manifestOutput", out_manifest) 713 output_files.append(out_manifest) 714 args.add("--rTxtOut", out_aapt2_r_txt) 715 output_files.append(out_aapt2_r_txt) 716 args.add("--androidJar", android_jar) 717 args.add("--primaryManifest", manifest) 718 input_files.append(manifest) 719 if java_package: 720 args.add("--packageForR", java_package) 721 args.add( 722 "--primaryData", 723 _make_serialized_resources_flag( 724 label = str(ctx.label), 725 symbols = compiled_resources, 726 ), 727 ) 728 input_files.append(compiled_resources) 729 args.add_joined( 730 "--directData", 731 direct_resources_nodes, 732 map_each = _make_merge_compiled_flags, 733 join_with = "&", 734 ) 735 transitive_input_files.append(direct_compiled_resources) 736 if _ANDROID_RESOURCES_STRICT_DEPS in ctx.disabled_features: 737 args.add_joined( 738 "--data", 739 transitive_resources_nodes, 740 map_each = _make_merge_compiled_flags, 741 join_with = "&", 742 ) 743 transitive_input_files.append(transitive_compiled_resources) 744 745 _java.run( 746 ctx = ctx, 747 host_javabase = host_javabase, 748 executable = busybox, 749 arguments = [args], 750 inputs = depset(input_files, transitive = transitive_input_files), 751 outputs = output_files, 752 mnemonic = "StarlarkMergeCompiledAndroidResources", 753 progress_message = 754 "Merging compiled Android Resources in " + out_class_jar.short_path, 755 ) 756 757def _escape_mv(s): 758 """Escapes `:` and `,` in manifest values so they can be used as a busybox flag.""" 759 return s.replace(":", "\\:").replace(",", "\\,") 760 761def _owner_label(file): 762 return "//" + file.owner.package + ":" + file.owner.name 763 764# We need to remove the "/_migrated/" path segment from file paths in order for sorting to 765# match the order of the native manifest merging action. 766def _manifest_short_path(manifest): 767 return manifest.short_path.replace("/_migrated/", "/") 768 769def _mergee_manifests_flag(manifests): 770 ordered_manifests = sorted(manifests.to_list(), key = _manifest_short_path) 771 entries = [] 772 for manifest in ordered_manifests: 773 label = _owner_label(manifest).replace(":", "\\:") 774 entries.append((manifest.path + ":" + label).replace(",", "\\,")) 775 flag_entry = ",".join(entries) 776 if not flag_entry: 777 return None 778 return flag_entry 779 780def _merge_manifests( 781 ctx, 782 out_file = None, 783 out_log_file = None, 784 merge_type = "APPLICATION", 785 manifest = None, 786 mergee_manifests = depset(), 787 manifest_values = None, 788 java_package = None, 789 busybox = None, 790 host_javabase = None): 791 """Merge multiple AndroidManifest.xml files into a single one. 792 793 Args: 794 ctx: The context. 795 out_file: A File. The output merged manifest. 796 out_log_file: A File. The output log from the merge tool. 797 merge_type: A string, either APPLICATION or LIBRARY. Type of merging. 798 manifest: A File. The primary AndroidManifest.xml. 799 mergee_manifests: A depset of Files. All transitive manifests to be merged. 800 manifest_values: A dictionary. Manifest values to substitute. 801 java_package: A string. Custom java package to insert in manifest package attribute. 802 busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. 803 host_javabase: Target. The host javabase. 804 """ 805 if merge_type not in ["APPLICATION", "LIBRARY"]: 806 fail("Unexpected manifest merge type: " + merge_type) 807 808 outputs = [out_file] 809 directs = [manifest] if manifest else [] 810 transitives = [mergee_manifests] 811 812 # Args for busybox 813 args = ctx.actions.args() 814 args.use_param_file("@%s", use_always = True) 815 args.add("--tool", "MERGE_MANIFEST") 816 args.add("--") 817 if manifest: 818 args.add("--manifest", manifest) 819 args.add_all( 820 "--mergeeManifests", 821 [mergee_manifests], 822 map_each = _mergee_manifests_flag, 823 ) 824 if manifest_values: 825 args.add( 826 "--manifestValues", 827 ",".join(["%s:%s" % (_escape_mv(k), _escape_mv(v)) for k, v in manifest_values.items()]), 828 ) 829 args.add("--mergeType", merge_type) 830 if java_package: 831 args.add("--customPackage", java_package) 832 args.add("--manifestOutput", out_file) 833 if out_log_file: 834 args.add("--log", out_log_file) 835 outputs.append(out_log_file) 836 837 _java.run( 838 ctx = ctx, 839 host_javabase = host_javabase, 840 executable = busybox, 841 arguments = [args], 842 inputs = depset(directs, transitive = transitives), 843 outputs = outputs, 844 mnemonic = "MergeManifests", 845 progress_message = "Merging Android Manifests in %s" % out_file.short_path, 846 ) 847 848def _process_databinding( 849 ctx, 850 out_databinding_info = None, 851 out_databinding_processed_resources = None, 852 databinding_resources_dirname = None, 853 resource_files = None, 854 java_package = None, 855 busybox = None, 856 host_javabase = None): 857 """Processes databinding for android_binary. 858 859 Processes databinding declarations over resources, populates the databinding layout 860 info file, and generates new resources with databinding expressions stripped out. 861 862 Args: 863 ctx: The context. 864 out_databinding_info: File. The output databinding layout info zip file. 865 out_databinding_processed_resources: List of Files. The generated databinding 866 processed resource files. 867 databinding_resources_dirname: String. The execution path to the directory where 868 the out_databinding_processed_resources are generated. 869 resource_files: List of Files. The resource files to be processed. 870 java_package: String. Java package for which java sources will be 871 generated. By default the package is inferred from the directory where 872 the BUILD file containing the rule is. 873 busybox: FilesToRunProvider. The ResourceBusyBox executable or 874 FilesToRunprovider 875 host_javabase: A Target. The host javabase. 876 """ 877 res_dirs = _get_unique_res_dirs(resource_files) 878 879 args = ctx.actions.args() 880 args.add("--tool", "PROCESS_DATABINDING") 881 args.add("--") 882 args.add("--output_resource_directory", databinding_resources_dirname) 883 args.add_all(res_dirs, before_each = "--resource_root") 884 args.add("--dataBindingInfoOut", out_databinding_info) 885 args.add("--appId", java_package) 886 887 _java.run( 888 ctx = ctx, 889 host_javabase = host_javabase, 890 executable = busybox, 891 arguments = [args], 892 inputs = resource_files, 893 outputs = [out_databinding_info] + out_databinding_processed_resources, 894 mnemonic = "StarlarkProcessDatabinding", 895 progress_message = "Processing data binding", 896 ) 897 898def _make_generate_binay_r_flags(resources_node): 899 if not (resources_node.r_txt or resources_node.manifest): 900 return None 901 return ",".join( 902 [ 903 resources_node.r_txt.path if resources_node.r_txt else "", 904 resources_node.manifest.path if resources_node.manifest else "", 905 ], 906 ) 907 908def _generate_binary_r( 909 ctx, 910 out_class_jar = None, 911 r_txt = None, 912 manifest = None, 913 package_for_r = None, 914 final_fields = None, 915 resources_nodes = depset(), 916 transitive_r_txts = [], 917 transitive_manifests = [], 918 busybox = None, 919 host_javabase = None): 920 """Generate compiled resources class jar. 921 922 Args: 923 ctx: The context. 924 out_class_jar: File. The output class jar file. 925 r_txt: File. The resource IDs outputted by linking resources in text. 926 manifest: File. The primary AndroidManifest.xml. 927 package_for_r: String. The Java package for the generated R class files. 928 final_fields: Bool. Whether fields get declared as final. 929 busybox: FilesToRunProvider. The ResourceBusyBox executable or 930 FilesToRunprovider 931 host_javabase: A Target. The host javabase. 932 """ 933 args = ctx.actions.args() 934 args.add("--tool", "GENERATE_BINARY_R") 935 args.add("--") 936 args.add("--primaryRTxt", r_txt) 937 args.add("--primaryManifest", manifest) 938 if package_for_r: 939 args.add("--packageForR", package_for_r) 940 args.add_all( 941 resources_nodes, 942 map_each = _make_generate_binay_r_flags, 943 before_each = "--library", 944 ) 945 if final_fields: 946 args.add("--finalFields") 947 else: 948 args.add("--nofinalFields") 949 950 # TODO(b/154003916): support transitive "--library transitive_r_txt_path,transitive_manifest_path" flags 951 args.add("--classJarOutput", out_class_jar) 952 args.add("--targetLabel", str(ctx.label)) 953 args.use_param_file("@%s") 954 955 _java.run( 956 ctx = ctx, 957 host_javabase = host_javabase, 958 executable = busybox, 959 arguments = [args], 960 inputs = depset([r_txt, manifest], transitive = transitive_r_txts + transitive_manifests), 961 outputs = [out_class_jar], 962 mnemonic = "StarlarkRClassGenerator", 963 progress_message = "Generating R classes", 964 ) 965 966def _make_aar( 967 ctx, 968 out_aar = None, 969 assets = [], 970 assets_dir = None, 971 resource_files = [], 972 class_jar = None, 973 r_txt = None, 974 manifest = None, 975 proguard_specs = [], 976 should_throw_on_conflict = False, 977 busybox = None, 978 host_javabase = None): 979 """Generate an android archive file. 980 981 Args: 982 ctx: The context. 983 out_aar: File. The output AAR file. 984 assets: sequence of Files. A list of Android assets files to be processed. 985 assets_dir: String. The name of the assets directory. 986 resource_files: A list of Files. The resource files. 987 class_jar: File. The class jar file. 988 r_txt: File. The resource IDs outputted by linking resources in text. 989 manifest: File. The primary AndroidManifest.xml. 990 proguard_specs: List of File. The proguard spec files. 991 busybox: FilesToRunProvider. The ResourceBusyBox executable or 992 FilesToRunprovider 993 host_javabase: A Target. The host javabase. 994 should_throw_on_conflict: A boolean. Determines whether an error should be thrown 995 when a resource conflict occurs. 996 """ 997 args = ctx.actions.args() 998 args.add("--tool", "GENERATE_AAR") 999 args.add("--") 1000 args.add( 1001 "--mainData", 1002 _make_resources_flag( 1003 manifest = manifest, 1004 assets = assets, 1005 assets_dir = assets_dir, 1006 resource_files = resource_files, 1007 ), 1008 ) 1009 args.add("--manifest", manifest) 1010 args.add("--rtxt", r_txt) 1011 args.add("--classes", class_jar) 1012 args.add("--aarOutput", out_aar) 1013 args.add_all(proguard_specs, before_each = "--proguardSpec") 1014 if should_throw_on_conflict: 1015 args.add("--throwOnResourceConflict") 1016 1017 _java.run( 1018 ctx = ctx, 1019 host_javabase = host_javabase, 1020 executable = busybox, 1021 arguments = [args], 1022 inputs = ( 1023 resource_files + 1024 assets + 1025 proguard_specs + 1026 [r_txt, manifest, class_jar] 1027 ), 1028 outputs = [out_aar], 1029 mnemonic = "StarlarkAARGenerator", 1030 progress_message = "Generating AAR package for %s" % ctx.label, 1031 ) 1032 1033busybox = struct( 1034 compile = _compile, 1035 merge_compiled = _merge_compiled, 1036 validate_and_link = _validate_and_link, 1037 merge_manifests = _merge_manifests, 1038 package = _package, 1039 parse = _parse, 1040 merge_assets = _merge_assets, 1041 make_resources_flag = _make_resources_flag, 1042 process_databinding = _process_databinding, 1043 generate_binary_r = _generate_binary_r, 1044 make_aar = _make_aar, 1045 1046 # Exposed for testing 1047 mergee_manifests_flag = _mergee_manifests_flag, 1048 get_unique_res_dirs = _get_unique_res_dirs, 1049 sanitize_assets_dir = _sanitize_assets_dir, 1050 extract_filters = _extract_filters, 1051) 1052