1# Copyright 2024 The Bazel Authors. All rights reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Setup a python-build-standalone based toolchain.""" 15 16load("@rules_cc//cc:defs.bzl", "cc_import", "cc_library") 17load("//python:py_runtime.bzl", "py_runtime") 18load("//python:py_runtime_pair.bzl", "py_runtime_pair") 19load("//python/cc:py_cc_toolchain.bzl", "py_cc_toolchain") 20load(":py_exec_tools_toolchain.bzl", "py_exec_tools_toolchain") 21load(":semver.bzl", "semver") 22 23def define_hermetic_runtime_toolchain_impl( 24 *, 25 name, 26 extra_files_glob_include, 27 extra_files_glob_exclude, 28 python_version, 29 python_bin, 30 coverage_tool): 31 """Define a toolchain implementation for a python-build-standalone repo. 32 33 It expected this macro is called in the top-level package of an extracted 34 python-build-standalone repository. See 35 python/private/python_repositories.bzl for how it is invoked. 36 37 Args: 38 name: {type}`str` name used for tools to identify the invocation. 39 extra_files_glob_include: {type}`list[str]` additional glob include 40 patterns for the target runtime files (the one included in 41 binaries). 42 extra_files_glob_exclude: {type}`list[str]` additional glob exclude 43 patterns for the target runtime files. 44 python_version: {type}`str` The Python version, in `major.minor.micro` 45 format. 46 python_bin: {type}`str` The path to the Python binary within the 47 repositoroy. 48 coverage_tool: {type}`str` optional target to the coverage tool to 49 use. 50 """ 51 _ = name # @unused 52 version_info = semver(python_version) 53 version_dict = version_info.to_dict() 54 native.filegroup( 55 name = "files", 56 srcs = native.glob( 57 include = [ 58 "bin/**", 59 "extensions/**", 60 "include/**", 61 "libs/**", 62 "share/**", 63 ] + extra_files_glob_include, 64 # Platform-agnostic filegroup can't match on all patterns. 65 allow_empty = True, 66 exclude = [ 67 "**/* *", # Bazel does not support spaces in file names. 68 # Unused shared libraries. `python` executable and the `:libpython` target 69 # depend on `libpython{python_version}.so.1.0`. 70 "lib/libpython{major}.{minor}.so".format(**version_dict), 71 # static libraries 72 "lib/**/*.a", 73 # tests for the standard libraries. 74 "lib/python{major}.{minor}/**/test/**".format(**version_dict), 75 "lib/python{major}.{minor}/**/tests/**".format(**version_dict), 76 "**/__pycache__/*.pyc.*", # During pyc creation, temp files named *.pyc.NNN are created 77 ] + extra_files_glob_exclude, 78 ), 79 ) 80 cc_import( 81 name = "interface", 82 interface_library = "libs/python{major}{minor}.lib".format(**version_dict), 83 system_provided = True, 84 ) 85 86 native.filegroup( 87 name = "includes", 88 srcs = native.glob(["include/**/*.h"]), 89 ) 90 cc_library( 91 name = "python_headers", 92 deps = select({ 93 "@bazel_tools//src/conditions:windows": [":interface"], 94 "//conditions:default": None, 95 }), 96 hdrs = [":includes"], 97 includes = [ 98 "include", 99 "include/python{major}.{minor}".format(**version_dict), 100 "include/python{major}.{minor}m".format(**version_dict), 101 ], 102 ) 103 cc_library( 104 name = "libpython", 105 hdrs = [":includes"], 106 srcs = select({ 107 "@platforms//os:linux": [ 108 "lib/libpython{major}.{minor}.so".format(**version_dict), 109 "lib/libpython{major}.{minor}.so.1.0".format(**version_dict), 110 ], 111 "@platforms//os:macos": ["lib/libpython{major}.{minor}.dylib".format(**version_dict)], 112 "@platforms//os:windows": ["python3.dll", "libs/python{major}{minor}.lib".format(**version_dict)], 113 }), 114 ) 115 116 native.exports_files(["python", python_bin]) 117 118 # Used to only download coverage toolchain when the coverage is collected by 119 # bazel. 120 native.config_setting( 121 name = "coverage_enabled", 122 values = {"collect_code_coverage": "true"}, 123 visibility = ["//visibility:private"], 124 ) 125 126 py_runtime( 127 name = "py3_runtime", 128 files = [":files"], 129 interpreter = python_bin, 130 interpreter_version_info = { 131 "major": str(version_info.major), 132 "micro": str(version_info.patch), 133 "minor": str(version_info.minor), 134 }, 135 # Convert empty string to None 136 coverage_tool = coverage_tool or None, 137 python_version = "PY3", 138 implementation_name = "cpython", 139 # See https://peps.python.org/pep-3147/ for pyc tag infix format 140 pyc_tag = "cpython-{major}{minor}".format(**version_dict), 141 ) 142 143 py_runtime_pair( 144 name = "python_runtimes", 145 py2_runtime = None, 146 py3_runtime = ":py3_runtime", 147 ) 148 149 py_cc_toolchain( 150 name = "py_cc_toolchain", 151 headers = ":python_headers", 152 libs = ":libpython", 153 python_version = python_version, 154 ) 155 156 py_exec_tools_toolchain( 157 name = "py_exec_tools_toolchain", 158 # This macro is called in another repo: use Label() to ensure it 159 # resolves in the rules_python context. 160 precompiler = Label("//tools/precompiler:precompiler"), 161 ) 162