Skip to content

Commit 31bfdc7

Browse files
authored
Improved rust-analyzer tests (#3670)
This change adds `rust_analyzer` binaries to `rust_analyzer_toolchain` so that it can be used with a new `//tools/rust_analyzer/validate` target to ensure generated `rust-project.json` files are actually correct.
1 parent f4f0188 commit 31bfdc7

File tree

13 files changed

+324
-40
lines changed

13 files changed

+324
-40
lines changed

.bazelci/presubmit.yml

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,12 +430,26 @@ tasks:
430430
platform: ubuntu2204
431431
run_targets:
432432
- "//test/rustfmt:rustfmt_failure_tester"
433-
rust_analyzer_tests:
434-
name: Rust-Analyzer Tests
433+
rust_analyzer_tests_linux:
434+
name: Rust-Analyzer Linux Tests
435435
platform: ubuntu2204
436436
run_targets:
437437
- "//tools/rust_analyzer:gen_rust_project"
438+
- "//tools/rust_analyzer:discover_bazel_rust_project"
438439
- "//test/rust_analyzer:rust_analyzer_test"
440+
rust_analyzer_tests_macos:
441+
name: Rust-Analyzer Macos Tests
442+
platform: macos_arm64
443+
run_targets:
444+
- "//tools/rust_analyzer:gen_rust_project"
445+
- "//tools/rust_analyzer:discover_bazel_rust_project"
446+
- "//test/rust_analyzer:rust_analyzer_test"
447+
rust_analyzer_tests_windows:
448+
name: Rust-Analyzer Windows Tests
449+
platform: windows
450+
run_targets:
451+
- "//tools/rust_analyzer:gen_rust_project"
452+
- "//tools/rust_analyzer:discover_bazel_rust_project"
439453
crate_universe_examples_ubuntu2204:
440454
name: Crate Universe Examples
441455
platform: ubuntu2204

docs/BUILD.bazel

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -101,18 +101,6 @@ stardoc(
101101
deps = [":all_docs"],
102102
)
103103

104-
stardoc(
105-
name = "rust_analyzer",
106-
out = "src/rust_analyzer.md",
107-
header_template = ":rust_analyzer.vm",
108-
input = "@rules_rust//rust:defs.bzl",
109-
symbol_names = [
110-
"rust_analyzer_aspect",
111-
],
112-
table_of_contents_template = "@stardoc//stardoc:templates/markdown_tables/table_of_contents.vm",
113-
deps = [":all_docs"],
114-
)
115-
116104
stardoc(
117105
name = "rust_clippy",
118106
out = "src/rust_clippy.md",
@@ -281,7 +269,6 @@ mdbook(
281269
":crate_universe_workspace",
282270
":providers",
283271
":rust",
284-
":rust_analyzer",
285272
":rust_bindgen",
286273
":rust_bzlmod",
287274
":rust_clippy",

docs/settings_table_of_contents.vm

Lines changed: 0 additions & 7 deletions
This file was deleted.

docs/rust_analyzer.vm renamed to docs/src/rust_analyzer.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
#[[
2-
## Overview
1+
# Rust Analyzer
32

43
For [non-Cargo projects](https://rust-analyzer.github.io/manual.html#non-cargo-based-projects),
5-
[rust-analyzer](https://rust-analyzer.github.io/) depends on either a `rust-project.json` file
6-
at the root of the project that describes its structure or on build system specific
4+
[rust-analyzer](https://rust-analyzer.github.io/) depends on either a `rust-project.json` file
5+
at the root of the project that describes its structure or on build system specific
76
[project auto-discovery](https://rust-analyzer.github.io/manual.html#rust-analyzer.workspace.discoverConfig).
87
The `rust_analyzer` rules facilitate both approaches.
98

109
## rust-project.json approach
10+
1111
### Setup
1212

1313
#### Bzlmod
@@ -95,26 +95,28 @@ to ensure a `rust-project.json` file is created and up to date when the editor i
9595
"runOptions": {
9696
"runOn": "folderOpen"
9797
}
98-
},
98+
}
9999
]
100100
}
101101
```
102102

103103
#### Alternative vscode option (prototype)
104104

105105
Add the following to your bazelrc:
106+
106107
```
107108
build --@rules_rust//rust/settings:rustc_output_diagnostics=true --output_groups=+rust_lib_rustc_output,+rust_metadata_rustc_output
108109
```
109110

110111
Then you can use a prototype [rust-analyzer plugin](https://marketplace.visualstudio.com/items?itemName=MattStark.bazel-rust-analyzer) that automatically collects the outputs whenever you recompile.
111112

112113
## Project auto-discovery
114+
113115
### Setup
114116

115117
Auto-discovery makes `rust-analyzer` behave in a Bazel project in a similar fashion to how it does
116-
in a Cargo project. This is achieved by generating a structure similar to what `rust-project.json`
117-
contains but, instead of writing that to a file, the data gets piped to `rust-analyzer` directly
118+
in a Cargo project. This is achieved by generating a structure similar to what `rust-project.json`
119+
contains but, instead of writing that to a file, the data gets piped to `rust-analyzer` directly
118120
through `stdout`. To use auto-discovery the `rust-analyzer` IDE settings must be configured similar to:
119121

120122
```json
@@ -130,7 +132,7 @@ through `stdout`. To use auto-discovery the `rust-analyzer` IDE settings must be
130132
```
131133

132134
The shell script passed to `discoverConfig.command` is typically meant to wrap the bazel rule invocation,
133-
primarily for muting `stderr` (because `rust-analyzer` will consider that an error has occurred if anything
135+
primarily for muting `stderr` (because `rust-analyzer` will consider that an error has occurred if anything
134136
is passed through `stderr`) and, additionally, for specifying rule arguments. E.g:
135137

136138
```shell
@@ -150,7 +152,7 @@ enabled. The script path should be either absolute or relative to the project ro
150152
### Workspace splitting
151153

152154
The above configuration treats the entire project as a single workspace. However, large codebases might be
153-
too much to handle for `rust-analyzer` all at once. This can be addressed by splitting the codebase in
155+
too much to handle for `rust-analyzer` all at once. This can be addressed by splitting the codebase in
154156
multiple workspaces by extending the `discoverConfig.command` setting:
155157

156158
```json
@@ -169,11 +171,9 @@ multiple workspaces by extending the `discoverConfig.command` setting:
169171
that gets opened.
170172

171173
The root of the workspace will, in this configuration, be the package the crate currently being worked on
172-
belongs to. This means that only that package and its dependencies get built and indexed by `rust-analyzer`,
173-
thus allowing for a smaller footprint.
174+
belongs to. This means that only that package and its dependencies get built and indexed by `rust-analyzer`,
175+
thus allowing for a smaller footprint.
174176

175177
`rust-analyzer` will switch workspaces whenever an out-of-tree file gets opened, essentially indexing that
176-
crate and its dependencies separately. A caveat of this is that *dependents* of the crate currently being
178+
crate and its dependencies separately. A caveat of this is that _dependents_ of the crate currently being
177179
worked on are not indexed and won't be tracked by `rust-analyzer`.
178-
179-
]]#

rust/private/repository_utils.bzl

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,27 @@ def BUILD_for_cargo(target_triple):
9797
binary_ext = system_to_binary_ext(target_triple.system),
9898
)
9999

100+
_build_file_for_rust_analyzer_template = """\
101+
filegroup(
102+
name = "rust_analyzer",
103+
srcs = ["bin/rust-analyzer{binary_ext}"],
104+
visibility = ["//visibility:public"],
105+
)
106+
"""
107+
108+
def BUILD_for_rust_analyzer(target_triple):
109+
"""Emits a BUILD file for the rust-analyzer archive.
110+
111+
Args:
112+
target_triple (str): The triple of the target platform
113+
114+
Returns:
115+
str: The contents of a BUILD file
116+
"""
117+
return _build_file_for_rust_analyzer_template.format(
118+
binary_ext = system_to_binary_ext(target_triple.system),
119+
)
120+
100121
_build_file_for_rustfmt_template = """\
101122
load("@rules_shell//shell:sh_binary.bzl", "sh_binary")
102123
@@ -396,6 +417,30 @@ def BUILD_for_toolchain(
396417
target_settings = target_settings_value,
397418
)
398419

420+
def load_rust_analyzer(ctx, target_triple, version, iso_date):
421+
"""Loads a rust-analyzer binary and yields corresponding BUILD for it
422+
423+
Args:
424+
ctx (repository_ctx): The repository rule's context object.
425+
target_triple (struct): The platform triple to download rust-analyzer for.
426+
version (str): The version or channel of rust-analyzer.
427+
iso_date (str): The date of the tool (or None, if the version is a specific version).
428+
429+
Returns:
430+
Tuple[str, Dict[str, str]]: The BUILD file contents for this rust-analyzer binary and sha256 of its artifact.
431+
"""
432+
433+
sha256 = load_arbitrary_tool(
434+
ctx,
435+
iso_date = iso_date,
436+
target_triple = target_triple,
437+
tool_name = "rust-analyzer",
438+
tool_subdirectories = ["rust-analyzer-preview"],
439+
version = version,
440+
)
441+
442+
return BUILD_for_rust_analyzer(target_triple), sha256
443+
399444
def load_rustfmt(ctx, target_triple, version, iso_date):
400445
"""Loads a rustfmt binary and yields corresponding BUILD for it
401446
@@ -566,17 +611,19 @@ load("@rules_rust//rust:toolchain.bzl", "rust_analyzer_toolchain")
566611
rust_analyzer_toolchain(
567612
name = "{name}",
568613
proc_macro_srv = {proc_macro_srv},
614+
rust_analyzer = {rust_analyzer},
569615
rustc = "{rustc}",
570616
rustc_srcs = "//lib/rustlib/src:rustc_srcs",
571617
visibility = ["//visibility:public"],
572618
)
573619
"""
574620

575-
def BUILD_for_rust_analyzer_toolchain(name, rustc, proc_macro_srv):
621+
def BUILD_for_rust_analyzer_toolchain(name, rustc, proc_macro_srv, rust_analyzer = None):
576622
return _build_file_for_rust_analyzer_toolchain_template.format(
577623
name = name,
578624
rustc = rustc,
579625
proc_macro_srv = repr(proc_macro_srv),
626+
rust_analyzer = repr(rust_analyzer) if rust_analyzer else "None",
580627
)
581628

582629
_build_file_for_rustfmt_toolchain_template = """\

rust/private/rust_analyzer.bzl

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,15 +293,31 @@ def _create_single_crate(ctx, attrs, info):
293293
crate["proc_macro_dylib_path"] = _EXEC_ROOT_TEMPLATE + info.proc_macro_dylib.path
294294
return crate
295295

296+
def _rlocationpath(file, workspace_name):
297+
if file.short_path.startswith("../"):
298+
return file.short_path[len("../"):]
299+
300+
return "{}/{}".format(workspace_name, file.short_path)
301+
296302
def _rust_analyzer_toolchain_impl(ctx):
303+
make_variable_info = platform_common.TemplateVariableInfo({
304+
"RUST_ANALYZER": ctx.file.rust_analyzer.path,
305+
"RUST_ANALYZER_RLOCATIONPATH": _rlocationpath(ctx.file.rust_analyzer, ctx.workspace_name),
306+
})
307+
297308
toolchain = platform_common.ToolchainInfo(
298309
proc_macro_srv = ctx.executable.proc_macro_srv,
310+
rust_analyzer = ctx.executable.rust_analyzer,
299311
rustc = ctx.executable.rustc,
300312
rustc_srcs = ctx.attr.rustc_srcs,
301313
rustc_srcs_path = ctx.attr.rustc_srcs_path,
314+
make_variables = make_variable_info,
302315
)
303316

304-
return [toolchain]
317+
return [
318+
make_variable_info,
319+
toolchain,
320+
]
305321

306322
rust_analyzer_toolchain = rule(
307323
implementation = _rust_analyzer_toolchain_impl,
@@ -313,6 +329,12 @@ rust_analyzer_toolchain = rule(
313329
executable = True,
314330
allow_single_file = True,
315331
),
332+
"rust_analyzer": attr.label(
333+
doc = "The path to a `rust-analyzer` binary.",
334+
cfg = "exec",
335+
executable = True,
336+
allow_single_file = True,
337+
),
316338
"rustc": attr.label(
317339
doc = "The path to a `rustc` binary.",
318340
cfg = "exec",
@@ -331,6 +353,33 @@ rust_analyzer_toolchain = rule(
331353
},
332354
)
333355

356+
def _current_rust_analyzer_toolchain_impl(ctx):
357+
toolchain = ctx.toolchains[str(Label("@rules_rust//rust/rust_analyzer:toolchain_type"))]
358+
359+
files = []
360+
if toolchain.rust_analyzer:
361+
files.append(toolchain.rust_analyzer)
362+
if toolchain.proc_macro_srv:
363+
files.append(toolchain.proc_macro_srv)
364+
if toolchain.rustc:
365+
files.append(toolchain.rustc)
366+
367+
return [
368+
toolchain,
369+
toolchain.make_variables,
370+
DefaultInfo(
371+
files = depset(files),
372+
),
373+
]
374+
375+
current_rust_analyzer_toolchain = rule(
376+
doc = "A rule for exposing the current registered `rust_analyzer_toolchain`.",
377+
implementation = _current_rust_analyzer_toolchain_impl,
378+
toolchains = [
379+
str(Label("@rules_rust//rust/rust_analyzer:toolchain_type")),
380+
],
381+
)
382+
334383
def _rust_analyzer_detect_sysroot_impl(ctx):
335384
rust_analyzer_toolchain = ctx.toolchains[Label("@rules_rust//rust/rust_analyzer:toolchain_type")]
336385

rust/repositories.bzl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ load(
2525
"load_cargo",
2626
"load_clippy",
2727
"load_llvm_tools",
28+
"load_rust_analyzer",
2829
"load_rust_compiler",
2930
"load_rust_src",
3031
"load_rust_stdlib",
@@ -786,10 +787,24 @@ def _rust_analyzer_toolchain_tools_repository_impl(repository_ctx):
786787
build_contents.append(BUILD_for_rust_analyzer_proc_macro_srv(host_triple))
787788
proc_macro_srv = "//:rust_analyzer_proc_macro_srv"
788789

790+
# Load rust-analyzer binary from official Rust distribution
791+
rust_analyzer = None
792+
rust_analyzer_content, rust_analyzer_sha256 = load_rust_analyzer(
793+
ctx = repository_ctx,
794+
target_triple = host_triple,
795+
version = version,
796+
iso_date = iso_date,
797+
)
798+
if rust_analyzer_content:
799+
build_contents.append(rust_analyzer_content)
800+
sha256s.update(rust_analyzer_sha256)
801+
rust_analyzer = "//:rust_analyzer"
802+
789803
build_contents.append(BUILD_for_rust_analyzer_toolchain(
790804
name = "rust_analyzer_toolchain",
791805
rustc = rustc,
792806
proc_macro_srv = proc_macro_srv,
807+
rust_analyzer = rust_analyzer,
793808
))
794809

795810
repository_ctx.file("BUILD.bazel", "\n".join(build_contents))

rust/toolchain.bzl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
99
load("//rust/platform:triple.bzl", "triple")
1010
load("//rust/private:common.bzl", "rust_common")
1111
load("//rust/private:lto.bzl", "RustLtoInfo")
12-
load("//rust/private:rust_analyzer.bzl", _rust_analyzer_toolchain = "rust_analyzer_toolchain")
12+
load(
13+
"//rust/private:rust_analyzer.bzl",
14+
_current_rust_analyzer_toolchain = "current_rust_analyzer_toolchain",
15+
_rust_analyzer_toolchain = "rust_analyzer_toolchain",
16+
)
1317
load(
1418
"//rust/private:rustfmt.bzl",
1519
_current_rustfmt_toolchain = "current_rustfmt_toolchain",
@@ -27,6 +31,7 @@ load(
2731
load("//rust/settings:incompatible.bzl", "IncompatibleFlagInfo")
2832

2933
rust_analyzer_toolchain = _rust_analyzer_toolchain
34+
current_rust_analyzer_toolchain = _current_rust_analyzer_toolchain
3035
rustfmt_toolchain = _rustfmt_toolchain
3136
current_rustfmt_toolchain = _current_rustfmt_toolchain
3237

rust/toolchain/BUILD.bazel

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
load("//rust/private:rust_analyzer.bzl", "current_rust_analyzer_toolchain")
12
load("//rust/private:rustfmt.bzl", "current_rustfmt_toolchain")
23
load("//rust/private:toolchain_utils.bzl", "current_rust_toolchain", "toolchain_files", "toolchain_files_for_target")
34

@@ -46,6 +47,11 @@ current_rustfmt_toolchain(
4647
name = "current_rustfmt_toolchain",
4748
)
4849

50+
current_rust_analyzer_toolchain(
51+
name = "current_rust_analyzer_toolchain",
52+
tags = ["manual"],
53+
)
54+
4955
toolchain_files_for_target(
5056
name = "current_rustfmt_toolchain_for_target",
5157
toolchain_files = ":current_rustfmt_toolchain",

test/rust_analyzer/rust_analyzer_test_runner.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ function rust_analyzer_test() {
103103
RUST_LOG="${rust_log}" bazel run "@rules_rust//tools/rust_analyzer:gen_rust_project"
104104
fi
105105

106+
echo "Validating rust-project.json..."
107+
bazel run "@rules_rust//tools/rust_analyzer:validate" -- rust-project.json
108+
106109
echo "Generating auto-discovery.json..."
107110
RUST_LOG="${rust_log}" bazel run "@rules_rust//tools/rust_analyzer:discover_bazel_rust_project" > auto-discovery.json
108111

0 commit comments

Comments
 (0)