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