Skip to main content
Glama
aapt2_link.bzl9.66 kB
# Copyright (c) Meta Platforms, Inc. and affiliates. # # This source code is licensed under both the MIT license found in the # LICENSE-MIT file in the root directory of this source tree and the Apache # License, Version 2.0 found in the LICENSE-APACHE file in the root directory # of this source tree. load("@prelude//android:android_providers.bzl", "Aapt2LinkInfo", "AndroidResourceInfo", "RESOURCE_PRIORITY_LOW") load("@prelude//android:android_toolchain.bzl", "AndroidToolchainInfo") BASE_PACKAGE_ID = 0x7f def normalize_locale(locale: str) -> str: if locale == "NONE": return "en" if regex("^[a-z][a-z]$").match(locale): return locale if regex("^[a-z][a-z]_[A-Z][A-Z]$").match(locale): # This transforms locales from xx_YY to xx-rYY. # E.g.: like "zh_CN" to "zh-rCN". return locale[:2] + "-r" + locale[-2:] if regex("^[a-z][a-z]-r[A-Z][A-Z]$").match(locale): # Already in xx-rYY format return locale fail("Invalid locale format passed: {} {}".format(locale, regex("/^[a-z][a-z]$/").match(locale))) def get_aapt2_link( ctx: AnalysisContext, android_toolchain: AndroidToolchainInfo, resource_infos: list[AndroidResourceInfo], android_manifest: Artifact, manifest_entries: dict, includes_vector_drawables: bool, no_auto_version: bool, no_version_transitions: bool, no_auto_add_overlay: bool, no_resource_removal: bool, should_keep_raw_values: bool, package_id_offset: int, resource_stable_ids: Artifact | None, preferred_density: [str, None], filter_locales: bool, locales: list[str], compiled_resource_apks: list[Artifact], additional_aapt2_params: list[str], extra_filtered_resources: list[str]) -> (Aapt2LinkInfo, Aapt2LinkInfo): link_infos = [] for use_proto_format in [False, True]: if use_proto_format: identifier = "use_proto" else: identifier = "not_proto" aapt2_command = cmd_args(android_toolchain.aapt2) aapt2_command.add("link") # aapt2 only supports @ for -R or input files, not for all args, so we pass in all "normal" # args here. resources_apk = ctx.actions.declare_output("{}/resource-apk.ap_".format(identifier)) aapt2_command.add(["-o", resources_apk.as_output()]) proguard_config = ctx.actions.declare_output("{}/proguard_config.pro".format(identifier)) aapt2_command.add(["--proguard", proguard_config.as_output()]) # We don't need the R.java output, but aapt2 won't output R.txt unless we also request R.java. # A drawback of this is that the directory structure for the R.java output is deep, resulting # in long path issues on Windows. The structure is <path to target>/<identifier>/unused-rjava/<package>/R.java # We can declare a custom dummy package to drastically shorten <package>, which is sketchy, but effective r_dot_java = ctx.actions.declare_output("{}/unused-rjava".format(identifier), dir = True) aapt2_command.add(["--java", r_dot_java.as_output()]) aapt2_command.add(["--custom-package", "dummy.package"]) r_dot_txt = ctx.actions.declare_output("{}/R.txt".format(identifier)) aapt2_command.add(["--output-text-symbols", r_dot_txt.as_output()]) aapt2_command.add(["--manifest", android_manifest]) aapt2_command.add(["-I", android_toolchain.android_jar]) if includes_vector_drawables: aapt2_command.add("--no-version-vectors") if no_auto_version: aapt2_command.add("--no-auto-version") if no_version_transitions: aapt2_command.add("--no-version-transitions") if not no_auto_add_overlay: aapt2_command.add("--auto-add-overlay") if use_proto_format: aapt2_command.add("--proto-format") if no_resource_removal: aapt2_command.add("--no-resource-removal") if should_keep_raw_values: aapt2_command.add("--keep-raw-values") if package_id_offset != 0: aapt2_command.add(["--package-id", "0x{}".format(BASE_PACKAGE_ID + package_id_offset)]) if resource_stable_ids != None: aapt2_command.add(["--stable-ids", resource_stable_ids]) if preferred_density != None: aapt2_command.add(["--preferred-density", preferred_density]) manifest_entries_min_sdk = manifest_entries.get("min_sdk_version", None) if manifest_entries_min_sdk != None: aapt2_command.add(["--min-sdk-version", str(manifest_entries_min_sdk)]) manifest_entries_target_sdk = manifest_entries.get("target_sdk_version", None) if manifest_entries_target_sdk != None: aapt2_command.add(["--target-sdk-version", str(manifest_entries_target_sdk)]) manifest_entries_version_code = manifest_entries.get("version_code", None) if manifest_entries_version_code != None: aapt2_command.add(["--version-code", manifest_entries_version_code]) manifest_entries_version_name = manifest_entries.get("version_name", None) if manifest_entries_version_name != None: aapt2_command.add(["--version-name", manifest_entries_version_name]) manifest_entries_debug_mode = str(manifest_entries.get("debug_mode", "False")).lower() == "true" if manifest_entries_debug_mode: aapt2_command.add(["--debug-mode"]) if filter_locales and len(locales) > 0: for locale in locales: aapt2_command.add(["-c", normalize_locale(locale)]) for compiled_resource_apk in compiled_resource_apks: aapt2_command.add(["-I", compiled_resource_apk]) # put low priority resources first so that they get overwritten by higher priority resources low_priority_aapt2_compile_rules = [] normal_priority_aapt2_compile_rules = [] for resource_info in resource_infos: if resource_info.aapt2_compile_output: (low_priority_aapt2_compile_rules if resource_info.res_priority == RESOURCE_PRIORITY_LOW else normal_priority_aapt2_compile_rules).append(resource_info.aapt2_compile_output) aapt2_compile_rules = low_priority_aapt2_compile_rules + normal_priority_aapt2_compile_rules aapt2_compile_rules_args_file = ctx.actions.write("{}/aapt2_compile_rules_args_file".format(identifier), cmd_args(aapt2_compile_rules, delimiter = " ")) aapt2_command.add("-R") aapt2_command.add(cmd_args( aapt2_compile_rules_args_file, format = "@{}", hidden = aapt2_compile_rules, )) aapt2_command.add(additional_aapt2_params) ctx.actions.run(aapt2_command, category = "aapt2_link", identifier = identifier) # The normal resource filtering apparatus is super slow, because it extracts the whole apk, # strips files out of it, then repackages it. # # This is a faster filtering step that just uses zip -d to remove entries from the archive. # It's also superbly dangerous. # # If zip -d returns that there was nothing to do, then we don't fail. if len(extra_filtered_resources) > 0: filtered_resources_apk = ctx.actions.declare_output("{}/filtered-resource-apk.ap_".format(identifier)) filter_resources_cmd = cmd_args(ctx.attrs._android_toolchain[AndroidToolchainInfo].aapt2_filter_resources) filter_resources_cmd.add(cmd_args(resources_apk, format = "--input-apk={}")) filter_resources_cmd.add(cmd_args(filtered_resources_apk.as_output(), format = "--output-apk={}")) filter_resources_cmd.add(cmd_args(extra_filtered_resources, format = "--extra-filtered-resources={}")) ctx.actions.run(filter_resources_cmd, category = "aapt2_filter_resources", identifier = identifier) primary_resources_apk = filtered_resources_apk else: primary_resources_apk = resources_apk link_infos.append(Aapt2LinkInfo( primary_resources_apk = primary_resources_apk, proguard_config_file = proguard_config, r_dot_txt = r_dot_txt, )) return link_infos[0], link_infos[1] def get_module_manifest_in_proto_format( ctx: AnalysisContext, android_toolchain: AndroidToolchainInfo, android_manifest: Artifact, primary_resources_apk: Artifact, module_name: str) -> Artifact: aapt2_command = cmd_args(android_toolchain.aapt2) aapt2_command.add("link") # aapt2 only supports @ for -R or input files, not for all args, so we pass in all "normal" # args here. resources_apk = ctx.actions.declare_output("{}/resource-apk.ap_".format(module_name)) aapt2_command.add(["-o", resources_apk.as_output()]) aapt2_command.add(["--manifest", android_manifest]) aapt2_command.add(["-I", android_toolchain.android_jar]) aapt2_command.add(["-I", primary_resources_apk]) aapt2_command.add("--proto-format") ctx.actions.run(aapt2_command, category = "aapt2_link", identifier = module_name) proto_manifest_dir = ctx.actions.declare_output("{}/proto_format_manifest".format(module_name)) proto_manifest = proto_manifest_dir.project("AndroidManifest.xml") ctx.actions.run( cmd_args(["unzip", resources_apk, "AndroidManifest.xml", "-d", proto_manifest_dir.as_output()]), category = "unzip_proto_format_manifest", identifier = module_name, ) return proto_manifest

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server