| # Copyright 2022 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. | |
| """The Python versions we use for the toolchains. | |
| """ | |
| load("//python/private:pbs_manifest.bzl", "parse_runtime_manifest") | |
| load("//python/private:platform_info.bzl", "platform_info") | |
| ##load("@rules_python_internal//:manifest_tool_versions.bzl", "MANIFEST_ENTRIES") | |
| load("//python/private:runtimes_manifest_workspace.bzl", "MANIFEST_TEXT") | |
| # Values present in the @platforms//os package | |
| MACOS_NAME = "osx" | |
| LINUX_NAME = "linux" | |
| WINDOWS_NAME = "windows" | |
| FREETHREADED = "-freethreaded" | |
| MUSL = "-musl" | |
| INSTALL_ONLY = "install_only" | |
| _GITHUB_PREFIX = "https://github.com/astral-sh/python-build-standalone/releases/download" | |
| _LEGACY_GITHUB_PREFIX = "https://github.com/indygreg/python-build-standalone/releases/download" | |
| _ASTRAL_PREFIX = "https://releases.astral.sh/github/python-build-standalone/releases/download" | |
| DEFAULT_RELEASE_BASE_URL = _GITHUB_PREFIX | |
| DEFAULT_RELEASE_BASE_URLS = [ | |
| _GITHUB_PREFIX, | |
| _ASTRAL_PREFIX, | |
| _LEGACY_GITHUB_PREFIX, | |
| ] | |
| # buildifier: disable=unsorted-dict-items | |
| MINOR_MAPPING = { | |
| "3.9": "3.9.25", | |
| "3.10": "3.10.20", | |
| "3.11": "3.11.15", | |
| "3.12": "3.12.13", | |
| "3.13": "3.13.13", | |
| "3.14": "3.14.4", | |
| "3.15": "3.15.0a8", | |
| } | |
| def _generate_platforms(): | |
| is_libc_glibc = str(Label("//python/config_settings:_is_py_linux_libc_glibc")) | |
| is_libc_musl = str(Label("//python/config_settings:_is_py_linux_libc_musl")) | |
| platforms = { | |
| "aarch64-apple-darwin": platform_info( | |
| compatible_with = [ | |
| "@platforms//os:macos", | |
| "@platforms//cpu:aarch64", | |
| ], | |
| os_name = MACOS_NAME, | |
| arch = "aarch64", | |
| ), | |
| "aarch64-pc-windows-msvc": platform_info( | |
| compatible_with = [ | |
| "@platforms//os:windows", | |
| "@platforms//cpu:aarch64", | |
| ], | |
| os_name = WINDOWS_NAME, | |
| arch = "aarch64", | |
| ), | |
| "aarch64-unknown-linux-gnu": platform_info( | |
| compatible_with = [ | |
| "@platforms//os:linux", | |
| "@platforms//cpu:aarch64", | |
| ], | |
| target_settings = [ | |
| is_libc_glibc, | |
| ], | |
| os_name = LINUX_NAME, | |
| arch = "aarch64", | |
| ), | |
| "arm64e-apple-darwin": platform_info( | |
| compatible_with = [ | |
| "@platforms//os:macos", | |
| "@platforms//cpu:arm64e", | |
| ], | |
| os_name = MACOS_NAME, | |
| arch = "aarch64", | |
| ), | |
| "armv7-unknown-linux-gnu": platform_info( | |
| compatible_with = [ | |
| "@platforms//os:linux", | |
| "@platforms//cpu:armv7", | |
| ], | |
| target_settings = [ | |
| is_libc_glibc, | |
| ], | |
| os_name = LINUX_NAME, | |
| arch = "arm", | |
| ), | |
| "i386-unknown-linux-gnu": platform_info( | |
| compatible_with = [ | |
| "@platforms//os:linux", | |
| "@platforms//cpu:i386", | |
| ], | |
| target_settings = [ | |
| is_libc_glibc, | |
| ], | |
| os_name = LINUX_NAME, | |
| arch = "x86_32", | |
| ), | |
| "ppc64le-unknown-linux-gnu": platform_info( | |
| compatible_with = [ | |
| "@platforms//os:linux", | |
| "@platforms//cpu:ppc", | |
| ], | |
| target_settings = [ | |
| is_libc_glibc, | |
| ], | |
| os_name = LINUX_NAME, | |
| arch = "ppc", | |
| ), | |
| "riscv64-unknown-linux-gnu": platform_info( | |
| compatible_with = [ | |
| "@platforms//os:linux", | |
| "@platforms//cpu:riscv64", | |
| ], | |
| target_settings = [ | |
| is_libc_glibc, | |
| ], | |
| os_name = LINUX_NAME, | |
| arch = "riscv64", | |
| ), | |
| "s390x-unknown-linux-gnu": platform_info( | |
| compatible_with = [ | |
| "@platforms//os:linux", | |
| "@platforms//cpu:s390x", | |
| ], | |
| target_settings = [ | |
| is_libc_glibc, | |
| ], | |
| os_name = LINUX_NAME, | |
| arch = "s390x", | |
| ), | |
| "x86_64-apple-darwin": platform_info( | |
| compatible_with = [ | |
| "@platforms//os:macos", | |
| "@platforms//cpu:x86_64", | |
| ], | |
| os_name = MACOS_NAME, | |
| arch = "x86_64", | |
| ), | |
| "x86_64-pc-windows-msvc": platform_info( | |
| compatible_with = [ | |
| "@platforms//os:windows", | |
| "@platforms//cpu:x86_64", | |
| ], | |
| os_name = WINDOWS_NAME, | |
| arch = "x86_64", | |
| ), | |
| "x86_64-unknown-linux-gnu": platform_info( | |
| compatible_with = [ | |
| "@platforms//os:linux", | |
| "@platforms//cpu:x86_64", | |
| ], | |
| target_settings = [ | |
| is_libc_glibc, | |
| ], | |
| os_name = LINUX_NAME, | |
| arch = "x86_64", | |
| ), | |
| "x86_64-unknown-linux-musl": platform_info( | |
| compatible_with = [ | |
| "@platforms//os:linux", | |
| "@platforms//cpu:x86_64", | |
| ], | |
| target_settings = [ | |
| is_libc_musl, | |
| ], | |
| os_name = LINUX_NAME, | |
| arch = "x86_64", | |
| ), | |
| } | |
| is_freethreaded_yes = str(Label("//python/config_settings:_is_py_freethreaded_yes")) | |
| is_freethreaded_no = str(Label("//python/config_settings:_is_py_freethreaded_no")) | |
| return { | |
| p + suffix: platform_info( | |
| compatible_with = v.compatible_with, | |
| target_settings = [ | |
| freethreadedness, | |
| ] + v.target_settings, | |
| os_name = v.os_name, | |
| arch = v.arch, | |
| ) | |
| for p, v in platforms.items() | |
| for suffix, freethreadedness in { | |
| "": is_freethreaded_no, | |
| FREETHREADED: is_freethreaded_yes, | |
| }.items() | |
| } | |
| PLATFORMS = _generate_platforms() | |
| def get_release_info(platform, python_version, base_urls = DEFAULT_RELEASE_BASE_URLS, tool_versions = None): | |
| """Resolve the release URL for the requested interpreter version | |
| Args: | |
| platform: The platform string for the interpreter | |
| python_version: The version of the interpreter to get | |
| base_urls: The list of URLs to prepend to the 'url' attr in the tool_versions dict | |
| tool_versions: A dict listing the interpreter versions, their SHAs and URL | |
| Returns: | |
| A tuple of (filename, url, archive strip prefix, patches, patch_strip) | |
| """ | |
| if tool_versions == None: | |
| tool_versions = TOOL_VERSIONS | |
| if type(base_urls) == type(""): | |
| base_urls = [base_urls] | |
| elif base_urls == None: | |
| base_urls = DEFAULT_RELEASE_BASE_URLS | |
| expanded_base_urls = [] | |
| for b_url in base_urls: | |
| if b_url not in expanded_base_urls: | |
| expanded_base_urls.append(b_url) | |
| if b_url.startswith(_GITHUB_PREFIX): | |
| suffix = b_url[len(_GITHUB_PREFIX):] | |
| a_url = _ASTRAL_PREFIX + suffix | |
| if a_url not in expanded_base_urls: | |
| expanded_base_urls.append(a_url) | |
| elif b_url.startswith(_LEGACY_GITHUB_PREFIX): | |
| suffix = b_url[len(_LEGACY_GITHUB_PREFIX):] | |
| a_url = _ASTRAL_PREFIX + suffix | |
| if a_url not in expanded_base_urls: | |
| expanded_base_urls.append(a_url) | |
| base_urls = expanded_base_urls | |
| url = tool_versions[python_version]["url"] | |
| if type(url) == type({}): | |
| url = url[platform] | |
| if type(url) != type([]): | |
| url = [url] | |
| strip_prefix = tool_versions[python_version].get("strip_prefix", None) | |
| if type(strip_prefix) == type({}): | |
| strip_prefix = strip_prefix[platform] | |
| release_filename = None | |
| rendered_urls = [] | |
| for u in url: | |
| p, _, _ = platform.partition(FREETHREADED) | |
| # Assume an unknown release_id is a newer url format | |
| release_id = 99999999 | |
| url_parts = u.split("/") | |
| if len(url_parts) >= 2 and url_parts[-2].isdigit(): | |
| maybe_release_id = url_parts[-2] | |
| release_id = int(maybe_release_id) | |
| if FREETHREADED.lstrip("-") in platform and release_id < 20260325: | |
| build = "{}+{}-full".format( | |
| FREETHREADED.lstrip("-"), | |
| { | |
| "aarch64-apple-darwin": "pgo+lto", | |
| "aarch64-pc-windows-msvc": "pgo", | |
| "aarch64-unknown-linux-gnu": "lto" if release_id < 20250702 else "pgo+lto", | |
| "ppc64le-unknown-linux-gnu": "lto", | |
| "riscv64-unknown-linux-gnu": "lto", | |
| "s390x-unknown-linux-gnu": "lto", | |
| "x86_64-apple-darwin": "pgo+lto", | |
| "x86_64-pc-windows-msvc": "pgo", | |
| "x86_64-unknown-linux-gnu": "pgo+lto", | |
| "x86_64-unknown-linux-musl": "pgo+lto", | |
| }[p], | |
| ) | |
| else: | |
| build = INSTALL_ONLY | |
| if WINDOWS_NAME in platform and release_id < 20250317: | |
| build = "shared-" + build | |
| release_filename = u.format( | |
| platform = p, | |
| python_version = python_version, | |
| build = build, | |
| ext = "tar.zst" if build.endswith("full") else "tar.gz", | |
| ) | |
| if "://" in release_filename: # is absolute url? | |
| rendered_urls.append(release_filename) | |
| else: | |
| for b_url in base_urls: | |
| rendered_urls.append("/".join([b_url, release_filename])) | |
| if release_filename == None: | |
| fail("release_filename should be set by now; were any download URLs given?") | |
| patches = tool_versions[python_version].get("patches", []) | |
| if type(patches) == type({}): | |
| if platform in patches.keys(): | |
| patches = patches[platform] | |
| else: | |
| patches = [] | |
| patch_strip = tool_versions[python_version].get("patch_strip", None) | |
| if type(patch_strip) == type({}): | |
| if platform in patch_strip.keys(): | |
| patch_strip = patch_strip[platform] | |
| else: | |
| patch_strip = None | |
| return (release_filename, rendered_urls, strip_prefix, patches, patch_strip) | |
| def gen_python_config_settings(name = ""): | |
| for platform in PLATFORMS.keys(): | |
| native.config_setting( | |
| name = "{name}{platform}".format(name = name, platform = platform), | |
| flag_values = PLATFORMS[platform].flag_values, | |
| constraint_values = PLATFORMS[platform].compatible_with, | |
| ) | |
| def _manifest_entry_sort_key(entry): | |
| flavor_rank = {"full": 3, "install_only": 1, "install_only_stripped": 2}.get(entry.archive_flavor, 4) | |
| microarch = entry.microarch | |
| if not microarch: | |
| microarch_rank = 0 | |
| elif microarch.startswith("v") and microarch[1:].isdigit(): | |
| microarch_rank = int(microarch[1:]) | |
| else: | |
| microarch_rank = 999 | |
| return (flavor_rank, microarch_rank) | |
| def _tool_versions_from_manifest_entries(entries, base_url = DEFAULT_RELEASE_BASE_URL): | |
| """Converts parsed manifest entries into the TOOL_VERSIONS dictionary format. | |
| Args: | |
| entries: {type}`list[struct]` the parsed manifest entries. | |
| base_url: {type}`str` fallback base URL for standalone distributions. | |
| Returns: | |
| {type}`dict[str, dict]` the tool versions map. | |
| """ | |
| available_versions = {} | |
| entries = sorted( | |
| entries, | |
| key = _manifest_entry_sort_key, | |
| ) | |
| for entry in entries: | |
| location = entry.location | |
| sha256 = entry.sha256 | |
| py_version = entry.python_version | |
| matched_platform = "{}-{}-{}".format(entry.arch, entry.vendor, entry.os) | |
| if entry.libc: | |
| matched_platform += "-" + entry.libc | |
| if entry.freethreaded: | |
| matched_platform += "-freethreaded" | |
| if matched_platform not in PLATFORMS: | |
| continue | |
| archive_flavor = entry.archive_flavor | |
| if archive_flavor not in ["install_only", "install_only_stripped", "full"]: | |
| continue | |
| v_dict = available_versions.setdefault(py_version, {}) | |
| if matched_platform in v_dict.get("sha256", {}): | |
| continue | |
| if "://" in location: | |
| urls = [location] | |
| else: | |
| urls = ["{}/{}".format(base_url, location)] | |
| strip_prefix = "python/install" if archive_flavor == "full" else "python" | |
| v_dict.setdefault("sha256", {})[matched_platform] = sha256 | |
| v_dict.setdefault("url", {})[matched_platform] = urls | |
| v_dict.setdefault("strip_prefix", {})[matched_platform] = strip_prefix | |
| return available_versions | |
| TOOL_VERSIONS = _tool_versions_from_manifest_entries(parse_runtime_manifest(MANIFEST_TEXT)) |