Skip to content

Commit 978412c

Browse files
committed
doc: overhaul non-cargo build system docs
1 parent 277cf5f commit 978412c

File tree

4 files changed

+122
-31
lines changed

4 files changed

+122
-31
lines changed

crates/rust-analyzer/src/config.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ config_data! {
494494
/// }
495495
/// ```
496496
///
497-
/// ## On `DiscoverWorkspaceConfig::command`
497+
/// ## Workspace Discovery Protocol
498498
///
499499
/// **Warning**: This format is provisional and subject to change.
500500
///
@@ -862,10 +862,18 @@ config_data! {
862862
/// (i.e., the folder containing the `Cargo.toml`). This can be overwritten
863863
/// by changing `#rust-analyzer.check.invocationStrategy#`.
864864
///
865-
/// If `$saved_file` is part of the command, rust-analyzer will pass
866-
/// the absolute path of the saved file to the provided command. This is
867-
/// intended to be used with non-Cargo build systems.
868-
/// Note that `$saved_file` is experimental and may be removed in the future.
865+
/// It supports two interpolation syntaxes, both mainly intended to be used with
866+
/// [non-Cargo build systems](./non_cargo_based_projects.md):
867+
///
868+
/// - If `{saved_file}` is part of the command, rust-analyzer will pass
869+
/// the absolute path of the saved file to the provided command.
870+
/// (A previous version, `$saved_file`, also works.)
871+
/// - If `{label}` is part of the command, rust-analyzer will pass the
872+
/// Cargo package ID, which can be used with `cargo check -p`, or a build label from
873+
/// `rust-project.json`. If `{label}` is included, rust-analyzer behaves much like
874+
/// [`"rust-analyzer.check.workspace": false`](#check.workspace).
875+
///
876+
///
869877
///
870878
/// An example command would be:
871879
///

docs/book/src/configuration_generated.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -323,10 +323,18 @@ each of them, with the working directory being the workspace root
323323
(i.e., the folder containing the `Cargo.toml`). This can be overwritten
324324
by changing `#rust-analyzer.check.invocationStrategy#`.
325325

326-
If `$saved_file` is part of the command, rust-analyzer will pass
327-
the absolute path of the saved file to the provided command. This is
328-
intended to be used with non-Cargo build systems.
329-
Note that `$saved_file` is experimental and may be removed in the future.
326+
It supports two interpolation syntaxes, both mainly intended to be used with
327+
[non-Cargo build systems](./non_cargo_based_projects.md):
328+
329+
- If `{saved_file}` is part of the command, rust-analyzer will pass
330+
the absolute path of the saved file to the provided command.
331+
(A previous version, `$saved_file`, also works.)
332+
- If `{label}` is part of the command, rust-analyzer will pass the
333+
Cargo package ID, which can be used with `cargo check -p`, or a build label from
334+
`rust-project.json`. If `{label}` is included, rust-analyzer behaves much like
335+
[`"rust-analyzer.check.workspace": false`](#check.workspace).
336+
337+
330338

331339
An example command would be:
332340

@@ -1542,8 +1550,8 @@ Default: `null`
15421550

15431551
Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
15441552

1545-
[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`.
1546-
`progress_label` is used for the title in progress indicators, whereas `files_to_watch`
1553+
[`DiscoverWorkspaceConfig`] also requires setting `progressLabel` and `filesToWatch`.
1554+
`progressLabel` is used for the title in progress indicators, whereas `filesToWatch`
15471555
is used to determine which build system-specific files should be watched in order to
15481556
reload rust-analyzer.
15491557

@@ -1562,7 +1570,7 @@ Below is an example of a valid configuration:
15621570
}
15631571
```
15641572

1565-
## On `DiscoverWorkspaceConfig::command`
1573+
## Workspace Discovery Protocol
15661574

15671575
**Warning**: This format is provisional and subject to change.
15681576

docs/book/src/non_cargo_based_projects.md

Lines changed: 92 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,15 @@ interface Runnable {
220220
This format is provisional and subject to change. Specifically, the
221221
`roots` setup will be different eventually.
222222

223-
There are three ways to feed `rust-project.json` to rust-analyzer:
223+
### Providing a JSON project to rust-analyzer
224+
225+
There are four ways to feed `rust-project.json` to rust-analyzer:
226+
227+
- Use
228+
[`"rust-analyzer.workspace.discoverConfig": … }`](./configuration.md#workspace.discoverConfig)
229+
to specify a workspace discovery command to generate project descriptions
230+
on-the-fly. Please note that the command output is message-oriented and must
231+
follow [the discovery protocol](./configuration.md#workspace-discovery-protocol).
224232

225233
- Place `rust-project.json` file at the root of the project, and
226234
rust-analyzer will discover it.
@@ -240,19 +248,86 @@ location or (for inline JSON) relative to `rootUri`.
240248
You can set the `RA_LOG` environment variable to `rust_analyzer=info` to
241249
inspect how rust-analyzer handles config and project loading.
242250

243-
Note that calls to `cargo check` are disabled when using
244-
`rust-project.json` by default, so compilation errors and warnings will
245-
no longer be sent to your LSP client. To enable these compilation errors
246-
you will need to specify explicitly what command rust-analyzer should
247-
run to perform the checks using the
248-
`rust-analyzer.check.overrideCommand` configuration. As an example, the
249-
following configuration explicitly sets `cargo check` as the `check`
250-
command.
251-
252-
{ "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] }
253-
254-
`check.overrideCommand` requires the command specified to output json
255-
error messages for rust-analyzer to consume. The `--message-format=json`
256-
flag does this for `cargo check` so whichever command you use must also
257-
output errors in this format. See the [Configuration](#_configuration)
258-
section for more information.
251+
### Flycheck support
252+
253+
Rust-analyzer has functionality to run an actual build of a crate when the user saves a file, to
254+
fill in diagnostics it does not implement natively. This is known as "flycheck".
255+
256+
**Flycheck is disabled when using `rust-project.json` unless explicitly configured**, so compilation
257+
errors and warnings will no longer be sent to your LSP client by default. To enable these
258+
compilation errors you will need to specify explicitly what command rust-analyzer should run to
259+
perform the checks. There are two ways to do this:
260+
261+
- `rust-project.json` may contain a `runnables` field. The `flycheck` runnable may be used to
262+
configure a check command. See above for documentation.
263+
264+
- Using the [`rust-analyzer.check.overrideCommand`](./configuration.md#check.overrideCommand)
265+
configuration. This will also override anything in `rust-project.json`. As an example, the
266+
following configuration explicitly sets `cargo check` as the `check` command.
267+
268+
```json
269+
{ "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] }
270+
```
271+
272+
Note also that this works with cargo projects.
273+
274+
Either option requires the command specified to output JSON error messages for rust-analyzer to
275+
consume. The `--message-format=json` flag does this for `cargo check` so whichever command you use
276+
must also output errors in this format.
277+
278+
Either option also supports two syntaxes within each argument:
279+
280+
- `{label}` will be replaced with the `BuildInfo::label` of the crate
281+
containing a saved file, if `BuildInfo` is provided. In the case of `check.overrideCommand` being
282+
used in a Cargo project, this will be the cargo package ID, which can be used with `cargo check -p`.
283+
- `{saved_file}` will be replaced with an absolute path to the saved file. This can be queried against a
284+
build system to find targets that include the file.
285+
286+
For example:
287+
288+
```json
289+
{ "rust-analyzer.check.overrideCommand": ["custom_crate_checker", "{label}"] }
290+
```
291+
292+
If you do use `{label}` or `{saved_file}`, the command will not be run unless the relevant value can
293+
be substituted.
294+
295+
296+
#### Flycheck considerations
297+
298+
##### Diagnostic output on error
299+
300+
A flycheck command using a complex build orchestrator like `"bazel", "build", "{label}"`, even with
301+
a tweak to return JSON messages, is often insufficient. Such a command will typically succeed if
302+
there are warnings, but if there are errors, it might "fail to compile" the diagnostics and not
303+
produce any output. You must build a package in such a way that the build succeeds even if `rustc`
304+
exits with an error, and prints the JSON build messages in every case.
305+
306+
##### Diagnostics for upstream crates
307+
308+
`cargo check -p` re-prints any errors and warnings in crates higher up in the dependency graph
309+
than the one requested. We do clear all diagnostics when flychecking, so if you manage to
310+
replicate this behaviour, diagnostics for crates other than the one being checked will show up in
311+
the editor. If you do not, then users may be confused that diagnostics are "stuck" or disappear
312+
entirely when there is a build error in an upstream crate.
313+
314+
##### Compiler options
315+
316+
`cargo check` invokes rustc differently from `cargo build`. It turns off codegen (with `rustc
317+
--emit=metadata`), which results in lower latency to get to diagnostics. If your build system can
318+
configure this, it is recommended.
319+
320+
If your build tool can configure rustc for incremental compiles, this is also recommended.
321+
322+
##### Locking and pre-emption
323+
324+
In any good build system, including Cargo, build commands sometimes block each other. Running a
325+
flycheck will (by default) frequently block you from running other build commands. Generally this is
326+
undesirable. Users will have to (unintuitively) press save again in the editor to cancel a
327+
flycheck, so that some other command may proceed.
328+
329+
If your build system has the ability to isolate any rust-analyzer-driven flychecks and prevent lock
330+
contention, for example a separate build output directory and/or daemon instance, this is
331+
recommended. Alternatively, consider using a feature if available that can set the priority of
332+
various build invocations and automatically cancel lower-priority ones when needed. Flychecks should
333+
be set to a lower priority than general direct build invocations.

editors/code/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,7 +1208,7 @@
12081208
"title": "Check",
12091209
"properties": {
12101210
"rust-analyzer.check.overrideCommand": {
1211-
"markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#`.\n\nIf `$saved_file` is part of the command, rust-analyzer will pass\nthe absolute path of the saved file to the provided command. This is\nintended to be used with non-Cargo build systems.\nNote that `$saved_file` is experimental and may be removed in the future.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.",
1211+
"markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#`.\n\nIt supports two interpolation syntaxes, both mainly intended to be used with\n[non-Cargo build systems](./non_cargo_based_projects.md):\n\n- If `{saved_file}` is part of the command, rust-analyzer will pass\n the absolute path of the saved file to the provided command.\n (A previous version, `$saved_file`, also works.)\n- If `{label}` is part of the command, rust-analyzer will pass the\n Cargo package ID, which can be used with `cargo check -p`, or a build label from\n `rust-project.json`. If `{label}` is included, rust-analyzer behaves much like\n [`\"rust-analyzer.check.workspace\": false`](#check.workspace).\n\n\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.",
12121212
"default": null,
12131213
"type": [
12141214
"null",
@@ -3032,7 +3032,7 @@
30323032
"title": "Workspace",
30333033
"properties": {
30343034
"rust-analyzer.workspace.discoverConfig": {
3035-
"markdownDescription": "Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].\n\n[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`.\n`progress_label` is used for the title in progress indicators, whereas `files_to_watch`\nis used to determine which build system-specific files should be watched in order to\nreload rust-analyzer.\n\nBelow is an example of a valid configuration:\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n \"command\": [\n \"rust-project\",\n \"develop-json\"\n ],\n \"progressLabel\": \"rust-analyzer\",\n \"filesToWatch\": [\n \"BUCK\"\n ]\n}\n```\n\n## On `DiscoverWorkspaceConfig::command`\n\n**Warning**: This format is provisional and subject to change.\n\n[`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to\n`DiscoverProjectData::Finished`:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n Error { error: String, source: Option<String> },\n Progress { message: String },\n}\n```\n\nAs JSON, `DiscoverProjectData::Finished` is:\n\n```json\n{\n // the internally-tagged representation of the enum.\n \"kind\": \"finished\",\n // the file used by a non-Cargo build system to define\n // a package or target.\n \"buildfile\": \"rust-analyzer/BUILD\",\n // the contents of a rust-project.json, elided for brevity\n \"project\": {\n \"sysroot\": \"foo\",\n \"crates\": []\n }\n}\n```\n\nIt is encouraged, but not required, to use the other variants on `DiscoverProjectData`\nto provide a more polished end-user experience.\n\n`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be\nsubstituted with the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n Path(AbsPathBuf),\n Buildfile(AbsPathBuf),\n}\n```\n\nThe JSON representation of `DiscoverArgument::Path` is:\n\n```json\n{\n \"path\": \"src/main.rs\"\n}\n```\n\nSimilarly, the JSON representation of `DiscoverArgument::Buildfile` is:\n\n```json\n{\n \"buildfile\": \"BUILD\"\n}\n```\n\n`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and\ntherefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an\nexisting workspace. As a reference for implementors, buck2's `rust-project` will likely\nbe useful: <https://github.com/facebook/buck2/tree/main/integrations/rust-project>.",
3035+
"markdownDescription": "Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].\n\n[`DiscoverWorkspaceConfig`] also requires setting `progressLabel` and `filesToWatch`.\n`progressLabel` is used for the title in progress indicators, whereas `filesToWatch`\nis used to determine which build system-specific files should be watched in order to\nreload rust-analyzer.\n\nBelow is an example of a valid configuration:\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n \"command\": [\n \"rust-project\",\n \"develop-json\",\n \"{arg}\"\n ],\n \"progressLabel\": \"buck2/rust-project\",\n \"filesToWatch\": [\n \"BUCK\"\n ]\n}\n```\n\n## Workspace Discovery Protocol\n\n**Warning**: This format is provisional and subject to change.\n\n[`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to\n`DiscoverProjectData::Finished`:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n Error { error: String, source: Option<String> },\n Progress { message: String },\n}\n```\n\nAs JSON, `DiscoverProjectData::Finished` is:\n\n```json\n{\n // the internally-tagged representation of the enum.\n \"kind\": \"finished\",\n // the file used by a non-Cargo build system to define\n // a package or target.\n \"buildfile\": \"rust-analyzer/BUILD\",\n // the contents of a rust-project.json, elided for brevity\n \"project\": {\n \"sysroot\": \"foo\",\n \"crates\": []\n }\n}\n```\n\nIt is encouraged, but not required, to use the other variants on `DiscoverProjectData`\nto provide a more polished end-user experience.\n\n`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be\nsubstituted with the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n Path(AbsPathBuf),\n Buildfile(AbsPathBuf),\n}\n```\n\nThe JSON representation of `DiscoverArgument::Path` is:\n\n```json\n{\n \"path\": \"src/main.rs\"\n}\n```\n\nSimilarly, the JSON representation of `DiscoverArgument::Buildfile` is:\n\n```json\n{\n \"buildfile\": \"BUILD\"\n}\n```\n\n`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and\ntherefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an\nexisting workspace. As a reference for implementors, buck2's `rust-project` will likely\nbe useful: <https://github.com/facebook/buck2/tree/main/integrations/rust-project>.",
30363036
"default": null,
30373037
"anyOf": [
30383038
{

0 commit comments

Comments
 (0)