xref: /aosp_15_r20/external/pigweed/pw_toolchain/rust/extensions.bzl (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1# Copyright 2024 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Extension for declaring Pigweed Rust toolchains."""
15
16load("//pw_env_setup/bazel/cipd_setup:cipd_rules.bzl", "cipd_repository")
17load(":templates.bzl", "rust_analyzer_toolchain_template", "rust_toolchain_template", "toolchain_template")
18load(":toolchains.bzl", "CHANNELS", "EXTRA_TARGETS", "HOSTS")
19
20def _module_cipd_tag(module):
21    """\
22    Returns the `cipd_tag` tag for the given module.
23
24    Latter delcations will take precedence of ealier ones.
25    """
26    cipd_tag = None
27    for toolchain in module.tags.toolchain:
28        cipd_tag = toolchain.cipd_tag
29
30    return cipd_tag
31
32def _find_cipd_tag(ctx):
33    """\
34    Returns the CIPD tag specified in either the root or pigweed modules.
35
36    The tag from the root module will take priority over the tag from the
37    pigweed module.
38    """
39
40    pigweed_module = None
41    root_tag = None
42
43    for module in ctx.modules:
44        if module.is_root:
45            root_tag = _module_cipd_tag(module)
46        if module.name == "pigweed":
47            pigweed_module = module
48
49    if pigweed_module == None:
50        fail("Unable to find pigweed module")
51
52    return root_tag or _module_cipd_tag(pigweed_module)
53
54def _normalize_os_to_cipd(os):
55    """\
56    Translate a bazel OS name to one used by CIPD.
57    """
58    if os == "macos":
59        return "mac"
60
61    return os
62
63def _pw_rust_impl(ctx):
64    cipd_tag = _find_cipd_tag(ctx)
65
66    # Register CIPD repositories for toolchain binaries
67    for host in HOSTS:
68        cipd_os = _normalize_os_to_cipd(host["os"])
69
70        cipd_repository(
71            name = "rust_toolchain_host_{}_{}".format(host["os"], host["cpu"]),
72            build_file = "//pw_toolchain/rust:rust_toolchain.BUILD",
73            path = "fuchsia/third_party/rust/host/{}-{}".format(cipd_os, host["cipd_arch"]),
74            tag = cipd_tag,
75        )
76
77        cipd_repository(
78            name = "rust_toolchain_target_{}_{}".format(host["triple"], host["cpu"]),
79            build_file = "//pw_toolchain/rust:rust_stdlib.BUILD",
80            path = "fuchsia/third_party/rust/target/{}".format(host["triple"]),
81            tag = cipd_tag,
82        )
83
84    for target in EXTRA_TARGETS:
85        cipd_repository(
86            name = "rust_toolchain_target_{}_{}".format(target["triple"], target["cpu"]),
87            build_file = "//pw_toolchain/rust:rust_stdlib.BUILD",
88            path = "fuchsia/third_party/rust/target/{}".format(target["triple"]),
89            tag = cipd_tag,
90        )
91
92    _toolchain_repository_hub(name = "pw_rust_toolchains")
93
94_RUST_TOOLCHAIN_TAG = tag_class(
95    attrs = dict(
96        cipd_tag = attr.string(
97            doc = "The CIPD tag to use when fetching the Rust toolchain.",
98        ),
99    ),
100)
101
102pw_rust = module_extension(
103    implementation = _pw_rust_impl,
104    tag_classes = {
105        "toolchain": _RUST_TOOLCHAIN_TAG,
106    },
107    doc = """Generate a repository for all Pigweed Rust toolchains.
108
109        Declares a suite of Rust toolchains that may be registered in a
110        MODULE.bazel file. If you would like to use the Toolchains provided
111        by Pigweed, add these lines to your MOUDLE.bazel:
112        ```
113        pw_rust = use_extension("@pigweed//pw_toolchain/rust:extensions.bzl", "pw_rust")
114        use_repo(pw_rust, "pw_rust_toolchains")
115        register_toolchains(
116            "@pw_rust_toolchains//:all",
117            dev_dependency = True,
118        )
119        ```
120
121        If you would like to override the rust compiler version, you can specify a
122        CIPD version for an alternative toolchain to use in your project. Note that
123        only the root module's specification of this tag is applied, and that if no
124        version tag is specified Pigweed's value will be used as a fallback.
125        ```
126        pw_rust = use_extension("@pigweed//pw_toolchain/rust:extensions.bzl", "pw_rust")
127        pw_rust.toolchain(cipd_tag = "rust_revision:bf9c7a64ad222b85397573668b39e6d1ab9f4a72")
128        use_repo(pw_rust, "pw_rust_toolchains")
129        register_toolchains(
130            "@pw_rust_toolchains//:all",
131            dev_dependency = True,
132        )
133        ```
134    """,
135)
136
137def _pw_rust_toolchain(
138        name,
139        exec_triple,
140        target_triple,
141        toolchain_repo,
142        target_repo,
143        dylib_ext,
144        exec_compatible_with,
145        target_compatible_with,
146        target_settings,
147        extra_rustc_flags,
148        analyzer_toolchain_name = None):
149    build_file = rust_toolchain_template(
150        name = name,
151        exec_compatible_with = exec_compatible_with,
152        target_compatible_with = target_compatible_with,
153        dylib_ext = dylib_ext,
154        target_repo = target_repo,
155        toolchain_repo = toolchain_repo,
156        exec_triple = exec_triple,
157        target_triple = target_triple,
158        extra_rustc_flags = extra_rustc_flags,
159    )
160
161    build_file += toolchain_template(
162        name = name,
163        exec_compatible_with = exec_compatible_with,
164        target_compatible_with = target_compatible_with,
165        target_settings = target_settings,
166    )
167
168    if analyzer_toolchain_name:
169        build_file += rust_analyzer_toolchain_template(
170            name = analyzer_toolchain_name,
171            toolchain_repo = toolchain_repo,
172            exec_compatible_with = exec_compatible_with,
173            target_compatible_with = target_compatible_with,
174            target_settings = target_settings,
175        )
176
177    return build_file
178
179def _BUILD_for_toolchain_repo():
180    # Declare rust toolchains
181    build_file = """load("@rules_rust//rust:toolchain.bzl", "rust_analyzer_toolchain", "rust_toolchain")\n"""
182    for channel in CHANNELS:
183        for host in HOSTS:
184            build_file += _pw_rust_toolchain(
185                name = "host_rust_toolchain_{}_{}_{}".format(host["os"], host["cpu"], channel["name"]),
186                analyzer_toolchain_name = "host_rust_analyzer_toolchain_{}_{}_{}".format(host["os"], host["cpu"], channel["name"]),
187                exec_compatible_with = [
188                    "@platforms//cpu:{}".format(host["cpu"]),
189                    "@platforms//os:{}".format(host["os"]),
190                ],
191                target_compatible_with = [
192                    "@platforms//cpu:{}".format(host["cpu"]),
193                    "@platforms//os:{}".format(host["os"]),
194                ],
195                target_settings = channel["target_settings"],
196                dylib_ext = host["dylib_ext"],
197                target_repo = "@rust_toolchain_target_{}_{}".format(host["triple"], host["cpu"]),
198                toolchain_repo = "@rust_toolchain_host_{}_{}".format(host["os"], host["cpu"]),
199                exec_triple = host["triple"],
200                target_triple = host["triple"],
201                extra_rustc_flags = channel["extra_rustc_flags"],
202            )
203
204            for target in EXTRA_TARGETS:
205                build_file += _pw_rust_toolchain(
206                    name = "{}_{}_rust_toolchain_{}_{}_{}".format(host["os"], host["cpu"], target["triple"], target["cpu"], channel["name"]),
207                    exec_triple = host["triple"],
208                    target_triple = target["triple"],
209                    target_repo = "@rust_toolchain_target_{}_{}".format(target["triple"], target["cpu"]),
210                    toolchain_repo = "@rust_toolchain_host_{}_{}".format(host["os"], host["cpu"]),
211                    dylib_ext = "*.so",
212                    exec_compatible_with = [
213                        "@platforms//cpu:{}".format(host["cpu"]),
214                        "@platforms//os:{}".format(host["os"]),
215                    ],
216                    target_compatible_with = [
217                        "@platforms//cpu:{}".format(target["cpu"]),
218                    ],
219                    target_settings = channel["target_settings"],
220                    extra_rustc_flags = channel["extra_rustc_flags"],
221                )
222    return build_file
223
224def _toolchain_repository_hub_impl(repository_ctx):
225    repository_ctx.file("WORKSPACE.bazel", """workspace(name = "{}")""".format(
226        repository_ctx.name,
227    ))
228
229    repository_ctx.file("BUILD.bazel", _BUILD_for_toolchain_repo())
230
231_toolchain_repository_hub = repository_rule(
232    doc = "A repository of Pigweed Rust toolchains",
233    implementation = _toolchain_repository_hub_impl,
234)
235