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