Skip to content

Commit aa6d45b

Browse files
committed
Add $manifest_path substitution for cargo override commands
1 parent d022e0e commit aa6d45b

File tree

7 files changed

+102
-71
lines changed

7 files changed

+102
-71
lines changed

crates/flycheck/src/lib.rs

+44-52
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,9 @@ impl FlycheckHandle {
8585
sender: Box<dyn Fn(Message) + Send>,
8686
config: FlycheckConfig,
8787
workspace_root: AbsPathBuf,
88+
project_file: AbsPathBuf,
8889
) -> FlycheckHandle {
89-
let actor = FlycheckActor::new(id, sender, config, workspace_root);
90+
let actor = FlycheckActor::new(id, sender, config, workspace_root, project_file);
9091
let (sender, receiver) = unbounded::<Restart>();
9192
let thread = jod_thread::Builder::new()
9293
.name("Flycheck".to_owned())
@@ -96,8 +97,8 @@ impl FlycheckHandle {
9697
}
9798

9899
/// Schedule a re-start of the cargo check worker.
99-
pub fn restart(&self) {
100-
self.sender.send(Restart::Yes).unwrap();
100+
pub fn restart(&self, saved_file: Option<AbsPathBuf>) {
101+
self.sender.send(Restart::Yes { saved_file }).unwrap();
101102
}
102103

103104
/// Stop this cargo check worker.
@@ -148,7 +149,7 @@ pub enum Progress {
148149
}
149150

150151
enum Restart {
151-
Yes,
152+
Yes { saved_file: Option<AbsPathBuf> },
152153
No,
153154
}
154155

@@ -161,12 +162,14 @@ struct FlycheckActor {
161162
/// Either the workspace root of the workspace we are flychecking,
162163
/// or the project root of the project.
163164
root: AbsPathBuf,
165+
/// The Cargo.toml or rust-project.json file of the project.
166+
project_file: AbsPathBuf,
164167
/// CargoHandle exists to wrap around the communication needed to be able to
165168
/// run `cargo check` without blocking. Currently the Rust standard library
166169
/// doesn't provide a way to read sub-process output without blocking, so we
167170
/// have to wrap sub-processes output handling in a thread and pass messages
168171
/// back over a channel.
169-
cargo_handle: Option<CargoHandle>,
172+
cargo_handle: Option<(CargoHandle, String)>,
170173
}
171174

172175
enum Event {
@@ -180,17 +183,18 @@ impl FlycheckActor {
180183
sender: Box<dyn Fn(Message) + Send>,
181184
config: FlycheckConfig,
182185
workspace_root: AbsPathBuf,
186+
project_file: AbsPathBuf,
183187
) -> FlycheckActor {
184188
tracing::info!(%id, ?workspace_root, "Spawning flycheck");
185-
FlycheckActor { id, sender, config, root: workspace_root, cargo_handle: None }
189+
FlycheckActor { id, sender, config, root: workspace_root, project_file, cargo_handle: None }
186190
}
187191

188192
fn report_progress(&self, progress: Progress) {
189193
self.send(Message::Progress { id: self.id, progress });
190194
}
191195

192196
fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
193-
let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver);
197+
let check_chan = self.cargo_handle.as_ref().map(|(cargo, _)| &cargo.receiver);
194198
if let Ok(msg) = inbox.try_recv() {
195199
// give restarts a preference so check outputs don't block a restart or stop
196200
return Some(Event::Restart(msg));
@@ -204,10 +208,8 @@ impl FlycheckActor {
204208
fn run(mut self, inbox: Receiver<Restart>) {
205209
'event: while let Some(event) = self.next_event(&inbox) {
206210
match event {
207-
Event::Restart(Restart::No) => {
208-
self.cancel_check_process();
209-
}
210-
Event::Restart(Restart::Yes) => {
211+
Event::Restart(Restart::No) => self.cancel_check_process(),
212+
Event::Restart(Restart::Yes { saved_file }) => {
211213
// Cancel the previously spawned process
212214
self.cancel_check_process();
213215
while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) {
@@ -217,22 +219,18 @@ impl FlycheckActor {
217219
}
218220
}
219221

220-
let command = self.check_command();
221-
tracing::debug!(?command, "will restart flycheck");
222+
let command = self.check_command(saved_file);
223+
let command_string = format!("{:?}", command);
224+
tracing::debug!(?command, "restarting flycheck");
222225
match CargoHandle::spawn(command) {
223226
Ok(cargo_handle) => {
224-
tracing::debug!(
225-
command = ?self.check_command(),
226-
"did restart flycheck"
227-
);
228-
self.cargo_handle = Some(cargo_handle);
227+
self.cargo_handle = Some((cargo_handle, command_string));
229228
self.report_progress(Progress::DidStart);
230229
}
231230
Err(error) => {
232231
self.report_progress(Progress::DidFailToRestart(format!(
233232
"Failed to run the following command: {:?} error={}",
234-
self.check_command(),
235-
error
233+
command_string, error
236234
)));
237235
}
238236
}
@@ -241,12 +239,12 @@ impl FlycheckActor {
241239
tracing::debug!(flycheck_id = self.id, "flycheck finished");
242240

243241
// Watcher finished
244-
let cargo_handle = self.cargo_handle.take().unwrap();
242+
let (cargo_handle, cmd_string) = self.cargo_handle.take().unwrap();
245243
let res = cargo_handle.join();
246244
if res.is_err() {
247245
tracing::error!(
248-
"Flycheck failed to run the following command: {:?}",
249-
self.check_command()
246+
command = cmd_string,
247+
"Flycheck failed to run the following command"
250248
);
251249
}
252250
self.report_progress(Progress::DidFinish(res));
@@ -271,18 +269,17 @@ impl FlycheckActor {
271269
}
272270

273271
fn cancel_check_process(&mut self) {
274-
if let Some(cargo_handle) = self.cargo_handle.take() {
275-
tracing::debug!(
276-
command = ?self.check_command(),
277-
"did cancel flycheck"
278-
);
272+
if let Some((cargo_handle, cmd_string)) = self.cargo_handle.take() {
273+
tracing::debug!(command = cmd_string, "did cancel flycheck");
279274
cargo_handle.cancel();
280275
self.report_progress(Progress::DidCancel);
281276
}
282277
}
283278

284-
fn check_command(&self) -> Command {
285-
let (mut cmd, args) = match &self.config {
279+
fn check_command(&self, _saved_file: Option<AbsPathBuf>) -> Command {
280+
// FIXME: Figure out the story for exposing the saved file to the custom flycheck command
281+
// as it can be absent
282+
match &self.config {
286283
FlycheckConfig::CargoCommand {
287284
command,
288285
target_triple,
@@ -297,7 +294,7 @@ impl FlycheckActor {
297294
cmd.arg(command);
298295
cmd.current_dir(&self.root);
299296
cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
300-
.arg(self.root.join("Cargo.toml").as_os_str());
297+
.arg(self.project_file.as_os_str());
301298

302299
if let Some(target) = target_triple {
303300
cmd.args(&["--target", target.as_str()]);
@@ -317,7 +314,8 @@ impl FlycheckActor {
317314
}
318315
}
319316
cmd.envs(extra_env);
320-
(cmd, extra_args)
317+
cmd.args(extra_args);
318+
cmd
321319
}
322320
FlycheckConfig::CustomCommand {
323321
command,
@@ -328,30 +326,24 @@ impl FlycheckActor {
328326
} => {
329327
let mut cmd = Command::new(command);
330328
cmd.envs(extra_env);
329+
args.iter().for_each(|arg| {
330+
match invocation_strategy {
331+
InvocationStrategy::Once => cmd.arg(arg),
332+
InvocationStrategy::PerWorkspace => cmd.arg(arg.replace(
333+
"$manifest_path",
334+
&self.project_file.as_os_str().to_string_lossy(),
335+
)),
336+
};
337+
});
331338

332339
match invocation_location {
333-
InvocationLocation::Workspace => {
334-
match invocation_strategy {
335-
InvocationStrategy::Once => {
336-
cmd.current_dir(&self.root);
337-
}
338-
InvocationStrategy::PerWorkspace => {
339-
// FIXME: cmd.current_dir(&affected_workspace);
340-
cmd.current_dir(&self.root);
341-
}
342-
}
343-
}
344-
InvocationLocation::Root(root) => {
345-
cmd.current_dir(root);
346-
}
347-
}
340+
InvocationLocation::Workspace => cmd.current_dir(&self.root),
341+
InvocationLocation::Root(root) => cmd.current_dir(root),
342+
};
348343

349-
(cmd, args)
344+
cmd
350345
}
351-
};
352-
353-
cmd.args(args);
354-
cmd
346+
}
355347
}
356348

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

crates/paths/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ impl AbsPath {
208208
pub fn exists(&self) -> bool {
209209
self.0.exists()
210210
}
211+
pub fn with_extension<S: AsRef<OsStr>>(&self, extension: S) -> AbsPathBuf {
212+
AbsPathBuf::try_from(self.0.with_extension(extension)).unwrap()
213+
}
211214
// endregion:delegate-methods
212215
}
213216

crates/project-model/src/build_scripts.rs

+25-5
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,23 @@ impl BuildScriptOutput {
5656
}
5757

5858
impl WorkspaceBuildScripts {
59-
fn build_command(config: &CargoConfig) -> io::Result<Command> {
59+
fn build_command(
60+
config: &CargoConfig,
61+
manifest_path: Option<AbsPathBuf>,
62+
) -> io::Result<Command> {
6063
let mut cmd = match config.run_build_script_command.as_deref() {
6164
Some([program, args @ ..]) => {
6265
let mut cmd = Command::new(program);
63-
cmd.args(args);
66+
for arg in args {
67+
if let Some(manifest_path) = &manifest_path {
68+
cmd.arg(arg.replace(
69+
"$manifest_path",
70+
&manifest_path.as_os_str().to_string_lossy(),
71+
));
72+
} else {
73+
cmd.arg(arg);
74+
}
75+
}
6476
cmd
6577
}
6678
_ => {
@@ -126,13 +138,21 @@ impl WorkspaceBuildScripts {
126138
}
127139
.as_ref();
128140

129-
match Self::run_per_ws(Self::build_command(config)?, workspace, current_dir, progress) {
141+
match Self::run_per_ws(
142+
Self::build_command(config, Some(workspace.workspace_root().join("Cargo.toml")))?,
143+
workspace,
144+
current_dir,
145+
progress,
146+
) {
130147
Ok(WorkspaceBuildScripts { error: Some(error), .. })
131148
if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_62) =>
132149
{
133150
// building build scripts failed, attempt to build with --keep-going so
134151
// that we potentially get more build data
135-
let mut cmd = Self::build_command(config)?;
152+
let mut cmd = Self::build_command(
153+
config,
154+
Some(workspace.workspace_root().join("Cargo.toml")),
155+
)?;
136156
cmd.args(&["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1");
137157
let mut res = Self::run_per_ws(cmd, workspace, current_dir, progress)?;
138158
res.error = Some(error);
@@ -160,7 +180,7 @@ impl WorkspaceBuildScripts {
160180
))
161181
}
162182
};
163-
let cmd = Self::build_command(config)?;
183+
let cmd = Self::build_command(config, None)?;
164184
// NB: Cargo.toml could have been modified between `cargo metadata` and
165185
// `cargo check`. We shouldn't assume that package ids we see here are
166186
// exactly those from `config`.

crates/project-model/src/project_json.rs

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub struct ProjectJson {
2121
pub(crate) sysroot: Option<AbsPathBuf>,
2222
/// e.g. `path/to/sysroot/lib/rustlib/src/rust`
2323
pub(crate) sysroot_src: Option<AbsPathBuf>,
24+
/// the folder containing `rust-project.json`)
2425
project_root: AbsPathBuf,
2526
crates: Vec<Crate>,
2627
}

crates/rust-analyzer/src/config.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,10 @@ config_data! {
7777
/// is set.
7878
cargo_buildScripts_invocationLocation: InvocationLocation = "\"workspace\"",
7979
/// Specifies the invocation strategy to use when running the build scripts command.
80-
/// If `per_workspace` is set, the command will be executed for each workspace.
81-
/// If `once` is set, the command will be executed once.
80+
/// - `per_workspace`: Executes the command for each workspace separately.
81+
/// Allows the use of the `$manifest_path` substitution variable in the override command
82+
/// which will be replaced by the path of the workspace's Cargo.toml.
83+
/// - `once`: Executes the command once.
8284
/// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
8385
/// is set.
8486
cargo_buildScripts_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
@@ -144,8 +146,10 @@ config_data! {
144146
/// is set.
145147
checkOnSave_invocationLocation: InvocationLocation = "\"workspace\"",
146148
/// Specifies the invocation strategy to use when running the checkOnSave command.
147-
/// If `per_workspace` is set, the command will be executed for each workspace.
148-
/// If `once` is set, the command will be executed once.
149+
/// - `per_workspace`: Executes the command for each workspace separately.
150+
/// Allows the use of the `$manifest_path` substitution variable in the override command
151+
/// which will be replaced by the path of the workspace's Cargo.toml or rust-project.json.
152+
/// - `once`: Executes the command once.
149153
/// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
150154
/// is set.
151155
checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"",

crates/rust-analyzer/src/main_loop.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use std::{
99

1010
use always_assert::always;
1111
use crossbeam_channel::{select, Receiver};
12-
use flycheck::FlycheckHandle;
1312
use ide_db::base_db::{SourceDatabaseExt, VfsPath};
1413
use itertools::Itertools;
1514
use lsp_server::{Connection, Notification, Request};
@@ -288,7 +287,7 @@ impl GlobalState {
288287

289288
if became_quiescent {
290289
// Project has loaded properly, kick off initial flycheck
291-
self.flycheck.iter().for_each(FlycheckHandle::restart);
290+
self.flycheck.iter().for_each(|flycheck| flycheck.restart(None));
292291
if self.config.prefill_caches() {
293292
self.prime_caches_queue.request_op("became quiescent".to_string());
294293
}
@@ -783,12 +782,15 @@ impl GlobalState {
783782
.on::<lsp_types::notification::DidSaveTextDocument>(|this, params| {
784783
if let Ok(vfs_path) = from_proto::vfs_path(&params.text_document.uri) {
785784
// Re-fetch workspaces if a workspace related file has changed
786-
if let Some(abs_path) = vfs_path.as_path() {
785+
let saved_file_path = if let Some(abs_path) = vfs_path.as_path() {
787786
if reload::should_refresh_for_change(&abs_path, ChangeKind::Modify) {
788787
this.fetch_workspaces_queue
789788
.request_op(format!("DidSaveTextDocument {}", abs_path.display()));
790789
}
791-
}
790+
Some(abs_path.to_path_buf())
791+
} else {
792+
None
793+
};
792794

793795
let file_id = this.vfs.read().0.file_id(&vfs_path);
794796
if let Some(file_id) = file_id {
@@ -848,15 +850,15 @@ impl GlobalState {
848850
for (id, _) in workspace_ids.clone() {
849851
if id == flycheck.id() {
850852
updated = true;
851-
flycheck.restart();
853+
flycheck.restart(saved_file_path.clone());
852854
continue;
853855
}
854856
}
855857
}
856858
// No specific flycheck was triggered, so let's trigger all of them.
857859
if !updated {
858860
for flycheck in world.flycheck.iter() {
859-
flycheck.restart();
861+
flycheck.restart(saved_file_path.clone());
860862
}
861863
}
862864
Ok(())
@@ -872,7 +874,7 @@ impl GlobalState {
872874

873875
// No specific flycheck was triggered, so let's trigger all of them.
874876
for flycheck in this.flycheck.iter() {
875-
flycheck.restart();
877+
flycheck.restart(None);
876878
}
877879
Ok(())
878880
})?

0 commit comments

Comments
 (0)