Skip to content

Commit

Permalink
{manifest-path} interpolation
Browse files Browse the repository at this point in the history
  • Loading branch information
Veykril committed Sep 26, 2022
1 parent b2a20da commit aba8ad7
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 103 deletions.
69 changes: 32 additions & 37 deletions crates/flycheck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use std::{
fmt, io,
path::Path,
process::{ChildStderr, ChildStdout, Command, Stdio},
time::Duration,
};
Expand All @@ -25,7 +24,6 @@ pub use cargo_metadata::diagnostic::{
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum InvocationStrategy {
OnceInRoot,
PerWorkspaceWithManifestPath,
#[default]
PerWorkspace,
}
Expand Down Expand Up @@ -153,7 +151,9 @@ struct FlycheckActor {
id: usize,
sender: Box<dyn Fn(Message) + Send>,
config: FlycheckConfig,
workspace_root: AbsPathBuf,
/// Either the workspace root of the workspace we are flychecking,
/// or the project root of the project.
root: AbsPathBuf,
/// CargoHandle exists to wrap around the communication needed to be able to
/// run `cargo check` without blocking. Currently the Rust standard library
/// doesn't provide a way to read sub-process output without blocking, so we
Expand All @@ -175,7 +175,7 @@ impl FlycheckActor {
workspace_root: AbsPathBuf,
) -> FlycheckActor {
tracing::info!(%id, ?workspace_root, "Spawning flycheck");
FlycheckActor { id, sender, config, workspace_root, cargo_handle: None }
FlycheckActor { id, sender, config, root: workspace_root, cargo_handle: None }
}

fn report_progress(&self, progress: Progress) {
Expand All @@ -201,20 +201,7 @@ impl FlycheckActor {
self.cancel_check_process();
while let Ok(_) = inbox.recv_timeout(Duration::from_millis(50)) {}

let mut command = self.check_command();
let invocation_strategy = self.invocation_strategy();
match invocation_strategy {
InvocationStrategy::OnceInRoot => (),
InvocationStrategy::PerWorkspaceWithManifestPath => {
command.arg("--manifest-path");
command.arg(<_ as AsRef<Path>>::as_ref(
&self.workspace_root.join("Cargo.toml"),
));
}
InvocationStrategy::PerWorkspace => {
command.current_dir(&self.workspace_root);
}
}
let command = self.check_command();
tracing::debug!(?command, "will restart flycheck");
match CargoHandle::spawn(command) {
Ok(cargo_handle) => {
Expand Down Expand Up @@ -256,7 +243,7 @@ impl FlycheckActor {
CargoMessage::Diagnostic(msg) => {
self.send(Message::AddDiagnostic {
id: self.id,
workspace_root: self.workspace_root.clone(),
workspace_root: self.root.clone(),
diagnostic: msg,
});
}
Expand All @@ -278,15 +265,8 @@ impl FlycheckActor {
}
}

fn invocation_strategy(&self) -> InvocationStrategy {
match self.config {
FlycheckConfig::CargoCommand { invocation_strategy, .. }
| FlycheckConfig::CustomCommand { invocation_strategy, .. } => invocation_strategy,
}
}

fn check_command(&self) -> Command {
let mut cmd = match &self.config {
let (mut cmd, args, invocation_strategy) = match &self.config {
FlycheckConfig::CargoCommand {
command,
target_triple,
Expand All @@ -296,13 +276,11 @@ impl FlycheckActor {
extra_args,
features,
extra_env,
invocation_strategy: _,
invocation_strategy,
} => {
let mut cmd = Command::new(toolchain::cargo());
cmd.arg(command);
cmd.current_dir(&self.workspace_root);
cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
.arg(self.workspace_root.join("Cargo.toml").as_os_str());
cmd.args(&["--workspace", "--message-format=json"]);

if let Some(target) = target_triple {
cmd.args(&["--target", target.as_str()]);
Expand All @@ -321,18 +299,35 @@ impl FlycheckActor {
cmd.arg(features.join(" "));
}
}
cmd.args(extra_args);
cmd.envs(extra_env);
cmd
(cmd, extra_args, invocation_strategy)
}
FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy: _ } => {
FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => {
let mut cmd = Command::new(command);
cmd.args(args);
cmd.envs(extra_env);
cmd
(cmd, args, invocation_strategy)
}
};
cmd.current_dir(&self.workspace_root);
if let InvocationStrategy::PerWorkspace = invocation_strategy {
let mut with_manifest_path = false;
for arg in args {
if let Some(_) = arg.find("$manifest_path") {
with_manifest_path = true;
cmd.arg(arg.replace(
"$manifest_path",
&self.root.join("Cargo.toml").display().to_string(),
));
} else {
cmd.arg(arg);
}
}

if !with_manifest_path {
cmd.current_dir(&self.root);
}
} else {
cmd.args(args);
}
cmd
}

Expand Down
66 changes: 45 additions & 21 deletions crates/project-model/src/build_scripts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,37 @@ impl BuildScriptOutput {
}

impl WorkspaceBuildScripts {
fn build_command(config: &CargoConfig) -> io::Result<Command> {
fn build_command(
config: &CargoConfig,
workspace_root: Option<&path::Path>,
) -> io::Result<Command> {
let mut cmd = match config.run_build_script_command.as_deref() {
Some([program, args @ ..]) => {
let mut cmd = Command::new(program);
cmd.args(args);

// FIXME: strategy and workspace root are coupled, express that in code
if let (InvocationStrategy::PerWorkspace, Some(workspace_root)) =
(config.invocation_strategy, workspace_root)
{
let mut with_manifest_path = false;
for arg in args {
if let Some(_) = arg.find("$manifest_path") {
with_manifest_path = true;
cmd.arg(arg.replace(
"$manifest_path",
&workspace_root.join("Cargo.toml").display().to_string(),
));
} else {
cmd.arg(arg);
}
}

if !with_manifest_path {
cmd.current_dir(workspace_root);
}
} else {
cmd.args(args);
}
cmd
}
_ => {
Expand Down Expand Up @@ -90,9 +116,15 @@ impl WorkspaceBuildScripts {
}
}
}

if let Some(workspace_root) = workspace_root {
cmd.current_dir(workspace_root);
}

cmd
}
};

cmd.envs(&config.extra_env);
if config.wrap_rustc_in_build_scripts {
// Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
Expand All @@ -115,15 +147,21 @@ impl WorkspaceBuildScripts {
) -> io::Result<WorkspaceBuildScripts> {
const RUST_1_62: Version = Version::new(1, 62, 0);

match Self::run_per_ws(Self::build_command(config)?, config, workspace, progress) {
let workspace_root: &path::Path = &workspace.workspace_root().as_ref();

match Self::run_per_ws(
Self::build_command(config, Some(workspace_root))?,
workspace,
progress,
) {
Ok(WorkspaceBuildScripts { error: Some(error), .. })
if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_62) =>
{
// building build scripts failed, attempt to build with --keep-going so
// that we potentially get more build data
let mut cmd = Self::build_command(config)?;
let mut cmd = Self::build_command(config, Some(workspace_root))?;
cmd.args(&["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1");
let mut res = Self::run_per_ws(cmd, config, workspace, progress)?;
let mut res = Self::run_per_ws(cmd, workspace, progress)?;
res.error = Some(error);
Ok(res)
}
Expand All @@ -139,7 +177,7 @@ impl WorkspaceBuildScripts {
progress: &dyn Fn(String),
) -> io::Result<Vec<WorkspaceBuildScripts>> {
assert_eq!(config.invocation_strategy, InvocationStrategy::OnceInRoot);
let cmd = Self::build_command(config)?;
let cmd = Self::build_command(config, None)?;
// NB: Cargo.toml could have been modified between `cargo metadata` and
// `cargo check`. We shouldn't assume that package ids we see here are
// exactly those from `config`.
Expand Down Expand Up @@ -188,24 +226,10 @@ impl WorkspaceBuildScripts {
}

fn run_per_ws(
mut cmd: Command,
config: &CargoConfig,
cmd: Command,
workspace: &CargoWorkspace,
progress: &dyn Fn(String),
) -> io::Result<WorkspaceBuildScripts> {
let workspace_root: &path::Path = &workspace.workspace_root().as_ref();

match config.invocation_strategy {
InvocationStrategy::OnceInRoot => (),
InvocationStrategy::PerWorkspaceWithManifestPath => {
cmd.arg("--manifest-path");
cmd.arg(workspace_root.join("Cargo.toml"));
}
InvocationStrategy::PerWorkspace => {
cmd.current_dir(workspace_root);
}
}

let mut res = WorkspaceBuildScripts::default();
let outputs = &mut res.outputs;
// NB: Cargo.toml could have been modified between `cargo metadata` and
Expand Down
3 changes: 1 addition & 2 deletions crates/project-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,9 @@ fn utf8_stdout(mut cmd: Command) -> Result<String> {
Ok(stdout.trim().to_string())
}

#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum InvocationStrategy {
OnceInRoot,
PerWorkspaceWithManifestPath,
#[default]
PerWorkspace,
}
4 changes: 1 addition & 3 deletions crates/project-model/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,9 +314,7 @@ impl ProjectWorkspace {
config: &CargoConfig,
progress: &dyn Fn(String),
) -> Vec<Result<WorkspaceBuildScripts>> {
if let InvocationStrategy::PerWorkspaceWithManifestPath | InvocationStrategy::PerWorkspace =
config.invocation_strategy
{
if let InvocationStrategy::PerWorkspace = config.invocation_strategy {
return workspaces.iter().map(|it| it.run_build_scripts(config, progress)).collect();
}

Expand Down
38 changes: 18 additions & 20 deletions crates/rust-analyzer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,15 @@ config_data! {
/// 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.
/// If `per_workspace_with_manifest_path` is set, the command will be executed for each
/// workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and
/// the command will be executed from the project root.
/// If `per_workspace` is set, the command will be executed for each workspace and the
/// command will be executed from the corresponding workspace root.
/// If `per_workspace` is set, the command will be executed for each workspace and all
/// occurrences of `$manifest_path` in the command will be replaced by the corresponding
/// manifest path of the workspace that the command is being invoked for. If interpolation
/// for the manifest path happens at least once, the commands will be executed from the
/// project root, otherwise the commands will be executed from the corresponding workspace
/// root.
/// If `once_in_root` is set, the command will be executed once in the project root.
/// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
/// is set.
cargo_buildScripts_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
/// Override the command rust-analyzer uses to run build scripts and
/// build procedural macros. The command is required to output json
Expand Down Expand Up @@ -126,12 +129,15 @@ config_data! {
/// Set to `"all"` to pass `--all-features` to Cargo.
checkOnSave_features: Option<CargoFeaturesDef> = "null",
/// Specifies the invocation strategy to use when running the checkOnSave command.
/// If `per_workspace_with_manifest_path` is set, the command will be executed for each
/// workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and
/// the command will be executed from the project root.
/// If `per_workspace` is set, the command will be executed for each workspace and the
/// command will be executed from the corresponding workspace root.
/// If `per_workspace` is set, the command will be executed for each workspace and all
/// occurrences of `$manifest_path` in the command will be replaced by the corresponding
/// manifest path of the workspace that the command is being invoked for. If interpolation
/// for the manifest path happens at least once, the commands will be executed from the
/// project root, otherwise the commands will be executed from the corresponding workspace
/// root.
/// If `once_in_root` is set, the command will be executed once in the project root.
/// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
/// is set.
checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
/// Whether to pass `--no-default-features` to Cargo. Defaults to
/// `#rust-analyzer.cargo.noDefaultFeatures#`.
Expand Down Expand Up @@ -1060,9 +1066,6 @@ impl Config {
wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper,
invocation_strategy: match self.data.cargo_buildScripts_invocationStrategy {
InvocationStrategy::OnceInRoot => project_model::InvocationStrategy::OnceInRoot,
InvocationStrategy::PerWorkspaceWithManifestPath => {
project_model::InvocationStrategy::PerWorkspaceWithManifestPath
}
InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace,
},
run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(),
Expand Down Expand Up @@ -1090,9 +1093,6 @@ impl Config {
}
let invocation_strategy = match self.data.checkOnSave_invocationStrategy {
InvocationStrategy::OnceInRoot => flycheck::InvocationStrategy::OnceInRoot,
InvocationStrategy::PerWorkspaceWithManifestPath => {
flycheck::InvocationStrategy::PerWorkspaceWithManifestPath
}
InvocationStrategy::PerWorkspace => flycheck::InvocationStrategy::PerWorkspace,
};
let flycheck_config = match &self.data.checkOnSave_overrideCommand {
Expand Down Expand Up @@ -1609,7 +1609,6 @@ enum CargoFeaturesDef {
#[serde(rename_all = "snake_case")]
enum InvocationStrategy {
OnceInRoot,
PerWorkspaceWithManifestPath,
PerWorkspace,
}

Expand Down Expand Up @@ -2029,10 +2028,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
},
"InvocationStrategy" => set! {
"type": "string",
"enum": ["per_workspace", "per_workspace_with_manifest_path", "once_in_root"],
"enum": ["per_workspace", "once_in_root"],
"enumDescriptions": [
"The command will be executed for each workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and the command will be executed from the project root.",
"The command will be executed for each workspace and the command will be executed from the corresponding workspace root.",
"The command will be executed for each workspace and `{manifest-path}` usages will be interpolated with the corresponding workspace manifests. If `{manifest-path}` is used, the commands will be executed in the project root, otherwise in the corresponding workspace roots.",
"The command will be executed once in the project root."
],
},
Expand Down
3 changes: 1 addition & 2 deletions crates/rust-analyzer/src/reload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,8 +474,7 @@ impl GlobalState {
config.clone(),
self.config.root_path().clone(),
)],
flycheck::InvocationStrategy::PerWorkspaceWithManifestPath
| flycheck::InvocationStrategy::PerWorkspace => {
flycheck::InvocationStrategy::PerWorkspace => {
self.workspaces
.iter()
.enumerate()
Expand Down
Loading

0 comments on commit aba8ad7

Please sign in to comment.