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 15"""Setup code called by the code generated by `local_runtime_repo`.""" 16 17load("@bazel_skylib//lib:selects.bzl", "selects") 18load("@rules_cc//cc:cc_library.bzl", "cc_library") 19load("@rules_python//python:py_runtime.bzl", "py_runtime") 20load("@rules_python//python:py_runtime_pair.bzl", "py_runtime_pair") 21load("@rules_python//python/cc:py_cc_toolchain.bzl", "py_cc_toolchain") 22load("@rules_python//python/private:py_exec_tools_toolchain.bzl", "py_exec_tools_toolchain") 23 24_PYTHON_VERSION_FLAG = Label("@rules_python//python/config_settings:python_version") 25 26def define_local_runtime_toolchain_impl( 27 name, 28 lib_ext, 29 major, 30 minor, 31 micro, 32 interpreter_path, 33 implementation_name, 34 os): 35 """Defines a toolchain implementation for a local Python runtime. 36 37 Generates public targets: 38 * `python_runtimes`: The target toolchain type implementation 39 * `py_exec_tools_toolchain`: The exec tools toolchain type implementation 40 * `py_cc_toolchain`: The py cc toolchain type implementation 41 * `os`: A constraint (or alias to one) for the `target_compatible_with` this 42 toolchain is compatible with. 43 * `is_matching_python_version`: A `config_setting` for `target_settings` 44 this toolchain is compatible with. 45 46 Args: 47 name: `str` Only present to satisfy tooling 48 lib_ext: `str` The file extension for the `libpython` shared libraries 49 major: `str` The major Python version, e.g. `3` of `3.9.1`. 50 minor: `str` The minor Python version, e.g. `9` of `3.9.1`. 51 micro: `str` The micro Python version, e.g. "1" of `3.9.1`. 52 interpreter_path: `str` Absolute path to the interpreter. 53 implementation_name: `str` The implementation name, as returned by 54 `sys.implementation.name`. 55 os: `str` A label to the OS constraint (e.g. `@platforms//os:linux`) for 56 this runtime. 57 """ 58 major_minor = "{}.{}".format(major, minor) 59 major_minor_micro = "{}.{}".format(major_minor, micro) 60 61 cc_library( 62 name = "_python_headers", 63 # NOTE: Keep in sync with watch_tree() called in local_runtime_repo 64 srcs = native.glob( 65 ["include/**/*.h"], 66 # A Python install may not have C headers 67 allow_empty = True, 68 ), 69 includes = ["include"], 70 ) 71 72 cc_library( 73 name = "_libpython", 74 # Don't use a recursive glob because the lib/ directory usually contains 75 # a subdirectory of the stdlib -- lots of unrelated files 76 srcs = native.glob( 77 [ 78 "lib/*{}".format(lib_ext), # Match libpython*.so 79 "lib/*{}*".format(lib_ext), # Also match libpython*.so.1.0 80 ], 81 # A Python install may not have shared libraries. 82 allow_empty = True, 83 ), 84 hdrs = [":_python_headers"], 85 ) 86 87 py_runtime( 88 name = "_py3_runtime", 89 interpreter_path = interpreter_path, 90 python_version = "PY3", 91 interpreter_version_info = { 92 "major": major, 93 "micro": micro, 94 "minor": minor, 95 }, 96 implementation_name = implementation_name, 97 ) 98 99 py_runtime_pair( 100 name = "python_runtimes", 101 py2_runtime = None, 102 py3_runtime = ":_py3_runtime", 103 visibility = ["//visibility:public"], 104 ) 105 106 py_exec_tools_toolchain( 107 name = "py_exec_tools_toolchain", 108 visibility = ["//visibility:public"], 109 precompiler = "@rules_python//tools/precompiler:precompiler", 110 ) 111 112 py_cc_toolchain( 113 name = "py_cc_toolchain", 114 headers = ":_python_headers", 115 libs = ":_libpython", 116 python_version = major_minor_micro, 117 visibility = ["//visibility:public"], 118 ) 119 120 native.alias( 121 name = "os", 122 # Call Label() to force the string to evaluate in the context of 123 # rules_python, not the calling BUILD-file code. This is because 124 # the value is an `@platforms//foo` string, which @rules_python has 125 # visibility to, but the calling repo may not. 126 actual = Label(os), 127 visibility = ["//visibility:public"], 128 ) 129 130 native.config_setting( 131 name = "_is_major_minor", 132 flag_values = { 133 _PYTHON_VERSION_FLAG: major_minor, 134 }, 135 ) 136 native.config_setting( 137 name = "_is_major_minor_micro", 138 flag_values = { 139 _PYTHON_VERSION_FLAG: major_minor_micro, 140 }, 141 ) 142 selects.config_setting_group( 143 name = "is_matching_python_version", 144 match_any = [ 145 ":_is_major_minor", 146 ":_is_major_minor_micro", 147 ], 148 visibility = ["//visibility:public"], 149 ) 150