@@ -18,6 +18,7 @@ load("@bazel_features//:features.bzl", "bazel_features")
1818load ("//python:versions.bzl" , "DEFAULT_RELEASE_BASE_URL" , "PLATFORMS" , "TOOL_VERSIONS" )
1919load (":auth.bzl" , "AUTH_ATTRS" )
2020load (":full_version.bzl" , "full_version" )
21+ load (":pbs_manifest.bzl" , "parse_sha_manifest" )
2122load (":platform_info.bzl" , "platform_info" )
2223load (":python_register_toolchains.bzl" , "python_register_toolchains" )
2324load (":pythons_hub.bzl" , "hub_repo" )
@@ -76,7 +77,7 @@ def parse_modules(*, module_ctx, logger = None, _fail = fail):
7677 # Map of string Major.Minor or Major.Minor.Patch to the toolchain_info struct
7778 global_toolchain_versions = {}
7879
79- config = _get_toolchain_config (modules = module_ctx .modules , _fail = _fail )
80+ config = _get_toolchain_config (mctx = module_ctx , modules = module_ctx .modules , _fail = _fail )
8081
8182 default_python_version = _compute_default_python_version (module_ctx )
8283
@@ -741,10 +742,71 @@ def _override_defaults(*overrides, modules, _fail = fail, default):
741742
742743 override .fn (tag = tag , _fail = _fail , default = default )
743744
744- def _get_toolchain_config (* , modules , _fail = fail ):
745+ def _populate_from_pbs_manifest (* , mctx , add_runtime_manifest_urls , runtime_manifest_sha = "" , available_versions , _fail ):
746+ manifest_path = mctx .path ("runtime_manifest" )
747+ result = mctx .download (
748+ url = add_runtime_manifest_urls ,
749+ output = manifest_path ,
750+ sha256 = runtime_manifest_sha ,
751+ )
752+ if not result .success :
753+ _fail ("Failed to download manifest from {}: {}" .format (add_runtime_manifest_urls , result ))
754+ return
755+
756+ content = mctx .read (manifest_path )
757+ base_download_urls = [url .rpartition ("/" )[0 ] for url in add_runtime_manifest_urls ]
758+
759+ parsed_entries = parse_sha_manifest (content )
760+
761+ for entry in parsed_entries :
762+ location = entry .location
763+ sha256 = entry .sha256
764+ py_version = entry .python_version
765+
766+ # Fallback to matching against PLATFORMS keys as before to ensure compatibility
767+ # with rules_python expected platform keys.
768+ matched_platform = None
769+ for platform in PLATFORMS .keys ():
770+ if platform in location :
771+ matched_platform = platform
772+ break
773+
774+ if not matched_platform :
775+ continue
776+
777+ expects_full = matched_platform in [
778+ "aarch64-apple-darwin" ,
779+ "aarch64-unknown-linux-gnu" ,
780+ "ppc64le-unknown-linux-gnu" ,
781+ "riscv64-unknown-linux-gnu" ,
782+ "s390x-unknown-linux-gnu" ,
783+ "x86_64-apple-darwin" ,
784+ "x86_64-pc-windows-msvc" ,
785+ "x86_64-unknown-linux-gnu" ,
786+ "x86_64-unknown-linux-musl" ,
787+ ]
788+ is_full = entry .flavor .endswith ("-full" )
789+ if expects_full != is_full :
790+ continue
791+
792+ if "://" in location :
793+ urls = [location ]
794+ else :
795+ urls = ["{}/{}" .format (base_url , location ) for base_url in base_download_urls ]
796+
797+ v_dict = available_versions .setdefault (py_version , {})
798+ v_dict .setdefault ("sha256" , {})[matched_platform ] = sha256
799+ v_dict .setdefault ("url" , {})[matched_platform ] = urls
800+ if is_full :
801+ v_dict .setdefault ("strip_prefix" , {})[matched_platform ] = "python/install"
802+ else :
803+ v_dict .setdefault ("strip_prefix" , {})[matched_platform ] = "python"
804+
805+ def _get_toolchain_config (* , mctx , modules , _fail = fail ):
745806 """Computes the configs for toolchains.
746807
747808 Args:
809+ mctx: The module context.
748810 modules: The modules from module_ctx
749811 _fail: Function to call for failing; only used for testing.
750812
@@ -786,6 +848,19 @@ def _get_toolchain_config(*, modules, _fail = fail):
786848 else :
787849 available_versions [py_version ]["url" ] = dict (url )
788850
851+ # Check for add_runtime_manifest_urls in override tags in root module
852+ root_module = modules [0 ] if modules else None
853+ if root_module and root_module .is_root :
854+ for tag in root_module .tags .override :
855+ if tag .add_runtime_manifest_urls :
856+ _populate_from_pbs_manifest (
857+ mctx = mctx ,
858+ add_runtime_manifest_urls = tag .add_runtime_manifest_urls ,
859+ runtime_manifest_sha = tag .runtime_manifest_sha ,
860+ available_versions = available_versions ,
861+ _fail = _fail ,
862+ )
863+
789864 default = {
790865 "base_url" : DEFAULT_RELEASE_BASE_URL ,
791866 "platforms" : dict (PLATFORMS ), # Copy so it's mutable.
@@ -1111,6 +1186,21 @@ _override = tag_class(
11111186:::
11121187""" ,
11131188 attrs = {
1189+ "add_runtime_manifest_urls" : attr .string_list (
1190+ mandatory = False ,
1191+ doc = """
1192+ URLs pointing to python-build-standalone manifest files (e.g., SHA256SUMS).
1193+
1194+ Example:
1195+ `https://github.com/astral-sh/python-build-standalone/releases/download/20260414/SHA256SUMS`
1196+
1197+ Note that `/latest/` can be used in place of a specific release date (e.g., `20260414`) to automatically use the latest release:
1198+ `https://github.com/astral-sh/python-build-standalone/releases/latest/download/SHA256SUMS`
1199+
1200+ :::{versionadded} VERSION_NEXT_FEATURE
1201+ :::
1202+ """ ,
1203+ ),
11141204 "add_target_settings" : attr .string_list (
11151205 mandatory = False ,
11161206 doc = """\
@@ -1180,6 +1270,15 @@ The values in this mapping override the default values and do not replace them.
11801270 default = {},
11811271 ),
11821272 "register_all_versions" : attr .bool (default = False , doc = "Add all versions" ),
1273+ "runtime_manifest_sha" : attr .string (
1274+ mandatory = False ,
1275+ doc = """
1276+ SHA256 hash for the add_runtime_manifest_urls.
1277+
1278+ :::{versionadded} VERSION_NEXT_FEATURE
1279+ :::
1280+ """ ,
1281+ ),
11831282 } | AUTH_ATTRS ,
11841283)
11851284
0 commit comments