xref: /aosp_15_r20/external/bazelbuild-rules_python/python/private/toolchains_repo.bzl (revision 60517a1edbc8ecf509223e9af94a7adec7d736b8)
1*60517a1eSAndroid Build Coastguard Worker# Copyright 2022 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"""Create a repository to hold the toolchains.
16*60517a1eSAndroid Build Coastguard Worker
17*60517a1eSAndroid Build Coastguard WorkerThis follows guidance here:
18*60517a1eSAndroid Build Coastguard Workerhttps://docs.bazel.build/versions/main/skylark/deploying.html#registering-toolchains
19*60517a1eSAndroid Build Coastguard Worker
20*60517a1eSAndroid Build Coastguard WorkerThe "complex computation" in our case is simply downloading large artifacts.
21*60517a1eSAndroid Build Coastguard WorkerThis guidance tells us how to avoid that: we put the toolchain targets in the
22*60517a1eSAndroid Build Coastguard Workeralias repository with only the toolchain attribute pointing into the
23*60517a1eSAndroid Build Coastguard Workerplatform-specific repositories.
24*60517a1eSAndroid Build Coastguard Worker"""
25*60517a1eSAndroid Build Coastguard Worker
26*60517a1eSAndroid Build Coastguard Workerload(
27*60517a1eSAndroid Build Coastguard Worker    "//python:versions.bzl",
28*60517a1eSAndroid Build Coastguard Worker    "LINUX_NAME",
29*60517a1eSAndroid Build Coastguard Worker    "MACOS_NAME",
30*60517a1eSAndroid Build Coastguard Worker    "PLATFORMS",
31*60517a1eSAndroid Build Coastguard Worker    "WINDOWS_NAME",
32*60517a1eSAndroid Build Coastguard Worker)
33*60517a1eSAndroid Build Coastguard Workerload("//python/private:repo_utils.bzl", "REPO_DEBUG_ENV_VAR", "repo_utils")
34*60517a1eSAndroid Build Coastguard Workerload("//python/private:text_util.bzl", "render")
35*60517a1eSAndroid Build Coastguard Worker
36*60517a1eSAndroid Build Coastguard Workerdef get_repository_name(repository_workspace):
37*60517a1eSAndroid Build Coastguard Worker    dummy_label = "//:_"
38*60517a1eSAndroid Build Coastguard Worker    return str(repository_workspace.relative(dummy_label))[:-len(dummy_label)] or "@"
39*60517a1eSAndroid Build Coastguard Worker
40*60517a1eSAndroid Build Coastguard Workerdef python_toolchain_build_file_content(
41*60517a1eSAndroid Build Coastguard Worker        prefix,
42*60517a1eSAndroid Build Coastguard Worker        python_version,
43*60517a1eSAndroid Build Coastguard Worker        set_python_version_constraint,
44*60517a1eSAndroid Build Coastguard Worker        user_repository_name):
45*60517a1eSAndroid Build Coastguard Worker    """Creates the content for toolchain definitions for a build file.
46*60517a1eSAndroid Build Coastguard Worker
47*60517a1eSAndroid Build Coastguard Worker    Args:
48*60517a1eSAndroid Build Coastguard Worker        prefix: Python toolchain name prefixes
49*60517a1eSAndroid Build Coastguard Worker        python_version: Python versions for the toolchains
50*60517a1eSAndroid Build Coastguard Worker        set_python_version_constraint: string, "True" if the toolchain should
51*60517a1eSAndroid Build Coastguard Worker            have the Python version constraint added as a requirement for
52*60517a1eSAndroid Build Coastguard Worker            matching the toolchain, "False" if not.
53*60517a1eSAndroid Build Coastguard Worker        user_repository_name: names for the user repos
54*60517a1eSAndroid Build Coastguard Worker
55*60517a1eSAndroid Build Coastguard Worker    Returns:
56*60517a1eSAndroid Build Coastguard Worker        build_content: Text containing toolchain definitions
57*60517a1eSAndroid Build Coastguard Worker    """
58*60517a1eSAndroid Build Coastguard Worker
59*60517a1eSAndroid Build Coastguard Worker    return "\n\n".join([
60*60517a1eSAndroid Build Coastguard Worker        """\
61*60517a1eSAndroid Build Coastguard Workerpy_toolchain_suite(
62*60517a1eSAndroid Build Coastguard Worker    user_repository_name = "{user_repository_name}_{platform}",
63*60517a1eSAndroid Build Coastguard Worker    prefix = "{prefix}{platform}",
64*60517a1eSAndroid Build Coastguard Worker    target_compatible_with = {compatible_with},
65*60517a1eSAndroid Build Coastguard Worker    flag_values = {flag_values},
66*60517a1eSAndroid Build Coastguard Worker    python_version = "{python_version}",
67*60517a1eSAndroid Build Coastguard Worker    set_python_version_constraint = "{set_python_version_constraint}",
68*60517a1eSAndroid Build Coastguard Worker)""".format(
69*60517a1eSAndroid Build Coastguard Worker            compatible_with = render.indent(render.list(meta.compatible_with)).lstrip(),
70*60517a1eSAndroid Build Coastguard Worker            flag_values = render.indent(render.dict(
71*60517a1eSAndroid Build Coastguard Worker                meta.flag_values,
72*60517a1eSAndroid Build Coastguard Worker                key_repr = lambda x: repr(str(x)),  # this is to correctly display labels
73*60517a1eSAndroid Build Coastguard Worker            )).lstrip(),
74*60517a1eSAndroid Build Coastguard Worker            platform = platform,
75*60517a1eSAndroid Build Coastguard Worker            set_python_version_constraint = set_python_version_constraint,
76*60517a1eSAndroid Build Coastguard Worker            user_repository_name = user_repository_name,
77*60517a1eSAndroid Build Coastguard Worker            prefix = prefix,
78*60517a1eSAndroid Build Coastguard Worker            python_version = python_version,
79*60517a1eSAndroid Build Coastguard Worker        )
80*60517a1eSAndroid Build Coastguard Worker        for platform, meta in PLATFORMS.items()
81*60517a1eSAndroid Build Coastguard Worker    ])
82*60517a1eSAndroid Build Coastguard Worker
83*60517a1eSAndroid Build Coastguard Workerdef _toolchains_repo_impl(rctx):
84*60517a1eSAndroid Build Coastguard Worker    build_content = """\
85*60517a1eSAndroid Build Coastguard Worker# Generated by python/private/toolchains_repo.bzl
86*60517a1eSAndroid Build Coastguard Worker#
87*60517a1eSAndroid Build Coastguard Worker# These can be registered in the workspace file or passed to --extra_toolchains
88*60517a1eSAndroid Build Coastguard Worker# flag. By default all these toolchains are registered by the
89*60517a1eSAndroid Build Coastguard Worker# python_register_toolchains macro so you don't normally need to interact with
90*60517a1eSAndroid Build Coastguard Worker# these targets.
91*60517a1eSAndroid Build Coastguard Worker
92*60517a1eSAndroid Build Coastguard Workerload("@{rules_python}//python/private:py_toolchain_suite.bzl", "py_toolchain_suite")
93*60517a1eSAndroid Build Coastguard Worker
94*60517a1eSAndroid Build Coastguard Worker""".format(
95*60517a1eSAndroid Build Coastguard Worker        rules_python = rctx.attr._rules_python_workspace.workspace_name,
96*60517a1eSAndroid Build Coastguard Worker    )
97*60517a1eSAndroid Build Coastguard Worker
98*60517a1eSAndroid Build Coastguard Worker    toolchains = python_toolchain_build_file_content(
99*60517a1eSAndroid Build Coastguard Worker        prefix = "",
100*60517a1eSAndroid Build Coastguard Worker        python_version = rctx.attr.python_version,
101*60517a1eSAndroid Build Coastguard Worker        set_python_version_constraint = str(rctx.attr.set_python_version_constraint),
102*60517a1eSAndroid Build Coastguard Worker        user_repository_name = rctx.attr.user_repository_name,
103*60517a1eSAndroid Build Coastguard Worker    )
104*60517a1eSAndroid Build Coastguard Worker
105*60517a1eSAndroid Build Coastguard Worker    rctx.file("BUILD.bazel", build_content + toolchains)
106*60517a1eSAndroid Build Coastguard Worker
107*60517a1eSAndroid Build Coastguard Workertoolchains_repo = repository_rule(
108*60517a1eSAndroid Build Coastguard Worker    _toolchains_repo_impl,
109*60517a1eSAndroid Build Coastguard Worker    doc = "Creates a repository with toolchain definitions for all known platforms " +
110*60517a1eSAndroid Build Coastguard Worker          "which can be registered or selected.",
111*60517a1eSAndroid Build Coastguard Worker    attrs = {
112*60517a1eSAndroid Build Coastguard Worker        "python_version": attr.string(doc = "The Python version."),
113*60517a1eSAndroid Build Coastguard Worker        "set_python_version_constraint": attr.bool(doc = "if target_compatible_with for the toolchain should set the version constraint"),
114*60517a1eSAndroid Build Coastguard Worker        "user_repository_name": attr.string(doc = "what the user chose for the base name"),
115*60517a1eSAndroid Build Coastguard Worker        "_rules_python_workspace": attr.label(default = Label("//:WORKSPACE")),
116*60517a1eSAndroid Build Coastguard Worker    },
117*60517a1eSAndroid Build Coastguard Worker)
118*60517a1eSAndroid Build Coastguard Worker
119*60517a1eSAndroid Build Coastguard Workerdef _toolchain_aliases_impl(rctx):
120*60517a1eSAndroid Build Coastguard Worker    logger = repo_utils.logger(rctx)
121*60517a1eSAndroid Build Coastguard Worker    (os_name, arch) = _get_host_os_arch(rctx, logger)
122*60517a1eSAndroid Build Coastguard Worker
123*60517a1eSAndroid Build Coastguard Worker    host_platform = _get_host_platform(os_name, arch)
124*60517a1eSAndroid Build Coastguard Worker
125*60517a1eSAndroid Build Coastguard Worker    is_windows = (os_name == WINDOWS_NAME)
126*60517a1eSAndroid Build Coastguard Worker    python3_binary_path = "python.exe" if is_windows else "bin/python3"
127*60517a1eSAndroid Build Coastguard Worker
128*60517a1eSAndroid Build Coastguard Worker    # Base BUILD file for this repository.
129*60517a1eSAndroid Build Coastguard Worker    build_contents = """\
130*60517a1eSAndroid Build Coastguard Worker# Generated by python/private/toolchains_repo.bzl
131*60517a1eSAndroid Build Coastguard Workerpackage(default_visibility = ["//visibility:public"])
132*60517a1eSAndroid Build Coastguard Workerload("@rules_python//python:versions.bzl", "gen_python_config_settings")
133*60517a1eSAndroid Build Coastguard Workergen_python_config_settings()
134*60517a1eSAndroid Build Coastguard Workerexports_files(["defs.bzl"])
135*60517a1eSAndroid Build Coastguard Worker
136*60517a1eSAndroid Build Coastguard WorkerPLATFORMS = [
137*60517a1eSAndroid Build Coastguard Worker{loaded_platforms}
138*60517a1eSAndroid Build Coastguard Worker]
139*60517a1eSAndroid Build Coastguard Workeralias(name = "files",           actual = select({{":" + item: "@{py_repository}_" + item + "//:files" for item in PLATFORMS}}))
140*60517a1eSAndroid Build Coastguard Workeralias(name = "includes",        actual = select({{":" + item: "@{py_repository}_" + item + "//:includes" for item in PLATFORMS}}))
141*60517a1eSAndroid Build Coastguard Workeralias(name = "libpython",       actual = select({{":" + item: "@{py_repository}_" + item + "//:libpython" for item in PLATFORMS}}))
142*60517a1eSAndroid Build Coastguard Workeralias(name = "py3_runtime",     actual = select({{":" + item: "@{py_repository}_" + item + "//:py3_runtime" for item in PLATFORMS}}))
143*60517a1eSAndroid Build Coastguard Workeralias(name = "python_headers",  actual = select({{":" + item: "@{py_repository}_" + item + "//:python_headers" for item in PLATFORMS}}))
144*60517a1eSAndroid Build Coastguard Workeralias(name = "python_runtimes", actual = select({{":" + item: "@{py_repository}_" + item + "//:python_runtimes" for item in PLATFORMS}}))
145*60517a1eSAndroid Build Coastguard Workeralias(name = "python3",         actual = select({{":" + item: "@{py_repository}_" + item + "//:" + ("python.exe" if "windows" in item else "bin/python3") for item in PLATFORMS}}))
146*60517a1eSAndroid Build Coastguard Worker""".format(
147*60517a1eSAndroid Build Coastguard Worker        py_repository = rctx.attr.user_repository_name,
148*60517a1eSAndroid Build Coastguard Worker        loaded_platforms = "\n".join(["    \"{}\",".format(p) for p in rctx.attr.platforms]),
149*60517a1eSAndroid Build Coastguard Worker    )
150*60517a1eSAndroid Build Coastguard Worker    if not is_windows:
151*60517a1eSAndroid Build Coastguard Worker        build_contents += """\
152*60517a1eSAndroid Build Coastguard Workeralias(name = "pip",             actual = select({{":" + item: "@{py_repository}_" + item + "//:python_runtimes" for item in PLATFORMS if "windows" not in item}}))
153*60517a1eSAndroid Build Coastguard Worker""".format(
154*60517a1eSAndroid Build Coastguard Worker            py_repository = rctx.attr.user_repository_name,
155*60517a1eSAndroid Build Coastguard Worker            host_platform = host_platform,
156*60517a1eSAndroid Build Coastguard Worker        )
157*60517a1eSAndroid Build Coastguard Worker    rctx.file("BUILD.bazel", build_contents)
158*60517a1eSAndroid Build Coastguard Worker
159*60517a1eSAndroid Build Coastguard Worker    # Expose a Starlark file so rules can know what host platform we used and where to find an interpreter
160*60517a1eSAndroid Build Coastguard Worker    # when using repository_ctx.path, which doesn't understand aliases.
161*60517a1eSAndroid Build Coastguard Worker    rctx.file("defs.bzl", content = """\
162*60517a1eSAndroid Build Coastguard Worker# Generated by python/private/toolchains_repo.bzl
163*60517a1eSAndroid Build Coastguard Worker
164*60517a1eSAndroid Build Coastguard Workerload(
165*60517a1eSAndroid Build Coastguard Worker    "{rules_python}//python/config_settings:transition.bzl",
166*60517a1eSAndroid Build Coastguard Worker    _py_binary = "py_binary",
167*60517a1eSAndroid Build Coastguard Worker    _py_test = "py_test",
168*60517a1eSAndroid Build Coastguard Worker)
169*60517a1eSAndroid Build Coastguard Workerload(
170*60517a1eSAndroid Build Coastguard Worker    "{rules_python}//python/entry_points:py_console_script_binary.bzl",
171*60517a1eSAndroid Build Coastguard Worker    _py_console_script_binary = "py_console_script_binary",
172*60517a1eSAndroid Build Coastguard Worker)
173*60517a1eSAndroid Build Coastguard Workerload("{rules_python}//python:pip.bzl", _compile_pip_requirements = "compile_pip_requirements")
174*60517a1eSAndroid Build Coastguard Worker
175*60517a1eSAndroid Build Coastguard Workerhost_platform = "{host_platform}"
176*60517a1eSAndroid Build Coastguard Workerinterpreter = "@{py_repository}_{host_platform}//:{python3_binary_path}"
177*60517a1eSAndroid Build Coastguard Worker
178*60517a1eSAndroid Build Coastguard Workerdef py_binary(name, **kwargs):
179*60517a1eSAndroid Build Coastguard Worker    return _py_binary(
180*60517a1eSAndroid Build Coastguard Worker        name = name,
181*60517a1eSAndroid Build Coastguard Worker        python_version = "{python_version}",
182*60517a1eSAndroid Build Coastguard Worker        **kwargs
183*60517a1eSAndroid Build Coastguard Worker    )
184*60517a1eSAndroid Build Coastguard Worker
185*60517a1eSAndroid Build Coastguard Workerdef py_console_script_binary(name, **kwargs):
186*60517a1eSAndroid Build Coastguard Worker    return _py_console_script_binary(
187*60517a1eSAndroid Build Coastguard Worker        name = name,
188*60517a1eSAndroid Build Coastguard Worker        binary_rule = py_binary,
189*60517a1eSAndroid Build Coastguard Worker        **kwargs
190*60517a1eSAndroid Build Coastguard Worker    )
191*60517a1eSAndroid Build Coastguard Worker
192*60517a1eSAndroid Build Coastguard Workerdef py_test(name, **kwargs):
193*60517a1eSAndroid Build Coastguard Worker    return _py_test(
194*60517a1eSAndroid Build Coastguard Worker        name = name,
195*60517a1eSAndroid Build Coastguard Worker        python_version = "{python_version}",
196*60517a1eSAndroid Build Coastguard Worker        **kwargs
197*60517a1eSAndroid Build Coastguard Worker    )
198*60517a1eSAndroid Build Coastguard Worker
199*60517a1eSAndroid Build Coastguard Workerdef compile_pip_requirements(name, **kwargs):
200*60517a1eSAndroid Build Coastguard Worker    return _compile_pip_requirements(
201*60517a1eSAndroid Build Coastguard Worker        name = name,
202*60517a1eSAndroid Build Coastguard Worker        py_binary = py_binary,
203*60517a1eSAndroid Build Coastguard Worker        py_test = py_test,
204*60517a1eSAndroid Build Coastguard Worker        **kwargs
205*60517a1eSAndroid Build Coastguard Worker    )
206*60517a1eSAndroid Build Coastguard Worker
207*60517a1eSAndroid Build Coastguard Worker""".format(
208*60517a1eSAndroid Build Coastguard Worker        host_platform = host_platform,
209*60517a1eSAndroid Build Coastguard Worker        py_repository = rctx.attr.user_repository_name,
210*60517a1eSAndroid Build Coastguard Worker        python_version = rctx.attr.python_version,
211*60517a1eSAndroid Build Coastguard Worker        python3_binary_path = python3_binary_path,
212*60517a1eSAndroid Build Coastguard Worker        rules_python = get_repository_name(rctx.attr._rules_python_workspace),
213*60517a1eSAndroid Build Coastguard Worker    ))
214*60517a1eSAndroid Build Coastguard Worker
215*60517a1eSAndroid Build Coastguard Workertoolchain_aliases = repository_rule(
216*60517a1eSAndroid Build Coastguard Worker    _toolchain_aliases_impl,
217*60517a1eSAndroid Build Coastguard Worker    doc = """\
218*60517a1eSAndroid Build Coastguard WorkerCreates a repository with a shorter name only referencing the python version,
219*60517a1eSAndroid Build Coastguard Workerit contains a BUILD.bazel file declaring aliases to the host platform's targets
220*60517a1eSAndroid Build Coastguard Workerand is a great fit for any usage related to setting up toolchains for build
221*60517a1eSAndroid Build Coastguard Workeractions.""",
222*60517a1eSAndroid Build Coastguard Worker    attrs = {
223*60517a1eSAndroid Build Coastguard Worker        "platforms": attr.string_list(
224*60517a1eSAndroid Build Coastguard Worker            doc = "List of platforms for which aliases shall be created",
225*60517a1eSAndroid Build Coastguard Worker        ),
226*60517a1eSAndroid Build Coastguard Worker        "python_version": attr.string(doc = "The Python version."),
227*60517a1eSAndroid Build Coastguard Worker        "user_repository_name": attr.string(
228*60517a1eSAndroid Build Coastguard Worker            mandatory = True,
229*60517a1eSAndroid Build Coastguard Worker            doc = "The base name for all created repositories, like 'python38'.",
230*60517a1eSAndroid Build Coastguard Worker        ),
231*60517a1eSAndroid Build Coastguard Worker        "_rules_python_workspace": attr.label(default = Label("//:WORKSPACE")),
232*60517a1eSAndroid Build Coastguard Worker    },
233*60517a1eSAndroid Build Coastguard Worker    environ = [REPO_DEBUG_ENV_VAR],
234*60517a1eSAndroid Build Coastguard Worker)
235*60517a1eSAndroid Build Coastguard Worker
236*60517a1eSAndroid Build Coastguard Workerdef _host_toolchain_impl(rctx):
237*60517a1eSAndroid Build Coastguard Worker    logger = repo_utils.logger(rctx)
238*60517a1eSAndroid Build Coastguard Worker    rctx.file("BUILD.bazel", """\
239*60517a1eSAndroid Build Coastguard Worker# Generated by python/private/toolchains_repo.bzl
240*60517a1eSAndroid Build Coastguard Worker
241*60517a1eSAndroid Build Coastguard Workerexports_files(["python"], visibility = ["//visibility:public"])
242*60517a1eSAndroid Build Coastguard Worker""")
243*60517a1eSAndroid Build Coastguard Worker
244*60517a1eSAndroid Build Coastguard Worker    (os_name, arch) = _get_host_os_arch(rctx, logger)
245*60517a1eSAndroid Build Coastguard Worker    host_platform = _get_host_platform(os_name, arch)
246*60517a1eSAndroid Build Coastguard Worker    repo = "@@{py_repository}_{host_platform}".format(
247*60517a1eSAndroid Build Coastguard Worker        py_repository = rctx.attr.name[:-len("_host")],
248*60517a1eSAndroid Build Coastguard Worker        host_platform = host_platform,
249*60517a1eSAndroid Build Coastguard Worker    )
250*60517a1eSAndroid Build Coastguard Worker
251*60517a1eSAndroid Build Coastguard Worker    rctx.report_progress("Symlinking interpreter files to the target platform")
252*60517a1eSAndroid Build Coastguard Worker    host_python_repo = rctx.path(Label("{repo}//:BUILD.bazel".format(repo = repo)))
253*60517a1eSAndroid Build Coastguard Worker
254*60517a1eSAndroid Build Coastguard Worker    # The interpreter might not work on platfroms that don't have symlink support if
255*60517a1eSAndroid Build Coastguard Worker    # we just symlink the interpreter itself. rctx.symlink does a copy in such cases
256*60517a1eSAndroid Build Coastguard Worker    # so we can just attempt to symlink all of the directories in the host interpreter
257*60517a1eSAndroid Build Coastguard Worker    # repo, which should be faster than re-downloading it.
258*60517a1eSAndroid Build Coastguard Worker    for p in host_python_repo.dirname.readdir():
259*60517a1eSAndroid Build Coastguard Worker        if p.basename in [
260*60517a1eSAndroid Build Coastguard Worker            # ignore special files created by the repo rule automatically
261*60517a1eSAndroid Build Coastguard Worker            "BUILD.bazel",
262*60517a1eSAndroid Build Coastguard Worker            "MODULE.bazel",
263*60517a1eSAndroid Build Coastguard Worker            "REPO.bazel",
264*60517a1eSAndroid Build Coastguard Worker            "WORKSPACE",
265*60517a1eSAndroid Build Coastguard Worker            "WORKSPACE.bazel",
266*60517a1eSAndroid Build Coastguard Worker            "WORKSPACE.bzlmod",
267*60517a1eSAndroid Build Coastguard Worker        ]:
268*60517a1eSAndroid Build Coastguard Worker            continue
269*60517a1eSAndroid Build Coastguard Worker
270*60517a1eSAndroid Build Coastguard Worker        # symlink works on all platforms that bazel supports, so it should work on
271*60517a1eSAndroid Build Coastguard Worker        # UNIX and Windows with and without symlink support. For better performance
272*60517a1eSAndroid Build Coastguard Worker        # users should enable the symlink startup option, however that requires admin
273*60517a1eSAndroid Build Coastguard Worker        # privileges.
274*60517a1eSAndroid Build Coastguard Worker        rctx.symlink(p, p.basename)
275*60517a1eSAndroid Build Coastguard Worker
276*60517a1eSAndroid Build Coastguard Worker    is_windows = (os_name == WINDOWS_NAME)
277*60517a1eSAndroid Build Coastguard Worker    python_binary = "python.exe" if is_windows else "python"
278*60517a1eSAndroid Build Coastguard Worker
279*60517a1eSAndroid Build Coastguard Worker    # Ensure that we can run the interpreter and check that we are not
280*60517a1eSAndroid Build Coastguard Worker    # using the host interpreter.
281*60517a1eSAndroid Build Coastguard Worker    python_tester_contents = """\
282*60517a1eSAndroid Build Coastguard Workerfrom pathlib import Path
283*60517a1eSAndroid Build Coastguard Workerimport sys
284*60517a1eSAndroid Build Coastguard Worker
285*60517a1eSAndroid Build Coastguard Workerpython = Path(sys.executable)
286*60517a1eSAndroid Build Coastguard Workerwant_python = str(Path("{python}").resolve())
287*60517a1eSAndroid Build Coastguard Workergot_python = str(Path(sys.executable).resolve())
288*60517a1eSAndroid Build Coastguard Worker
289*60517a1eSAndroid Build Coastguard Workerassert want_python == got_python, \
290*60517a1eSAndroid Build Coastguard Worker    "Expected to use a different interpreter:\\nwant: '{{}}'\\n got: '{{}}'".format(
291*60517a1eSAndroid Build Coastguard Worker        want_python,
292*60517a1eSAndroid Build Coastguard Worker        got_python,
293*60517a1eSAndroid Build Coastguard Worker    )
294*60517a1eSAndroid Build Coastguard Worker""".format(repo = repo.strip("@"), python = python_binary)
295*60517a1eSAndroid Build Coastguard Worker    python_tester = rctx.path("python_tester.py")
296*60517a1eSAndroid Build Coastguard Worker    rctx.file(python_tester, python_tester_contents)
297*60517a1eSAndroid Build Coastguard Worker    repo_utils.execute_checked(
298*60517a1eSAndroid Build Coastguard Worker        rctx,
299*60517a1eSAndroid Build Coastguard Worker        op = "CheckHostInterpreter",
300*60517a1eSAndroid Build Coastguard Worker        arguments = [rctx.path(python_binary), python_tester],
301*60517a1eSAndroid Build Coastguard Worker    )
302*60517a1eSAndroid Build Coastguard Worker    if not rctx.delete(python_tester):
303*60517a1eSAndroid Build Coastguard Worker        fail("Failed to delete the python tester")
304*60517a1eSAndroid Build Coastguard Worker
305*60517a1eSAndroid Build Coastguard Workerhost_toolchain = repository_rule(
306*60517a1eSAndroid Build Coastguard Worker    _host_toolchain_impl,
307*60517a1eSAndroid Build Coastguard Worker    doc = """\
308*60517a1eSAndroid Build Coastguard WorkerCreates a repository with a shorter name meant to be used in the repository_ctx,
309*60517a1eSAndroid Build Coastguard Workerwhich needs to have `symlinks` for the interpreter. This is separate from the
310*60517a1eSAndroid Build Coastguard Workertoolchain_aliases repo because referencing the `python` interpreter target from
311*60517a1eSAndroid Build Coastguard Workerthis repo causes an eager fetch of the toolchain for the host platform.
312*60517a1eSAndroid Build Coastguard Worker    """,
313*60517a1eSAndroid Build Coastguard Worker    attrs = {
314*60517a1eSAndroid Build Coastguard Worker        "_rule_name": attr.string(default = "host_toolchain"),
315*60517a1eSAndroid Build Coastguard Worker        "_rules_python_workspace": attr.label(default = Label("//:WORKSPACE")),
316*60517a1eSAndroid Build Coastguard Worker    },
317*60517a1eSAndroid Build Coastguard Worker)
318*60517a1eSAndroid Build Coastguard Worker
319*60517a1eSAndroid Build Coastguard Workerdef _multi_toolchain_aliases_impl(rctx):
320*60517a1eSAndroid Build Coastguard Worker    rules_python = rctx.attr._rules_python_workspace.workspace_name
321*60517a1eSAndroid Build Coastguard Worker
322*60517a1eSAndroid Build Coastguard Worker    for python_version, repository_name in rctx.attr.python_versions.items():
323*60517a1eSAndroid Build Coastguard Worker        file = "{}/defs.bzl".format(python_version)
324*60517a1eSAndroid Build Coastguard Worker        rctx.file(file, content = """\
325*60517a1eSAndroid Build Coastguard Worker# Generated by python/private/toolchains_repo.bzl
326*60517a1eSAndroid Build Coastguard Worker
327*60517a1eSAndroid Build Coastguard Workerload(
328*60517a1eSAndroid Build Coastguard Worker    "@{repository_name}//:defs.bzl",
329*60517a1eSAndroid Build Coastguard Worker    _compile_pip_requirements = "compile_pip_requirements",
330*60517a1eSAndroid Build Coastguard Worker    _host_platform = "host_platform",
331*60517a1eSAndroid Build Coastguard Worker    _interpreter = "interpreter",
332*60517a1eSAndroid Build Coastguard Worker    _py_binary = "py_binary",
333*60517a1eSAndroid Build Coastguard Worker    _py_console_script_binary = "py_console_script_binary",
334*60517a1eSAndroid Build Coastguard Worker    _py_test = "py_test",
335*60517a1eSAndroid Build Coastguard Worker)
336*60517a1eSAndroid Build Coastguard Worker
337*60517a1eSAndroid Build Coastguard Workercompile_pip_requirements = _compile_pip_requirements
338*60517a1eSAndroid Build Coastguard Workerhost_platform = _host_platform
339*60517a1eSAndroid Build Coastguard Workerinterpreter = _interpreter
340*60517a1eSAndroid Build Coastguard Workerpy_binary = _py_binary
341*60517a1eSAndroid Build Coastguard Workerpy_console_script_binary = _py_console_script_binary
342*60517a1eSAndroid Build Coastguard Workerpy_test = _py_test
343*60517a1eSAndroid Build Coastguard Worker""".format(
344*60517a1eSAndroid Build Coastguard Worker            repository_name = repository_name,
345*60517a1eSAndroid Build Coastguard Worker        ))
346*60517a1eSAndroid Build Coastguard Worker        rctx.file("{}/BUILD.bazel".format(python_version), "")
347*60517a1eSAndroid Build Coastguard Worker
348*60517a1eSAndroid Build Coastguard Worker    pip_bzl = """\
349*60517a1eSAndroid Build Coastguard Worker# Generated by python/private/toolchains_repo.bzl
350*60517a1eSAndroid Build Coastguard Worker
351*60517a1eSAndroid Build Coastguard Workerload("@{rules_python}//python:pip.bzl", "pip_parse", _multi_pip_parse = "multi_pip_parse")
352*60517a1eSAndroid Build Coastguard Worker
353*60517a1eSAndroid Build Coastguard Workerdef multi_pip_parse(name, requirements_lock, **kwargs):
354*60517a1eSAndroid Build Coastguard Worker    return _multi_pip_parse(
355*60517a1eSAndroid Build Coastguard Worker        name = name,
356*60517a1eSAndroid Build Coastguard Worker        python_versions = {python_versions},
357*60517a1eSAndroid Build Coastguard Worker        requirements_lock = requirements_lock,
358*60517a1eSAndroid Build Coastguard Worker        minor_mapping = {minor_mapping},
359*60517a1eSAndroid Build Coastguard Worker        **kwargs
360*60517a1eSAndroid Build Coastguard Worker    )
361*60517a1eSAndroid Build Coastguard Worker
362*60517a1eSAndroid Build Coastguard Worker""".format(
363*60517a1eSAndroid Build Coastguard Worker        python_versions = rctx.attr.python_versions.keys(),
364*60517a1eSAndroid Build Coastguard Worker        minor_mapping = render.indent(render.dict(rctx.attr.minor_mapping), indent = " " * 8).lstrip(),
365*60517a1eSAndroid Build Coastguard Worker        rules_python = rules_python,
366*60517a1eSAndroid Build Coastguard Worker    )
367*60517a1eSAndroid Build Coastguard Worker    rctx.file("pip.bzl", content = pip_bzl)
368*60517a1eSAndroid Build Coastguard Worker    rctx.file("BUILD.bazel", "")
369*60517a1eSAndroid Build Coastguard Worker
370*60517a1eSAndroid Build Coastguard Workermulti_toolchain_aliases = repository_rule(
371*60517a1eSAndroid Build Coastguard Worker    _multi_toolchain_aliases_impl,
372*60517a1eSAndroid Build Coastguard Worker    attrs = {
373*60517a1eSAndroid Build Coastguard Worker        "minor_mapping": attr.string_dict(doc = "The mapping between `X.Y` and `X.Y.Z` python version values"),
374*60517a1eSAndroid Build Coastguard Worker        "python_versions": attr.string_dict(doc = "The Python versions."),
375*60517a1eSAndroid Build Coastguard Worker        "_rules_python_workspace": attr.label(default = Label("//:WORKSPACE")),
376*60517a1eSAndroid Build Coastguard Worker    },
377*60517a1eSAndroid Build Coastguard Worker)
378*60517a1eSAndroid Build Coastguard Worker
379*60517a1eSAndroid Build Coastguard Workerdef sanitize_platform_name(platform):
380*60517a1eSAndroid Build Coastguard Worker    return platform.replace("-", "_")
381*60517a1eSAndroid Build Coastguard Worker
382*60517a1eSAndroid Build Coastguard Workerdef _get_host_platform(os_name, arch):
383*60517a1eSAndroid Build Coastguard Worker    """Gets the host platform.
384*60517a1eSAndroid Build Coastguard Worker
385*60517a1eSAndroid Build Coastguard Worker    Args:
386*60517a1eSAndroid Build Coastguard Worker        os_name: the host OS name.
387*60517a1eSAndroid Build Coastguard Worker        arch: the host arch.
388*60517a1eSAndroid Build Coastguard Worker    Returns:
389*60517a1eSAndroid Build Coastguard Worker        The host platform.
390*60517a1eSAndroid Build Coastguard Worker    """
391*60517a1eSAndroid Build Coastguard Worker    host_platform = None
392*60517a1eSAndroid Build Coastguard Worker    for platform, meta in PLATFORMS.items():
393*60517a1eSAndroid Build Coastguard Worker        if meta.os_name == os_name and meta.arch == arch:
394*60517a1eSAndroid Build Coastguard Worker            host_platform = platform
395*60517a1eSAndroid Build Coastguard Worker    if not host_platform:
396*60517a1eSAndroid Build Coastguard Worker        fail("No platform declared for host OS {} on arch {}".format(os_name, arch))
397*60517a1eSAndroid Build Coastguard Worker    return host_platform
398*60517a1eSAndroid Build Coastguard Worker
399*60517a1eSAndroid Build Coastguard Workerdef _get_host_os_arch(rctx, logger):
400*60517a1eSAndroid Build Coastguard Worker    """Infer the host OS name and arch from a repository context.
401*60517a1eSAndroid Build Coastguard Worker
402*60517a1eSAndroid Build Coastguard Worker    Args:
403*60517a1eSAndroid Build Coastguard Worker        rctx: Bazel's repository_ctx.
404*60517a1eSAndroid Build Coastguard Worker        logger: Logger to use for operations.
405*60517a1eSAndroid Build Coastguard Worker
406*60517a1eSAndroid Build Coastguard Worker    Returns:
407*60517a1eSAndroid Build Coastguard Worker        A tuple with the host OS name and arch.
408*60517a1eSAndroid Build Coastguard Worker    """
409*60517a1eSAndroid Build Coastguard Worker    os_name = rctx.os.name
410*60517a1eSAndroid Build Coastguard Worker
411*60517a1eSAndroid Build Coastguard Worker    # We assume the arch for Windows is always x86_64.
412*60517a1eSAndroid Build Coastguard Worker    if "windows" in os_name.lower():
413*60517a1eSAndroid Build Coastguard Worker        arch = "x86_64"
414*60517a1eSAndroid Build Coastguard Worker
415*60517a1eSAndroid Build Coastguard Worker        # Normalize the os_name. E.g. os_name could be "OS windows server 2019".
416*60517a1eSAndroid Build Coastguard Worker        os_name = WINDOWS_NAME
417*60517a1eSAndroid Build Coastguard Worker    else:
418*60517a1eSAndroid Build Coastguard Worker        # This is not ideal, but bazel doesn't directly expose arch.
419*60517a1eSAndroid Build Coastguard Worker        arch = repo_utils.execute_unchecked(
420*60517a1eSAndroid Build Coastguard Worker            rctx,
421*60517a1eSAndroid Build Coastguard Worker            op = "GetUname",
422*60517a1eSAndroid Build Coastguard Worker            arguments = [repo_utils.which_checked(rctx, "uname"), "-m"],
423*60517a1eSAndroid Build Coastguard Worker            logger = logger,
424*60517a1eSAndroid Build Coastguard Worker        ).stdout.strip()
425*60517a1eSAndroid Build Coastguard Worker
426*60517a1eSAndroid Build Coastguard Worker        # Normalize the os_name.
427*60517a1eSAndroid Build Coastguard Worker        if "mac" in os_name.lower():
428*60517a1eSAndroid Build Coastguard Worker            os_name = MACOS_NAME
429*60517a1eSAndroid Build Coastguard Worker        elif "linux" in os_name.lower():
430*60517a1eSAndroid Build Coastguard Worker            os_name = LINUX_NAME
431*60517a1eSAndroid Build Coastguard Worker
432*60517a1eSAndroid Build Coastguard Worker    return (os_name, arch)
433