xref: /aosp_15_r20/external/bazelbuild-rules_python/python/private/python.bzl (revision 60517a1edbc8ecf509223e9af94a7adec7d736b8)
1*60517a1eSAndroid Build Coastguard Worker# Copyright 2023 The Bazel Authors. All rights reserved.
2*60517a1eSAndroid Build Coastguard Worker#
3*60517a1eSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*60517a1eSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*60517a1eSAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*60517a1eSAndroid Build Coastguard Worker#
7*60517a1eSAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
8*60517a1eSAndroid Build Coastguard Worker#
9*60517a1eSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*60517a1eSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*60517a1eSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*60517a1eSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*60517a1eSAndroid Build Coastguard Worker# limitations under the License.
14*60517a1eSAndroid Build Coastguard Worker
15*60517a1eSAndroid Build Coastguard Worker"Python toolchain module extensions for use with bzlmod."
16*60517a1eSAndroid Build Coastguard Worker
17*60517a1eSAndroid Build Coastguard Workerload("@bazel_features//:features.bzl", "bazel_features")
18*60517a1eSAndroid Build Coastguard Workerload("//python:versions.bzl", "DEFAULT_RELEASE_BASE_URL", "PLATFORMS", "TOOL_VERSIONS")
19*60517a1eSAndroid Build Coastguard Workerload(":auth.bzl", "AUTH_ATTRS")
20*60517a1eSAndroid Build Coastguard Workerload(":full_version.bzl", "full_version")
21*60517a1eSAndroid Build Coastguard Workerload(":python_register_toolchains.bzl", "python_register_toolchains")
22*60517a1eSAndroid Build Coastguard Workerload(":pythons_hub.bzl", "hub_repo")
23*60517a1eSAndroid Build Coastguard Workerload(":repo_utils.bzl", "repo_utils")
24*60517a1eSAndroid Build Coastguard Workerload(":semver.bzl", "semver")
25*60517a1eSAndroid Build Coastguard Workerload(":text_util.bzl", "render")
26*60517a1eSAndroid Build Coastguard Workerload(":toolchains_repo.bzl", "multi_toolchain_aliases")
27*60517a1eSAndroid Build Coastguard Workerload(":util.bzl", "IS_BAZEL_6_4_OR_HIGHER")
28*60517a1eSAndroid Build Coastguard Worker
29*60517a1eSAndroid Build Coastguard Worker# This limit can be increased essentially arbitrarily, but doing so will cause a rebuild of all
30*60517a1eSAndroid Build Coastguard Worker# targets using any of these toolchains due to the changed repository name.
31*60517a1eSAndroid Build Coastguard Worker_MAX_NUM_TOOLCHAINS = 9999
32*60517a1eSAndroid Build Coastguard Worker_TOOLCHAIN_INDEX_PAD_LENGTH = len(str(_MAX_NUM_TOOLCHAINS))
33*60517a1eSAndroid Build Coastguard Worker
34*60517a1eSAndroid Build Coastguard Workerdef parse_modules(*, module_ctx, _fail = fail):
35*60517a1eSAndroid Build Coastguard Worker    """Parse the modules and return a struct for registrations.
36*60517a1eSAndroid Build Coastguard Worker
37*60517a1eSAndroid Build Coastguard Worker    Args:
38*60517a1eSAndroid Build Coastguard Worker        module_ctx: {type}`module_ctx` module context.
39*60517a1eSAndroid Build Coastguard Worker        _fail: {type}`function` the failure function, mainly for testing.
40*60517a1eSAndroid Build Coastguard Worker
41*60517a1eSAndroid Build Coastguard Worker    Returns:
42*60517a1eSAndroid Build Coastguard Worker        A struct with the following attributes:
43*60517a1eSAndroid Build Coastguard Worker            * `toolchains`: The list of toolchains to register. The last
44*60517a1eSAndroid Build Coastguard Worker              element is special and is treated as the default toolchain.
45*60517a1eSAndroid Build Coastguard Worker            * `defaults`: The default `kwargs` passed to
46*60517a1eSAndroid Build Coastguard Worker              {bzl:obj}`python_register_toolchains`.
47*60517a1eSAndroid Build Coastguard Worker            * `debug_info`: {type}`None | dict` extra information to be passed
48*60517a1eSAndroid Build Coastguard Worker              to the debug repo.
49*60517a1eSAndroid Build Coastguard Worker    """
50*60517a1eSAndroid Build Coastguard Worker    if module_ctx.os.environ.get("RULES_PYTHON_BZLMOD_DEBUG", "0") == "1":
51*60517a1eSAndroid Build Coastguard Worker        debug_info = {
52*60517a1eSAndroid Build Coastguard Worker            "toolchains_registered": [],
53*60517a1eSAndroid Build Coastguard Worker        }
54*60517a1eSAndroid Build Coastguard Worker    else:
55*60517a1eSAndroid Build Coastguard Worker        debug_info = None
56*60517a1eSAndroid Build Coastguard Worker
57*60517a1eSAndroid Build Coastguard Worker    # The toolchain_info structs to register, in the order to register them in.
58*60517a1eSAndroid Build Coastguard Worker    # NOTE: The last element is special: it is treated as the default toolchain,
59*60517a1eSAndroid Build Coastguard Worker    # so there is special handling to ensure the last entry is the correct one.
60*60517a1eSAndroid Build Coastguard Worker    toolchains = []
61*60517a1eSAndroid Build Coastguard Worker
62*60517a1eSAndroid Build Coastguard Worker    # We store the default toolchain separately to ensure it is the last
63*60517a1eSAndroid Build Coastguard Worker    # toolchain added to toolchains.
64*60517a1eSAndroid Build Coastguard Worker    # This is a toolchain_info struct.
65*60517a1eSAndroid Build Coastguard Worker    default_toolchain = None
66*60517a1eSAndroid Build Coastguard Worker
67*60517a1eSAndroid Build Coastguard Worker    # Map of string Major.Minor or Major.Minor.Patch to the toolchain_info struct
68*60517a1eSAndroid Build Coastguard Worker    global_toolchain_versions = {}
69*60517a1eSAndroid Build Coastguard Worker
70*60517a1eSAndroid Build Coastguard Worker    ignore_root_user_error = None
71*60517a1eSAndroid Build Coastguard Worker
72*60517a1eSAndroid Build Coastguard Worker    logger = repo_utils.logger(module_ctx, "python")
73*60517a1eSAndroid Build Coastguard Worker
74*60517a1eSAndroid Build Coastguard Worker    # if the root module does not register any toolchain then the
75*60517a1eSAndroid Build Coastguard Worker    # ignore_root_user_error takes its default value: False
76*60517a1eSAndroid Build Coastguard Worker    if not module_ctx.modules[0].tags.toolchain:
77*60517a1eSAndroid Build Coastguard Worker        ignore_root_user_error = False
78*60517a1eSAndroid Build Coastguard Worker
79*60517a1eSAndroid Build Coastguard Worker    config = _get_toolchain_config(modules = module_ctx.modules, _fail = _fail)
80*60517a1eSAndroid Build Coastguard Worker
81*60517a1eSAndroid Build Coastguard Worker    seen_versions = {}
82*60517a1eSAndroid Build Coastguard Worker    for mod in module_ctx.modules:
83*60517a1eSAndroid Build Coastguard Worker        module_toolchain_versions = []
84*60517a1eSAndroid Build Coastguard Worker        toolchain_attr_structs = _create_toolchain_attr_structs(
85*60517a1eSAndroid Build Coastguard Worker            mod = mod,
86*60517a1eSAndroid Build Coastguard Worker            seen_versions = seen_versions,
87*60517a1eSAndroid Build Coastguard Worker            config = config,
88*60517a1eSAndroid Build Coastguard Worker        )
89*60517a1eSAndroid Build Coastguard Worker
90*60517a1eSAndroid Build Coastguard Worker        for toolchain_attr in toolchain_attr_structs:
91*60517a1eSAndroid Build Coastguard Worker            toolchain_version = toolchain_attr.python_version
92*60517a1eSAndroid Build Coastguard Worker            toolchain_name = "python_" + toolchain_version.replace(".", "_")
93*60517a1eSAndroid Build Coastguard Worker
94*60517a1eSAndroid Build Coastguard Worker            # Duplicate versions within a module indicate a misconfigured module.
95*60517a1eSAndroid Build Coastguard Worker            if toolchain_version in module_toolchain_versions:
96*60517a1eSAndroid Build Coastguard Worker                _fail_duplicate_module_toolchain_version(toolchain_version, mod.name)
97*60517a1eSAndroid Build Coastguard Worker            module_toolchain_versions.append(toolchain_version)
98*60517a1eSAndroid Build Coastguard Worker
99*60517a1eSAndroid Build Coastguard Worker            if mod.is_root:
100*60517a1eSAndroid Build Coastguard Worker                # Only the root module and rules_python are allowed to specify the default
101*60517a1eSAndroid Build Coastguard Worker                # toolchain for a couple reasons:
102*60517a1eSAndroid Build Coastguard Worker                # * It prevents submodules from specifying different defaults and only
103*60517a1eSAndroid Build Coastguard Worker                #   one of them winning.
104*60517a1eSAndroid Build Coastguard Worker                # * rules_python needs to set a soft default in case the root module doesn't,
105*60517a1eSAndroid Build Coastguard Worker                #   e.g. if the root module doesn't use Python itself.
106*60517a1eSAndroid Build Coastguard Worker                # * The root module is allowed to override the rules_python default.
107*60517a1eSAndroid Build Coastguard Worker                is_default = toolchain_attr.is_default
108*60517a1eSAndroid Build Coastguard Worker
109*60517a1eSAndroid Build Coastguard Worker                # Also only the root module should be able to decide ignore_root_user_error.
110*60517a1eSAndroid Build Coastguard Worker                # Modules being depended upon don't know the final environment, so they aren't
111*60517a1eSAndroid Build Coastguard Worker                # in the right position to know or decide what the correct setting is.
112*60517a1eSAndroid Build Coastguard Worker
113*60517a1eSAndroid Build Coastguard Worker                # If an inconsistency in the ignore_root_user_error among multiple toolchains is detected, fail.
114*60517a1eSAndroid Build Coastguard Worker                if ignore_root_user_error != None and toolchain_attr.ignore_root_user_error != ignore_root_user_error:
115*60517a1eSAndroid Build Coastguard Worker                    fail("Toolchains in the root module must have consistent 'ignore_root_user_error' attributes")
116*60517a1eSAndroid Build Coastguard Worker
117*60517a1eSAndroid Build Coastguard Worker                ignore_root_user_error = toolchain_attr.ignore_root_user_error
118*60517a1eSAndroid Build Coastguard Worker            elif mod.name == "rules_python" and not default_toolchain:
119*60517a1eSAndroid Build Coastguard Worker                # We don't do the len() check because we want the default that rules_python
120*60517a1eSAndroid Build Coastguard Worker                # sets to be clearly visible.
121*60517a1eSAndroid Build Coastguard Worker                is_default = toolchain_attr.is_default
122*60517a1eSAndroid Build Coastguard Worker            else:
123*60517a1eSAndroid Build Coastguard Worker                is_default = False
124*60517a1eSAndroid Build Coastguard Worker
125*60517a1eSAndroid Build Coastguard Worker            if is_default and default_toolchain != None:
126*60517a1eSAndroid Build Coastguard Worker                _fail_multiple_default_toolchains(
127*60517a1eSAndroid Build Coastguard Worker                    first = default_toolchain.name,
128*60517a1eSAndroid Build Coastguard Worker                    second = toolchain_name,
129*60517a1eSAndroid Build Coastguard Worker                )
130*60517a1eSAndroid Build Coastguard Worker
131*60517a1eSAndroid Build Coastguard Worker            # Ignore version collisions in the global scope because there isn't
132*60517a1eSAndroid Build Coastguard Worker            # much else that can be done. Modules don't know and can't control
133*60517a1eSAndroid Build Coastguard Worker            # what other modules do, so the first in the dependency graph wins.
134*60517a1eSAndroid Build Coastguard Worker            if toolchain_version in global_toolchain_versions:
135*60517a1eSAndroid Build Coastguard Worker                # If the python version is explicitly provided by the root
136*60517a1eSAndroid Build Coastguard Worker                # module, they should not be warned for choosing the same
137*60517a1eSAndroid Build Coastguard Worker                # version that rules_python provides as default.
138*60517a1eSAndroid Build Coastguard Worker                first = global_toolchain_versions[toolchain_version]
139*60517a1eSAndroid Build Coastguard Worker                if mod.name != "rules_python" or not first.module.is_root:
140*60517a1eSAndroid Build Coastguard Worker                    # The warning can be enabled by setting the verbosity:
141*60517a1eSAndroid Build Coastguard Worker                    # env RULES_PYTHON_REPO_DEBUG_VERBOSITY=INFO bazel build //...
142*60517a1eSAndroid Build Coastguard Worker                    _warn_duplicate_global_toolchain_version(
143*60517a1eSAndroid Build Coastguard Worker                        toolchain_version,
144*60517a1eSAndroid Build Coastguard Worker                        first = first,
145*60517a1eSAndroid Build Coastguard Worker                        second_toolchain_name = toolchain_name,
146*60517a1eSAndroid Build Coastguard Worker                        second_module_name = mod.name,
147*60517a1eSAndroid Build Coastguard Worker                        logger = logger,
148*60517a1eSAndroid Build Coastguard Worker                    )
149*60517a1eSAndroid Build Coastguard Worker                toolchain_info = None
150*60517a1eSAndroid Build Coastguard Worker            else:
151*60517a1eSAndroid Build Coastguard Worker                toolchain_info = struct(
152*60517a1eSAndroid Build Coastguard Worker                    python_version = toolchain_attr.python_version,
153*60517a1eSAndroid Build Coastguard Worker                    name = toolchain_name,
154*60517a1eSAndroid Build Coastguard Worker                    register_coverage_tool = toolchain_attr.configure_coverage_tool,
155*60517a1eSAndroid Build Coastguard Worker                    module = struct(name = mod.name, is_root = mod.is_root),
156*60517a1eSAndroid Build Coastguard Worker                )
157*60517a1eSAndroid Build Coastguard Worker                global_toolchain_versions[toolchain_version] = toolchain_info
158*60517a1eSAndroid Build Coastguard Worker                if debug_info:
159*60517a1eSAndroid Build Coastguard Worker                    debug_info["toolchains_registered"].append({
160*60517a1eSAndroid Build Coastguard Worker                        "ignore_root_user_error": ignore_root_user_error,
161*60517a1eSAndroid Build Coastguard Worker                        "module": {"is_root": mod.is_root, "name": mod.name},
162*60517a1eSAndroid Build Coastguard Worker                        "name": toolchain_name,
163*60517a1eSAndroid Build Coastguard Worker                    })
164*60517a1eSAndroid Build Coastguard Worker
165*60517a1eSAndroid Build Coastguard Worker            if is_default:
166*60517a1eSAndroid Build Coastguard Worker                # This toolchain is setting the default, but the actual
167*60517a1eSAndroid Build Coastguard Worker                # registration was performed previously, by a different module.
168*60517a1eSAndroid Build Coastguard Worker                if toolchain_info == None:
169*60517a1eSAndroid Build Coastguard Worker                    default_toolchain = global_toolchain_versions[toolchain_version]
170*60517a1eSAndroid Build Coastguard Worker
171*60517a1eSAndroid Build Coastguard Worker                    # Remove it because later code will add it at the end to
172*60517a1eSAndroid Build Coastguard Worker                    # ensure it is last in the list.
173*60517a1eSAndroid Build Coastguard Worker                    toolchains.remove(default_toolchain)
174*60517a1eSAndroid Build Coastguard Worker                else:
175*60517a1eSAndroid Build Coastguard Worker                    default_toolchain = toolchain_info
176*60517a1eSAndroid Build Coastguard Worker            elif toolchain_info:
177*60517a1eSAndroid Build Coastguard Worker                toolchains.append(toolchain_info)
178*60517a1eSAndroid Build Coastguard Worker
179*60517a1eSAndroid Build Coastguard Worker    config.default.setdefault("ignore_root_user_error", ignore_root_user_error)
180*60517a1eSAndroid Build Coastguard Worker
181*60517a1eSAndroid Build Coastguard Worker    # A default toolchain is required so that the non-version-specific rules
182*60517a1eSAndroid Build Coastguard Worker    # are able to match a toolchain.
183*60517a1eSAndroid Build Coastguard Worker    if default_toolchain == None:
184*60517a1eSAndroid Build Coastguard Worker        fail("No default Python toolchain configured. Is rules_python missing `is_default=True`?")
185*60517a1eSAndroid Build Coastguard Worker    elif default_toolchain.python_version not in global_toolchain_versions:
186*60517a1eSAndroid Build Coastguard Worker        fail('Default version "{python_version}" selected by module ' +
187*60517a1eSAndroid Build Coastguard Worker             '"{module_name}", but no toolchain with that version registered'.format(
188*60517a1eSAndroid Build Coastguard Worker                 python_version = default_toolchain.python_version,
189*60517a1eSAndroid Build Coastguard Worker                 module_name = default_toolchain.module.name,
190*60517a1eSAndroid Build Coastguard Worker             ))
191*60517a1eSAndroid Build Coastguard Worker
192*60517a1eSAndroid Build Coastguard Worker    # The last toolchain in the BUILD file is set as the default
193*60517a1eSAndroid Build Coastguard Worker    # toolchain. We need the default last.
194*60517a1eSAndroid Build Coastguard Worker    toolchains.append(default_toolchain)
195*60517a1eSAndroid Build Coastguard Worker
196*60517a1eSAndroid Build Coastguard Worker    if len(toolchains) > _MAX_NUM_TOOLCHAINS:
197*60517a1eSAndroid Build Coastguard Worker        fail("more than {} python versions are not supported".format(_MAX_NUM_TOOLCHAINS))
198*60517a1eSAndroid Build Coastguard Worker
199*60517a1eSAndroid Build Coastguard Worker    return struct(
200*60517a1eSAndroid Build Coastguard Worker        config = config,
201*60517a1eSAndroid Build Coastguard Worker        debug_info = debug_info,
202*60517a1eSAndroid Build Coastguard Worker        default_python_version = toolchains[-1].python_version,
203*60517a1eSAndroid Build Coastguard Worker        toolchains = [
204*60517a1eSAndroid Build Coastguard Worker            struct(
205*60517a1eSAndroid Build Coastguard Worker                python_version = t.python_version,
206*60517a1eSAndroid Build Coastguard Worker                name = t.name,
207*60517a1eSAndroid Build Coastguard Worker                register_coverage_tool = t.register_coverage_tool,
208*60517a1eSAndroid Build Coastguard Worker            )
209*60517a1eSAndroid Build Coastguard Worker            for t in toolchains
210*60517a1eSAndroid Build Coastguard Worker        ],
211*60517a1eSAndroid Build Coastguard Worker    )
212*60517a1eSAndroid Build Coastguard Worker
213*60517a1eSAndroid Build Coastguard Workerdef _python_impl(module_ctx):
214*60517a1eSAndroid Build Coastguard Worker    py = parse_modules(module_ctx = module_ctx)
215*60517a1eSAndroid Build Coastguard Worker
216*60517a1eSAndroid Build Coastguard Worker    for toolchain_info in py.toolchains:
217*60517a1eSAndroid Build Coastguard Worker        # Ensure that we pass the full version here.
218*60517a1eSAndroid Build Coastguard Worker        full_python_version = full_version(
219*60517a1eSAndroid Build Coastguard Worker            version = toolchain_info.python_version,
220*60517a1eSAndroid Build Coastguard Worker            minor_mapping = py.config.minor_mapping,
221*60517a1eSAndroid Build Coastguard Worker        )
222*60517a1eSAndroid Build Coastguard Worker        kwargs = {
223*60517a1eSAndroid Build Coastguard Worker            "python_version": full_python_version,
224*60517a1eSAndroid Build Coastguard Worker            "register_coverage_tool": toolchain_info.register_coverage_tool,
225*60517a1eSAndroid Build Coastguard Worker        }
226*60517a1eSAndroid Build Coastguard Worker
227*60517a1eSAndroid Build Coastguard Worker        # Allow overrides per python version
228*60517a1eSAndroid Build Coastguard Worker        kwargs.update(py.config.kwargs.get(toolchain_info.python_version, {}))
229*60517a1eSAndroid Build Coastguard Worker        kwargs.update(py.config.kwargs.get(full_python_version, {}))
230*60517a1eSAndroid Build Coastguard Worker        kwargs.update(py.config.default)
231*60517a1eSAndroid Build Coastguard Worker        python_register_toolchains(name = toolchain_info.name, **kwargs)
232*60517a1eSAndroid Build Coastguard Worker
233*60517a1eSAndroid Build Coastguard Worker    # Create the pythons_hub repo for the interpreter meta data and the
234*60517a1eSAndroid Build Coastguard Worker    # the various toolchains.
235*60517a1eSAndroid Build Coastguard Worker    hub_repo(
236*60517a1eSAndroid Build Coastguard Worker        name = "pythons_hub",
237*60517a1eSAndroid Build Coastguard Worker        # Last toolchain is default
238*60517a1eSAndroid Build Coastguard Worker        default_python_version = py.default_python_version,
239*60517a1eSAndroid Build Coastguard Worker        toolchain_prefixes = [
240*60517a1eSAndroid Build Coastguard Worker            render.toolchain_prefix(index, toolchain.name, _TOOLCHAIN_INDEX_PAD_LENGTH)
241*60517a1eSAndroid Build Coastguard Worker            for index, toolchain in enumerate(py.toolchains)
242*60517a1eSAndroid Build Coastguard Worker        ],
243*60517a1eSAndroid Build Coastguard Worker        toolchain_python_versions = [
244*60517a1eSAndroid Build Coastguard Worker            full_version(version = t.python_version, minor_mapping = py.config.minor_mapping)
245*60517a1eSAndroid Build Coastguard Worker            for t in py.toolchains
246*60517a1eSAndroid Build Coastguard Worker        ],
247*60517a1eSAndroid Build Coastguard Worker        # The last toolchain is the default; it can't have version constraints
248*60517a1eSAndroid Build Coastguard Worker        # Despite the implication of the arg name, the values are strs, not bools
249*60517a1eSAndroid Build Coastguard Worker        toolchain_set_python_version_constraints = [
250*60517a1eSAndroid Build Coastguard Worker            "True" if i != len(py.toolchains) - 1 else "False"
251*60517a1eSAndroid Build Coastguard Worker            for i in range(len(py.toolchains))
252*60517a1eSAndroid Build Coastguard Worker        ],
253*60517a1eSAndroid Build Coastguard Worker        toolchain_user_repository_names = [t.name for t in py.toolchains],
254*60517a1eSAndroid Build Coastguard Worker    )
255*60517a1eSAndroid Build Coastguard Worker
256*60517a1eSAndroid Build Coastguard Worker    # This is require in order to support multiple version py_test
257*60517a1eSAndroid Build Coastguard Worker    # and py_binary
258*60517a1eSAndroid Build Coastguard Worker    multi_toolchain_aliases(
259*60517a1eSAndroid Build Coastguard Worker        name = "python_versions",
260*60517a1eSAndroid Build Coastguard Worker        python_versions = {
261*60517a1eSAndroid Build Coastguard Worker            toolchain.python_version: toolchain.name
262*60517a1eSAndroid Build Coastguard Worker            for toolchain in py.toolchains
263*60517a1eSAndroid Build Coastguard Worker        },
264*60517a1eSAndroid Build Coastguard Worker    )
265*60517a1eSAndroid Build Coastguard Worker
266*60517a1eSAndroid Build Coastguard Worker    if py.debug_info != None:
267*60517a1eSAndroid Build Coastguard Worker        _debug_repo(
268*60517a1eSAndroid Build Coastguard Worker            name = "rules_python_bzlmod_debug",
269*60517a1eSAndroid Build Coastguard Worker            debug_info = json.encode_indent(py.debug_info),
270*60517a1eSAndroid Build Coastguard Worker        )
271*60517a1eSAndroid Build Coastguard Worker
272*60517a1eSAndroid Build Coastguard Worker    if bazel_features.external_deps.extension_metadata_has_reproducible:
273*60517a1eSAndroid Build Coastguard Worker        return module_ctx.extension_metadata(reproducible = True)
274*60517a1eSAndroid Build Coastguard Worker    else:
275*60517a1eSAndroid Build Coastguard Worker        return None
276*60517a1eSAndroid Build Coastguard Worker
277*60517a1eSAndroid Build Coastguard Workerdef _fail_duplicate_module_toolchain_version(version, module):
278*60517a1eSAndroid Build Coastguard Worker    fail(("Duplicate module toolchain version: module '{module}' attempted " +
279*60517a1eSAndroid Build Coastguard Worker          "to use version '{version}' multiple times in itself").format(
280*60517a1eSAndroid Build Coastguard Worker        version = version,
281*60517a1eSAndroid Build Coastguard Worker        module = module,
282*60517a1eSAndroid Build Coastguard Worker    ))
283*60517a1eSAndroid Build Coastguard Worker
284*60517a1eSAndroid Build Coastguard Workerdef _warn_duplicate_global_toolchain_version(version, first, second_toolchain_name, second_module_name, logger):
285*60517a1eSAndroid Build Coastguard Worker    if not logger:
286*60517a1eSAndroid Build Coastguard Worker        return
287*60517a1eSAndroid Build Coastguard Worker
288*60517a1eSAndroid Build Coastguard Worker    logger.info(lambda: (
289*60517a1eSAndroid Build Coastguard Worker        "Ignoring toolchain '{second_toolchain}' from module '{second_module}': " +
290*60517a1eSAndroid Build Coastguard Worker        "Toolchain '{first_toolchain}' from module '{first_module}' " +
291*60517a1eSAndroid Build Coastguard Worker        "already registered Python version {version} and has precedence."
292*60517a1eSAndroid Build Coastguard Worker    ).format(
293*60517a1eSAndroid Build Coastguard Worker        first_toolchain = first.name,
294*60517a1eSAndroid Build Coastguard Worker        first_module = first.module.name,
295*60517a1eSAndroid Build Coastguard Worker        second_module = second_module_name,
296*60517a1eSAndroid Build Coastguard Worker        second_toolchain = second_toolchain_name,
297*60517a1eSAndroid Build Coastguard Worker        version = version,
298*60517a1eSAndroid Build Coastguard Worker    ))
299*60517a1eSAndroid Build Coastguard Worker
300*60517a1eSAndroid Build Coastguard Workerdef _fail_multiple_default_toolchains(first, second):
301*60517a1eSAndroid Build Coastguard Worker    fail(("Multiple default toolchains: only one toolchain " +
302*60517a1eSAndroid Build Coastguard Worker          "can have is_default=True. First default " +
303*60517a1eSAndroid Build Coastguard Worker          "was toolchain '{first}'. Second was '{second}'").format(
304*60517a1eSAndroid Build Coastguard Worker        first = first,
305*60517a1eSAndroid Build Coastguard Worker        second = second,
306*60517a1eSAndroid Build Coastguard Worker    ))
307*60517a1eSAndroid Build Coastguard Worker
308*60517a1eSAndroid Build Coastguard Workerdef _validate_version(*, version, _fail = fail):
309*60517a1eSAndroid Build Coastguard Worker    parsed = semver(version)
310*60517a1eSAndroid Build Coastguard Worker    if parsed.patch == None or parsed.build or parsed.pre_release:
311*60517a1eSAndroid Build Coastguard Worker        _fail("The 'python_version' attribute needs to specify an 'X.Y.Z' semver-compatible version, got: '{}'".format(version))
312*60517a1eSAndroid Build Coastguard Worker        return False
313*60517a1eSAndroid Build Coastguard Worker
314*60517a1eSAndroid Build Coastguard Worker    return True
315*60517a1eSAndroid Build Coastguard Worker
316*60517a1eSAndroid Build Coastguard Workerdef _process_single_version_overrides(*, tag, _fail = fail, default):
317*60517a1eSAndroid Build Coastguard Worker    if not _validate_version(version = tag.python_version, _fail = _fail):
318*60517a1eSAndroid Build Coastguard Worker        return
319*60517a1eSAndroid Build Coastguard Worker
320*60517a1eSAndroid Build Coastguard Worker    available_versions = default["tool_versions"]
321*60517a1eSAndroid Build Coastguard Worker    kwargs = default.setdefault("kwargs", {})
322*60517a1eSAndroid Build Coastguard Worker
323*60517a1eSAndroid Build Coastguard Worker    if tag.sha256 or tag.urls:
324*60517a1eSAndroid Build Coastguard Worker        if not (tag.sha256 and tag.urls):
325*60517a1eSAndroid Build Coastguard Worker            _fail("Both `sha256` and `urls` overrides need to be provided together")
326*60517a1eSAndroid Build Coastguard Worker            return
327*60517a1eSAndroid Build Coastguard Worker
328*60517a1eSAndroid Build Coastguard Worker        for platform in (tag.sha256 or []):
329*60517a1eSAndroid Build Coastguard Worker            if platform not in PLATFORMS:
330*60517a1eSAndroid Build Coastguard Worker                _fail("The platform must be one of {allowed} but got '{got}'".format(
331*60517a1eSAndroid Build Coastguard Worker                    allowed = sorted(PLATFORMS),
332*60517a1eSAndroid Build Coastguard Worker                    got = platform,
333*60517a1eSAndroid Build Coastguard Worker                ))
334*60517a1eSAndroid Build Coastguard Worker                return
335*60517a1eSAndroid Build Coastguard Worker
336*60517a1eSAndroid Build Coastguard Worker    sha256 = dict(tag.sha256) or available_versions[tag.python_version]["sha256"]
337*60517a1eSAndroid Build Coastguard Worker    override = {
338*60517a1eSAndroid Build Coastguard Worker        "sha256": sha256,
339*60517a1eSAndroid Build Coastguard Worker        "strip_prefix": {
340*60517a1eSAndroid Build Coastguard Worker            platform: tag.strip_prefix
341*60517a1eSAndroid Build Coastguard Worker            for platform in sha256
342*60517a1eSAndroid Build Coastguard Worker        },
343*60517a1eSAndroid Build Coastguard Worker        "url": {
344*60517a1eSAndroid Build Coastguard Worker            platform: list(tag.urls)
345*60517a1eSAndroid Build Coastguard Worker            for platform in tag.sha256
346*60517a1eSAndroid Build Coastguard Worker        } or available_versions[tag.python_version]["url"],
347*60517a1eSAndroid Build Coastguard Worker    }
348*60517a1eSAndroid Build Coastguard Worker
349*60517a1eSAndroid Build Coastguard Worker    if tag.patches:
350*60517a1eSAndroid Build Coastguard Worker        override["patch_strip"] = {
351*60517a1eSAndroid Build Coastguard Worker            platform: tag.patch_strip
352*60517a1eSAndroid Build Coastguard Worker            for platform in sha256
353*60517a1eSAndroid Build Coastguard Worker        }
354*60517a1eSAndroid Build Coastguard Worker        override["patches"] = {
355*60517a1eSAndroid Build Coastguard Worker            platform: list(tag.patches)
356*60517a1eSAndroid Build Coastguard Worker            for platform in sha256
357*60517a1eSAndroid Build Coastguard Worker        }
358*60517a1eSAndroid Build Coastguard Worker
359*60517a1eSAndroid Build Coastguard Worker    available_versions[tag.python_version] = {k: v for k, v in override.items() if v}
360*60517a1eSAndroid Build Coastguard Worker
361*60517a1eSAndroid Build Coastguard Worker    if tag.distutils_content:
362*60517a1eSAndroid Build Coastguard Worker        kwargs.setdefault(tag.python_version, {})["distutils_content"] = tag.distutils_content
363*60517a1eSAndroid Build Coastguard Worker    if tag.distutils:
364*60517a1eSAndroid Build Coastguard Worker        kwargs.setdefault(tag.python_version, {})["distutils"] = tag.distutils
365*60517a1eSAndroid Build Coastguard Worker
366*60517a1eSAndroid Build Coastguard Workerdef _process_single_version_platform_overrides(*, tag, _fail = fail, default):
367*60517a1eSAndroid Build Coastguard Worker    if not _validate_version(version = tag.python_version, _fail = _fail):
368*60517a1eSAndroid Build Coastguard Worker        return
369*60517a1eSAndroid Build Coastguard Worker
370*60517a1eSAndroid Build Coastguard Worker    available_versions = default["tool_versions"]
371*60517a1eSAndroid Build Coastguard Worker
372*60517a1eSAndroid Build Coastguard Worker    if tag.python_version not in available_versions:
373*60517a1eSAndroid Build Coastguard Worker        if not tag.urls or not tag.sha256 or not tag.strip_prefix:
374*60517a1eSAndroid Build Coastguard Worker            _fail("When introducing a new python_version '{}', 'sha256', 'strip_prefix' and 'urls' must be specified".format(tag.python_version))
375*60517a1eSAndroid Build Coastguard Worker            return
376*60517a1eSAndroid Build Coastguard Worker        available_versions[tag.python_version] = {}
377*60517a1eSAndroid Build Coastguard Worker
378*60517a1eSAndroid Build Coastguard Worker    if tag.coverage_tool:
379*60517a1eSAndroid Build Coastguard Worker        available_versions[tag.python_version].setdefault("coverage_tool", {})[tag.platform] = tag.coverage_tool
380*60517a1eSAndroid Build Coastguard Worker    if tag.patch_strip:
381*60517a1eSAndroid Build Coastguard Worker        available_versions[tag.python_version].setdefault("patch_strip", {})[tag.platform] = tag.patch_strip
382*60517a1eSAndroid Build Coastguard Worker    if tag.patches:
383*60517a1eSAndroid Build Coastguard Worker        available_versions[tag.python_version].setdefault("patches", {})[tag.platform] = list(tag.patches)
384*60517a1eSAndroid Build Coastguard Worker    if tag.sha256:
385*60517a1eSAndroid Build Coastguard Worker        available_versions[tag.python_version].setdefault("sha256", {})[tag.platform] = tag.sha256
386*60517a1eSAndroid Build Coastguard Worker    if tag.strip_prefix:
387*60517a1eSAndroid Build Coastguard Worker        available_versions[tag.python_version].setdefault("strip_prefix", {})[tag.platform] = tag.strip_prefix
388*60517a1eSAndroid Build Coastguard Worker    if tag.urls:
389*60517a1eSAndroid Build Coastguard Worker        available_versions[tag.python_version].setdefault("url", {})[tag.platform] = tag.urls
390*60517a1eSAndroid Build Coastguard Worker
391*60517a1eSAndroid Build Coastguard Workerdef _process_global_overrides(*, tag, default, _fail = fail):
392*60517a1eSAndroid Build Coastguard Worker    if tag.available_python_versions:
393*60517a1eSAndroid Build Coastguard Worker        available_versions = default["tool_versions"]
394*60517a1eSAndroid Build Coastguard Worker        all_versions = dict(available_versions)
395*60517a1eSAndroid Build Coastguard Worker        available_versions.clear()
396*60517a1eSAndroid Build Coastguard Worker        for v in tag.available_python_versions:
397*60517a1eSAndroid Build Coastguard Worker            if v not in all_versions:
398*60517a1eSAndroid Build Coastguard Worker                _fail("unknown version '{}', known versions are: {}".format(
399*60517a1eSAndroid Build Coastguard Worker                    v,
400*60517a1eSAndroid Build Coastguard Worker                    sorted(all_versions),
401*60517a1eSAndroid Build Coastguard Worker                ))
402*60517a1eSAndroid Build Coastguard Worker                return
403*60517a1eSAndroid Build Coastguard Worker
404*60517a1eSAndroid Build Coastguard Worker            available_versions[v] = all_versions[v]
405*60517a1eSAndroid Build Coastguard Worker
406*60517a1eSAndroid Build Coastguard Worker    if tag.minor_mapping:
407*60517a1eSAndroid Build Coastguard Worker        for minor_version, full_version in tag.minor_mapping.items():
408*60517a1eSAndroid Build Coastguard Worker            parsed = semver(minor_version)
409*60517a1eSAndroid Build Coastguard Worker            if parsed.patch != None or parsed.build or parsed.pre_release:
410*60517a1eSAndroid Build Coastguard Worker                fail("Expected the key to be of `X.Y` format but got `{}`".format(minor_version))
411*60517a1eSAndroid Build Coastguard Worker            parsed = semver(full_version)
412*60517a1eSAndroid Build Coastguard Worker            if parsed.patch == None:
413*60517a1eSAndroid Build Coastguard Worker                fail("Expected the value to at least be of `X.Y.Z` format but got `{}`".format(minor_version))
414*60517a1eSAndroid Build Coastguard Worker
415*60517a1eSAndroid Build Coastguard Worker        default["minor_mapping"] = tag.minor_mapping
416*60517a1eSAndroid Build Coastguard Worker
417*60517a1eSAndroid Build Coastguard Worker    forwarded_attrs = sorted(AUTH_ATTRS) + [
418*60517a1eSAndroid Build Coastguard Worker        "ignore_root_user_error",
419*60517a1eSAndroid Build Coastguard Worker        "base_url",
420*60517a1eSAndroid Build Coastguard Worker        "register_all_versions",
421*60517a1eSAndroid Build Coastguard Worker    ]
422*60517a1eSAndroid Build Coastguard Worker    for key in forwarded_attrs:
423*60517a1eSAndroid Build Coastguard Worker        if getattr(tag, key, None):
424*60517a1eSAndroid Build Coastguard Worker            default[key] = getattr(tag, key)
425*60517a1eSAndroid Build Coastguard Worker
426*60517a1eSAndroid Build Coastguard Workerdef _override_defaults(*overrides, modules, _fail = fail, default):
427*60517a1eSAndroid Build Coastguard Worker    mod = modules[0] if modules else None
428*60517a1eSAndroid Build Coastguard Worker    if not mod or not mod.is_root:
429*60517a1eSAndroid Build Coastguard Worker        return
430*60517a1eSAndroid Build Coastguard Worker
431*60517a1eSAndroid Build Coastguard Worker    overriden_keys = []
432*60517a1eSAndroid Build Coastguard Worker
433*60517a1eSAndroid Build Coastguard Worker    for override in overrides:
434*60517a1eSAndroid Build Coastguard Worker        for tag in getattr(mod.tags, override.name):
435*60517a1eSAndroid Build Coastguard Worker            key = override.key(tag)
436*60517a1eSAndroid Build Coastguard Worker            if key not in overriden_keys:
437*60517a1eSAndroid Build Coastguard Worker                overriden_keys.append(key)
438*60517a1eSAndroid Build Coastguard Worker            elif key:
439*60517a1eSAndroid Build Coastguard Worker                _fail("Only a single 'python.{}' can be present for '{}'".format(override.name, key))
440*60517a1eSAndroid Build Coastguard Worker                return
441*60517a1eSAndroid Build Coastguard Worker            else:
442*60517a1eSAndroid Build Coastguard Worker                _fail("Only a single 'python.{}' can be present".format(override.name))
443*60517a1eSAndroid Build Coastguard Worker                return
444*60517a1eSAndroid Build Coastguard Worker
445*60517a1eSAndroid Build Coastguard Worker            override.fn(tag = tag, _fail = _fail, default = default)
446*60517a1eSAndroid Build Coastguard Worker
447*60517a1eSAndroid Build Coastguard Workerdef _get_toolchain_config(*, modules, _fail = fail):
448*60517a1eSAndroid Build Coastguard Worker    # Items that can be overridden
449*60517a1eSAndroid Build Coastguard Worker    available_versions = {
450*60517a1eSAndroid Build Coastguard Worker        version: {
451*60517a1eSAndroid Build Coastguard Worker            # Use a dicts straight away so that we could do URL overrides for a
452*60517a1eSAndroid Build Coastguard Worker            # single version.
453*60517a1eSAndroid Build Coastguard Worker            "sha256": dict(item["sha256"]),
454*60517a1eSAndroid Build Coastguard Worker            "strip_prefix": {
455*60517a1eSAndroid Build Coastguard Worker                platform: item["strip_prefix"]
456*60517a1eSAndroid Build Coastguard Worker                for platform in item["sha256"]
457*60517a1eSAndroid Build Coastguard Worker            },
458*60517a1eSAndroid Build Coastguard Worker            "url": {
459*60517a1eSAndroid Build Coastguard Worker                platform: [item["url"]]
460*60517a1eSAndroid Build Coastguard Worker                for platform in item["sha256"]
461*60517a1eSAndroid Build Coastguard Worker            },
462*60517a1eSAndroid Build Coastguard Worker        }
463*60517a1eSAndroid Build Coastguard Worker        for version, item in TOOL_VERSIONS.items()
464*60517a1eSAndroid Build Coastguard Worker    }
465*60517a1eSAndroid Build Coastguard Worker    default = {
466*60517a1eSAndroid Build Coastguard Worker        "base_url": DEFAULT_RELEASE_BASE_URL,
467*60517a1eSAndroid Build Coastguard Worker        "tool_versions": available_versions,
468*60517a1eSAndroid Build Coastguard Worker    }
469*60517a1eSAndroid Build Coastguard Worker
470*60517a1eSAndroid Build Coastguard Worker    _override_defaults(
471*60517a1eSAndroid Build Coastguard Worker        # First override by single version, because the sha256 will replace
472*60517a1eSAndroid Build Coastguard Worker        # anything that has been there before.
473*60517a1eSAndroid Build Coastguard Worker        struct(
474*60517a1eSAndroid Build Coastguard Worker            name = "single_version_override",
475*60517a1eSAndroid Build Coastguard Worker            key = lambda t: t.python_version,
476*60517a1eSAndroid Build Coastguard Worker            fn = _process_single_version_overrides,
477*60517a1eSAndroid Build Coastguard Worker        ),
478*60517a1eSAndroid Build Coastguard Worker        # Then override particular platform entries if they need to be overridden.
479*60517a1eSAndroid Build Coastguard Worker        struct(
480*60517a1eSAndroid Build Coastguard Worker            name = "single_version_platform_override",
481*60517a1eSAndroid Build Coastguard Worker            key = lambda t: (t.python_version, t.platform),
482*60517a1eSAndroid Build Coastguard Worker            fn = _process_single_version_platform_overrides,
483*60517a1eSAndroid Build Coastguard Worker        ),
484*60517a1eSAndroid Build Coastguard Worker        # Then finally add global args and remove the unnecessary toolchains.
485*60517a1eSAndroid Build Coastguard Worker        # This ensures that we can do further validations when removing.
486*60517a1eSAndroid Build Coastguard Worker        struct(
487*60517a1eSAndroid Build Coastguard Worker            name = "override",
488*60517a1eSAndroid Build Coastguard Worker            key = lambda t: None,
489*60517a1eSAndroid Build Coastguard Worker            fn = _process_global_overrides,
490*60517a1eSAndroid Build Coastguard Worker        ),
491*60517a1eSAndroid Build Coastguard Worker        modules = modules,
492*60517a1eSAndroid Build Coastguard Worker        default = default,
493*60517a1eSAndroid Build Coastguard Worker        _fail = _fail,
494*60517a1eSAndroid Build Coastguard Worker    )
495*60517a1eSAndroid Build Coastguard Worker
496*60517a1eSAndroid Build Coastguard Worker    minor_mapping = default.pop("minor_mapping", {})
497*60517a1eSAndroid Build Coastguard Worker    register_all_versions = default.pop("register_all_versions", False)
498*60517a1eSAndroid Build Coastguard Worker    kwargs = default.pop("kwargs", {})
499*60517a1eSAndroid Build Coastguard Worker
500*60517a1eSAndroid Build Coastguard Worker    if not minor_mapping:
501*60517a1eSAndroid Build Coastguard Worker        versions = {}
502*60517a1eSAndroid Build Coastguard Worker        for version_string in available_versions:
503*60517a1eSAndroid Build Coastguard Worker            v = semver(version_string)
504*60517a1eSAndroid Build Coastguard Worker            versions.setdefault("{}.{}".format(v.major, v.minor), []).append((int(v.patch), version_string))
505*60517a1eSAndroid Build Coastguard Worker
506*60517a1eSAndroid Build Coastguard Worker        minor_mapping = {
507*60517a1eSAndroid Build Coastguard Worker            major_minor: max(subset)[1]
508*60517a1eSAndroid Build Coastguard Worker            for major_minor, subset in versions.items()
509*60517a1eSAndroid Build Coastguard Worker        }
510*60517a1eSAndroid Build Coastguard Worker
511*60517a1eSAndroid Build Coastguard Worker    return struct(
512*60517a1eSAndroid Build Coastguard Worker        kwargs = kwargs,
513*60517a1eSAndroid Build Coastguard Worker        minor_mapping = minor_mapping,
514*60517a1eSAndroid Build Coastguard Worker        default = default,
515*60517a1eSAndroid Build Coastguard Worker        register_all_versions = register_all_versions,
516*60517a1eSAndroid Build Coastguard Worker    )
517*60517a1eSAndroid Build Coastguard Worker
518*60517a1eSAndroid Build Coastguard Workerdef _create_toolchain_attr_structs(*, mod, config, seen_versions):
519*60517a1eSAndroid Build Coastguard Worker    arg_structs = []
520*60517a1eSAndroid Build Coastguard Worker
521*60517a1eSAndroid Build Coastguard Worker    for tag in mod.tags.toolchain:
522*60517a1eSAndroid Build Coastguard Worker        arg_structs.append(_create_toolchain_attrs_struct(
523*60517a1eSAndroid Build Coastguard Worker            tag = tag,
524*60517a1eSAndroid Build Coastguard Worker            toolchain_tag_count = len(mod.tags.toolchain),
525*60517a1eSAndroid Build Coastguard Worker        ))
526*60517a1eSAndroid Build Coastguard Worker
527*60517a1eSAndroid Build Coastguard Worker        seen_versions[tag.python_version] = True
528*60517a1eSAndroid Build Coastguard Worker
529*60517a1eSAndroid Build Coastguard Worker    if config.register_all_versions:
530*60517a1eSAndroid Build Coastguard Worker        arg_structs.extend([
531*60517a1eSAndroid Build Coastguard Worker            _create_toolchain_attrs_struct(python_version = v)
532*60517a1eSAndroid Build Coastguard Worker            for v in config.default["tool_versions"].keys() + config.minor_mapping.keys()
533*60517a1eSAndroid Build Coastguard Worker            if v not in seen_versions
534*60517a1eSAndroid Build Coastguard Worker        ])
535*60517a1eSAndroid Build Coastguard Worker
536*60517a1eSAndroid Build Coastguard Worker    return arg_structs
537*60517a1eSAndroid Build Coastguard Worker
538*60517a1eSAndroid Build Coastguard Workerdef _create_toolchain_attrs_struct(*, tag = None, python_version = None, toolchain_tag_count = None):
539*60517a1eSAndroid Build Coastguard Worker    if tag and python_version:
540*60517a1eSAndroid Build Coastguard Worker        fail("Only one of tag and python version can be specified")
541*60517a1eSAndroid Build Coastguard Worker    if tag:
542*60517a1eSAndroid Build Coastguard Worker        # A single toolchain is treated as the default because it's unambiguous.
543*60517a1eSAndroid Build Coastguard Worker        is_default = tag.is_default or toolchain_tag_count == 1
544*60517a1eSAndroid Build Coastguard Worker    else:
545*60517a1eSAndroid Build Coastguard Worker        is_default = False
546*60517a1eSAndroid Build Coastguard Worker
547*60517a1eSAndroid Build Coastguard Worker    return struct(
548*60517a1eSAndroid Build Coastguard Worker        is_default = is_default,
549*60517a1eSAndroid Build Coastguard Worker        python_version = python_version if python_version else tag.python_version,
550*60517a1eSAndroid Build Coastguard Worker        configure_coverage_tool = getattr(tag, "configure_coverage_tool", False),
551*60517a1eSAndroid Build Coastguard Worker        ignore_root_user_error = getattr(tag, "ignore_root_user_error", False),
552*60517a1eSAndroid Build Coastguard Worker    )
553*60517a1eSAndroid Build Coastguard Worker
554*60517a1eSAndroid Build Coastguard Workerdef _get_bazel_version_specific_kwargs():
555*60517a1eSAndroid Build Coastguard Worker    kwargs = {}
556*60517a1eSAndroid Build Coastguard Worker
557*60517a1eSAndroid Build Coastguard Worker    if IS_BAZEL_6_4_OR_HIGHER:
558*60517a1eSAndroid Build Coastguard Worker        kwargs["environ"] = ["RULES_PYTHON_BZLMOD_DEBUG"]
559*60517a1eSAndroid Build Coastguard Worker
560*60517a1eSAndroid Build Coastguard Worker    return kwargs
561*60517a1eSAndroid Build Coastguard Worker
562*60517a1eSAndroid Build Coastguard Worker_toolchain = tag_class(
563*60517a1eSAndroid Build Coastguard Worker    doc = """Tag class used to register Python toolchains.
564*60517a1eSAndroid Build Coastguard WorkerUse this tag class to register one or more Python toolchains. This class
565*60517a1eSAndroid Build Coastguard Workeris also potentially called by sub modules. The following covers different
566*60517a1eSAndroid Build Coastguard Workerbusiness rules and use cases.
567*60517a1eSAndroid Build Coastguard Worker
568*60517a1eSAndroid Build Coastguard Worker:::{topic} Toolchains in the Root Module
569*60517a1eSAndroid Build Coastguard Worker
570*60517a1eSAndroid Build Coastguard WorkerThis class registers all toolchains in the root module.
571*60517a1eSAndroid Build Coastguard Worker:::
572*60517a1eSAndroid Build Coastguard Worker
573*60517a1eSAndroid Build Coastguard Worker:::{topic} Toolchains in Sub Modules
574*60517a1eSAndroid Build Coastguard Worker
575*60517a1eSAndroid Build Coastguard WorkerIt will create a toolchain that is in a sub module, if the toolchain
576*60517a1eSAndroid Build Coastguard Workerof the same name does not exist in the root module.  The extension stops name
577*60517a1eSAndroid Build Coastguard Workerclashing between toolchains in the root module and toolchains in sub modules.
578*60517a1eSAndroid Build Coastguard WorkerYou cannot configure more than one toolchain as the default toolchain.
579*60517a1eSAndroid Build Coastguard Worker:::
580*60517a1eSAndroid Build Coastguard Worker
581*60517a1eSAndroid Build Coastguard Worker:::{topic} Toolchain set as the default version
582*60517a1eSAndroid Build Coastguard Worker
583*60517a1eSAndroid Build Coastguard WorkerThis extension will not create a toolchain that exists in a sub module,
584*60517a1eSAndroid Build Coastguard Workerif the sub module toolchain is marked as the default version. If you have
585*60517a1eSAndroid Build Coastguard Workermore than one toolchain in your root module, you need to set one of the
586*60517a1eSAndroid Build Coastguard Workertoolchains as the default version.  If there is only one toolchain it
587*60517a1eSAndroid Build Coastguard Workeris set as the default toolchain.
588*60517a1eSAndroid Build Coastguard Worker:::
589*60517a1eSAndroid Build Coastguard Worker
590*60517a1eSAndroid Build Coastguard Worker:::{topic} Toolchain repository name
591*60517a1eSAndroid Build Coastguard Worker
592*60517a1eSAndroid Build Coastguard WorkerA toolchain's repository name uses the format `python_{major}_{minor}`, e.g.
593*60517a1eSAndroid Build Coastguard Worker`python_3_10`. The `major` and `minor` components are
594*60517a1eSAndroid Build Coastguard Worker`major` and `minor` are the Python version from the `python_version` attribute.
595*60517a1eSAndroid Build Coastguard Worker
596*60517a1eSAndroid Build Coastguard WorkerIf a toolchain is registered in `X.Y.Z`, then similarly the toolchain name will
597*60517a1eSAndroid Build Coastguard Workerbe `python_{major}_{minor}_{patch}`, e.g. `python_3_10_19`.
598*60517a1eSAndroid Build Coastguard Worker:::
599*60517a1eSAndroid Build Coastguard Worker
600*60517a1eSAndroid Build Coastguard Worker:::{topic} Toolchain detection
601*60517a1eSAndroid Build Coastguard WorkerThe definition of the first toolchain wins, which means that the root module
602*60517a1eSAndroid Build Coastguard Workercan override settings for any python toolchain available. This relies on the
603*60517a1eSAndroid Build Coastguard Workerdocumented module traversal from the {obj}`module_ctx.modules`.
604*60517a1eSAndroid Build Coastguard Worker:::
605*60517a1eSAndroid Build Coastguard Worker
606*60517a1eSAndroid Build Coastguard Worker:::{tip}
607*60517a1eSAndroid Build Coastguard WorkerIn order to use a different name than the above, you can use the following `MODULE.bazel`
608*60517a1eSAndroid Build Coastguard Workersyntax:
609*60517a1eSAndroid Build Coastguard Worker```starlark
610*60517a1eSAndroid Build Coastguard Workerpython = use_extension("@rules_python//python/extensions:python.bzl", "python")
611*60517a1eSAndroid Build Coastguard Workerpython.toolchain(
612*60517a1eSAndroid Build Coastguard Worker    is_default = True,
613*60517a1eSAndroid Build Coastguard Worker    python_version = "3.11",
614*60517a1eSAndroid Build Coastguard Worker)
615*60517a1eSAndroid Build Coastguard Worker
616*60517a1eSAndroid Build Coastguard Workeruse_repo(python, my_python_name = "python_3_11")
617*60517a1eSAndroid Build Coastguard Worker```
618*60517a1eSAndroid Build Coastguard Worker
619*60517a1eSAndroid Build Coastguard WorkerThen the python interpreter will be available as `my_python_name`.
620*60517a1eSAndroid Build Coastguard Worker:::
621*60517a1eSAndroid Build Coastguard Worker""",
622*60517a1eSAndroid Build Coastguard Worker    attrs = {
623*60517a1eSAndroid Build Coastguard Worker        "configure_coverage_tool": attr.bool(
624*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
625*60517a1eSAndroid Build Coastguard Worker            doc = "Whether or not to configure the default coverage tool provided by `rules_python` for the compatible toolchains.",
626*60517a1eSAndroid Build Coastguard Worker        ),
627*60517a1eSAndroid Build Coastguard Worker        "ignore_root_user_error": attr.bool(
628*60517a1eSAndroid Build Coastguard Worker            default = False,
629*60517a1eSAndroid Build Coastguard Worker            doc = """\
630*60517a1eSAndroid Build Coastguard WorkerIf `False`, the Python runtime installation will be made read only. This improves
631*60517a1eSAndroid Build Coastguard Workerthe ability for Bazel to cache it, but prevents the interpreter from creating
632*60517a1eSAndroid Build Coastguard Worker`.pyc` files for the standard library dynamically at runtime as they are loaded.
633*60517a1eSAndroid Build Coastguard Worker
634*60517a1eSAndroid Build Coastguard WorkerIf `True`, the Python runtime installation is read-write. This allows the
635*60517a1eSAndroid Build Coastguard Workerinterpreter to create `.pyc` files for the standard library, but, because they are
636*60517a1eSAndroid Build Coastguard Workercreated as needed, it adversely affects Bazel's ability to cache the runtime and
637*60517a1eSAndroid Build Coastguard Workercan result in spurious build failures.
638*60517a1eSAndroid Build Coastguard Worker""",
639*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
640*60517a1eSAndroid Build Coastguard Worker        ),
641*60517a1eSAndroid Build Coastguard Worker        "is_default": attr.bool(
642*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
643*60517a1eSAndroid Build Coastguard Worker            doc = "Whether the toolchain is the default version",
644*60517a1eSAndroid Build Coastguard Worker        ),
645*60517a1eSAndroid Build Coastguard Worker        "python_version": attr.string(
646*60517a1eSAndroid Build Coastguard Worker            mandatory = True,
647*60517a1eSAndroid Build Coastguard Worker            doc = """\
648*60517a1eSAndroid Build Coastguard WorkerThe Python version, in `major.minor` or `major.minor.patch` format, e.g
649*60517a1eSAndroid Build Coastguard Worker`3.12` (or `3.12.3`), to create a toolchain for.
650*60517a1eSAndroid Build Coastguard Worker""",
651*60517a1eSAndroid Build Coastguard Worker        ),
652*60517a1eSAndroid Build Coastguard Worker    },
653*60517a1eSAndroid Build Coastguard Worker)
654*60517a1eSAndroid Build Coastguard Worker
655*60517a1eSAndroid Build Coastguard Worker_override = tag_class(
656*60517a1eSAndroid Build Coastguard Worker    doc = """Tag class used to override defaults and behaviour of the module extension.
657*60517a1eSAndroid Build Coastguard Worker
658*60517a1eSAndroid Build Coastguard Worker:::{versionadded} 0.36.0
659*60517a1eSAndroid Build Coastguard Worker:::
660*60517a1eSAndroid Build Coastguard Worker""",
661*60517a1eSAndroid Build Coastguard Worker    attrs = {
662*60517a1eSAndroid Build Coastguard Worker        "available_python_versions": attr.string_list(
663*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
664*60517a1eSAndroid Build Coastguard Worker            doc = """\
665*60517a1eSAndroid Build Coastguard WorkerThe list of available python tool versions to use. Must be in `X.Y.Z` format.
666*60517a1eSAndroid Build Coastguard WorkerIf the unknown version given the processing of the extension will fail - all of
667*60517a1eSAndroid Build Coastguard Workerthe versions in the list have to be defined with
668*60517a1eSAndroid Build Coastguard Worker{obj}`python.single_version_override` or
669*60517a1eSAndroid Build Coastguard Worker{obj}`python.single_version_platform_override` before they are used in this
670*60517a1eSAndroid Build Coastguard Workerlist.
671*60517a1eSAndroid Build Coastguard Worker
672*60517a1eSAndroid Build Coastguard WorkerThis attribute is usually used in order to ensure that no unexpected transitive
673*60517a1eSAndroid Build Coastguard Workerdependencies are introduced.
674*60517a1eSAndroid Build Coastguard Worker""",
675*60517a1eSAndroid Build Coastguard Worker        ),
676*60517a1eSAndroid Build Coastguard Worker        "base_url": attr.string(
677*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
678*60517a1eSAndroid Build Coastguard Worker            doc = "The base URL to be used when downloading toolchains.",
679*60517a1eSAndroid Build Coastguard Worker            default = DEFAULT_RELEASE_BASE_URL,
680*60517a1eSAndroid Build Coastguard Worker        ),
681*60517a1eSAndroid Build Coastguard Worker        "ignore_root_user_error": attr.bool(
682*60517a1eSAndroid Build Coastguard Worker            default = False,
683*60517a1eSAndroid Build Coastguard Worker            doc = """\
684*60517a1eSAndroid Build Coastguard WorkerIf `False`, the Python runtime installation will be made read only. This improves
685*60517a1eSAndroid Build Coastguard Workerthe ability for Bazel to cache it, but prevents the interpreter from creating
686*60517a1eSAndroid Build Coastguard Worker`.pyc` files for the standard library dynamically at runtime as they are loaded.
687*60517a1eSAndroid Build Coastguard Worker
688*60517a1eSAndroid Build Coastguard WorkerIf `True`, the Python runtime installation is read-write. This allows the
689*60517a1eSAndroid Build Coastguard Workerinterpreter to create `.pyc` files for the standard library, but, because they are
690*60517a1eSAndroid Build Coastguard Workercreated as needed, it adversely affects Bazel's ability to cache the runtime and
691*60517a1eSAndroid Build Coastguard Workercan result in spurious build failures.
692*60517a1eSAndroid Build Coastguard Worker""",
693*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
694*60517a1eSAndroid Build Coastguard Worker        ),
695*60517a1eSAndroid Build Coastguard Worker        "minor_mapping": attr.string_dict(
696*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
697*60517a1eSAndroid Build Coastguard Worker            doc = """\
698*60517a1eSAndroid Build Coastguard WorkerThe mapping between `X.Y` to `X.Y.Z` versions to be used when setting up
699*60517a1eSAndroid Build Coastguard Workertoolchains. It defaults to the interpreter with the highest available patch
700*60517a1eSAndroid Build Coastguard Workerversion for each minor version. For example if one registers `3.10.3`, `3.10.4`
701*60517a1eSAndroid Build Coastguard Workerand `3.11.4` then the default for the `minor_mapping` dict will be:
702*60517a1eSAndroid Build Coastguard Worker```starlark
703*60517a1eSAndroid Build Coastguard Worker{
704*60517a1eSAndroid Build Coastguard Worker"3.10": "3.10.4",
705*60517a1eSAndroid Build Coastguard Worker"3.11": "3.11.4",
706*60517a1eSAndroid Build Coastguard Worker}
707*60517a1eSAndroid Build Coastguard Worker```
708*60517a1eSAndroid Build Coastguard Worker""",
709*60517a1eSAndroid Build Coastguard Worker            default = {},
710*60517a1eSAndroid Build Coastguard Worker        ),
711*60517a1eSAndroid Build Coastguard Worker        "register_all_versions": attr.bool(default = False, doc = "Add all versions"),
712*60517a1eSAndroid Build Coastguard Worker    } | AUTH_ATTRS,
713*60517a1eSAndroid Build Coastguard Worker)
714*60517a1eSAndroid Build Coastguard Worker
715*60517a1eSAndroid Build Coastguard Worker_single_version_override = tag_class(
716*60517a1eSAndroid Build Coastguard Worker    doc = """Override single python version URLs and patches for all platforms.
717*60517a1eSAndroid Build Coastguard Worker
718*60517a1eSAndroid Build Coastguard Worker:::{note}
719*60517a1eSAndroid Build Coastguard WorkerThis will replace any existing configuration for the given python version.
720*60517a1eSAndroid Build Coastguard Worker:::
721*60517a1eSAndroid Build Coastguard Worker
722*60517a1eSAndroid Build Coastguard Worker:::{tip}
723*60517a1eSAndroid Build Coastguard WorkerIf you would like to modify the configuration for a specific `(version,
724*60517a1eSAndroid Build Coastguard Workerplatform)`, please use the {obj}`single_version_platform_override` tag
725*60517a1eSAndroid Build Coastguard Workerclass.
726*60517a1eSAndroid Build Coastguard Worker:::
727*60517a1eSAndroid Build Coastguard Worker
728*60517a1eSAndroid Build Coastguard Worker:::{versionadded} 0.36.0
729*60517a1eSAndroid Build Coastguard Worker:::
730*60517a1eSAndroid Build Coastguard Worker""",
731*60517a1eSAndroid Build Coastguard Worker    attrs = {
732*60517a1eSAndroid Build Coastguard Worker        # NOTE @aignas 2024-09-01: all of the attributes except for `version`
733*60517a1eSAndroid Build Coastguard Worker        # can be part of the `python.toolchain` call. That would make it more
734*60517a1eSAndroid Build Coastguard Worker        # ergonomic to define new toolchains and to override values for old
735*60517a1eSAndroid Build Coastguard Worker        # toolchains. The same semantics of the `first one wins` would apply,
736*60517a1eSAndroid Build Coastguard Worker        # so technically there is no need for any overrides?
737*60517a1eSAndroid Build Coastguard Worker        #
738*60517a1eSAndroid Build Coastguard Worker        # Although these attributes would override the code that is used by the
739*60517a1eSAndroid Build Coastguard Worker        # code in non-root modules, so technically this could be thought as
740*60517a1eSAndroid Build Coastguard Worker        # being overridden.
741*60517a1eSAndroid Build Coastguard Worker        #
742*60517a1eSAndroid Build Coastguard Worker        # rules_go has a single download call:
743*60517a1eSAndroid Build Coastguard Worker        # https://github.com/bazelbuild/rules_go/blob/master/go/private/extensions.bzl#L38
744*60517a1eSAndroid Build Coastguard Worker        #
745*60517a1eSAndroid Build Coastguard Worker        # However, we need to understand how to accommodate the fact that
746*60517a1eSAndroid Build Coastguard Worker        # {attr}`single_version_override.version` only allows patch versions.
747*60517a1eSAndroid Build Coastguard Worker        "distutils": attr.label(
748*60517a1eSAndroid Build Coastguard Worker            allow_single_file = True,
749*60517a1eSAndroid Build Coastguard Worker            doc = "A distutils.cfg file to be included in the Python installation. " +
750*60517a1eSAndroid Build Coastguard Worker                  "Either {attr}`distutils` or {attr}`distutils_content` can be specified, but not both.",
751*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
752*60517a1eSAndroid Build Coastguard Worker        ),
753*60517a1eSAndroid Build Coastguard Worker        "distutils_content": attr.string(
754*60517a1eSAndroid Build Coastguard Worker            doc = "A distutils.cfg file content to be included in the Python installation. " +
755*60517a1eSAndroid Build Coastguard Worker                  "Either {attr}`distutils` or {attr}`distutils_content` can be specified, but not both.",
756*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
757*60517a1eSAndroid Build Coastguard Worker        ),
758*60517a1eSAndroid Build Coastguard Worker        "patch_strip": attr.int(
759*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
760*60517a1eSAndroid Build Coastguard Worker            doc = "Same as the --strip argument of Unix patch.",
761*60517a1eSAndroid Build Coastguard Worker            default = 0,
762*60517a1eSAndroid Build Coastguard Worker        ),
763*60517a1eSAndroid Build Coastguard Worker        "patches": attr.label_list(
764*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
765*60517a1eSAndroid Build Coastguard Worker            doc = "A list of labels pointing to patch files to apply for the interpreter repository. They are applied in the list order and are applied before any platform-specific patches are applied.",
766*60517a1eSAndroid Build Coastguard Worker        ),
767*60517a1eSAndroid Build Coastguard Worker        "python_version": attr.string(
768*60517a1eSAndroid Build Coastguard Worker            mandatory = True,
769*60517a1eSAndroid Build Coastguard Worker            doc = "The python version to override URLs for. Must be in `X.Y.Z` format.",
770*60517a1eSAndroid Build Coastguard Worker        ),
771*60517a1eSAndroid Build Coastguard Worker        "sha256": attr.string_dict(
772*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
773*60517a1eSAndroid Build Coastguard Worker            doc = "The python platform to sha256 dict. See {attr}`python.single_version_platform_override.platform` for allowed key values.",
774*60517a1eSAndroid Build Coastguard Worker        ),
775*60517a1eSAndroid Build Coastguard Worker        "strip_prefix": attr.string(
776*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
777*60517a1eSAndroid Build Coastguard Worker            doc = "The 'strip_prefix' for the archive, defaults to 'python'.",
778*60517a1eSAndroid Build Coastguard Worker            default = "python",
779*60517a1eSAndroid Build Coastguard Worker        ),
780*60517a1eSAndroid Build Coastguard Worker        "urls": attr.string_list(
781*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
782*60517a1eSAndroid Build Coastguard Worker            doc = "The URL template to fetch releases for this Python version. See {attr}`python.single_version_platform_override.urls` for documentation.",
783*60517a1eSAndroid Build Coastguard Worker        ),
784*60517a1eSAndroid Build Coastguard Worker    },
785*60517a1eSAndroid Build Coastguard Worker)
786*60517a1eSAndroid Build Coastguard Worker
787*60517a1eSAndroid Build Coastguard Worker_single_version_platform_override = tag_class(
788*60517a1eSAndroid Build Coastguard Worker    doc = """Override single python version for a single existing platform.
789*60517a1eSAndroid Build Coastguard Worker
790*60517a1eSAndroid Build Coastguard WorkerIf the `(version, platform)` is new, we will add it to the existing versions and will
791*60517a1eSAndroid Build Coastguard Workeruse the same `url` template.
792*60517a1eSAndroid Build Coastguard Worker
793*60517a1eSAndroid Build Coastguard Worker:::{tip}
794*60517a1eSAndroid Build Coastguard WorkerIf you would like to add or remove platforms to a single python version toolchain
795*60517a1eSAndroid Build Coastguard Workerconfiguration, please use {obj}`single_version_override`.
796*60517a1eSAndroid Build Coastguard Worker:::
797*60517a1eSAndroid Build Coastguard Worker
798*60517a1eSAndroid Build Coastguard Worker:::{versionadded} 0.36.0
799*60517a1eSAndroid Build Coastguard Worker:::
800*60517a1eSAndroid Build Coastguard Worker""",
801*60517a1eSAndroid Build Coastguard Worker    attrs = {
802*60517a1eSAndroid Build Coastguard Worker        "coverage_tool": attr.label(
803*60517a1eSAndroid Build Coastguard Worker            doc = """\
804*60517a1eSAndroid Build Coastguard WorkerThe coverage tool to be used for a particular Python interpreter. This can override
805*60517a1eSAndroid Build Coastguard Worker`rules_python` defaults.
806*60517a1eSAndroid Build Coastguard Worker""",
807*60517a1eSAndroid Build Coastguard Worker        ),
808*60517a1eSAndroid Build Coastguard Worker        "patch_strip": attr.int(
809*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
810*60517a1eSAndroid Build Coastguard Worker            doc = "Same as the --strip argument of Unix patch.",
811*60517a1eSAndroid Build Coastguard Worker            default = 0,
812*60517a1eSAndroid Build Coastguard Worker        ),
813*60517a1eSAndroid Build Coastguard Worker        "patches": attr.label_list(
814*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
815*60517a1eSAndroid Build Coastguard Worker            doc = "A list of labels pointing to patch files to apply for the interpreter repository. They are applied in the list order and are applied after the common patches are applied.",
816*60517a1eSAndroid Build Coastguard Worker        ),
817*60517a1eSAndroid Build Coastguard Worker        "platform": attr.string(
818*60517a1eSAndroid Build Coastguard Worker            mandatory = True,
819*60517a1eSAndroid Build Coastguard Worker            values = PLATFORMS.keys(),
820*60517a1eSAndroid Build Coastguard Worker            doc = "The platform to override the values for, must be one of:\n{}.".format("\n".join(sorted(["* `{}`".format(p) for p in PLATFORMS]))),
821*60517a1eSAndroid Build Coastguard Worker        ),
822*60517a1eSAndroid Build Coastguard Worker        "python_version": attr.string(
823*60517a1eSAndroid Build Coastguard Worker            mandatory = True,
824*60517a1eSAndroid Build Coastguard Worker            doc = "The python version to override URLs for. Must be in `X.Y.Z` format.",
825*60517a1eSAndroid Build Coastguard Worker        ),
826*60517a1eSAndroid Build Coastguard Worker        "sha256": attr.string(
827*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
828*60517a1eSAndroid Build Coastguard Worker            doc = "The sha256 for the archive",
829*60517a1eSAndroid Build Coastguard Worker        ),
830*60517a1eSAndroid Build Coastguard Worker        "strip_prefix": attr.string(
831*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
832*60517a1eSAndroid Build Coastguard Worker            doc = "The 'strip_prefix' for the archive, defaults to 'python'.",
833*60517a1eSAndroid Build Coastguard Worker            default = "python",
834*60517a1eSAndroid Build Coastguard Worker        ),
835*60517a1eSAndroid Build Coastguard Worker        "urls": attr.string_list(
836*60517a1eSAndroid Build Coastguard Worker            mandatory = False,
837*60517a1eSAndroid Build Coastguard Worker            doc = "The URL template to fetch releases for this Python version. If the URL template results in a relative fragment, default base URL is going to be used. Occurrences of `{python_version}`, `{platform}` and `{build}` will be interpolated based on the contents in the override and the known {attr}`platform` values.",
838*60517a1eSAndroid Build Coastguard Worker        ),
839*60517a1eSAndroid Build Coastguard Worker    },
840*60517a1eSAndroid Build Coastguard Worker)
841*60517a1eSAndroid Build Coastguard Worker
842*60517a1eSAndroid Build Coastguard Workerpython = module_extension(
843*60517a1eSAndroid Build Coastguard Worker    doc = """Bzlmod extension that is used to register Python toolchains.
844*60517a1eSAndroid Build Coastguard Worker""",
845*60517a1eSAndroid Build Coastguard Worker    implementation = _python_impl,
846*60517a1eSAndroid Build Coastguard Worker    tag_classes = {
847*60517a1eSAndroid Build Coastguard Worker        "override": _override,
848*60517a1eSAndroid Build Coastguard Worker        "single_version_override": _single_version_override,
849*60517a1eSAndroid Build Coastguard Worker        "single_version_platform_override": _single_version_platform_override,
850*60517a1eSAndroid Build Coastguard Worker        "toolchain": _toolchain,
851*60517a1eSAndroid Build Coastguard Worker    },
852*60517a1eSAndroid Build Coastguard Worker    **_get_bazel_version_specific_kwargs()
853*60517a1eSAndroid Build Coastguard Worker)
854*60517a1eSAndroid Build Coastguard Worker
855*60517a1eSAndroid Build Coastguard Worker_DEBUG_BUILD_CONTENT = """
856*60517a1eSAndroid Build Coastguard Workerpackage(
857*60517a1eSAndroid Build Coastguard Worker    default_visibility = ["//visibility:public"],
858*60517a1eSAndroid Build Coastguard Worker)
859*60517a1eSAndroid Build Coastguard Workerexports_files(["debug_info.json"])
860*60517a1eSAndroid Build Coastguard Worker"""
861*60517a1eSAndroid Build Coastguard Worker
862*60517a1eSAndroid Build Coastguard Workerdef _debug_repo_impl(repo_ctx):
863*60517a1eSAndroid Build Coastguard Worker    repo_ctx.file("BUILD.bazel", _DEBUG_BUILD_CONTENT)
864*60517a1eSAndroid Build Coastguard Worker    repo_ctx.file("debug_info.json", repo_ctx.attr.debug_info)
865*60517a1eSAndroid Build Coastguard Worker
866*60517a1eSAndroid Build Coastguard Worker_debug_repo = repository_rule(
867*60517a1eSAndroid Build Coastguard Worker    implementation = _debug_repo_impl,
868*60517a1eSAndroid Build Coastguard Worker    attrs = {
869*60517a1eSAndroid Build Coastguard Worker        "debug_info": attr.string(),
870*60517a1eSAndroid Build Coastguard Worker    },
871*60517a1eSAndroid Build Coastguard Worker)
872