# 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. """Setup a python-build-standalone based toolchain.""" load("@rules_cc//cc:cc_import.bzl", "cc_import") load("@rules_cc//cc:cc_library.bzl", "cc_library") load("//python:py_runtime.bzl", "py_runtime") load("//python:py_runtime_pair.bzl", "py_runtime_pair") load("//python/cc:py_cc_toolchain.bzl", "py_cc_toolchain") load(":glob_excludes.bzl", "glob_excludes") load(":py_exec_tools_toolchain.bzl", "py_exec_tools_toolchain") load(":semver.bzl", "semver") _IS_FREETHREADED = Label("//python/config_settings:is_py_freethreaded") def define_hermetic_runtime_toolchain_impl( *, name, extra_files_glob_include, extra_files_glob_exclude, python_version, python_bin, coverage_tool): """Define a toolchain implementation for a python-build-standalone repo. It expected this macro is called in the top-level package of an extracted python-build-standalone repository. See python/private/python_repositories.bzl for how it is invoked. Args: name: {type}`str` name used for tools to identify the invocation. extra_files_glob_include: {type}`list[str]` additional glob include patterns for the target runtime files (the one included in binaries). extra_files_glob_exclude: {type}`list[str]` additional glob exclude patterns for the target runtime files. python_version: {type}`str` The Python version, in `major.minor.micro` format. python_bin: {type}`str` The path to the Python binary within the repository. coverage_tool: {type}`str` optional target to the coverage tool to use. """ _ = name # @unused version_info = semver(python_version) version_dict = version_info.to_dict() native.filegroup( name = "files", srcs = native.glob( include = [ "bin/**", "extensions/**", "include/**", "libs/**", "share/**", ] + extra_files_glob_include, # Platform-agnostic filegroup can't match on all patterns. allow_empty = True, exclude = [ # Unused shared libraries. `python` executable and the `:libpython` target # depend on `libpython{python_version}.so.1.0`. "lib/libpython{major}.{minor}*.so".format(**version_dict), # static libraries "lib/**/*.a", # tests for the standard libraries. "lib/python{major}.{minor}*/**/test/**".format(**version_dict), "lib/python{major}.{minor}*/**/tests/**".format(**version_dict), # During pyc creation, temp files named *.pyc.NNN are created "**/__pycache__/*.pyc.*", ] + glob_excludes.version_dependent_exclusions() + extra_files_glob_exclude, ), ) cc_import( name = "interface", interface_library = select({ _IS_FREETHREADED: "libs/python{major}{minor}t.lib".format(**version_dict), "//conditions:default": "libs/python{major}{minor}.lib".format(**version_dict), }), system_provided = True, ) cc_import( name = "abi3_interface", interface_library = select({ _IS_FREETHREADED: "libs/python3t.lib", "//conditions:default": "libs/python3.lib", }), system_provided = True, ) native.filegroup( name = "includes", srcs = native.glob(["include/**/*.h"]), ) cc_library( name = "python_headers", deps = select({ "@bazel_tools//src/conditions:windows": [":interface", ":abi3_interface"], "//conditions:default": None, }), hdrs = [":includes"], includes = [ "include", ] + select({ _IS_FREETHREADED: [ "include/python{major}.{minor}t".format(**version_dict), ], "//conditions:default": [ "include/python{major}.{minor}".format(**version_dict), "include/python{major}.{minor}m".format(**version_dict), ], }), ) native.config_setting( name = "is_freethreaded_linux", flag_values = { Label("//python/config_settings:py_freethreaded"): "yes", }, constraint_values = [ "@platforms//os:linux", ], visibility = ["//visibility:private"], ) native.config_setting( name = "is_freethreaded_osx", flag_values = { Label("//python/config_settings:py_freethreaded"): "yes", }, constraint_values = [ "@platforms//os:osx", ], visibility = ["//visibility:private"], ) native.config_setting( name = "is_freethreaded_windows", flag_values = { Label("//python/config_settings:py_freethreaded"): "yes", }, constraint_values = [ "@platforms//os:windows", ], visibility = ["//visibility:private"], ) cc_library( name = "libpython", hdrs = [":includes"], srcs = select({ ":is_freethreaded_linux": [ "lib/libpython{major}.{minor}t.so".format(**version_dict), "lib/libpython{major}.{minor}t.so.1.0".format(**version_dict), ], ":is_freethreaded_osx": [ "lib/libpython{major}.{minor}t.dylib".format(**version_dict), ], ":is_freethreaded_windows": [ "python3t.dll", "python{major}{minor}t.dll".format(**version_dict), "libs/python{major}{minor}t.lib".format(**version_dict), "libs/python3t.lib", ], "@platforms//os:linux": [ "lib/libpython{major}.{minor}.so".format(**version_dict), "lib/libpython{major}.{minor}.so.1.0".format(**version_dict), ], "@platforms//os:macos": ["lib/libpython{major}.{minor}.dylib".format(**version_dict)], "@platforms//os:windows": [ "python3.dll", "python{major}{minor}.dll".format(**version_dict), "libs/python{major}{minor}.lib".format(**version_dict), "libs/python3.lib", ], }), ) native.exports_files(["python", python_bin]) # Used to only download coverage toolchain when the coverage is collected by # bazel. native.config_setting( name = "coverage_enabled", values = {"collect_code_coverage": "true"}, visibility = ["//visibility:private"], ) py_runtime( name = "py3_runtime", files = [":files"], interpreter = python_bin, interpreter_version_info = { "major": str(version_info.major), "micro": str(version_info.patch), "minor": str(version_info.minor), }, coverage_tool = select({ # Convert empty string to None ":coverage_enabled": coverage_tool or None, "//conditions:default": None, }), python_version = "PY3", implementation_name = "cpython", # See https://peps.python.org/pep-3147/ for pyc tag infix format pyc_tag = select({ _IS_FREETHREADED: "cpython-{major}{minor}t".format(**version_dict), "//conditions:default": "cpython-{major}{minor}".format(**version_dict), }), ) py_runtime_pair( name = "python_runtimes", py2_runtime = None, py3_runtime = ":py3_runtime", ) py_cc_toolchain( name = "py_cc_toolchain", headers = ":python_headers", libs = ":libpython", python_version = python_version, ) py_exec_tools_toolchain( name = "py_exec_tools_toolchain", # This macro is called in another repo: use Label() to ensure it # resolves in the rules_python context. precompiler = Label("//tools/precompiler:precompiler"), )