Skip to main content
Glama
android_binary_resources_rules.bzl30.2 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//:resources.bzl", "gather_resources") load("@prelude//android:aapt2_link.bzl", "get_aapt2_link", "get_module_manifest_in_proto_format") load("@prelude//android:android_manifest.bzl", "generate_android_manifest") load("@prelude//android:android_providers.bzl", "AndroidBinaryResourcesInfo", "AndroidPackageableInfo", "AndroidResourceInfo", "ExopackageResourcesInfo") load("@prelude//android:android_resource.bzl", "aapt2_compile") load("@prelude//android:android_toolchain.bzl", "AndroidToolchainInfo") load("@prelude//android:r_dot_java.bzl", "generate_r_dot_javas") load("@prelude//android:voltron.bzl", "ROOT_MODULE", "get_apk_module_graph_info", "is_root_module") load("@prelude//decls:android_rules.bzl", "RType") load( "@prelude//java:java_providers.bzl", "JavaPackagingDep", # @unused Used as type ) load("@prelude//utils:expect.bzl", "expect") load("@prelude//utils:utils.bzl", "flatten") _FilteredResourcesOutput = record( resource_infos = list[AndroidResourceInfo], voltron_res = list[Artifact], override_symbols = Artifact | None, string_files_list = Artifact | None, string_files_res_dirs = list[Artifact], ) def get_android_binary_resources_info( ctx: AnalysisContext, deps: list[Dependency], android_packageable_info: AndroidPackageableInfo, java_packaging_deps: list[JavaPackagingDep], use_proto_format: bool, referenced_resources_lists: list[Artifact], apk_module_graph_file: Artifact | None = None, manifest_entries: dict = {}, resource_infos_to_exclude: set[TargetLabel] = set(), r_dot_java_packages_to_exclude: set[str] = set(), generate_strings_and_ids_separately: [bool, None] = True, aapt2_preferred_density: [str, None] = None) -> AndroidBinaryResourcesInfo: android_toolchain = ctx.attrs._android_toolchain[AndroidToolchainInfo] # Use reverse topological sort in resource merging to make sure a resource target will overwrite its dependencies. unfiltered_resource_infos = reversed([ resource_info for resource_info in list(android_packageable_info.resource_infos.traverse(ordering = "topological") if android_packageable_info.resource_infos else []) if resource_info.raw_target not in resource_infos_to_exclude ]) filtered_resources_output = _maybe_filter_resources( ctx, unfiltered_resource_infos, android_toolchain, ) resource_infos = filtered_resources_output.resource_infos android_manifest = get_manifest(ctx, android_packageable_info, manifest_entries, should_replace_application_id_placeholders = True) non_proto_format_aapt2_link_info, proto_format_aapt2_link_info = get_aapt2_link( ctx, ctx.attrs._android_toolchain[AndroidToolchainInfo], resource_infos, android_manifest, manifest_entries = getattr(ctx.attrs, "manifest_entries", {}), includes_vector_drawables = getattr(ctx.attrs, "includes_vector_drawables", False), no_auto_version = getattr(ctx.attrs, "no_auto_version_resources", False), no_version_transitions = getattr(ctx.attrs, "no_version_transitions_resources", False), no_auto_add_overlay = getattr(ctx.attrs, "no_auto_add_overlay_resources", False), no_resource_removal = True, package_id_offset = 0, should_keep_raw_values = getattr(ctx.attrs, "aapt2_keep_raw_values", False), resource_stable_ids = getattr(ctx.attrs, "resource_stable_ids", None), compiled_resource_apks = getattr(ctx.attrs, "compiled_resource_apks", []), additional_aapt2_params = getattr(ctx.attrs, "additional_aapt_params", []), extra_filtered_resources = getattr(ctx.attrs, "extra_filtered_resources", []), locales = getattr(ctx.attrs, "locales", []) or getattr(ctx.attrs, "locales_for_binary_resources", []), filter_locales = getattr(ctx.attrs, "aapt2_locale_filtering", False) or bool(getattr(ctx.attrs, "locales_for_binary_resources", [])), preferred_density = aapt2_preferred_density, ) module_manifests = _get_module_manifests( ctx, manifest_entries, apk_module_graph_file, use_proto_format, non_proto_format_aapt2_link_info.primary_resources_apk, ) aapt2_link_info = proto_format_aapt2_link_info if use_proto_format else non_proto_format_aapt2_link_info prebuilt_jars = [packaging_dep.jar for packaging_dep in java_packaging_deps if packaging_dep.is_prebuilt_jar] cxx_resources = get_cxx_resources(ctx, deps) is_exopackaged_enabled_for_resources = "resources" in getattr(ctx.attrs, "exopackage_modes", []) primary_resources_apk, exopackaged_assets, exopackaged_assets_hash, module_assets_apks_dir = _merge_assets( ctx, is_exopackaged_enabled_for_resources, aapt2_link_info.primary_resources_apk, resource_infos, cxx_resources, use_proto_format, # indicates that this is a .aab build apk_module_graph_file, ) if is_exopackaged_enabled_for_resources: r_dot_txt = ctx.actions.declare_output("after_exo/R.txt") primary_resources_apk = ctx.actions.declare_output("after_exo/primary_resources_apk.apk") exo_resources = ctx.actions.declare_output("exo_resources.apk") exo_resources_hash = ctx.actions.declare_output("exo_resources.apk.hash") ctx.actions.run(cmd_args([ android_toolchain.exo_resources_rewriter[RunInfo], "--original-r-dot-txt", aapt2_link_info.r_dot_txt, "--new-r-dot-txt", r_dot_txt.as_output(), "--original-primary-apk-resources", aapt2_link_info.primary_resources_apk, "--new-primary-apk-resources", primary_resources_apk.as_output(), "--exo-resources", exo_resources.as_output(), "--exo-resources-hash", exo_resources_hash.as_output(), "--zipalign-tool", android_toolchain.zipalign[RunInfo], ]), category = "write_exo_resources") exopackage_info = ExopackageResourcesInfo( assets = exopackaged_assets, assets_hash = exopackaged_assets_hash, res = exo_resources, res_hash = exo_resources_hash, ) else: exopackage_info = None r_dot_txt = aapt2_link_info.r_dot_txt override_symbols_paths = [filtered_resources_output.override_symbols] if filtered_resources_output.override_symbols else [] resources = [resource for resource in resource_infos if resource.res != None] r_dot_java_infos = generate_r_dot_javas( ctx, ctx.attrs._android_toolchain[AndroidToolchainInfo].merge_android_resources[RunInfo], resources, get_effective_banned_duplicate_resource_types( getattr(ctx.attrs, "duplicate_resource_behavior", "allow_by_default"), getattr(ctx.attrs, "allowed_duplicate_resource_types", []), getattr(ctx.attrs, "banned_duplicate_resource_types", []), ), [r_dot_txt], override_symbols_paths, getattr(ctx.attrs, "duplicate_resource_whitelist", None), getattr(ctx.attrs, "resource_union_package", None), referenced_resources_lists, generate_strings_and_ids_separately = generate_strings_and_ids_separately, remove_classes = ["{}.R".format(r_dot_java_package) for r_dot_java_package in r_dot_java_packages_to_exclude], ) string_source_map = _maybe_generate_string_source_map( ctx.actions, getattr(ctx.attrs, "build_string_source_map", False), [resource.res for resource in resources if resource.res != None], android_toolchain, ) voltron_string_source_map = _maybe_generate_string_source_map( ctx.actions, getattr(ctx.attrs, "is_voltron_language_pack_enabled", False), filtered_resources_output.voltron_res, android_toolchain, is_voltron_string_source_map = True, ) packaged_string_assets = _maybe_package_strings_as_assets( ctx, filtered_resources_output.string_files_list, filtered_resources_output.string_files_res_dirs, r_dot_txt, android_toolchain, ) return AndroidBinaryResourcesInfo( exopackage_info = exopackage_info, manifest = android_manifest, module_manifests = module_manifests, module_assets = module_assets_apks_dir, packaged_string_assets = packaged_string_assets, primary_resources_apk = primary_resources_apk, proguard_config_file = aapt2_link_info.proguard_config_file, r_dot_java_infos = r_dot_java_infos, string_source_map = string_source_map, voltron_string_source_map = voltron_string_source_map, jar_files_that_may_contain_resources = prebuilt_jars, unfiltered_resource_infos = unfiltered_resource_infos, ) def _maybe_filter_resources( ctx: AnalysisContext, resources: list[AndroidResourceInfo], android_toolchain: AndroidToolchainInfo) -> _FilteredResourcesOutput: resources_filter_strings = getattr(ctx.attrs, "resource_filter", []) resources_filter = _get_resources_filter(resources_filter_strings) resource_compression_mode = getattr(ctx.attrs, "resource_compression", "disabled") is_store_strings_as_assets = _is_store_strings_as_assets(resource_compression_mode) locales = getattr(ctx.attrs, "locales", None) use_aapt2_locale_filtering = getattr(ctx.attrs, "aapt2_locale_filtering", False) needs_resource_filtering_for_locales = locales != None and len(locales) > 0 and not use_aapt2_locale_filtering post_filter_resources_cmd = getattr(ctx.attrs, "post_filter_resources_cmd", None) needs_resource_filtering = ( resources_filter != None or is_store_strings_as_assets or needs_resource_filtering_for_locales or post_filter_resources_cmd != None ) if not needs_resource_filtering: return _FilteredResourcesOutput( resource_infos = resources, voltron_res = [resource.res for resource in resources if resource.res != None], override_symbols = None, string_files_list = None, string_files_res_dirs = [], ) res_to_out_res_dir = {} voltron_res_to_out_res_dir = {} res_infos_with_no_res = [] skip_crunch_pngs = getattr(ctx.attrs, "skip_crunch_pngs", None) or False is_voltron_language_pack_enabled = getattr(ctx.attrs, "is_voltron_language_pack_enabled", False) for i, resource in enumerate(resources): if resource.res == None: res_infos_with_no_res.append(resource) else: filtered_res = ctx.actions.declare_output("filtered_res_{}".format(i), dir = True) res_to_out_res_dir[resource.res] = filtered_res if is_voltron_language_pack_enabled: filtered_res_for_voltron = ctx.actions.declare_output("filtered_res_for_voltron_{}".format(i), dir = True) voltron_res_to_out_res_dir[resource.res] = filtered_res_for_voltron filter_resources_cmd = cmd_args(android_toolchain.filter_resources[RunInfo]) in_res_dirs = res_to_out_res_dir.keys() filter_resources_cmd.add(cmd_args( hidden = in_res_dirs + [out_res.as_output() for out_res in res_to_out_res_dir.values()], )) filter_resources_cmd.add([ "--in-res-dir-to-out-res-dir-map", ctx.actions.write_json("in_res_dir_to_out_res_dir_map", {"res_dir_map": res_to_out_res_dir}), ]) if is_voltron_language_pack_enabled: filter_resources_cmd.add(cmd_args(hidden = [out_res.as_output() for out_res in voltron_res_to_out_res_dir.values()])) filter_resources_cmd.add([ "--voltron-in-res-dir-to-out-res-dir-map", ctx.actions.write_json("voltron_in_res_dir_to_out_res_dir_map", {"res_dir_map": voltron_res_to_out_res_dir}), ]) if resources_filter: filter_resources_cmd.add([ "--target-densities", ",".join(resources_filter.densities), ]) all_strings_files_list = None all_strings_files_res_dirs = [] if is_store_strings_as_assets: all_strings_files_list = ctx.actions.declare_output("all_strings_files") all_strings_files_res_dirs = in_res_dirs filter_resources_cmd.add([ "--enable-string-as-assets-filtering", "--string-files-list-output", all_strings_files_list.as_output(), ]) packaged_locales = getattr(ctx.attrs, "packaged_locales", []) if packaged_locales: filter_resources_cmd.add([ "--packaged-locales", ",".join(packaged_locales), ]) not_filtered_string_dirs = [resource.res for resource in resources if not resource.allow_strings_as_assets_resource_filtering] if not_filtered_string_dirs: filter_resources_cmd.add([ "--not-filtered-string-dirs", ctx.actions.write("not_filtered_string_dirs", not_filtered_string_dirs), ]) allowlisted_locales = {resource.res: resource.allowlisted_locales for resource in resources if resource.allowlisted_locales} filter_resources_cmd.add([ "--allowlisted-locales", ctx.actions.write_json("allowlisted_locales", allowlisted_locales), ]) if needs_resource_filtering_for_locales: filter_resources_cmd.add([ "--locales", ",".join(locales), ]) override_symbols_artifact = None if post_filter_resources_cmd != None: override_symbols_artifact = ctx.actions.declare_output("post_filter_resources_cmd/R.json") filter_resources_cmd.add([ "--post-filter-resources-cmd", post_filter_resources_cmd, "--post-filter-resources-cmd-override-symbols-output", override_symbols_artifact.as_output(), ]) ctx.actions.run( filter_resources_cmd, local_only = post_filter_resources_cmd != None and "run_post_filter_resources_cmd_locally" in ctx.attrs.labels, category = "filter_resources", ) filtered_resource_infos = [] for i, resource in enumerate(resources): if resource.res == None: continue filtered_res = res_to_out_res_dir[resource.res] filtered_aapt2_compile_output = aapt2_compile( ctx, filtered_res, android_toolchain, skip_crunch_pngs = skip_crunch_pngs, identifier = "filtered_res_{}".format(i), ) filtered_resource = AndroidResourceInfo( raw_target = resource.raw_target, aapt2_compile_output = filtered_aapt2_compile_output, assets = resource.assets, manifest_file = resource.manifest_file, r_dot_java_package = resource.r_dot_java_package, res = filtered_res, text_symbols = resource.text_symbols, ) filtered_resource_infos.append(filtered_resource) return _FilteredResourcesOutput( resource_infos = res_infos_with_no_res + filtered_resource_infos, voltron_res = voltron_res_to_out_res_dir.values(), override_symbols = override_symbols_artifact, string_files_list = all_strings_files_list, string_files_res_dirs = all_strings_files_res_dirs, ) ResourcesFilter = record( densities = list[str], downscale = bool, ) def _get_resources_filter(resources_filter_strings: list[str]) -> [ResourcesFilter, None]: if not resources_filter_strings: return None densities = [resources_filter_string for resources_filter_string in resources_filter_strings if resources_filter_string != "downscale"] if not densities: return None downscale = len(densities) < len(resources_filter_strings) return ResourcesFilter(densities = densities, downscale = downscale) def _maybe_generate_string_source_map( actions: AnalysisActions, should_build_source_string_map: bool, res_dirs: list[Artifact], android_toolchain: AndroidToolchainInfo, is_voltron_string_source_map: bool = False) -> Artifact | None: if not should_build_source_string_map or len(res_dirs) == 0: return None prefix = "voltron_" if is_voltron_string_source_map else "" output = actions.declare_output("{}string_source_map".format(prefix), dir = True) res_dirs_file = actions.write("resource_dirs_for_{}string_source_map".format(prefix), res_dirs) generate_string_source_map_cmd = cmd_args([ android_toolchain.copy_string_resources[RunInfo], "--res-dirs", res_dirs_file, "--output", output.as_output(), ], hidden = res_dirs) if is_voltron_string_source_map: generate_string_source_map_cmd.add("--is-voltron") actions.run(generate_string_source_map_cmd, category = "generate_{}string_source_map".format(prefix)) return output def _maybe_package_strings_as_assets( ctx: AnalysisContext, string_files_list: Artifact | None, string_files_res_dirs: list[Artifact], r_dot_txt: Artifact, android_toolchain: AndroidToolchainInfo) -> Artifact | None: resource_compression_mode = getattr(ctx.attrs, "resource_compression", "disabled") is_store_strings_as_assets = _is_store_strings_as_assets(resource_compression_mode) expect(is_store_strings_as_assets == (string_files_list != None)) if not is_store_strings_as_assets: return None string_assets_dir = ctx.actions.declare_output("package_strings_as_assets/string_assets", dir = True) string_assets_zip = ctx.actions.declare_output("package_strings_as_assets/string_assets_zip.zip") all_locales_string_assets_zip = ctx.actions.declare_output("package_strings_as_assets/all_locales_string_assets_zip.zip") locales = getattr(ctx.attrs, "locales", []) package_strings_as_assets_cmd = cmd_args([ android_toolchain.package_strings_as_assets[RunInfo], "--string-files-list", string_files_list, "--r-dot-txt", r_dot_txt, "--string-assets-dir", string_assets_dir.as_output(), "--string-assets-zip", string_assets_zip.as_output(), "--all-locales-string-assets-zip", all_locales_string_assets_zip.as_output(), ], hidden = string_files_res_dirs) if locales: package_strings_as_assets_cmd.add("--locales", ",".join(locales)) ctx.actions.run(package_strings_as_assets_cmd, category = "package_strings_as_assets") return string_assets_zip def get_manifest( ctx: AnalysisContext, android_packageable_info: AndroidPackageableInfo, manifest_entries: dict, should_replace_application_id_placeholders: bool) -> Artifact: android_toolchain = ctx.attrs._android_toolchain[AndroidToolchainInfo] if ctx.attrs.manifest: expect(getattr(ctx.attrs, "manifest_skeleton", None) == None, "Only one of manifest and manifest_skeleton should be declared") if isinstance(ctx.attrs.manifest, Dependency): android_manifest = ctx.attrs.manifest[DefaultInfo].default_outputs[0] else: android_manifest = ctx.attrs.manifest else: expect(ctx.attrs.manifest_skeleton != None, "Must declare one of manifest and manifest_skeleton") if isinstance(ctx.attrs.manifest_skeleton, Dependency): manifest_skeleton = ctx.attrs.manifest_skeleton[DefaultInfo].default_outputs[0] else: manifest_skeleton = ctx.attrs.manifest_skeleton android_manifest, _ = generate_android_manifest( ctx, android_toolchain.generate_manifest[RunInfo], manifest_skeleton, ROOT_MODULE, android_packageable_info.manifests, manifest_entries.get("placeholders", {}), ) if android_toolchain.set_application_id_to_specified_package and should_replace_application_id_placeholders: android_manifest_with_replaced_application_id = ctx.actions.declare_output("replaced/AndroidManifest.xml") replace_application_id_placeholders_cmd = cmd_args([ ctx.attrs._android_toolchain[AndroidToolchainInfo].replace_application_id_placeholders[RunInfo], "--manifest", android_manifest, "--output", android_manifest_with_replaced_application_id.as_output(), ]) if android_toolchain.should_run_sanity_check_for_placeholders: replace_application_id_placeholders_cmd.add("--sanity-check-placeholders") ctx.actions.run(replace_application_id_placeholders_cmd, category = "replace_application_id_placeholders") return android_manifest_with_replaced_application_id else: return android_manifest def _get_module_manifests( ctx: AnalysisContext, manifest_entries: dict, apk_module_graph_file: Artifact | None, use_proto_format: bool, primary_resources_apk: Artifact) -> list[Artifact]: if not apk_module_graph_file: return [] if not ctx.attrs.module_manifest_skeleton: return [] if isinstance(ctx.attrs.module_manifest_skeleton, Dependency): module_manifest_skeleton = ctx.attrs.module_manifest_skeleton[DefaultInfo].default_outputs[0] else: module_manifest_skeleton = ctx.attrs.module_manifest_skeleton android_toolchain = ctx.attrs._android_toolchain[AndroidToolchainInfo] module_manifests_dir = ctx.actions.declare_output("module_manifests_dir", dir = True) def get_manifests_modular(ctx: AnalysisContext, artifacts, outputs): apk_module_graph_info = get_apk_module_graph_info(ctx, apk_module_graph_file, artifacts) merged_module_manifests = {} for module_name in apk_module_graph_info.module_list: if is_root_module(module_name): continue merged_module_manifest, _ = generate_android_manifest( ctx, android_toolchain.generate_manifest[RunInfo], module_manifest_skeleton, module_name, # Note - the expectation of voltron modules is that the AndroidManifest entries are merged into the base APK's manifest. None, manifest_entries.get("placeholders", {}), ) if use_proto_format: merged_module_manifest = get_module_manifest_in_proto_format( ctx, android_toolchain, merged_module_manifest, primary_resources_apk, module_name, ) merged_module_manifests["assets/{}/AndroidManifest.xml".format(module_name)] = merged_module_manifest ctx.actions.symlinked_dir(outputs[module_manifests_dir], merged_module_manifests) ctx.actions.dynamic_output( dynamic = [apk_module_graph_file], inputs = [], outputs = [module_manifests_dir.as_output()], f = get_manifests_modular, ) return [module_manifests_dir] # Returns the "primary resources APK" (i.e. the resource that are packaged into the primary APK), # and optionally an "exopackaged assets APK" and the hash for that APK. def _merge_assets( ctx: AnalysisContext, is_exopackaged_enabled_for_resources: bool, base_apk: Artifact, resource_infos: list[AndroidResourceInfo], cxx_resources: Artifact | None, is_bundle_build: bool, apk_module_graph_file: Artifact | None) -> (Artifact, Artifact | None, Artifact | None, Artifact | None): expect( not (is_exopackaged_enabled_for_resources and is_bundle_build), "Cannot use exopackage-for-resources with AAB builds.", ) expect( not (is_exopackaged_enabled_for_resources and apk_module_graph_file), "Cannot use exopackage-for-resources with Voltron builds.", ) asset_resource_infos = [resource_info for resource_info in resource_infos if resource_info.assets] if not asset_resource_infos and not cxx_resources: return base_apk, None, None, None merged_assets_output = ctx.actions.declare_output("merged_assets.ap_") def get_common_merge_assets_cmd( ctx: AnalysisContext, output_apk: Artifact) -> (cmd_args, Artifact | None): merge_assets_cmd = cmd_args(ctx.attrs._android_toolchain[AndroidToolchainInfo].merge_assets[RunInfo]) merge_assets_cmd.add(["--output-apk", output_apk.as_output()]) if getattr(ctx.attrs, "extra_no_compress_asset_extensions", None): merge_assets_cmd.add("--extra-no-compress-asset-extensions") merge_assets_cmd.add(ctx.attrs.extra_no_compress_asset_extensions) if is_exopackaged_enabled_for_resources: merged_assets_output_hash = ctx.actions.declare_output("merged_assets.ap_.hash") merge_assets_cmd.add(["--output-apk-hash", merged_assets_output_hash.as_output()]) else: merge_assets_cmd.add(["--base-apk", base_apk]) merged_assets_output_hash = None merge_assets_cmd.add("--binary-type", "aab" if is_bundle_build else "apk") return merge_assets_cmd, merged_assets_output_hash if apk_module_graph_file: declared_outputs = [merged_assets_output] if is_bundle_build: # For Voltron AAB builds, we need to put assets into a separate "APK" for each module. module_assets_apks_dir = ctx.actions.declare_output("module_assets_apks") declared_outputs.append(module_assets_apks_dir) else: module_assets_apks_dir = None def merge_assets_modular(ctx: AnalysisContext, artifacts, outputs): apk_module_graph_info = get_apk_module_graph_info(ctx, apk_module_graph_file, artifacts) module_to_assets_dirs = {} if cxx_resources != None: module_to_assets_dirs.setdefault(ROOT_MODULE, []).extend([cxx_resources]) for asset_resource_info in asset_resource_infos: module_name = apk_module_graph_info.target_to_module_mapping_function(str(asset_resource_info.raw_target)) module_to_assets_dirs.setdefault(module_name, []).append(asset_resource_info.assets) merge_assets_cmd, _ = get_common_merge_assets_cmd(ctx, outputs[merged_assets_output]) if is_bundle_build: merge_assets_cmd.add(["--module-assets-apks-dir", outputs[module_assets_apks_dir].as_output()]) assets_dirs_file = ctx.actions.write_json("assets_dirs.json", module_to_assets_dirs) merge_assets_cmd.add(["--assets-dirs", assets_dirs_file]) merge_assets_cmd.add(cmd_args(hidden = flatten(module_to_assets_dirs.values()))) ctx.actions.run(merge_assets_cmd, category = "merge_assets") ctx.actions.dynamic_output( dynamic = [apk_module_graph_file], inputs = [], outputs = [o.as_output() for o in declared_outputs], f = merge_assets_modular, ) return merged_assets_output, None, None, module_assets_apks_dir else: merge_assets_cmd, merged_assets_output_hash = get_common_merge_assets_cmd(ctx, merged_assets_output) assets_dirs = [resource_info.assets for resource_info in asset_resource_infos] if cxx_resources: assets_dirs.extend([cxx_resources]) assets_dirs_file = ctx.actions.write_json("assets_dirs.json", {ROOT_MODULE: assets_dirs}) merge_assets_cmd.add(["--assets-dirs", assets_dirs_file]) merge_assets_cmd.add(cmd_args(hidden = assets_dirs)) ctx.actions.run(merge_assets_cmd, category = "merge_assets") if is_exopackaged_enabled_for_resources: return base_apk, merged_assets_output, merged_assets_output_hash, None else: return merged_assets_output, None, None, None def get_effective_banned_duplicate_resource_types( duplicate_resource_behavior: str, allowed_duplicate_resource_types: list[str], banned_duplicate_resource_types: list[str]) -> list[str]: if duplicate_resource_behavior == "allow_by_default": expect( len(allowed_duplicate_resource_types) == 0, "Cannot set allowed_duplicate_resource_types if duplicate_resource_behaviour is allow_by_default", ) return banned_duplicate_resource_types elif duplicate_resource_behavior == "ban_by_default": expect( len(banned_duplicate_resource_types) == 0, "Cannot set banned_duplicate_resource_types if duplicate_resource_behaviour is ban_by_default", ) return [rtype for rtype in RType if rtype not in allowed_duplicate_resource_types] else: fail("Unrecognized duplicate_resource_behavior: {}".format(duplicate_resource_behavior)) def get_cxx_resources(ctx: AnalysisContext, deps: list[Dependency], dir_name: str = "cxx_resources_dir") -> Artifact | None: cxx_resources = gather_resources( label = ctx.label, resources = {}, deps = deps, ) symlink_tree_dict = {} resource_maps = cxx_resources.values() for resource_map in resource_maps: for name, resource in resource_map.items(): symlink_tree_dict["cxx-resources/{}".format(name)] = resource.default_output return ctx.actions.symlinked_dir(dir_name, symlink_tree_dict) if symlink_tree_dict else None def _is_store_strings_as_assets(resource_compression: str) -> bool: return resource_compression == "enabled_strings_only" or resource_compression == "enabled_with_strings_as_assets"

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