| # 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. | |
| """This file contains macros to be called during WORKSPACE evaluation. | |
| For historic reasons, pip_repositories() is defined in //python:pip.bzl. | |
| """ | |
| load("//python/private:toolchains_repo.bzl", "resolved_interpreter_os_alias", "toolchains_repo") | |
| load( | |
| ":versions.bzl", | |
| "DEFAULT_RELEASE_BASE_URL", | |
| "MINOR_MAPPING", | |
| "PLATFORMS", | |
| "TOOL_VERSIONS", | |
| "get_release_url", | |
| ) | |
| def py_repositories(): | |
| # buildifier: disable=print | |
| print("py_repositories is a no-op and is deprecated. You can remove this from your WORKSPACE file") | |
| ######## | |
| # Remaining content of the file is only used to support toolchains. | |
| ######## | |
| def _python_repository_impl(rctx): | |
| if rctx.attr.distutils and rctx.attr.distutils_content: | |
| fail("Only one of (distutils, distutils_content) should be set.") | |
| platform = rctx.attr.platform | |
| python_version = rctx.attr.python_version | |
| python_short_version = python_version.rpartition(".")[0] | |
| release_filename = rctx.attr.release_filename | |
| url = rctx.attr.url | |
| if release_filename.endswith(".zst"): | |
| rctx.download( | |
| url = url, | |
| sha256 = rctx.attr.sha256, | |
| output = release_filename, | |
| ) | |
| unzstd = rctx.which("unzstd") | |
| if not unzstd: | |
| url = rctx.attr.zstd_url.format(version = rctx.attr.zstd_version) | |
| rctx.download_and_extract( | |
| url = url, | |
| sha256 = rctx.attr.zstd_sha256, | |
| ) | |
| working_directory = "zstd-{version}".format(version = rctx.attr.zstd_version) | |
| make_result = rctx.execute( | |
| ["make", "--jobs=4"], | |
| timeout = 600, | |
| quiet = True, | |
| working_directory = working_directory, | |
| ) | |
| if make_result.return_code: | |
| fail(make_result.stderr) | |
| zstd = "{working_directory}/zstd".format(working_directory = working_directory) | |
| unzstd = "./unzstd" | |
| rctx.symlink(zstd, unzstd) | |
| exec_result = rctx.execute([ | |
| "tar", | |
| "--extract", | |
| "--strip-components=2", | |
| "--use-compress-program={unzstd}".format(unzstd = unzstd), | |
| "--file={}".format(release_filename), | |
| ]) | |
| if exec_result.return_code: | |
| fail(exec_result.stderr) | |
| else: | |
| rctx.download_and_extract( | |
| url = url, | |
| sha256 = rctx.attr.sha256, | |
| stripPrefix = rctx.attr.strip_prefix, | |
| ) | |
| # Write distutils.cfg to the Python installation. | |
| if "windows" in rctx.os.name: | |
| distutils_path = "Lib/distutils/distutils.cfg" | |
| else: | |
| distutils_path = "lib/python{}/distutils/distutils.cfg".format(python_short_version) | |
| if rctx.attr.distutils: | |
| rctx.file(distutils_path, rctx.read(rctx.attr.distutils)) | |
| elif rctx.attr.distutils_content: | |
| rctx.file(distutils_path, rctx.attr.distutils_content) | |
| # Make the Python installation read-only. | |
| if "windows" not in rctx.os.name: | |
| exec_result = rctx.execute(["chmod", "-R", "ugo-w", "lib"]) | |
| if exec_result.return_code: | |
| fail(exec_result.stderr) | |
| python_bin = "python.exe" if ("windows" in platform) else "bin/python3" | |
| build_content = """\ | |
| # Generated by python/repositories.bzl | |
| load("@bazel_tools//tools/python:toolchain.bzl", "py_runtime_pair") | |
| package(default_visibility = ["//visibility:public"]) | |
| filegroup( | |
| name = "files", | |
| srcs = glob( | |
| include = [ | |
| "*.exe", | |
| "*.dll", | |
| "bin/**", | |
| "DLLs/**", | |
| "extensions/**", | |
| "include/**", | |
| "lib/**", | |
| "libs/**", | |
| "Scripts/**", | |
| "share/**", | |
| ], | |
| exclude = [ | |
| "**/* *", # Bazel does not support spaces in file names. | |
| ], | |
| ), | |
| ) | |
| filegroup( | |
| name = "includes", | |
| srcs = glob(["include/**/*.h"]), | |
| ) | |
| cc_library( | |
| name = "python_headers", | |
| hdrs = [":includes"], | |
| includes = [ | |
| "include", | |
| "include/python{python_version}", | |
| "include/python{python_version}m", | |
| ], | |
| ) | |
| exports_files(["{python_path}"]) | |
| py_runtime( | |
| name = "py3_runtime", | |
| files = [":files"], | |
| interpreter = "{python_path}", | |
| python_version = "PY3", | |
| ) | |
| py_runtime_pair( | |
| name = "python_runtimes", | |
| py2_runtime = None, | |
| py3_runtime = ":py3_runtime", | |
| ) | |
| """.format( | |
| python_path = python_bin, | |
| python_version = python_short_version, | |
| ) | |
| rctx.file("BUILD.bazel", build_content) | |
| return { | |
| "distutils": rctx.attr.distutils, | |
| "distutils_content": rctx.attr.distutils_content, | |
| "name": rctx.attr.name, | |
| "platform": platform, | |
| "python_version": python_version, | |
| "release_filename": release_filename, | |
| "sha256": rctx.attr.sha256, | |
| "strip_prefix": rctx.attr.strip_prefix, | |
| "url": url, | |
| } | |
| python_repository = repository_rule( | |
| _python_repository_impl, | |
| doc = "Fetches the external tools needed for the Python toolchain.", | |
| attrs = { | |
| "distutils": attr.label( | |
| allow_single_file = True, | |
| doc = "A distutils.cfg file to be included in the Python installation. " + | |
| "Either distutils or distutils_content can be specified, but not both.", | |
| mandatory = False, | |
| ), | |
| "distutils_content": attr.string( | |
| doc = "A distutils.cfg file content to be included in the Python installation. " + | |
| "Either distutils or distutils_content can be specified, but not both.", | |
| mandatory = False, | |
| ), | |
| "platform": attr.string( | |
| doc = "The platform name for the Python interpreter tarball.", | |
| mandatory = True, | |
| values = PLATFORMS.keys(), | |
| ), | |
| "python_version": attr.string( | |
| doc = "The Python version.", | |
| mandatory = True, | |
| ), | |
| "release_filename": attr.string( | |
| doc = "The filename of the interpreter to be downloaded", | |
| mandatory = True, | |
| ), | |
| "sha256": attr.string( | |
| doc = "The SHA256 integrity hash for the Python interpreter tarball.", | |
| mandatory = True, | |
| ), | |
| "strip_prefix": attr.string( | |
| doc = "A directory prefix to strip from the extracted files.", | |
| mandatory = True, | |
| ), | |
| "url": attr.string( | |
| doc = "The URL of the interpreter to download", | |
| mandatory = True, | |
| ), | |
| "zstd_sha256": attr.string( | |
| default = "7c42d56fac126929a6a85dbc73ff1db2411d04f104fae9bdea51305663a83fd0", | |
| ), | |
| "zstd_url": attr.string( | |
| default = "https://github.com/facebook/zstd/releases/download/v{version}/zstd-{version}.tar.gz", | |
| ), | |
| "zstd_version": attr.string( | |
| default = "1.5.2", | |
| ), | |
| }, | |
| ) | |
| # Wrapper macro around everything above, this is the primary API. | |
| def python_register_toolchains( | |
| name, | |
| python_version, | |
| distutils = None, | |
| distutils_content = None, | |
| register_toolchains = True, | |
| tool_versions = TOOL_VERSIONS, | |
| **kwargs): | |
| """Convenience macro for users which does typical setup. | |
| - Create a repository for each built-in platform like "python_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: base name for all created repos, like "python38". | |
| python_version: the Python version. | |
| distutils: see the distutils attribute in the python_repository repository rule. | |
| distutils_content: see the distutils_content attribute in the python_repository repository rule. | |
| register_toolchains: Whether or not to register the downloaded toolchains. | |
| tool_versions: a dict containing a mapping of version with SHASUM and platform info. If not supplied, the defaults | |
| in python/versions.bzl will be used | |
| **kwargs: passed to each python_repositories call. | |
| """ | |
| base_url = kwargs.pop("base_url", DEFAULT_RELEASE_BASE_URL) | |
| if python_version in MINOR_MAPPING: | |
| python_version = MINOR_MAPPING[python_version] | |
| for platform in PLATFORMS.keys(): | |
| sha256 = tool_versions[python_version]["sha256"].get(platform, None) | |
| if not sha256: | |
| continue | |
| (release_filename, url, strip_prefix) = get_release_url(platform, python_version, base_url, tool_versions) | |
| python_repository( | |
| name = "{name}_{platform}".format( | |
| name = name, | |
| platform = platform, | |
| ), | |
| sha256 = sha256, | |
| platform = platform, | |
| python_version = python_version, | |
| release_filename = release_filename, | |
| url = url, | |
| distutils = distutils, | |
| distutils_content = distutils_content, | |
| strip_prefix = strip_prefix, | |
| **kwargs | |
| ) | |
| if register_toolchains: | |
| native.register_toolchains("@{name}_toolchains//:{platform}_toolchain".format( | |
| name = name, | |
| platform = platform, | |
| )) | |
| resolved_interpreter_os_alias( | |
| name = name, | |
| user_repository_name = name, | |
| ) | |
| toolchains_repo( | |
| name = "{name}_toolchains".format(name = name), | |
| user_repository_name = name, | |
| ) |