• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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