blob: 6e3efb34d6da7c4710f59da051982c3c391849e4 [file]
# 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))