# Copyright 2024 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. """This file contains repository rules and macros to support toolchain registration. """ load( "//python:versions.bzl", "DEFAULT_RELEASE_BASE_URL", "MINOR_MAPPING", "PLATFORMS", "TOOL_VERSIONS", "get_release_info", ) load(":bzlmod_enabled.bzl", "BZLMOD_ENABLED") load(":coverage_deps.bzl", "coverage_dep") load(":full_version.bzl", "full_version") load(":python_repository.bzl", "python_repository") load( ":toolchains_repo.bzl", "host_toolchain", "toolchain_aliases", "toolchains_repo", ) # Wrapper macro around everything above, this is the primary API. def python_register_toolchains( name, python_version, register_toolchains = True, register_coverage_tool = False, set_python_version_constraint = False, tool_versions = None, minor_mapping = None, **kwargs): """Convenience macro for users which does typical setup. With `bzlmod` enabled, this function is not needed since `rules_python` is handling everything. In order to override the default behaviour from the root module one can see the docs for the {rule}`python` extension. - Create a repository for each built-in platform like "python_3_8_linux_amd64" - this repository is lazily fetched when Python is needed for that platform. - Create a repository exposing toolchains for each platform like "python_platforms". - Register a toolchain pointing at each platform. Users can avoid this macro and do these steps themselves, if they want more control. Args: name: {type}`str` base name for all created repos, e.g. "python_3_8". python_version: {type}`str` the Python version. register_toolchains: {type}`bool` Whether or not to register the downloaded toolchains. register_coverage_tool: {type}`bool` Whether or not to register the downloaded coverage tool to the toolchains. set_python_version_constraint: {type}`bool` When set to `True`, `target_compatible_with` for the toolchains will include a version constraint. tool_versions: {type}`dict` contains a mapping of version with SHASUM and platform info. If not supplied, the defaults in python/versions.bzl will be used. minor_mapping: {type}`dict[str, str]` contains a mapping from `X.Y` to `X.Y.Z` version. **kwargs: passed to each {obj}`python_repository` call. """ if BZLMOD_ENABLED: # you cannot used native.register_toolchains when using bzlmod. register_toolchains = False base_url = kwargs.pop("base_url", DEFAULT_RELEASE_BASE_URL) tool_versions = tool_versions or TOOL_VERSIONS minor_mapping = minor_mapping or MINOR_MAPPING python_version = full_version(version = python_version, minor_mapping = minor_mapping) toolchain_repo_name = "{name}_toolchains".format(name = name) # When using unreleased Bazel versions, the version is an empty string if native.bazel_version: bazel_major = int(native.bazel_version.split(".")[0]) if bazel_major < 6: if register_coverage_tool: # buildifier: disable=print print(( "WARNING: ignoring register_coverage_tool=True when " + "registering @{name}: Bazel 6+ required, got {version}" ).format( name = name, version = native.bazel_version, )) register_coverage_tool = False loaded_platforms = [] for platform in PLATFORMS.keys(): sha256 = tool_versions[python_version]["sha256"].get(platform, None) if not sha256: continue loaded_platforms.append(platform) (release_filename, urls, strip_prefix, patches, patch_strip) = get_release_info(platform, python_version, base_url, tool_versions) # allow passing in a tool version coverage_tool = None coverage_tool = tool_versions[python_version].get("coverage_tool", {}).get(platform, None) if register_coverage_tool and coverage_tool == None: coverage_tool = coverage_dep( name = "{name}_{platform}_coverage".format( name = name, platform = platform, ), python_version = python_version, platform = platform, visibility = ["@{name}_{platform}//:__subpackages__".format( name = name, platform = platform, )], ) python_repository( name = "{name}_{platform}".format( name = name, platform = platform, ), sha256 = sha256, patches = patches, patch_strip = patch_strip, platform = platform, python_version = python_version, release_filename = release_filename, urls = urls, strip_prefix = strip_prefix, coverage_tool = coverage_tool, **kwargs ) if register_toolchains: native.register_toolchains("@{toolchain_repo_name}//:{platform}_toolchain".format( toolchain_repo_name = toolchain_repo_name, platform = platform, )) native.register_toolchains("@{toolchain_repo_name}//:{platform}_py_cc_toolchain".format( toolchain_repo_name = toolchain_repo_name, platform = platform, )) native.register_toolchains("@{toolchain_repo_name}//:{platform}_py_exec_tools_toolchain".format( toolchain_repo_name = toolchain_repo_name, platform = platform, )) host_toolchain(name = name + "_host") toolchain_aliases( name = name, python_version = python_version, user_repository_name = name, platforms = loaded_platforms, ) # in bzlmod we write out our own toolchain repos if BZLMOD_ENABLED: return toolchains_repo( name = toolchain_repo_name, python_version = python_version, set_python_version_constraint = set_python_version_constraint, user_repository_name = name, )