| # Copyright 2019 The Bazel Authors. All rights reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| """Bazel ResourcesBusyBox Commands.""" |
| |
| load(":java.bzl", _java = "java") |
| |
| _ANDROID_RESOURCES_STRICT_DEPS = "android_resources_strict_deps" |
| |
| def _sanitize_assets_dir(assets_dir): |
| sanitized_assets_dir = "/".join( |
| [ |
| part |
| for part in assets_dir.split("/") |
| if part != "" and part != "." |
| ], |
| ) |
| |
| return "/" + sanitized_assets_dir if assets_dir.startswith("/") else sanitized_assets_dir |
| |
| def _get_unique_assets_dirs(assets, assets_dir): |
| """Find the unique assets directories, partitioned by assets_dir. |
| |
| Args: |
| assets: A list of Files. List of asset files to process. |
| assets_dir: String. String giving the path to the files in assets. |
| |
| Returns: |
| A list of short_paths representing unique asset dirs. |
| """ |
| if not assets: |
| return [] |
| |
| dirs = dict() |
| |
| assets_dir = _sanitize_assets_dir(assets_dir) |
| if assets_dir: |
| partition_by = "/%s/" % assets_dir.strip("/") |
| for f in assets: |
| if f.is_directory and f.path.endswith(partition_by[:-1]): |
| # If f is a directory, check if its path ends with the assets_dir. |
| dirs[f.path] = True |
| elif f.is_directory and "_aar/unzipped" in f.path: |
| # Assets from an aar_import rule are extracted in a |
| # "assets" subdirectory of the given path |
| dirs["%s/assets" % f.path] = True |
| else: |
| # Partition to remove subdirectories beneath assets_dir |
| # Also removes the trailing / |
| dirs["".join(f.path.rpartition(partition_by)[:2])[:-1]] = True |
| else: |
| # Use the dirname of the generating target if no assets_dir. |
| for f in assets: |
| if f.is_source: |
| dirs[f.owner.package] = True |
| else: |
| # Prepend the root path for generated files. |
| dirs[f.root.path + "/" + f.owner.package] = True |
| return dirs.keys() |
| |
| def _get_unique_res_dirs(resource_files): |
| """Find the unique res dirs. |
| |
| Args: |
| resource_files: A list of Files. A list of resource_files. |
| |
| Returns: |
| A list of short_paths representing unique res dirs from the given resource files. |
| """ |
| dirs = dict() |
| for f in resource_files: |
| if f.is_directory: |
| dirs[f.path] = True |
| else: |
| dirs[f.dirname.rpartition("/" + f.dirname.split("/")[-1])[0]] = True |
| return dirs.keys() |
| |
| def _make_serialized_resources_flag( |
| assets = [], |
| assets_dir = None, |
| resource_files = [], |
| label = "", |
| symbols = None): |
| return ";".join( |
| [ |
| "#".join(_get_unique_res_dirs(resource_files)), |
| "#".join(_get_unique_assets_dirs(assets, assets_dir)), |
| label, |
| symbols.path if symbols else "", |
| ], |
| ).rstrip(":") |
| |
| def _make_resources_flag( |
| assets = [], |
| assets_dir = None, |
| resource_files = [], |
| manifest = None, |
| r_txt = None, |
| symbols = None): |
| return ":".join( |
| [ |
| "#".join(_get_unique_res_dirs(resource_files)), |
| "#".join(_get_unique_assets_dirs(assets, assets_dir)), |
| manifest.path if manifest else "", |
| r_txt.path if r_txt else "", |
| symbols.path if symbols else "", |
| ], |
| ) |
| |
| def _path(f): |
| return f.path |
| |
| def _make_package_resources_flags(resources_node): |
| if not (resources_node.manifest and resources_node.r_txt and resources_node.compiled_resources): |
| return None |
| flag = _make_resources_flag( |
| resource_files = resources_node.resource_files.to_list(), |
| assets = resources_node.assets.to_list(), |
| assets_dir = resources_node.assets_dir, |
| manifest = resources_node.manifest, |
| r_txt = resources_node.r_txt, |
| symbols = resources_node.compiled_resources, |
| ) |
| return flag |
| |
| def _make_package_assets_flags(resources_node): |
| assets = resources_node.assets.to_list() |
| if not assets: |
| return None |
| return _make_serialized_resources_flag( |
| assets = assets, |
| assets_dir = resources_node.assets_dir, |
| label = str(resources_node.label), |
| symbols = resources_node.compiled_assets, |
| ) |
| |
| def _extract_filters( |
| raw_list): |
| """Extract densities and resource_configuration filters from raw string lists. |
| |
| In BUILD files, string lists can be represented as a list of strings, a single comma-separated |
| string, or a combination of both. This method outputs a single list of individual string values, |
| which can then be passed directly to resource processing actions. Empty strings are removed and |
| the final list is sorted. |
| |
| Args: |
| raw_list: List of strings. The raw densities or resource configuration filters. |
| |
| Returns: |
| List of strings extracted from the raw list. |
| """ |
| out_filters = [] |
| for item in raw_list: |
| if "," in item: |
| item_list = item.split(",") |
| for entry in item_list: |
| stripped_entry = entry.strip() |
| if stripped_entry: |
| out_filters.append(stripped_entry) |
| elif item: |
| out_filters.append(item) |
| return sorted(out_filters) |
| |
| def _package( |
| ctx, |
| out_r_src_jar = None, |
| out_r_txt = None, |
| out_symbols = None, |
| out_manifest = None, |
| out_proguard_cfg = None, |
| out_main_dex_proguard_cfg = None, |
| out_resource_files_zip = None, |
| out_file = None, |
| package_type = None, |
| java_package = None, |
| manifest = None, |
| assets = [], |
| assets_dir = None, |
| resource_files = [], |
| resource_configs = None, |
| densities = [], |
| application_id = None, |
| package_id = None, |
| direct_resources_nodes = [], |
| transitive_resources_nodes = [], |
| transitive_manifests = [], |
| transitive_assets = [], |
| transitive_compiled_assets = [], |
| transitive_resource_files = [], |
| transitive_compiled_resources = [], |
| transitive_r_txts = [], |
| additional_apks_to_link_against = [], |
| resource_apks = depset(), |
| nocompress_extensions = [], |
| proto_format = False, |
| shrink_resource_cycles = False, |
| version_name = None, |
| version_code = None, |
| android_jar = None, |
| aapt = None, |
| busybox = None, |
| host_javabase = None, |
| should_throw_on_conflict = True, # TODO: read this from allowlist at caller |
| debug = True): # TODO: we will set this to false in prod builds |
| """Packages the compiled Android Resources with AAPT. |
| |
| Args: |
| ctx: The context. |
| out_r_src_jar: A File. The R.java outputted by linking resources in a srcjar. |
| out_r_txt: A File. The resource IDs outputted by linking resources in text. |
| out_symbols: A File. The output zip containing compiled resources. |
| out_manifest: A File. The output processed manifest. |
| out_proguard_cfg: A File. The proguard config to be generated. |
| out_main_dex_proguard_cfg: A File. The main dex proguard config to be generated. |
| out_resource_files_zip: A File. The resource files zipped by linking resources. |
| out_file: A File. The Resource APK outputted by linking resources. |
| package_type: A string. The configuration type to use when packaging. |
| java_package: A string. The Java package for the generated R.java. |
| manifest: A File. The AndroidManifest.xml. |
| assets: sequence of Files. A list of Android assets files to be processed. |
| assets_dir: String. The name of the assets directory. |
| resource_files: A list of Files. The resource files. |
| resource_configs: A list of strings. The list of resource configuration |
| filters. |
| densities: A list of strings. The list of screen densities to filter for when |
| building the apk. |
| application_id: An optional string. The applicationId set in manifest values. |
| package_id: An optional integer in [2,255]. This is the prefix byte for |
| all generated resource IDs, defaults to 0x7F (127). 1 is reserved by the |
| framework, and some builds are known to crash when given IDs > 127. |
| Shared libraries are also assigned monotonically increasing IDs in |
| [2,126], so care should be taken that there is room at the lower end. |
| direct_resources_nodes: Depset of ResourcesNodeInfo providers. The set of |
| ResourcesNodeInfo from direct dependencies. |
| transitive_resources_nodes: Depset of ResourcesNodeInfo providers. The set |
| of ResourcesNodeInfo from transitive dependencies (not including directs). |
| transitive_manifests: List of Depsets. Depsets contain all transitive manifests. |
| transitive_assets: List of Depsets. Depsets contain all transitive assets. |
| transitive_compiled_assets: List of Depsets. Depsets contain all transitive |
| compiled_assets. |
| transitive_resource_files: List of Depsets. Depsets contain all transitive |
| resource files. |
| transitive_compiled_resources: List of Depsets. Depsets contain all transitive |
| compiled_resources. |
| transitive_r_txts: List of Depsets. Depsets contain all transitive R txt files. |
| additional_apks_to_link_against: A list of Files. Additional APKs to link |
| against. Optional. |
| resource_apks: Depset of resource only apk files to link against. |
| nocompress_extensions: A list of strings. File extension to leave uncompressed |
| in the apk. |
| proto_format: Boolean, whether to generate the resource table in proto format. |
| shrink_resource_cycles: Boolean, flag that enables more shrinking of |
| code and resources by instructing AAPT2 to emit conditional Proguard keep rules. |
| version_name: A string. The version name to stamp the generated manifest with. Optional. |
| version_code: A string. The version code to stamp the generated manifest with. Optional. |
| android_jar: A File. The Android Jar. |
| aapt: A FilesToRunProvider. The AAPT executable. |
| busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. |
| host_javabase: Target. The host javabase. |
| should_throw_on_conflict: A boolean. Determines whether an error should be thrown |
| when a resource conflict occurs. |
| debug: A boolean. Determines whether to enable debugging. |
| """ |
| if not manifest: |
| fail("No manifest given, the manifest is mandatory.") |
| |
| direct_data_flag = [] |
| direct_compiled_resources = [] |
| |
| output_files = [] |
| input_files = [] |
| transitive_input_files = [] |
| |
| args = ctx.actions.args() |
| args.use_param_file("@%s") |
| args.add("--tool", "AAPT2_PACKAGE") |
| args.add("--") |
| args.add("--aapt2", aapt.executable) |
| args.add_joined( |
| "--data", |
| transitive_resources_nodes, |
| map_each = _make_package_resources_flags, |
| join_with = ",", |
| ) |
| args.add_joined( |
| "--directData", |
| direct_resources_nodes, |
| map_each = _make_package_resources_flags, |
| join_with = ",", |
| ) |
| args.add_joined( |
| "--directAssets", |
| direct_resources_nodes, |
| map_each = _make_package_assets_flags, |
| join_with = "&", |
| omit_if_empty = True, |
| ) |
| args.add_joined( |
| "--assets", |
| transitive_resources_nodes, |
| map_each = _make_package_assets_flags, |
| join_with = "&", |
| omit_if_empty = True, |
| ) |
| transitive_input_files.extend(transitive_resource_files) |
| transitive_input_files.extend(transitive_assets) |
| transitive_input_files.extend(transitive_compiled_assets) |
| transitive_input_files.extend(transitive_compiled_resources) |
| transitive_input_files.extend(transitive_manifests) |
| transitive_input_files.extend(transitive_r_txts) |
| args.add( |
| "--primaryData", |
| _make_resources_flag( |
| manifest = manifest, |
| assets = assets, |
| assets_dir = assets_dir, |
| resource_files = resource_files, |
| ), |
| ) |
| input_files.append(manifest) |
| input_files.extend(resource_files) |
| input_files.extend(assets) |
| args.add("--androidJar", android_jar) |
| input_files.append(android_jar) |
| args.add("--rOutput", out_r_txt) |
| output_files.append(out_r_txt) |
| if out_symbols: |
| args.add("--symbolsOut", out_symbols) |
| output_files.append(out_symbols) |
| args.add("--srcJarOutput", out_r_src_jar) |
| output_files.append(out_r_src_jar) |
| if out_proguard_cfg: |
| args.add("--proguardOutput", out_proguard_cfg) |
| output_files.append(out_proguard_cfg) |
| if out_main_dex_proguard_cfg: |
| args.add("--mainDexProguardOutput", out_main_dex_proguard_cfg) |
| output_files.append(out_main_dex_proguard_cfg) |
| args.add("--manifestOutput", out_manifest) |
| output_files.append(out_manifest) |
| if out_resource_files_zip: |
| args.add("--resourcesOutput", out_resource_files_zip) |
| output_files.append(out_resource_files_zip) |
| if out_file: |
| args.add("--packagePath", out_file) |
| output_files.append(out_file) |
| args.add("--useAaptCruncher=no") # Unnecessary, used for AAPT1 only but added here to minimize diffs. |
| if package_type: |
| args.add("--packageType", package_type) |
| if debug: |
| args.add("--debug") |
| if should_throw_on_conflict: |
| args.add("--throwOnResourceConflict") |
| if resource_configs: |
| args.add_joined("--resourceConfigs", _extract_filters(resource_configs), join_with = ",") |
| if densities: |
| args.add_joined("--densities", _extract_filters(densities), join_with = ",") |
| if application_id: |
| args.add("--applicationId", application_id) |
| if package_id: |
| args.add("--packageId", package_id) |
| if additional_apks_to_link_against: |
| args.add_joined( |
| "--additionalApksToLinkAgainst", |
| additional_apks_to_link_against, |
| join_with = ":", |
| map_each = _path, |
| ) |
| input_files.extend(additional_apks_to_link_against) |
| if nocompress_extensions: |
| args.add_joined("--uncompressedExtensions", nocompress_extensions, join_with = ",") |
| if proto_format: |
| args.add("--resourceTableAsProto") |
| if shrink_resource_cycles: |
| args.add("--conditionalKeepRules=yes") |
| if version_name: |
| args.add("--versionName", version_name) |
| if version_code: |
| args.add("--versionCode", version_code) |
| if java_package: |
| args.add("--packageForR", java_package) |
| |
| args.add_joined( |
| "--resourceApks", |
| resource_apks, |
| join_with = ":", |
| ) |
| transitive_input_files.append(resource_apks) |
| |
| _java.run( |
| ctx = ctx, |
| host_javabase = host_javabase, |
| executable = busybox, |
| tools = [aapt], |
| arguments = [args], |
| inputs = depset(input_files, transitive = transitive_input_files), |
| outputs = output_files, |
| mnemonic = "PackageAndroidResources", |
| progress_message = "Packaging Android Resources in %s" % ctx.label, |
| ) |
| |
| def _parse( |
| ctx, |
| out_symbols = None, |
| assets = [], |
| assets_dir = None, |
| busybox = None, |
| host_javabase = None): |
| """Parses Android assets. |
| |
| Args: |
| ctx: The context. |
| out_symbols: A File. The output bin containing parsed assets. |
| assets: sequence of Files. A list of Android assets files to be processed. |
| assets_dir: String. The name of the assets directory. |
| busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. |
| host_javabase: Target. The host javabase. |
| """ |
| args = ctx.actions.args() |
| args.use_param_file("@%s") |
| args.add("--tool", "PARSE") |
| args.add("--") |
| args.add( |
| "--primaryData", |
| _make_resources_flag( |
| assets = assets, |
| assets_dir = assets_dir, |
| ), |
| ) |
| args.add("--output", out_symbols) |
| |
| _java.run( |
| ctx = ctx, |
| host_javabase = host_javabase, |
| executable = busybox, |
| arguments = [args], |
| inputs = assets, |
| outputs = [out_symbols], |
| mnemonic = "ParseAndroidResources", |
| progress_message = "Parsing Android Resources in %s" % out_symbols.short_path, |
| ) |
| |
| def _make_merge_assets_flags(resources_node): |
| assets = resources_node.assets.to_list() |
| if not (assets or resources_node.assets_dir): |
| return None |
| return _make_serialized_resources_flag( |
| assets = assets, |
| assets_dir = resources_node.assets_dir, |
| label = str(resources_node.label), |
| symbols = resources_node.assets_symbols, |
| ) |
| |
| def _merge_assets( |
| ctx, |
| out_assets_zip = None, |
| assets = [], |
| assets_dir = None, |
| symbols = None, |
| transitive_assets = [], |
| transitive_assets_symbols = [], |
| direct_resources_nodes = [], |
| transitive_resources_nodes = [], |
| busybox = None, |
| host_javabase = None): |
| """Merges Android assets. |
| |
| Args: |
| ctx: The context. |
| out_assets_zip: A File. |
| assets: sequence of Files. A list of Android assets files to be processed. |
| assets_dir: String. The name of the assets directory. |
| symbols: A File. The parsed assets. |
| transitive_assets: Sequence of Depsets. The list of transitive |
| assets from deps. |
| transitive_assets_symbols: Sequence of Depsets. The list of |
| transitive assets_symbols files from deps. |
| direct_resources_nodes: Sequence of ResourcesNodeInfo providers. The list |
| of ResourcesNodeInfo providers that are direct depencies. |
| transitive_resources_nodes: Sequence of ResourcesNodeInfo providers. The |
| list of ResourcesNodeInfo providers that are transitive depencies. |
| busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. |
| host_javabase: Target. The host javabase. |
| """ |
| args = ctx.actions.args() |
| args.use_param_file("@%s") |
| args.add("--tool", "MERGE_ASSETS") |
| args.add("--") |
| args.add("--assetsOutput", out_assets_zip) |
| args.add( |
| "--primaryData", |
| _make_serialized_resources_flag( |
| assets = assets, |
| assets_dir = assets_dir, |
| label = str(ctx.label), |
| symbols = symbols, |
| ), |
| ) |
| args.add_joined( |
| "--directData", |
| direct_resources_nodes, |
| map_each = _make_merge_assets_flags, |
| join_with = "&", |
| ) |
| args.add_joined( |
| "--data", |
| transitive_resources_nodes, |
| map_each = _make_merge_assets_flags, |
| join_with = "&", |
| ) |
| |
| _java.run( |
| ctx = ctx, |
| host_javabase = host_javabase, |
| executable = busybox, |
| arguments = [args], |
| inputs = depset( |
| assets + [symbols], |
| transitive = transitive_assets + transitive_assets_symbols, |
| ), |
| outputs = [out_assets_zip], |
| mnemonic = "MergeAndroidAssets", |
| progress_message = |
| "Merging Android Assets in %s" % out_assets_zip.short_path, |
| ) |
| |
| def _validate_and_link( |
| ctx, |
| out_r_src_jar = None, |
| out_r_txt = None, |
| out_file = None, |
| compiled_resources = None, |
| transitive_compiled_resources = depset(), |
| java_package = None, |
| manifest = None, |
| resource_apks = [], |
| android_jar = None, |
| busybox = None, |
| host_javabase = None, |
| aapt = None): |
| """Links compiled Android Resources with AAPT. |
| |
| Args: |
| ctx: The context. |
| out_r_src_jar: A File. The R.java outputted by linking resources in a srcjar. |
| out_r_txt: A File. The resource IDs outputted by linking resources in text. |
| out_file: A File. The Resource APK outputted by linking resources. |
| compiled_resources: A File. The symbols.zip of compiled resources for |
| this target. |
| transitive_compiled_resources: Depset of Files. The symbols.zip of the |
| compiled resources from the transitive dependencies of this target. |
| java_package: A string. The Java package for the generated R.java. |
| manifest: A File. The AndroidManifest.xml. |
| resource_apks: List of direct resource only apk files. |
| android_jar: A File. The Android Jar. |
| busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. |
| host_javabase: Target. The host javabase. |
| aapt: A FilesToRunProvider. The AAPT executable. |
| """ |
| output_files = [] |
| input_files = [android_jar] |
| transitive_input_files = [] |
| |
| # Retrieves the list of files at runtime when a directory is passed. |
| args = ctx.actions.args() |
| args.use_param_file("@%s") |
| args.add("--tool", "LINK_STATIC_LIBRARY") |
| args.add("--") |
| args.add("--aapt2", aapt.executable) |
| args.add("--libraries", android_jar) |
| if compiled_resources: |
| args.add("--compiled", compiled_resources) |
| input_files.append(compiled_resources) |
| args.add_joined( |
| "--compiledDep", |
| transitive_compiled_resources, |
| join_with = ":", |
| ) |
| transitive_input_files.append(transitive_compiled_resources) |
| args.add("--manifest", manifest) |
| input_files.append(manifest) |
| if java_package: |
| args.add("--packageForR", java_package) |
| args.add("--sourceJarOut", out_r_src_jar) |
| output_files.append(out_r_src_jar) |
| args.add("--rTxtOut", out_r_txt) |
| output_files.append(out_r_txt) |
| args.add("--staticLibraryOut", out_file) |
| output_files.append(out_file) |
| args.add_joined( |
| "--resourceApks", |
| resource_apks, |
| join_with = ":", |
| ) |
| input_files.extend(resource_apks) |
| |
| _java.run( |
| ctx = ctx, |
| host_javabase = host_javabase, |
| executable = busybox, |
| tools = [aapt], |
| arguments = [args], |
| inputs = depset(input_files, transitive = transitive_input_files), |
| outputs = output_files, |
| mnemonic = "LinkAndroidResources", |
| progress_message = |
| "Linking Android Resources in " + out_file.short_path, |
| ) |
| |
| def _compile( |
| ctx, |
| out_file = None, |
| assets = [], |
| assets_dir = None, |
| resource_files = [], |
| busybox = None, |
| aapt = None, |
| host_javabase = None): |
| """Compile and store resources in a single archive. |
| |
| Args: |
| ctx: The context. |
| out_file: File. The output zip containing compiled resources. |
| resource_files: A list of Files. The list of resource files or directories |
| assets: A list of Files. The list of assets files or directories |
| to process. |
| assets_dir: String. The name of the assets directory. |
| busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. |
| aapt: AAPT. Tool for compiling resources. |
| host_javabase: Target. The host javabase. |
| """ |
| if not out_file: |
| fail("No output directory specified.") |
| |
| # Retrieves the list of files at runtime when a directory is passed. |
| args = ctx.actions.args() |
| args.use_param_file("@%s") |
| args.add("--tool", "COMPILE_LIBRARY_RESOURCES") |
| args.add("--") |
| args.add("--aapt2", aapt.executable) |
| args.add( |
| "--resources", |
| _make_resources_flag( |
| resource_files = resource_files, |
| assets = assets, |
| assets_dir = assets_dir, |
| ), |
| ) |
| args.add("--output", out_file) |
| |
| _java.run( |
| ctx = ctx, |
| host_javabase = host_javabase, |
| executable = busybox, |
| tools = [aapt], |
| arguments = [args], |
| inputs = resource_files + assets, |
| outputs = [out_file], |
| mnemonic = "CompileAndroidResources", |
| progress_message = "Compiling Android Resources in %s" % out_file.short_path, |
| ) |
| |
| def _make_merge_compiled_flags(resources_node_info): |
| if not resources_node_info.compiled_resources: |
| return None |
| return _make_serialized_resources_flag( |
| label = str(resources_node_info.label), |
| symbols = resources_node_info.compiled_resources, |
| ) |
| |
| def _merge_compiled( |
| ctx, |
| out_class_jar = None, |
| out_manifest = None, |
| out_aapt2_r_txt = None, |
| java_package = None, |
| manifest = None, |
| compiled_resources = None, |
| direct_resources_nodes = [], |
| transitive_resources_nodes = [], |
| direct_compiled_resources = depset(), |
| transitive_compiled_resources = depset(), |
| android_jar = None, |
| busybox = None, |
| host_javabase = None): |
| """Merges the compile resources. |
| |
| Args: |
| ctx: The context. |
| out_class_jar: A File. The compiled R.java outputted by linking resources. |
| out_manifest: A File. The list of resource files or directories |
| out_aapt2_r_txt: A File. The resource IDs outputted by linking resources in text. |
| java_package: A string. The Java package for the generated R.java. |
| manifest: A File. The AndroidManifest.xml. |
| compiled_resources: A File. The symbols.zip of compiled resources for this target. |
| direct_resources_nodes: Sequence of ResourcesNodeInfo providers. The list |
| of ResourcesNodeInfo providers that are direct depencies. |
| transitive_resources_nodes: Sequence of ResourcesNodeInfo providers. The |
| list of ResourcesNodeInfo providers that are transitive depencies. |
| direct_compiled_resources: Depset of Files. A depset of symbols.zip of |
| compiled resources from direct dependencies. |
| transitive_compiled_resources: Depset of Files. A depset of symbols.zip of |
| compiled resources from transitive dependencies. |
| android_jar: A File. The Android Jar. |
| busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. |
| host_javabase: Target. The host javabase. |
| """ |
| output_files = [] |
| input_files = [android_jar] |
| transitive_input_files = [] |
| |
| args = ctx.actions.args() |
| args.use_param_file("@%s") |
| args.add("--tool", "MERGE_COMPILED") |
| args.add("--") |
| args.add("--classJarOutput", out_class_jar) |
| output_files.append(out_class_jar) |
| args.add("--targetLabel", ctx.label) |
| args.add("--manifestOutput", out_manifest) |
| output_files.append(out_manifest) |
| args.add("--rTxtOut", out_aapt2_r_txt) |
| output_files.append(out_aapt2_r_txt) |
| args.add("--androidJar", android_jar) |
| args.add("--primaryManifest", manifest) |
| input_files.append(manifest) |
| if java_package: |
| args.add("--packageForR", java_package) |
| args.add( |
| "--primaryData", |
| _make_serialized_resources_flag( |
| label = str(ctx.label), |
| symbols = compiled_resources, |
| ), |
| ) |
| input_files.append(compiled_resources) |
| args.add_joined( |
| "--directData", |
| direct_resources_nodes, |
| map_each = _make_merge_compiled_flags, |
| join_with = "&", |
| ) |
| transitive_input_files.append(direct_compiled_resources) |
| if _ANDROID_RESOURCES_STRICT_DEPS in ctx.disabled_features: |
| args.add_joined( |
| "--data", |
| transitive_resources_nodes, |
| map_each = _make_merge_compiled_flags, |
| join_with = "&", |
| ) |
| transitive_input_files.append(transitive_compiled_resources) |
| |
| _java.run( |
| ctx = ctx, |
| host_javabase = host_javabase, |
| executable = busybox, |
| arguments = [args], |
| inputs = depset(input_files, transitive = transitive_input_files), |
| outputs = output_files, |
| mnemonic = "StarlarkMergeCompiledAndroidResources", |
| progress_message = |
| "Merging compiled Android Resources in " + out_class_jar.short_path, |
| ) |
| |
| def _escape_mv(s): |
| """Escapes `:` and `,` in manifest values so they can be used as a busybox flag.""" |
| return s.replace(":", "\\:").replace(",", "\\,") |
| |
| def _owner_label(file): |
| return "//" + file.owner.package + ":" + file.owner.name |
| |
| # We need to remove the "/_migrated/" path segment from file paths in order for sorting to |
| # match the order of the native manifest merging action. |
| def _manifest_short_path(manifest): |
| return manifest.short_path.replace("/_migrated/", "/") |
| |
| def _mergee_manifests_flag(manifests): |
| ordered_manifests = sorted(manifests.to_list(), key = _manifest_short_path) |
| entries = [] |
| for manifest in ordered_manifests: |
| label = _owner_label(manifest).replace(":", "\\:") |
| entries.append((manifest.path + ":" + label).replace(",", "\\,")) |
| flag_entry = ",".join(entries) |
| if not flag_entry: |
| return None |
| return flag_entry |
| |
| def _merge_manifests( |
| ctx, |
| out_file = None, |
| out_log_file = None, |
| merge_type = "APPLICATION", |
| manifest = None, |
| mergee_manifests = depset(), |
| manifest_values = None, |
| java_package = None, |
| busybox = None, |
| host_javabase = None): |
| """Merge multiple AndroidManifest.xml files into a single one. |
| |
| Args: |
| ctx: The context. |
| out_file: A File. The output merged manifest. |
| out_log_file: A File. The output log from the merge tool. |
| merge_type: A string, either APPLICATION or LIBRARY. Type of merging. |
| manifest: A File. The primary AndroidManifest.xml. |
| mergee_manifests: A depset of Files. All transitive manifests to be merged. |
| manifest_values: A dictionary. Manifest values to substitute. |
| java_package: A string. Custom java package to insert in manifest package attribute. |
| busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. |
| host_javabase: Target. The host javabase. |
| """ |
| if merge_type not in ["APPLICATION", "LIBRARY"]: |
| fail("Unexpected manifest merge type: " + merge_type) |
| |
| outputs = [out_file] |
| directs = [manifest] if manifest else [] |
| transitives = [mergee_manifests] |
| |
| # Args for busybox |
| args = ctx.actions.args() |
| args.use_param_file("@%s", use_always = True) |
| args.add("--tool", "MERGE_MANIFEST") |
| args.add("--") |
| if manifest: |
| args.add("--manifest", manifest) |
| args.add_all( |
| "--mergeeManifests", |
| [mergee_manifests], |
| map_each = _mergee_manifests_flag, |
| ) |
| if manifest_values: |
| args.add( |
| "--manifestValues", |
| ",".join(["%s:%s" % (_escape_mv(k), _escape_mv(v)) for k, v in manifest_values.items()]), |
| ) |
| args.add("--mergeType", merge_type) |
| if java_package: |
| args.add("--customPackage", java_package) |
| args.add("--manifestOutput", out_file) |
| if out_log_file: |
| args.add("--log", out_log_file) |
| outputs.append(out_log_file) |
| |
| _java.run( |
| ctx = ctx, |
| host_javabase = host_javabase, |
| executable = busybox, |
| arguments = [args], |
| inputs = depset(directs, transitive = transitives), |
| outputs = outputs, |
| mnemonic = "MergeManifests", |
| progress_message = "Merging Android Manifests in %s" % out_file.short_path, |
| ) |
| |
| def _process_databinding( |
| ctx, |
| out_databinding_info = None, |
| out_databinding_processed_resources = None, |
| databinding_resources_dirname = None, |
| resource_files = None, |
| java_package = None, |
| busybox = None, |
| host_javabase = None): |
| """Processes databinding for android_binary. |
| |
| Processes databinding declarations over resources, populates the databinding layout |
| info file, and generates new resources with databinding expressions stripped out. |
| |
| Args: |
| ctx: The context. |
| out_databinding_info: File. The output databinding layout info zip file. |
| out_databinding_processed_resources: List of Files. The generated databinding |
| processed resource files. |
| databinding_resources_dirname: String. The execution path to the directory where |
| the out_databinding_processed_resources are generated. |
| resource_files: List of Files. The resource files to be processed. |
| java_package: String. Java package for which java sources will be |
| generated. By default the package is inferred from the directory where |
| the BUILD file containing the rule is. |
| busybox: FilesToRunProvider. The ResourceBusyBox executable or |
| FilesToRunprovider |
| host_javabase: A Target. The host javabase. |
| """ |
| res_dirs = _get_unique_res_dirs(resource_files) |
| |
| args = ctx.actions.args() |
| args.add("--tool", "PROCESS_DATABINDING") |
| args.add("--") |
| args.add("--output_resource_directory", databinding_resources_dirname) |
| args.add_all(res_dirs, before_each = "--resource_root") |
| args.add("--dataBindingInfoOut", out_databinding_info) |
| args.add("--appId", java_package) |
| |
| _java.run( |
| ctx = ctx, |
| host_javabase = host_javabase, |
| executable = busybox, |
| arguments = [args], |
| inputs = resource_files, |
| outputs = [out_databinding_info] + out_databinding_processed_resources, |
| mnemonic = "StarlarkProcessDatabinding", |
| progress_message = "Processing data binding", |
| ) |
| |
| def _make_generate_binay_r_flags(resources_node): |
| if not (resources_node.r_txt or resources_node.manifest): |
| return None |
| return ",".join( |
| [ |
| resources_node.r_txt.path if resources_node.r_txt else "", |
| resources_node.manifest.path if resources_node.manifest else "", |
| ], |
| ) |
| |
| def _generate_binary_r( |
| ctx, |
| out_class_jar = None, |
| r_txt = None, |
| manifest = None, |
| package_for_r = None, |
| final_fields = None, |
| resources_nodes = depset(), |
| transitive_r_txts = [], |
| transitive_manifests = [], |
| busybox = None, |
| host_javabase = None): |
| """Generate compiled resources class jar. |
| |
| Args: |
| ctx: The context. |
| out_class_jar: File. The output class jar file. |
| r_txt: File. The resource IDs outputted by linking resources in text. |
| manifest: File. The primary AndroidManifest.xml. |
| package_for_r: String. The Java package for the generated R class files. |
| final_fields: Bool. Whether fields get declared as final. |
| busybox: FilesToRunProvider. The ResourceBusyBox executable or |
| FilesToRunprovider |
| host_javabase: A Target. The host javabase. |
| """ |
| args = ctx.actions.args() |
| args.add("--tool", "GENERATE_BINARY_R") |
| args.add("--") |
| args.add("--primaryRTxt", r_txt) |
| args.add("--primaryManifest", manifest) |
| if package_for_r: |
| args.add("--packageForR", package_for_r) |
| args.add_all( |
| resources_nodes, |
| map_each = _make_generate_binay_r_flags, |
| before_each = "--library", |
| ) |
| if final_fields: |
| args.add("--finalFields") |
| else: |
| args.add("--nofinalFields") |
| |
| # TODO(b/154003916): support transitive "--library transitive_r_txt_path,transitive_manifest_path" flags |
| args.add("--classJarOutput", out_class_jar) |
| args.add("--targetLabel", str(ctx.label)) |
| args.use_param_file("@%s") |
| |
| _java.run( |
| ctx = ctx, |
| host_javabase = host_javabase, |
| executable = busybox, |
| arguments = [args], |
| inputs = depset([r_txt, manifest], transitive = transitive_r_txts + transitive_manifests), |
| outputs = [out_class_jar], |
| mnemonic = "StarlarkRClassGenerator", |
| progress_message = "Generating R classes", |
| ) |
| |
| def _make_aar( |
| ctx, |
| out_aar = None, |
| assets = [], |
| assets_dir = None, |
| resource_files = [], |
| class_jar = None, |
| r_txt = None, |
| manifest = None, |
| proguard_specs = [], |
| should_throw_on_conflict = False, |
| busybox = None, |
| host_javabase = None): |
| """Generate an android archive file. |
| |
| Args: |
| ctx: The context. |
| out_aar: File. The output AAR file. |
| assets: sequence of Files. A list of Android assets files to be processed. |
| assets_dir: String. The name of the assets directory. |
| resource_files: A list of Files. The resource files. |
| class_jar: File. The class jar file. |
| r_txt: File. The resource IDs outputted by linking resources in text. |
| manifest: File. The primary AndroidManifest.xml. |
| proguard_specs: List of File. The proguard spec files. |
| busybox: FilesToRunProvider. The ResourceBusyBox executable or |
| FilesToRunprovider |
| host_javabase: A Target. The host javabase. |
| should_throw_on_conflict: A boolean. Determines whether an error should be thrown |
| when a resource conflict occurs. |
| """ |
| args = ctx.actions.args() |
| args.add("--tool", "GENERATE_AAR") |
| args.add("--") |
| args.add( |
| "--mainData", |
| _make_resources_flag( |
| manifest = manifest, |
| assets = assets, |
| assets_dir = assets_dir, |
| resource_files = resource_files, |
| ), |
| ) |
| args.add("--manifest", manifest) |
| args.add("--rtxt", r_txt) |
| args.add("--classes", class_jar) |
| args.add("--aarOutput", out_aar) |
| args.add_all(proguard_specs, before_each = "--proguardSpec") |
| if should_throw_on_conflict: |
| args.add("--throwOnResourceConflict") |
| |
| _java.run( |
| ctx = ctx, |
| host_javabase = host_javabase, |
| executable = busybox, |
| arguments = [args], |
| inputs = ( |
| resource_files + |
| assets + |
| proguard_specs + |
| [r_txt, manifest, class_jar] |
| ), |
| outputs = [out_aar], |
| mnemonic = "StarlarkAARGenerator", |
| progress_message = "Generating AAR package for %s" % ctx.label, |
| ) |
| |
| def _shrink( |
| ctx, |
| out_apk, |
| out_zip, |
| out_log, |
| out_config = None, |
| resources_zip = None, |
| aapt = None, |
| android_jar = None, |
| r_txt = None, |
| shrunk_jar = None, |
| proguard_mapping = None, |
| debug = True, |
| busybox = None, |
| host_javabase = None): |
| """Shrinks the resource apk by removing the resources unused from the packaged app. |
| |
| Args: |
| ctx: The context. |
| out_apk: File. The output shrunk resource ap_ package. |
| out_zip: File. The output shrunk resources file zip. |
| out_log: File. The output shrinker log. |
| out_config: File. The output config for the optimizer. |
| resources_zip: File. The input resources file zip. |
| aapt: FilesToRunProvider. The AAPT executable. |
| android_jar: File. The Android Jar. |
| r_txt: File. The resource IDs outputted by linking resources in text. |
| shrunk_jar: File. The proguarded jar. |
| proguard_mapping: File. The Proguard Mapping file. |
| debug: Boolean. Whether to enable debug mode. |
| busybox: FilesToRunProvider. The ResourceBusyBox executable. |
| host_javabase: Target. The host javabase. |
| """ |
| |
| args = ctx.actions.args() |
| args.use_param_file("@%s") |
| args.add("--tool", "SHRINK_AAPT2") |
| args.add("--") |
| args.add("--aapt2", aapt.executable) |
| args.add("--androidJar", android_jar) |
| args.add("--resources", resources_zip) |
| args.add("--shrunkJar", shrunk_jar) |
| args.add("--proguardMapping", proguard_mapping) |
| args.add("--rTxt", r_txt) |
| args.add("--shrunkResourceApk", out_apk) |
| args.add("--shrunkResources", out_zip) |
| args.add("--log", out_log) |
| args.add("--useDataBindingAndroidX") |
| if debug: |
| args.add("--debug") |
| |
| input_files = [ |
| android_jar, |
| resources_zip, |
| shrunk_jar, |
| proguard_mapping, |
| r_txt, |
| ] |
| output_files = [ |
| out_apk, |
| out_zip, |
| out_log, |
| ] |
| if out_config: |
| args.add("--resourcesConfigOutput", out_config) |
| output_files.append(out_config) |
| |
| _java.run( |
| ctx = ctx, |
| executable = busybox, |
| tools = [aapt], |
| outputs = output_files, |
| inputs = input_files, |
| arguments = [args], |
| mnemonic = "ResourceShrinker", |
| progress_message = |
| "Shrinking resources for " + str(ctx.label), |
| host_javabase = host_javabase, |
| use_default_shell_env = True, |
| ) |
| |
| def _optimize( |
| ctx, |
| out_apk, |
| in_apk, |
| resource_path_shortening_map = None, |
| resource_optimization_config = None, |
| aapt = None, |
| busybox = None, |
| host_javabase = None): |
| """Optimizes the resource apk including resource obfuscation, sparse encoding and path shortening. |
| |
| Args: |
| ctx: The context. |
| out_apk: File. The output optimized resource ap_ package. |
| in_apk: File. The resource ap_ package to be optimized. |
| resource_path_shortening_map: File. The output path shortening map. Optional. |
| resource_optimization_config: File. The input optimization config. Optional. |
| aapt: FilesToRunProvider. The AAPT executable. |
| busybox: FilesToRunProvider. The ResourceBusyBox executable. |
| host_javabase: Target. The host javabase. |
| """ |
| |
| output_files = [out_apk] |
| input_files = [in_apk] |
| |
| args = ctx.actions.args() |
| args.use_param_file("@%s") |
| args.add("--tool", "AAPT2_OPTIMIZE") |
| args.add("--") |
| args.add("--aapt2", aapt.executable) |
| args.add("--") |
| if resource_path_shortening_map: |
| args.add("--shorten-resource-paths") |
| args.add("--resource-path-shortening-map", resource_path_shortening_map) |
| output_files.append(resource_path_shortening_map) |
| if resource_optimization_config: |
| args.add("--collapse-resource-names") |
| args.add("--resources-config-path", resource_optimization_config) |
| input_files.append(resource_optimization_config) |
| args.add("-o", out_apk) |
| args.add(in_apk) |
| |
| _java.run( |
| ctx = ctx, |
| host_javabase = host_javabase, |
| executable = busybox, |
| tools = [aapt], |
| arguments = [args], |
| inputs = input_files, |
| outputs = output_files, |
| mnemonic = "Aapt2Optimize", |
| progress_message = |
| "Optimizing Android resources for " + str(ctx.label), |
| use_default_shell_env = True, |
| ) |
| |
| busybox = struct( |
| compile = _compile, |
| merge_compiled = _merge_compiled, |
| validate_and_link = _validate_and_link, |
| merge_manifests = _merge_manifests, |
| package = _package, |
| parse = _parse, |
| merge_assets = _merge_assets, |
| make_resources_flag = _make_resources_flag, |
| process_databinding = _process_databinding, |
| generate_binary_r = _generate_binary_r, |
| make_aar = _make_aar, |
| shrink = _shrink, |
| optimize = _optimize, |
| |
| # Exposed for testing |
| mergee_manifests_flag = _mergee_manifests_flag, |
| get_unique_res_dirs = _get_unique_res_dirs, |
| sanitize_assets_dir = _sanitize_assets_dir, |
| extract_filters = _extract_filters, |
| ) |