mirror of
https://github.com/Noratrieb/cargo-minimize.git
synced 2026-01-14 16:35:01 +01:00
Support --script properly... hopefully
This commit is contained in:
parent
f649289b01
commit
5ac406e650
4 changed files with 184 additions and 53 deletions
189
src/build.rs
189
src/build.rs
|
|
@ -36,6 +36,7 @@ impl Debug for Verify {
|
|||
#[derive(Debug)]
|
||||
struct BuildInner {
|
||||
mode: BuildMode,
|
||||
lint_mode: BuildMode,
|
||||
input_path: PathBuf,
|
||||
verify: Verify,
|
||||
env: Vec<EnvVar>,
|
||||
|
|
@ -49,7 +50,6 @@ enum BuildMode {
|
|||
Cargo {
|
||||
/// May be something like `miri run`.
|
||||
subcommand: Vec<String>,
|
||||
diag_subcommand: Vec<String>,
|
||||
},
|
||||
Script(PathBuf),
|
||||
Rustc,
|
||||
|
|
@ -73,15 +73,24 @@ impl Build {
|
|||
BuildMode::Script(script.clone())
|
||||
} else {
|
||||
let subcommand = split_args(&options.cargo_subcmd);
|
||||
let diag_subcommand = options
|
||||
.diagnostics_cargo_subcmd
|
||||
BuildMode::Cargo { subcommand }
|
||||
};
|
||||
|
||||
let lint_mode = if options.rustc {
|
||||
BuildMode::Rustc
|
||||
} else if let Some(script) = options
|
||||
.script_path_lints
|
||||
.as_ref()
|
||||
.or(options.script_path.as_ref())
|
||||
{
|
||||
BuildMode::Script(script.clone())
|
||||
} else {
|
||||
let subcommand = options
|
||||
.cargo_subcmd_lints
|
||||
.as_deref()
|
||||
.map(split_args)
|
||||
.unwrap_or_else(|| subcommand.clone());
|
||||
BuildMode::Cargo {
|
||||
subcommand,
|
||||
diag_subcommand,
|
||||
}
|
||||
.unwrap_or_else(|| split_args(&options.cargo_subcmd));
|
||||
BuildMode::Cargo { subcommand }
|
||||
};
|
||||
|
||||
let verify = if options.no_verify {
|
||||
|
|
@ -95,6 +104,7 @@ impl Build {
|
|||
Ok(Self {
|
||||
inner: Rc::new(BuildInner {
|
||||
mode,
|
||||
lint_mode,
|
||||
input_path: options.path.clone(),
|
||||
verify,
|
||||
env: options.env.clone(),
|
||||
|
|
@ -126,10 +136,7 @@ impl Build {
|
|||
}
|
||||
|
||||
let (is_ice, output) = match &inner.mode {
|
||||
BuildMode::Cargo {
|
||||
subcommand,
|
||||
diag_subcommand: _,
|
||||
} => {
|
||||
BuildMode::Cargo { subcommand } => {
|
||||
let mut cmd = self.cmd("cargo");
|
||||
|
||||
cmd.args(subcommand);
|
||||
|
|
@ -154,21 +161,6 @@ impl Build {
|
|||
output,
|
||||
)
|
||||
}
|
||||
BuildMode::Script(script_path) => {
|
||||
let mut cmd = self.cmd(script_path);
|
||||
|
||||
cmd.args(&inner.extra_args);
|
||||
|
||||
for env in &inner.env {
|
||||
cmd.env(&env.key, &env.value);
|
||||
}
|
||||
|
||||
let outputs = cmd.output().context("spawning script")?;
|
||||
|
||||
let output = String::from_utf8(outputs.stderr)?;
|
||||
|
||||
(outputs.status.success(), output)
|
||||
}
|
||||
BuildMode::Rustc => {
|
||||
let mut cmd = self.cmd("rustc");
|
||||
cmd.args(["--edition", "2021"]);
|
||||
|
|
@ -194,6 +186,23 @@ impl Build {
|
|||
output,
|
||||
)
|
||||
}
|
||||
BuildMode::Script(script_path) => {
|
||||
let mut cmd = self.cmd(script_path);
|
||||
|
||||
cmd.args(&inner.extra_args);
|
||||
|
||||
for env in &inner.env {
|
||||
cmd.env(&env.key, &env.value);
|
||||
}
|
||||
|
||||
let outputs = cmd
|
||||
.output()
|
||||
.with_context(|| format!("spawning script: `{cmd:?}`"))?;
|
||||
|
||||
let output = String::from_utf8(outputs.stderr)?;
|
||||
|
||||
(outputs.status.success(), output)
|
||||
}
|
||||
};
|
||||
|
||||
let reproduces_issue = match inner.verify {
|
||||
|
|
@ -213,14 +222,30 @@ impl Build {
|
|||
pub fn get_diags(&self) -> Result<(Vec<Diagnostic>, Vec<rustfix::Suggestion>)> {
|
||||
let inner = &self.inner;
|
||||
|
||||
let diags = match &inner.mode {
|
||||
BuildMode::Cargo {
|
||||
subcommand: _,
|
||||
diag_subcommand,
|
||||
} => {
|
||||
fn grab_cargo_diags(output: &str) -> Result<Vec<Diagnostic>> {
|
||||
let messages = serde_json::Deserializer::from_str(output)
|
||||
.into_iter::<CargoJsonCompileMessage>()
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
Ok(messages
|
||||
.into_iter()
|
||||
.filter(|msg| msg.reason == "compiler-message")
|
||||
.flat_map(|msg| msg.message)
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn grab_rustc_diags(output: &str) -> Result<Vec<Diagnostic>> {
|
||||
serde_json::Deserializer::from_str(&output)
|
||||
.into_iter::<Diagnostic>()
|
||||
.collect::<Result<_, _>>()
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
let diags = match &inner.lint_mode {
|
||||
BuildMode::Cargo { subcommand } => {
|
||||
let mut cmd = self.cmd("cargo");
|
||||
|
||||
cmd.args(diag_subcommand);
|
||||
cmd.args(subcommand);
|
||||
|
||||
cmd.arg("--message-format=json");
|
||||
|
||||
|
|
@ -233,15 +258,7 @@ impl Build {
|
|||
let cmd_output = cmd.output()?;
|
||||
let output = String::from_utf8(cmd_output.stdout)?;
|
||||
|
||||
let messages = serde_json::Deserializer::from_str(&output)
|
||||
.into_iter::<CargoJsonCompileMessage>()
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
messages
|
||||
.into_iter()
|
||||
.filter(|msg| msg.reason == "compiler-message")
|
||||
.flat_map(|msg| msg.message)
|
||||
.collect()
|
||||
grab_cargo_diags(&output)?
|
||||
}
|
||||
BuildMode::Rustc => {
|
||||
let mut cmd = self.cmd("rustc");
|
||||
|
|
@ -255,13 +272,31 @@ impl Build {
|
|||
let output = cmd.output()?.stderr;
|
||||
let output = String::from_utf8(output)?;
|
||||
|
||||
let diags = serde_json::Deserializer::from_str(&output)
|
||||
.into_iter::<Diagnostic>()
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
diags
|
||||
grab_rustc_diags(&output)?
|
||||
}
|
||||
BuildMode::Script(script_path) => {
|
||||
let mut cmd = self.cmd(script_path);
|
||||
|
||||
cmd.args(&inner.extra_args);
|
||||
|
||||
for env in &inner.env {
|
||||
cmd.env(&env.key, &env.value);
|
||||
}
|
||||
|
||||
let outputs = cmd
|
||||
.output()
|
||||
.with_context(|| format!("spawning script: `{cmd:?}`"))?;
|
||||
|
||||
let stderr = String::from_utf8(outputs.stderr)?;
|
||||
let stdout = String::from_utf8(outputs.stdout)?;
|
||||
|
||||
let (output, mode) = read_script_output(&stdout, &stderr);
|
||||
|
||||
match mode {
|
||||
LintMode::Rustc => grab_rustc_diags(output)?,
|
||||
LintMode::Cargo => grab_cargo_diags(output)?,
|
||||
}
|
||||
}
|
||||
BuildMode::Script(_) => todo!(),
|
||||
};
|
||||
|
||||
let mut suggestions = Vec::new();
|
||||
|
|
@ -329,3 +364,61 @@ pub struct CargoJsonCompileMessage {
|
|||
fn split_args(s: &str) -> Vec<String> {
|
||||
s.split_whitespace().map(ToString::to_string).collect()
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum LintMode {
|
||||
Rustc,
|
||||
Cargo,
|
||||
}
|
||||
|
||||
fn read_script_output<'a>(stdout: &'a str, stderr: &'a str) -> (&'a str, LintMode) {
|
||||
let is_marked_output = |output: &str| {
|
||||
let first_line = output.lines().next();
|
||||
match first_line {
|
||||
None => None,
|
||||
Some(line) if line.contains("minimize-fmt-cargo") => Some(LintMode::Cargo),
|
||||
Some(line) if line.contains("minimize-fmt-rustc") => Some(LintMode::Rustc),
|
||||
Some(_) => None,
|
||||
}
|
||||
};
|
||||
|
||||
is_marked_output(stdout)
|
||||
.map(|mode| (stdout, mode))
|
||||
.or(is_marked_output(stderr).map(|mode| (stderr, mode)))
|
||||
.unwrap_or_else(|| (stdout, LintMode::Cargo))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::build::LintMode;
|
||||
|
||||
use super::read_script_output;
|
||||
|
||||
#[test]
|
||||
fn script_output_default() {
|
||||
let (output, mode) = read_script_output("uwu", "owo");
|
||||
assert_eq!(output, "uwu");
|
||||
assert_eq!(mode, LintMode::Cargo);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn script_output_rustc_stderr() {
|
||||
let (output, mode) = read_script_output("wrong", "minimize-fmt-rustc");
|
||||
assert_eq!(output, "minimize-fmt-rustc");
|
||||
assert_eq!(mode, LintMode::Rustc);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn script_output_cargo_stderr() {
|
||||
let (output, mode) = read_script_output("wrong", "minimize-fmt-cargo");
|
||||
assert_eq!(output, "minimize-fmt-cargo");
|
||||
assert_eq!(mode, LintMode::Cargo);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn script_output_rustc_stdout() {
|
||||
let (output, mode) = read_script_output("minimize-fmt-rustc", "wrong");
|
||||
assert_eq!(output, "minimize-fmt-rustc");
|
||||
assert_eq!(mode, LintMode::Rustc);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
19
src/lib.rs
19
src/lib.rs
|
|
@ -42,7 +42,7 @@ pub struct Options {
|
|||
/// The cargo subcommand used to get diagnostics like the dead_code lint from the compiler, seperated by whitespace.
|
||||
/// Defaults to the value of `--cargo-subcmd`.
|
||||
#[arg(long)]
|
||||
pub diagnostics_cargo_subcmd: Option<String>,
|
||||
pub cargo_subcmd_lints: Option<String>,
|
||||
|
||||
/// To disable colored output.
|
||||
#[arg(long)]
|
||||
|
|
@ -75,11 +75,19 @@ pub struct Options {
|
|||
#[arg(default_value = "src")]
|
||||
pub path: PathBuf,
|
||||
|
||||
/// NOTE: This is currently broken.
|
||||
/// A path to a script that is run to check whether code reproduces. When it exits with code 0, the
|
||||
/// problem reproduces.
|
||||
/// problem reproduces. If `--script-path-lints` isn't set, this script is also run to get lints.
|
||||
/// For lints, the `MINIMIZE_LINTS` environment variable will be set to `1`.
|
||||
/// The first line of the lint stdout or stderr can be `minimize-fmt-rustc` or `minimize-fmt-cargo` to show whether the rustc or wrapper cargo
|
||||
/// lint format and which output stream is used. Defaults to cargo and stdout.
|
||||
#[arg(long)]
|
||||
pub script_path: Option<PathBuf>,
|
||||
|
||||
/// A path to a script that is run to get lints.
|
||||
/// The first line of stdout or stderr must be `minimize-fmt-rustc` or `minimize-fmt-cargo` to show whether the rustc or wrapper cargo
|
||||
/// lint format and which output stream is used. Defaults to cargo and stdout.
|
||||
#[arg(long)]
|
||||
pub script_path_lints: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -137,10 +145,9 @@ pub fn init_recommended_tracing_subscriber(default_level: Level) {
|
|||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
script_path: None,
|
||||
extra_args: None,
|
||||
cargo_subcmd: "build".into(),
|
||||
diagnostics_cargo_subcmd: None,
|
||||
cargo_subcmd_lints: None,
|
||||
no_color: false,
|
||||
rustc: false,
|
||||
no_verify: false,
|
||||
|
|
@ -148,6 +155,8 @@ impl Default for Options {
|
|||
env: Vec::new(),
|
||||
project_dir: None,
|
||||
path: PathBuf::from("/the/wrong/path/you/need/to/change/it"),
|
||||
script_path: None,
|
||||
script_path_lints: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
3
tests/always_success.sh
Executable file
3
tests/always_success.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
exit 0 # we reproduce! aren't we great?!
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
mod helper;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use helper::run_test;
|
||||
|
|
@ -44,3 +46,27 @@ fn unused() -> Result<()> {
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
fn custom_script_success() -> Result<()> {
|
||||
let script_path = Path::new(file!())
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join("always_success.sh")
|
||||
.canonicalize()?;
|
||||
|
||||
run_test(
|
||||
r##"
|
||||
fn main() {}
|
||||
"##,
|
||||
r##"
|
||||
fn main() {
|
||||
loop {}
|
||||
}
|
||||
"##,
|
||||
|opts| {
|
||||
opts.script_path = Some(script_path);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue