blob: b6c83aa9ecea407b1c690722f7a959eaa086e40f [file] [log] [blame]
Brian Silvermancc09f182022-03-09 15:40:20 -08001"""Utility macros for use in rules_rust repository rules"""
2
3load("//rust:known_shas.bzl", "FILE_KEY_TO_SHA")
4load(
5 "//rust/platform:triple_mappings.bzl",
6 "system_to_binary_ext",
7 "system_to_dylib_ext",
8 "system_to_staticlib_ext",
9 "system_to_stdlib_linkflags",
10 "triple_to_constraint_set",
11 "triple_to_system",
12)
13
14DEFAULT_TOOLCHAIN_NAME_PREFIX = "toolchain_for"
15DEFAULT_STATIC_RUST_URL_TEMPLATES = ["https://static.rust-lang.org/dist/{}.tar.gz"]
16
17_build_file_for_compiler_template = """\
18load("@rules_rust//rust:toolchain.bzl", "rust_toolchain")
19
20filegroup(
21 name = "rustc",
22 srcs = ["bin/rustc{binary_ext}"],
23 visibility = ["//visibility:public"],
24)
25
26filegroup(
27 name = "rustc_lib",
28 srcs = glob(
29 [
30 "bin/*{dylib_ext}",
31 "lib/*{dylib_ext}",
32 "lib/rustlib/{target_triple}/codegen-backends/*{dylib_ext}",
33 "lib/rustlib/{target_triple}/bin/rust-lld{binary_ext}",
34 "lib/rustlib/{target_triple}/lib/*{dylib_ext}",
35 ],
36 allow_empty = True,
37 ),
38 visibility = ["//visibility:public"],
39)
40
41filegroup(
42 name = "rustdoc",
43 srcs = ["bin/rustdoc{binary_ext}"],
44 visibility = ["//visibility:public"],
45)
46"""
47
48def BUILD_for_compiler(target_triple):
49 """Emits a BUILD file the compiler `.tar.gz`.
50
51 Args:
52 target_triple (str): The triple of the target platform
53
54 Returns:
55 str: The contents of a BUILD file
56 """
57 system = triple_to_system(target_triple)
58 return _build_file_for_compiler_template.format(
59 binary_ext = system_to_binary_ext(system),
60 staticlib_ext = system_to_staticlib_ext(system),
61 dylib_ext = system_to_dylib_ext(system),
62 target_triple = target_triple,
63 )
64
65_build_file_for_cargo_template = """\
66load("@rules_rust//rust:toolchain.bzl", "rust_toolchain")
67
68filegroup(
69 name = "cargo",
70 srcs = ["bin/cargo{binary_ext}"],
71 visibility = ["//visibility:public"],
72)"""
73
74def BUILD_for_cargo(target_triple):
75 """Emits a BUILD file the cargo `.tar.gz`.
76
77 Args:
78 target_triple (str): The triple of the target platform
79
80 Returns:
81 str: The contents of a BUILD file
82 """
83 system = triple_to_system(target_triple)
84 return _build_file_for_cargo_template.format(
85 binary_ext = system_to_binary_ext(system),
86 )
87
88_build_file_for_rustfmt_template = """\
89load("@rules_rust//rust:toolchain.bzl", "rust_toolchain")
90
91filegroup(
92 name = "rustfmt_bin",
93 srcs = ["bin/rustfmt{binary_ext}"],
94 visibility = ["//visibility:public"],
95)
96
97sh_binary(
98 name = "rustfmt",
99 srcs = [":rustfmt_bin"],
100 visibility = ["//visibility:public"],
101)
102"""
103
104def BUILD_for_rustfmt(target_triple):
105 """Emits a BUILD file the rustfmt `.tar.gz`.
106
107 Args:
108 target_triple (str): The triple of the target platform
109
110 Returns:
111 str: The contents of a BUILD file
112 """
113 system = triple_to_system(target_triple)
114 return _build_file_for_rustfmt_template.format(
115 binary_ext = system_to_binary_ext(system),
116 )
117
118_build_file_for_clippy_template = """\
119load("@rules_rust//rust:toolchain.bzl", "rust_toolchain")
120
121filegroup(
122 name = "clippy_driver_bin",
123 srcs = ["bin/clippy-driver{binary_ext}"],
124 visibility = ["//visibility:public"],
125)
126"""
127
128def BUILD_for_clippy(target_triple):
129 """Emits a BUILD file the clippy `.tar.gz`.
130
131 Args:
132 target_triple (str): The triple of the target platform
133
134 Returns:
135 str: The contents of a BUILD file
136 """
137 system = triple_to_system(target_triple)
138 return _build_file_for_clippy_template.format(binary_ext = system_to_binary_ext(system))
139
140_build_file_for_stdlib_template = """\
141load("@rules_rust//rust:toolchain.bzl", "rust_stdlib_filegroup")
142
143rust_stdlib_filegroup(
144 name = "rust_std-{target_triple}",
145 srcs = glob(
146 [
147 "lib/rustlib/{target_triple}/lib/*.rlib",
148 "lib/rustlib/{target_triple}/lib/*{dylib_ext}",
149 "lib/rustlib/{target_triple}/lib/*{staticlib_ext}",
150 "lib/rustlib/{target_triple}/lib/self-contained/**",
151 ],
152 # Some patterns (e.g. `lib/*.a`) don't match anything, see https://github.com/bazelbuild/rules_rust/pull/245
153 allow_empty = True,
154 ),
155 visibility = ["//visibility:public"],
156)
157
158# For legacy support
159alias(
160 name = "rust_lib-{target_triple}",
161 actual = "rust_std-{target_triple}",
162 visibility = ["//visibility:public"],
163)
164"""
165
166def BUILD_for_stdlib(target_triple):
167 """Emits a BUILD file the stdlib `.tar.gz`.
168
169 Args:
170 target_triple (str): The triple of the target platform
171
172 Returns:
173 str: The contents of a BUILD file
174 """
175 system = triple_to_system(target_triple)
176 return _build_file_for_stdlib_template.format(
177 binary_ext = system_to_binary_ext(system),
178 staticlib_ext = system_to_staticlib_ext(system),
179 dylib_ext = system_to_dylib_ext(system),
180 target_triple = target_triple,
181 )
182
183_build_file_for_rust_toolchain_template = """\
184rust_toolchain(
185 name = "{toolchain_name}_impl",
186 rust_doc = "@{workspace_name}//:rustdoc",
187 rust_std = "@{workspace_name}//:rust_std-{target_triple}",
188 rustc = "@{workspace_name}//:rustc",
189 rustfmt = {rustfmt_label},
190 cargo = "@{workspace_name}//:cargo",
191 clippy_driver = "@{workspace_name}//:clippy_driver_bin",
192 rustc_lib = "@{workspace_name}//:rustc_lib",
193 rustc_srcs = {rustc_srcs},
194 binary_ext = "{binary_ext}",
195 staticlib_ext = "{staticlib_ext}",
196 dylib_ext = "{dylib_ext}",
197 stdlib_linkflags = [{stdlib_linkflags}],
198 os = "{system}",
199 default_edition = "{default_edition}",
200 exec_triple = "{exec_triple}",
201 target_triple = "{target_triple}",
202 visibility = ["//visibility:public"],
203)
204"""
205
206def BUILD_for_rust_toolchain(
207 workspace_name,
208 name,
209 exec_triple,
210 target_triple,
211 include_rustc_srcs,
212 default_edition,
213 include_rustfmt,
214 stdlib_linkflags = None):
215 """Emits a toolchain declaration to match an existing compiler and stdlib.
216
217 Args:
218 workspace_name (str): The name of the workspace that this toolchain resides in
219 name (str): The name of the toolchain declaration
220 exec_triple (str): The rust-style target that this compiler runs on
221 target_triple (str): The rust-style target triple of the tool
222 include_rustc_srcs (bool, optional): Whether to download rustc's src code. This is required in order to use rust-analyzer support. Defaults to False.
223 default_edition (str): Default Rust edition.
224 include_rustfmt (bool): Whether rustfmt is present in the toolchain.
225 stdlib_linkflags (list, optional): Overriden flags needed for linking to rust
226 stdlib, akin to BAZEL_LINKLIBS. Defaults to
227 None.
228
229
230 Returns:
231 str: A rendered template of a `rust_toolchain` declaration
232 """
233 system = triple_to_system(target_triple)
234 if stdlib_linkflags == None:
235 stdlib_linkflags = ", ".join(['"%s"' % x for x in system_to_stdlib_linkflags(system)])
236
237 rustc_srcs = "None"
238 if include_rustc_srcs:
239 rustc_srcs = "\"@{workspace_name}//lib/rustlib/src:rustc_srcs\"".format(workspace_name = workspace_name)
240 rustfmt_label = "None"
241 if include_rustfmt:
242 rustfmt_label = "\"@{workspace_name}//:rustfmt_bin\"".format(workspace_name = workspace_name)
243
244 return _build_file_for_rust_toolchain_template.format(
245 toolchain_name = name,
246 workspace_name = workspace_name,
247 binary_ext = system_to_binary_ext(system),
248 staticlib_ext = system_to_staticlib_ext(system),
249 dylib_ext = system_to_dylib_ext(system),
250 rustc_srcs = rustc_srcs,
251 stdlib_linkflags = stdlib_linkflags,
252 system = system,
253 default_edition = default_edition,
254 exec_triple = exec_triple,
255 target_triple = target_triple,
256 rustfmt_label = rustfmt_label,
257 )
258
259_build_file_for_toolchain_template = """\
260toolchain(
261 name = "{name}",
262 exec_compatible_with = {exec_constraint_sets_serialized},
263 target_compatible_with = {target_constraint_sets_serialized},
264 toolchain = "@{parent_workspace_name}//:{name}_impl",
265 toolchain_type = "@rules_rust//rust:toolchain",
266)
267"""
268
269def BUILD_for_toolchain(name, parent_workspace_name, exec_triple, target_triple):
270 return _build_file_for_toolchain_template.format(
271 name = name,
272 exec_constraint_sets_serialized = serialized_constraint_set_from_triple(exec_triple),
273 target_constraint_sets_serialized = serialized_constraint_set_from_triple(target_triple),
274 parent_workspace_name = parent_workspace_name,
275 )
276
277def load_rustfmt(ctx):
278 """Loads a rustfmt binary and yields corresponding BUILD for it
279
280 Args:
281 ctx (repository_ctx): The repository rule's context object
282
283 Returns:
284 str: The BUILD file contents for this rustfmt binary
285 """
286 target_triple = ctx.attr.exec_triple
287
288 load_arbitrary_tool(
289 ctx,
290 iso_date = ctx.attr.iso_date,
291 target_triple = target_triple,
292 tool_name = "rustfmt",
293 tool_subdirectories = ["rustfmt-preview"],
294 version = ctx.attr.rustfmt_version,
295 )
296
297 return BUILD_for_rustfmt(target_triple)
298
299def load_rust_compiler(ctx):
300 """Loads a rust compiler and yields corresponding BUILD for it
301
302 Args:
303 ctx (repository_ctx): A repository_ctx.
304
305 Returns:
306 str: The BUILD file contents for this compiler and compiler library
307 """
308
309 target_triple = ctx.attr.exec_triple
310 load_arbitrary_tool(
311 ctx,
312 iso_date = ctx.attr.iso_date,
313 target_triple = target_triple,
314 tool_name = "rust",
315 tool_subdirectories = ["rustc", "clippy-preview", "cargo"],
316 version = ctx.attr.version,
317 )
318
319 compiler_build_file = BUILD_for_compiler(target_triple) + BUILD_for_clippy(target_triple) + BUILD_for_cargo(target_triple)
320
321 return compiler_build_file
322
323def should_include_rustc_srcs(repository_ctx):
324 """Determing whether or not to include rustc sources in the toolchain.
325
326 Args:
327 repository_ctx (repository_ctx): The repository rule's context object
328
329 Returns:
330 bool: Whether or not to include rustc source files in a `rustc_toolchain`
331 """
332
333 # The environment variable will always take precedence over the attribute.
334 include_rustc_srcs_env = repository_ctx.os.environ.get("RULES_RUST_TOOLCHAIN_INCLUDE_RUSTC_SRCS")
335 if include_rustc_srcs_env != None:
336 return include_rustc_srcs_env.lower() in ["true", "1"]
337
338 return getattr(repository_ctx.attr, "include_rustc_srcs", False)
339
340def load_rust_src(ctx):
341 """Loads the rust source code. Used by the rust-analyzer rust-project.json generator.
342
343 Args:
344 ctx (ctx): A repository_ctx.
345 """
346 tool_suburl = produce_tool_suburl("rust-src", None, ctx.attr.version, ctx.attr.iso_date)
347 static_rust = ctx.os.environ.get("STATIC_RUST_URL", "https://static.rust-lang.org")
348 url = "{}/dist/{}.tar.gz".format(static_rust, tool_suburl)
349
350 tool_path = produce_tool_path("rust-src", None, ctx.attr.version)
351 archive_path = tool_path + ".tar.gz"
352 ctx.download(
353 url,
354 output = archive_path,
355 sha256 = ctx.attr.sha256s.get(tool_suburl) or FILE_KEY_TO_SHA.get(tool_suburl) or "",
356 auth = _make_auth_dict(ctx, [url]),
357 )
358 ctx.extract(
359 archive_path,
360 output = "lib/rustlib/src",
361 stripPrefix = "{}/rust-src/lib/rustlib/src/rust".format(tool_path),
362 )
363 ctx.file(
364 "lib/rustlib/src/BUILD.bazel",
365 """\
366filegroup(
367 name = "rustc_srcs",
368 srcs = glob(["**/*"]),
369 visibility = ["//visibility:public"],
370)""",
371 )
372
373def load_rust_stdlib(ctx, target_triple):
374 """Loads a rust standard library and yields corresponding BUILD for it
375
376 Args:
377 ctx (repository_ctx): A repository_ctx.
378 target_triple (str): The rust-style target triple of the tool
379
380 Returns:
381 str: The BUILD file contents for this stdlib, and a toolchain decl to match
382 """
383
384 load_arbitrary_tool(
385 ctx,
386 iso_date = ctx.attr.iso_date,
387 target_triple = target_triple,
388 tool_name = "rust-std",
389 tool_subdirectories = ["rust-std-{}".format(target_triple)],
390 version = ctx.attr.version,
391 )
392
393 toolchain_prefix = ctx.attr.toolchain_name_prefix or DEFAULT_TOOLCHAIN_NAME_PREFIX
394 stdlib_build_file = BUILD_for_stdlib(target_triple)
395
396 stdlib_linkflags = None
397 if "BAZEL_RUST_STDLIB_LINKFLAGS" in ctx.os.environ:
398 stdlib_linkflags = ctx.os.environ["BAZEL_RUST_STDLIB_LINKFLAGS"].split(":")
399
400 toolchain_build_file = BUILD_for_rust_toolchain(
401 name = "{toolchain_prefix}_{target_triple}".format(
402 toolchain_prefix = toolchain_prefix,
403 target_triple = target_triple,
404 ),
405 exec_triple = ctx.attr.exec_triple,
406 include_rustc_srcs = should_include_rustc_srcs(ctx),
407 target_triple = target_triple,
408 stdlib_linkflags = stdlib_linkflags,
409 workspace_name = ctx.attr.name,
410 default_edition = ctx.attr.edition,
411 include_rustfmt = not (not ctx.attr.rustfmt_version),
412 )
413
414 return stdlib_build_file + toolchain_build_file
415
416def load_rustc_dev_nightly(ctx, target_triple):
417 """Loads the nightly rustc dev component
418
419 Args:
420 ctx: A repository_ctx.
421 target_triple: The rust-style target triple of the tool
422 """
423
424 subdir_name = "rustc-dev"
425 if ctx.attr.iso_date < "2020-12-24":
426 subdir_name = "rustc-dev-{}".format(target_triple)
427
428 load_arbitrary_tool(
429 ctx,
430 iso_date = ctx.attr.iso_date,
431 target_triple = target_triple,
432 tool_name = "rustc-dev",
433 tool_subdirectories = [subdir_name],
434 version = ctx.attr.version,
435 )
436
437def load_llvm_tools(ctx, target_triple):
438 """Loads the llvm tools
439
440 Args:
441 ctx: A repository_ctx.
442 target_triple: The rust-style target triple of the tool
443 """
444 load_arbitrary_tool(
445 ctx,
446 iso_date = ctx.attr.iso_date,
447 target_triple = target_triple,
448 tool_name = "llvm-tools",
449 tool_subdirectories = ["llvm-tools-preview"],
450 version = ctx.attr.version,
451 )
452
453def check_version_valid(version, iso_date, param_prefix = ""):
454 """Verifies that the provided rust version and iso_date make sense.
455
456 Args:
457 version (str): The rustc version
458 iso_date (str): The rustc nightly version's iso date
459 param_prefix (str, optional): The name of the tool who's version is being checked.
460 """
461
462 if not version and iso_date:
463 fail("{param_prefix}iso_date must be paired with a {param_prefix}version".format(param_prefix = param_prefix))
464
465 if version in ("beta", "nightly") and not iso_date:
466 fail("{param_prefix}iso_date must be specified if version is 'beta' or 'nightly'".format(param_prefix = param_prefix))
467
468def serialized_constraint_set_from_triple(target_triple):
469 """Returns a string representing a set of constraints
470
471 Args:
472 target_triple (str): The target triple of the constraint set
473
474 Returns:
475 str: Formatted string representing the serialized constraint
476 """
477 constraint_set = triple_to_constraint_set(target_triple)
478 constraint_set_strs = []
479 for constraint in constraint_set:
480 constraint_set_strs.append("\"{}\"".format(constraint))
481 return "[{}]".format(", ".join(constraint_set_strs))
482
483def produce_tool_suburl(tool_name, target_triple, version, iso_date = None):
484 """Produces a fully qualified Rust tool name for URL
485
486 Args:
487 tool_name: The name of the tool per static.rust-lang.org
488 target_triple: The rust-style target triple of the tool
489 version: The version of the tool among "nightly", "beta', or an exact version.
490 iso_date: The date of the tool (or None, if the version is a specific version).
491
492 Returns:
493 str: The fully qualified url path for the specified tool.
494 """
495 path = produce_tool_path(tool_name, target_triple, version)
496 return iso_date + "/" + path if (iso_date and version in ("beta", "nightly")) else path
497
498def produce_tool_path(tool_name, target_triple, version):
499 """Produces a qualified Rust tool name
500
501 Args:
502 tool_name: The name of the tool per static.rust-lang.org
503 target_triple: The rust-style target triple of the tool
504 version: The version of the tool among "nightly", "beta', or an exact version.
505
506 Returns:
507 str: The qualified path for the specified tool.
508 """
509 if not tool_name:
510 fail("No tool name was provided")
511 if not version:
512 fail("No tool version was provided")
513 return "-".join([e for e in [tool_name, version, target_triple] if e])
514
515def load_arbitrary_tool(ctx, tool_name, tool_subdirectories, version, iso_date, target_triple, sha256 = ""):
516 """Loads a Rust tool, downloads, and extracts into the common workspace.
517
518 This function sources the tool from the Rust-lang static file server. The index is available at:
519 - https://static.rust-lang.org/dist/channel-rust-stable.toml
520 - https://static.rust-lang.org/dist/channel-rust-beta.toml
521 - https://static.rust-lang.org/dist/channel-rust-nightly.toml
522
523 The environment variable `STATIC_RUST_URL` can be used to replace the schema and hostname of
524 the URLs used for fetching assets. `https://static.rust-lang.org/dist/channel-rust-stable.toml`
525 becomes `${STATIC_RUST_URL}/dist/channel-rust-stable.toml`
526
527 Args:
528 ctx (repository_ctx): A repository_ctx (no attrs required).
529 tool_name (str): The name of the given tool per the archive naming.
530 tool_subdirectories (str): The subdirectories of the tool files (at a level below the root directory of
531 the archive). The root directory of the archive is expected to match
532 $TOOL_NAME-$VERSION-$TARGET_TRIPLE.
533 Example:
534 tool_name
535 | version
536 | | target_triple
537 v v v
538 rust-1.39.0-x86_64-unknown-linux-gnu/clippy-preview
539 .../rustc
540 .../etc
541 tool_subdirectories = ["clippy-preview", "rustc"]
542 version (str): The version of the tool among "nightly", "beta', or an exact version.
543 iso_date (str): The date of the tool (ignored if the version is a specific version).
544 target_triple (str): The rust-style target triple of the tool
545 sha256 (str, optional): The expected hash of hash of the Rust tool. Defaults to "".
546 """
547 check_version_valid(version, iso_date, param_prefix = tool_name + "_")
548
549 # View the indices mentioned in the docstring to find the tool_suburl for a given
550 # tool.
551 tool_suburl = produce_tool_suburl(tool_name, target_triple, version, iso_date)
552 urls = []
553
554 static_rust_url_from_env = ctx.os.environ.get("STATIC_RUST_URL")
555 if static_rust_url_from_env:
556 urls.append("{}/dist/{}.tar.gz".format(static_rust_url_from_env, tool_suburl))
557
558 for url in getattr(ctx.attr, "urls", DEFAULT_STATIC_RUST_URL_TEMPLATES):
559 new_url = url.format(tool_suburl)
560 if new_url not in urls:
561 urls.append(new_url)
562
563 tool_path = produce_tool_path(tool_name, target_triple, version)
564 archive_path = "{}.tar.gz".format(tool_path)
565 ctx.download(
566 urls,
567 output = archive_path,
568 sha256 = getattr(ctx.attr, "sha256s", dict()).get(tool_suburl) or
569 FILE_KEY_TO_SHA.get(tool_suburl) or
570 sha256,
571 auth = _make_auth_dict(ctx, urls),
572 )
573 for subdirectory in tool_subdirectories:
574 ctx.extract(
575 archive_path,
576 output = "",
577 stripPrefix = "{}/{}".format(tool_path, subdirectory),
578 )
579
580def _make_auth_dict(ctx, urls):
581 auth = getattr(ctx.attr, "auth", {})
582 if not auth:
583 return {}
584 ret = {}
585 for url in urls:
586 ret[url] = auth
587 return ret