| # 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(":pbs_manifest.bzl", "parse_runtime_manifest") | |
| 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", | |
| srcs = ["interpreters.bzl"], | |
| visibility = ["@rules_python//:__subpackages__"], | |
| ) | |
| bzl_library( | |
| name = "versions", | |
| 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, | |
| ) | |
| python_versions = rctx.attr.python_versions | |
| if not python_versions and not rctx.attr.toolchain_python_versions: | |
| content = rctx.read(rctx.path(Label("//python/private:runtimes_manifest.txt"))) | |
| entries = parse_runtime_manifest(content) | |
| python_versions_str = render.list(sorted({getattr(e, "python_version", ""): None for e in entries if getattr(e, "python_version", "")})) | |
| else: | |
| python_versions_str = render.list(python_versions) if python_versions else render.list(sorted({ | |
| v: None | |
| for v in rctx.attr.toolchain_python_versions | |
| })) | |
| 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 = python_versions_str, | |
| ), | |
| 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")), | |
| }, | |
| ) |