Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion crates/project-model/src/build_dependencies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ impl WorkspaceBuildScripts {
&allowed_features,
workspace.manifest_path(),
workspace.target_directory().as_ref(),
workspace.build_directory().map(AbsPath::as_ref),
current_dir,
sysroot,
toolchain,
Expand All @@ -105,12 +106,15 @@ impl WorkspaceBuildScripts {
) -> io::Result<Vec<WorkspaceBuildScripts>> {
assert_eq!(config.invocation_strategy, InvocationStrategy::Once);

let working_dir: &Utf8Path = working_directory.as_ref();

let (_guard, cmd) = Self::build_command(
config,
&Default::default(),
// These are not gonna be used anyways, so just construct a dummy here
&ManifestPath::try_from(working_directory.clone()).unwrap(),
working_directory.as_ref(),
working_dir,
Some(working_dir),
working_directory,
&Sysroot::empty(),
None,
Expand Down Expand Up @@ -434,6 +438,7 @@ impl WorkspaceBuildScripts {
allowed_features: &FxHashSet<String>,
manifest_path: &ManifestPath,
target_dir: &Utf8Path,
build_dir: Option<&Utf8Path>,
current_dir: &AbsPath,
sysroot: &Sysroot,
toolchain: Option<&semver::Version>,
Expand Down Expand Up @@ -461,6 +466,9 @@ impl WorkspaceBuildScripts {
cmd.arg("--target-dir");
cmd.arg(target_dir.as_ref());
}
if let Some(build_dir) = config.build_dir_config.target_dir(build_dir) {
cmd.env("CARGO_BUILD_BUILD_DIR", build_dir.as_ref());
}

toolchain::cargo_use_targets(toolchain, &mut cmd, config.target.as_slice());
let mut lockfile_copy = None;
Expand Down
2 changes: 2 additions & 0 deletions crates/project-model/src/cargo_workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ pub struct CargoConfig {
pub invocation_strategy: InvocationStrategy,
/// Optional path to use instead of `target` when building
pub target_dir_config: TargetDirectoryConfig,
/// Optional path to use instead of `target` when building
pub build_dir_config: TargetDirectoryConfig,
/// Gate `#[test]` behind `#[cfg(test)]`
pub set_test: bool,
/// Load the project without any dependencies
Expand Down
20 changes: 20 additions & 0 deletions crates/rust-analyzer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,15 @@ config_data! {
/// Automatically refresh project info via `cargo metadata` on
/// `Cargo.toml` or `.cargo/config.toml` changes.
cargo_autoreload: bool = true,
/// Optional path to a rust-analyzer specific build directory.
/// Since cargo's build-directory defaults to the target-directory, it should only
/// be needed to set this if a custom build-directory is configured.
/// Otherwise setting `cargo.targetDir` is sufficient to prevent rust-analyzer from
/// locking the `Cargo.lock`.
///
/// Set to `true` to use a subdirectory of the existing build directory or
/// set to a path relative to the workspace to use that path.
cargo_buildDir | rust_analyzerBuildDir: Option<TargetDirectory> = None,
/// Run build scripts (`build.rs`) for more precise code analysis.
cargo_buildScripts_enable: bool = true,
/// Specifies the invocation strategy to use when running the build scripts command.
Expand Down Expand Up @@ -2486,6 +2495,7 @@ impl Config {
extra_args: self.cargo_extraArgs(source_root).clone(),
extra_env: self.cargo_extraEnv(source_root).clone(),
target_dir_config: self.target_dir_from_config(source_root),
build_dir_config: self.build_dir_from_config(source_root),
set_test: *self.cfg_setTest(source_root),
no_deps: *self.cargo_noDeps(source_root),
metadata_extra_args: self.cargo_metadataExtraArgs(source_root).clone(),
Expand Down Expand Up @@ -2578,6 +2588,7 @@ impl Config {
extra_test_bin_args: self.runnables_extraTestBinaryArgs(source_root).clone(),
extra_env: self.extra_env(source_root).clone(),
target_dir_config: self.target_dir_from_config(source_root),
build_dir_config: self.build_dir_from_config(source_root),
set_test: true,
config_path: self.cargo_config_path(source_root),
}
Expand Down Expand Up @@ -2638,6 +2649,7 @@ impl Config {
extra_env: self.check_extra_env(source_root),
config_path: self.cargo_config_path(source_root),
target_dir_config: self.target_dir_from_config(source_root),
build_dir_config: self.build_dir_from_config(source_root),
set_test: *self.cfg_setTest(source_root),
},
ansi_color_output: self.color_diagnostic_output(),
Expand All @@ -2657,6 +2669,14 @@ impl Config {
}
}

fn build_dir_from_config(&self, source_root: Option<SourceRootId>) -> TargetDirectoryConfig {
match &self.cargo_buildDir(source_root) {
Some(TargetDirectory::UseSubdirectory(true)) => TargetDirectoryConfig::UseSubdirectory,
Some(TargetDirectory::UseSubdirectory(false)) | None => TargetDirectoryConfig::None,
Some(TargetDirectory::Directory(dir)) => TargetDirectoryConfig::Directory(dir.clone()),
}
}

pub fn check_on_save(&self, source_root: Option<SourceRootId>) -> bool {
*self.checkOnSave(source_root)
}
Expand Down
7 changes: 7 additions & 0 deletions crates/rust-analyzer/src/flycheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub(crate) struct CargoOptions {
pub(crate) extra_env: FxHashMap<String, Option<String>>,
pub(crate) config_path: Option<AbsPathBuf>,
pub(crate) target_dir_config: TargetDirectoryConfig,
pub(crate) build_dir_config: TargetDirectoryConfig,
}

#[derive(Clone, Debug)]
Expand All @@ -68,6 +69,7 @@ impl CargoOptions {
&self,
cmd: &mut Command,
ws_target_dir: Option<&Utf8Path>,
ws_build_dir: Option<&Utf8Path>,
package_repr: Option<&str>,
toolchain_version: Option<&semver::Version>,
) {
Expand Down Expand Up @@ -114,6 +116,9 @@ impl CargoOptions {
if let Some(target_dir) = self.target_dir_config.target_dir(ws_target_dir) {
cmd.arg("--target-dir").arg(target_dir.as_ref());
}
if let Some(build_dir) = self.build_dir_config.target_dir(ws_build_dir) {
cmd.env("CARGO_BUILD_BUILD_DIR", build_dir.as_ref());

@Veykril Veykril Aug 8, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take it there is no flag equivalent for this? Bit weird to see target dir and build dir being configured differently

View changes since the review

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there is no flag to set the build dir at the moment

}
}
}

Expand Down Expand Up @@ -966,6 +971,7 @@ impl FlycheckActor {
cargo_options.apply_on_command(
&mut cmd,
self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path),
self.ws_build_dir.as_ref().map(Utf8PathBuf::as_path),
package_repr,
self.toolchain_version.as_ref(),
);
Expand Down Expand Up @@ -1186,6 +1192,7 @@ mod tests {
extra_env: FxHashMap::default(),
config_path: None,
target_dir_config: TargetDirectoryConfig::default(),
build_dir_config: TargetDirectoryConfig::default(),
},
ansi_color_output: true,
};
Expand Down
1 change: 1 addition & 0 deletions crates/rust-analyzer/src/handlers/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ pub(crate) fn handle_run_test(
state.config.cargo_test_options(None),
cargo.workspace_root(),
Some(cargo.target_directory().as_ref()),
cargo.build_directory().map(AbsPath::as_ref),
target,
state.test_run_sender.clone(),
ws.toolchain.as_ref(),
Expand Down
2 changes: 2 additions & 0 deletions crates/rust-analyzer/src/test_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ impl CargoTestHandle {
options: CargoOptions,
root: &AbsPath,
ws_target_dir: Option<&Utf8Path>,
ws_build_dir: Option<&Utf8Path>,
test_target: TestTarget,
sender: Sender<CargoTestMessage>,
toolchain_version: Option<&semver::Version>,
Expand Down Expand Up @@ -135,6 +136,7 @@ impl CargoTestHandle {
options.apply_on_command(
&mut cmd,
ws_target_dir,
ws_build_dir,
Some(&test_target.package),
toolchain_version,
);
Expand Down
14 changes: 14 additions & 0 deletions docs/book/src/configuration_generated.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,20 @@ Automatically refresh project info via `cargo metadata` on
`Cargo.toml` or `.cargo/config.toml` changes.


## rust-analyzer.cargo.buildDir {#cargo.buildDir}

Default: `null`

Optional path to a rust-analyzer specific build directory.
Since cargo's build-directory defaults to the target-directory, it should only
be needed to set this if a custom build-directory is configured.
Otherwise setting `cargo.targetDir` is sufficient to prevent rust-analyzer from
locking the `Cargo.lock`.

Set to `true` to use a subdirectory of the existing build directory or
set to a path relative to the workspace to use that path.


## rust-analyzer.cargo.buildScripts.enable {#cargo.buildScripts.enable}

Default: `true`
Expand Down
20 changes: 20 additions & 0 deletions editors/code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,26 @@
}
}
},
{
"title": "Cargo",
"properties": {
"rust-analyzer.cargo.buildDir": {
"markdownDescription": "Optional path to a rust-analyzer specific build directory.\nSince cargo's build-directory defaults to the target-directory, it should only\nbe needed to set this if a custom build-directory is configured.\nOtherwise setting `cargo.targetDir` is sufficient to prevent rust-analyzer from\nlocking the `Cargo.lock`.\n\nSet to `true` to use a subdirectory of the existing build directory or\nset to a path relative to the workspace to use that path.",
"default": null,
"anyOf": [
{
"type": "null"
},
{
"type": "boolean"
},
{
"type": "string"
}
]
}
}
},
{
"title": "Cargo",
"properties": {
Expand Down