Skip to content

Commit 32fca82

Browse files
committed
Add $manifest_path substitution for cargo override commands
1 parent 59bcbaf commit 32fca82

File tree

10 files changed

+109
-61
lines changed

10 files changed

+109
-61
lines changed

crates/flycheck/src/lib.rs

+39-31
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,9 @@ impl FlycheckHandle {
8989
sender: Box<dyn Fn(Message) + Send>,
9090
config: FlycheckConfig,
9191
workspace_root: AbsPathBuf,
92+
project_file: AbsPathBuf,
9293
) -> FlycheckHandle {
93-
let actor = FlycheckActor::new(id, sender, config, workspace_root);
94+
let actor = FlycheckActor::new(id, sender, config, workspace_root, project_file);
9495
let (sender, receiver) = unbounded::<StateChange>();
9596
let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
9697
.name("Flycheck".to_owned())
@@ -100,8 +101,8 @@ impl FlycheckHandle {
100101
}
101102

102103
/// Schedule a re-start of the cargo check worker.
103-
pub fn restart(&self) {
104-
self.sender.send(StateChange::Restart).unwrap();
104+
pub fn restart(&self, saved_file: Option<AbsPathBuf>) {
105+
self.sender.send(StateChange::Restart { saved_file }).unwrap();
105106
}
106107

107108
/// Stop this cargo check worker.
@@ -152,7 +153,7 @@ pub enum Progress {
152153
}
153154

154155
enum StateChange {
155-
Restart,
156+
Restart { saved_file: Option<AbsPathBuf> },
156157
Cancel,
157158
}
158159

@@ -165,6 +166,8 @@ struct FlycheckActor {
165166
/// Either the workspace root of the workspace we are flychecking,
166167
/// or the project root of the project.
167168
root: AbsPathBuf,
169+
/// The Cargo.toml or rust-project.json file of the project.
170+
project_file: AbsPathBuf,
168171
/// CargoHandle exists to wrap around the communication needed to be able to
169172
/// run `cargo check` without blocking. Currently the Rust standard library
170173
/// doesn't provide a way to read sub-process output without blocking, so we
@@ -184,9 +187,17 @@ impl FlycheckActor {
184187
sender: Box<dyn Fn(Message) + Send>,
185188
config: FlycheckConfig,
186189
workspace_root: AbsPathBuf,
190+
project_file: AbsPathBuf,
187191
) -> FlycheckActor {
188192
tracing::info!(%id, ?workspace_root, "Spawning flycheck");
189-
FlycheckActor { id, sender, config, root: workspace_root, command_handle: None }
193+
FlycheckActor {
194+
id,
195+
sender,
196+
config,
197+
root: workspace_root,
198+
project_file,
199+
command_handle: None,
200+
}
190201
}
191202

192203
fn report_progress(&self, progress: Progress) {
@@ -212,7 +223,7 @@ impl FlycheckActor {
212223
tracing::debug!(flycheck_id = self.id, "flycheck cancelled");
213224
self.cancel_check_process();
214225
}
215-
Event::RequestStateChange(StateChange::Restart) => {
226+
Event::RequestStateChange(StateChange::Restart { saved_file }) => {
216227
// Cancel the previously spawned process
217228
self.cancel_check_process();
218229
while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) {
@@ -222,7 +233,7 @@ impl FlycheckActor {
222233
}
223234
}
224235

225-
let command = self.check_command();
236+
let command = self.check_command(saved_file);
226237
let formatted_command = format!("{:?}", command);
227238

228239
tracing::debug!(?command, "will restart flycheck");
@@ -296,8 +307,10 @@ impl FlycheckActor {
296307
}
297308
}
298309

299-
fn check_command(&self) -> Command {
300-
let (mut cmd, args) = match &self.config {
310+
fn check_command(&self, _saved_file: Option<AbsPathBuf>) -> Command {
311+
// FIXME: Figure out the story for exposing the saved file to the custom flycheck command
312+
// as it can be absent
313+
match &self.config {
301314
FlycheckConfig::CargoCommand {
302315
command,
303316
target_triples,
@@ -312,7 +325,7 @@ impl FlycheckActor {
312325
let mut cmd = Command::new(toolchain::cargo());
313326
cmd.arg(command);
314327
cmd.current_dir(&self.root);
315-
cmd.arg("--workspace");
328+
cmd.args(&["--workspace", "--manifest-path"]).arg(self.project_file.as_os_str());
316329

317330
cmd.arg(if *ansi_color_output {
318331
"--message-format=json-diagnostic-rendered-ansi"
@@ -341,7 +354,8 @@ impl FlycheckActor {
341354
}
342355
}
343356
cmd.envs(extra_env);
344-
(cmd, extra_args)
357+
cmd.args(extra_args);
358+
cmd
345359
}
346360
FlycheckConfig::CustomCommand {
347361
command,
@@ -352,30 +366,24 @@ impl FlycheckActor {
352366
} => {
353367
let mut cmd = Command::new(command);
354368
cmd.envs(extra_env);
369+
args.iter().for_each(|arg| {
370+
match invocation_strategy {
371+
InvocationStrategy::Once => cmd.arg(arg),
372+
InvocationStrategy::PerWorkspace => cmd.arg(arg.replace(
373+
"$manifest_path",
374+
&self.project_file.as_os_str().to_string_lossy(),
375+
)),
376+
};
377+
});
355378

356379
match invocation_location {
357-
InvocationLocation::Workspace => {
358-
match invocation_strategy {
359-
InvocationStrategy::Once => {
360-
cmd.current_dir(&self.root);
361-
}
362-
InvocationStrategy::PerWorkspace => {
363-
// FIXME: cmd.current_dir(&affected_workspace);
364-
cmd.current_dir(&self.root);
365-
}
366-
}
367-
}
368-
InvocationLocation::Root(root) => {
369-
cmd.current_dir(root);
370-
}
371-
}
380+
InvocationLocation::Workspace => cmd.current_dir(&self.root),
381+
InvocationLocation::Root(root) => cmd.current_dir(root),
382+
};
372383

373-
(cmd, args)
384+
cmd
374385
}
375-
};
376-
377-
cmd.args(args);
378-
cmd
386+
}
379387
}
380388

381389
fn send(&self, check_task: Message) {

crates/paths/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,9 @@ impl AbsPath {
231231
pub fn exists(&self) -> bool {
232232
self.0.exists()
233233
}
234+
pub fn with_extension<S: AsRef<OsStr>>(&self, extension: S) -> AbsPathBuf {
235+
AbsPathBuf::try_from(self.0.with_extension(extension)).unwrap()
236+
}
234237
// endregion:delegate-methods
235238
}
236239

crates/project-model/src/build_scripts.rs

+23-5
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,21 @@ impl WorkspaceBuildScripts {
6060
fn build_command(
6161
config: &CargoConfig,
6262
allowed_features: &FxHashSet<String>,
63+
manifest_path: Option<AbsPathBuf>,
6364
) -> io::Result<Command> {
6465
let mut cmd = match config.run_build_script_command.as_deref() {
6566
Some([program, args @ ..]) => {
6667
let mut cmd = Command::new(program);
67-
cmd.args(args);
68+
for arg in args {
69+
if let Some(manifest_path) = &manifest_path {
70+
cmd.arg(arg.replace(
71+
"$manifest_path",
72+
&manifest_path.as_os_str().to_string_lossy(),
73+
));
74+
} else {
75+
cmd.arg(arg);
76+
}
77+
}
6878
cmd
6979
}
7080
_ => {
@@ -139,7 +149,11 @@ impl WorkspaceBuildScripts {
139149
let allowed_features = workspace.workspace_features();
140150

141151
match Self::run_per_ws(
142-
Self::build_command(config, &allowed_features)?,
152+
Self::build_command(
153+
config,
154+
&allowed_features,
155+
Some(workspace.workspace_root().join("Cargo.toml")),
156+
)?,
143157
workspace,
144158
current_dir,
145159
progress,
@@ -149,8 +163,12 @@ impl WorkspaceBuildScripts {
149163
{
150164
// building build scripts failed, attempt to build with --keep-going so
151165
// that we potentially get more build data
152-
let mut cmd = Self::build_command(config, &allowed_features)?;
153-
cmd.args(["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1");
166+
let mut cmd = Self::build_command(
167+
config,
168+
&allowed_features,
169+
Some(workspace.workspace_root().join("Cargo.toml")),
170+
)?;
171+
cmd.args(&["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1");
154172
let mut res = Self::run_per_ws(cmd, workspace, current_dir, progress)?;
155173
res.error = Some(error);
156174
Ok(res)
@@ -177,7 +195,7 @@ impl WorkspaceBuildScripts {
177195
))
178196
}
179197
};
180-
let cmd = Self::build_command(config, &Default::default())?;
198+
let cmd = Self::build_command(config, &Default::default(), None)?;
181199
// NB: Cargo.toml could have been modified between `cargo metadata` and
182200
// `cargo check`. We shouldn't assume that package ids we see here are
183201
// exactly those from `config`.

crates/project-model/src/project_json.rs

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ pub struct ProjectJson {
6565
pub(crate) sysroot: Option<AbsPathBuf>,
6666
/// e.g. `path/to/sysroot/lib/rustlib/src/rust`
6767
pub(crate) sysroot_src: Option<AbsPathBuf>,
68+
/// the folder containing `rust-project.json`)
6869
project_root: AbsPathBuf,
6970
crates: Vec<Crate>,
7071
}

crates/rust-analyzer/src/config.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,10 @@ config_data! {
8181
/// is set.
8282
cargo_buildScripts_invocationLocation: InvocationLocation = "\"workspace\"",
8383
/// Specifies the invocation strategy to use when running the build scripts command.
84-
/// If `per_workspace` is set, the command will be executed for each workspace.
85-
/// If `once` is set, the command will be executed once.
84+
/// - `per_workspace`: Executes the command for each workspace separately.
85+
/// Allows the use of the `$manifest_path` substitution variable in the override command
86+
/// which will be replaced by the path of the workspace's Cargo.toml.
87+
/// - `once`: Executes the command once.
8688
/// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
8789
/// is set.
8890
cargo_buildScripts_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
@@ -164,8 +166,10 @@ config_data! {
164166
/// is set.
165167
check_invocationLocation | checkOnSave_invocationLocation: InvocationLocation = "\"workspace\"",
166168
/// Specifies the invocation strategy to use when running the check command.
167-
/// If `per_workspace` is set, the command will be executed for each workspace.
168-
/// If `once` is set, the command will be executed once.
169+
/// - `per_workspace`: Executes the command for each workspace separately.
170+
/// Allows the use of the `$manifest_path` substitution variable in the override command
171+
/// which will be replaced by the path of the workspace's Cargo.toml or rust-project.json.
172+
/// - `once`: Executes the command once.
169173
/// This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`
170174
/// is set.
171175
check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"",

crates/rust-analyzer/src/handlers/notification.rs

+12-10
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use lsp_types::{
1010
DidOpenTextDocumentParams, DidSaveTextDocumentParams, WorkDoneProgressCancelParams,
1111
};
1212
use triomphe::Arc;
13-
use vfs::{AbsPathBuf, ChangeKind, VfsPath};
13+
use vfs::{AbsPath, AbsPathBuf, ChangeKind, VfsPath};
1414

1515
use crate::{
1616
config::Config,
@@ -132,21 +132,22 @@ pub(crate) fn handle_did_save_text_document(
132132
) -> anyhow::Result<()> {
133133
if let Ok(vfs_path) = from_proto::vfs_path(&params.text_document.uri) {
134134
// Re-fetch workspaces if a workspace related file has changed
135-
if let Some(abs_path) = vfs_path.as_path() {
135+
let abs_path = vfs_path.as_path();
136+
if let Some(abs_path) = abs_path {
136137
if reload::should_refresh_for_change(abs_path, ChangeKind::Modify) {
137138
state
138139
.fetch_workspaces_queue
139140
.request_op(format!("DidSaveTextDocument {abs_path}"), false);
140141
}
141142
}
142143

143-
if !state.config.check_on_save() || run_flycheck(state, vfs_path) {
144+
if !state.config.check_on_save() || run_flycheck(state, &vfs_path, abs_path) {
144145
return Ok(());
145146
}
146147
} else if state.config.check_on_save() {
147148
// No specific flycheck was triggered, so let's trigger all of them.
148149
for flycheck in state.flycheck.iter() {
149-
flycheck.restart();
150+
flycheck.restart(None);
150151
}
151152
}
152153
Ok(())
@@ -232,10 +233,11 @@ pub(crate) fn handle_did_change_watched_files(
232233
Ok(())
233234
}
234235

235-
fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
236+
fn run_flycheck(state: &mut GlobalState, vfs_path: &VfsPath, abs_path: Option<&AbsPath>) -> bool {
236237
let _p = profile::span("run_flycheck");
237238

238-
let file_id = state.vfs.read().0.file_id(&vfs_path);
239+
let file_id = state.vfs.read().0.file_id(vfs_path);
240+
let abs_path = abs_path.map(AbsPath::to_owned);
239241
if let Some(file_id) = file_id {
240242
let world = state.snapshot();
241243
let mut updated = false;
@@ -287,15 +289,15 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
287289
for (id, _) in workspace_ids.clone() {
288290
if id == flycheck.id() {
289291
updated = true;
290-
flycheck.restart();
292+
flycheck.restart(abs_path.clone());
291293
continue;
292294
}
293295
}
294296
}
295297
// No specific flycheck was triggered, so let's trigger all of them.
296298
if !updated {
297299
for flycheck in world.flycheck.iter() {
298-
flycheck.restart();
300+
flycheck.restart(abs_path.clone());
299301
}
300302
}
301303
Ok(())
@@ -330,14 +332,14 @@ pub(crate) fn handle_run_flycheck(
330332
let _p = profile::span("handle_run_flycheck");
331333
if let Some(text_document) = params.text_document {
332334
if let Ok(vfs_path) = from_proto::vfs_path(&text_document.uri) {
333-
if run_flycheck(state, vfs_path) {
335+
if run_flycheck(state, &vfs_path, vfs_path.as_path()) {
334336
return Ok(());
335337
}
336338
}
337339
}
338340
// No specific flycheck was triggered, so let's trigger all of them.
339341
for flycheck in state.flycheck.iter() {
340-
flycheck.restart();
342+
flycheck.restart(None);
341343
}
342344
Ok(())
343345
}

crates/rust-analyzer/src/main_loop.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use std::{
77

88
use always_assert::always;
99
use crossbeam_channel::{select, Receiver};
10-
use flycheck::FlycheckHandle;
1110
use ide_db::base_db::{SourceDatabaseExt, VfsPath};
1211
use lsp_server::{Connection, Notification, Request};
1312
use lsp_types::notification::Notification as _;
@@ -299,7 +298,7 @@ impl GlobalState {
299298
if became_quiescent {
300299
if self.config.check_on_save() {
301300
// Project has loaded properly, kick off initial flycheck
302-
self.flycheck.iter().for_each(FlycheckHandle::restart);
301+
self.flycheck.iter().for_each(|flycheck| flycheck.restart(None));
303302
}
304303
if self.config.prefill_caches() {
305304
self.prime_caches_queue.request_op("became quiescent".to_string(), ());

0 commit comments

Comments
 (0)