| # Copyright 2023 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. |
| |
| "Repo rule used by bzlmod extension to create a repo that has a map of Python interpreters and their labels" |
| |
| load("//python:versions.bzl", "PLATFORMS") |
| load(":text_util.bzl", "render") |
| load(":toolchains_repo.bzl", "toolchain_suite_content") |
| |
| def _have_same_length(*lists): |
| if not lists: |
| fail("expected at least one list") |
| return len({len(length): None for length in lists}) == 1 |
| |
| _HUB_BUILD_FILE_TEMPLATE = """\ |
| # Generated by @rules_python//python/private:pythons_hub.bzl |
| |
| load("@@{rules_python}//python/private:py_toolchain_suite.bzl", "py_toolchain_suite") |
| load("@bazel_skylib//:bzl_library.bzl", "bzl_library") |
| |
| bzl_library( |
| name = "interpreters_bzl", |
| srcs = ["interpreters.bzl"], |
| visibility = ["@rules_python//:__subpackages__"], |
| ) |
| |
| bzl_library( |
| name = "versions_bzl", |
| srcs = ["versions.bzl"], |
| visibility = ["@rules_python//:__subpackages__"], |
| ) |
| |
| {toolchains} |
| """ |
| |
| def _hub_build_file_content(rctx): |
| # Verify a precondition. If these don't match, then something went wrong. |
| if not _have_same_length( |
| rctx.attr.toolchain_names, |
| rctx.attr.toolchain_platform_keys, |
| rctx.attr.toolchain_repo_names, |
| rctx.attr.toolchain_target_compatible_with_map, |
| rctx.attr.toolchain_target_settings_map, |
| rctx.attr.toolchain_set_python_version_constraints, |
| rctx.attr.toolchain_python_versions, |
| ): |
| fail("all lists must have the same length") |
| |
| #pad_length = len(str(len(rctx.attr.toolchain_names))) + 1 |
| pad_length = 4 |
| toolchains = [] |
| for i, base_name in enumerate(rctx.attr.toolchain_names): |
| key = str(i) |
| platform = rctx.attr.toolchain_platform_keys[key] |
| if platform in PLATFORMS: |
| flag_values = PLATFORMS[platform].flag_values |
| else: |
| flag_values = {} |
| |
| toolchains.append(toolchain_suite_content( |
| prefix = "_{}_{}".format(render.left_pad_zero(i, pad_length), base_name), |
| user_repository_name = rctx.attr.toolchain_repo_names[key], |
| target_compatible_with = rctx.attr.toolchain_target_compatible_with_map[key], |
| flag_values = flag_values, |
| target_settings = rctx.attr.toolchain_target_settings_map[key], |
| set_python_version_constraint = rctx.attr.toolchain_set_python_version_constraints[key], |
| python_version = rctx.attr.toolchain_python_versions[key], |
| )) |
| |
| return _HUB_BUILD_FILE_TEMPLATE.format( |
| toolchains = "\n".join(toolchains), |
| rules_python = rctx.attr._rules_python_workspace.repo_name, |
| ) |
| |
| _interpreters_bzl_template = """ |
| INTERPRETER_LABELS = {labels} |
| """ |
| |
| _versions_bzl_template = """ |
| DEFAULT_PYTHON_VERSION = "{default_python_version}" |
| MINOR_MAPPING = {minor_mapping} |
| PYTHON_VERSIONS = {python_versions} |
| """ |
| |
| def _hub_repo_impl(rctx): |
| # Create the various toolchain definitions and |
| # write them to the BUILD file. |
| rctx.file( |
| "BUILD.bazel", |
| _hub_build_file_content(rctx), |
| executable = False, |
| ) |
| |
| # Create a dict that is later used to create |
| # a symlink to a interpreter. |
| rctx.file( |
| "interpreters.bzl", |
| _interpreters_bzl_template.format( |
| labels = render.dict( |
| { |
| name: 'Label("@{}//:python")'.format(name) |
| for name in rctx.attr.host_compatible_repo_names |
| }, |
| value_repr = str, |
| ), |
| ), |
| executable = False, |
| ) |
| |
| rctx.file( |
| "versions.bzl", |
| _versions_bzl_template.format( |
| default_python_version = rctx.attr.default_python_version, |
| minor_mapping = render.dict(rctx.attr.minor_mapping), |
| python_versions = rctx.attr.python_versions or render.list(sorted({ |
| v: None |
| for v in rctx.attr.toolchain_python_versions |
| })), |
| ), |
| executable = False, |
| ) |
| |
| hub_repo = repository_rule( |
| doc = """\ |
| This private rule create a repo with a BUILD file that contains a map of interpreter names |
| and the labels to said interpreters. This map is used to by the interpreter hub extension. |
| This rule also writes out the various toolchains for the different Python versions. |
| """, |
| implementation = _hub_repo_impl, |
| attrs = { |
| "default_python_version": attr.string( |
| doc = "Default Python version for the build in `X.Y` or `X.Y.Z` format.", |
| mandatory = True, |
| ), |
| "host_compatible_repo_names": attr.string_list( |
| doc = "Names of `host_compatible_python_repo` repos.", |
| mandatory = True, |
| ), |
| "minor_mapping": attr.string_dict( |
| doc = "The minor mapping of the `X.Y` to `X.Y.Z` format that is used in config settings.", |
| mandatory = True, |
| ), |
| "python_versions": attr.string_list( |
| doc = "The list of python versions to include in the `interpreters.bzl` if the toolchains are not specified. Used in `WORKSPACE` builds.", |
| mandatory = False, |
| ), |
| "toolchain_names": attr.string_list( |
| doc = "Names of toolchains", |
| mandatory = True, |
| ), |
| "toolchain_platform_keys": attr.string_dict( |
| doc = "The platform key in PLATFORMS for toolchains.", |
| mandatory = True, |
| ), |
| "toolchain_python_versions": attr.string_dict( |
| doc = "List of Python versions for the toolchains. In `X.Y.Z` format.", |
| mandatory = True, |
| ), |
| "toolchain_repo_names": attr.string_dict( |
| doc = "The repo names containing toolchain implementations.", |
| mandatory = True, |
| ), |
| "toolchain_set_python_version_constraints": attr.string_dict( |
| doc = "List of version contraints for the toolchains", |
| mandatory = True, |
| ), |
| "toolchain_target_compatible_with_map": attr.string_list_dict( |
| doc = "The target_compatible_with settings for toolchains.", |
| mandatory = True, |
| ), |
| "toolchain_target_settings_map": attr.string_list_dict( |
| doc = "The target_settings for toolchains", |
| mandatory = True, |
| ), |
| "_rules_python_workspace": attr.label(default = Label("//:does_not_matter_what_this_name_is")), |
| }, |
| ) |