mirror of
https://github.com/Noratrieb/cargo-minimize.git
synced 2026-01-14 16:35:01 +01:00
pwetty
This commit is contained in:
parent
79b69fafb9
commit
64da92ab9e
7 changed files with 113 additions and 31 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -32,6 +32,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"clap",
|
||||
"libc",
|
||||
"owo-colors",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -250,6 +251,12 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
|
|
|
|||
|
|
@ -12,13 +12,14 @@ description = "A tool for minimizing rustc ICEs"
|
|||
[dependencies]
|
||||
anyhow = "1.0.65"
|
||||
clap = { version = "4.0.29", features = ["derive"] }
|
||||
owo-colors = "3.5.0"
|
||||
prettyplease = "0.1.19"
|
||||
proc-macro2 = { version = "1.0.48", features = ["span-locations"] }
|
||||
quote = "1.0.23"
|
||||
rustfix = "0.6.1"
|
||||
serde = { version = "1.0.151", features = ["derive"] }
|
||||
serde_json = "1.0.90"
|
||||
syn = { version = "1.0.101", features = ["full", "visit", "visit-mut"] }
|
||||
syn = { version = "1.0.101", features = ["full", "visit-mut"] }
|
||||
tempfile = "3.3.0"
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
||||
|
|
|
|||
42
src/build.rs
42
src/build.rs
|
|
@ -38,6 +38,7 @@ struct BuildInner {
|
|||
input_path: PathBuf,
|
||||
verify: Verify,
|
||||
env: Vec<EnvVar>,
|
||||
allow_color: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -76,20 +77,24 @@ impl Build {
|
|||
input_path: options.path.clone(),
|
||||
verify,
|
||||
env: options.env.clone(),
|
||||
allow_color: !options.no_color,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(&self) -> Result<BuildResult> {
|
||||
if let Verify::None = self.inner.verify {
|
||||
let inner = &self.inner;
|
||||
|
||||
if let Verify::None = inner.verify {
|
||||
return Ok(BuildResult {
|
||||
reproduces_issue: false,
|
||||
no_verify: true,
|
||||
output: String::new(),
|
||||
allow_color: inner.allow_color,
|
||||
});
|
||||
}
|
||||
|
||||
let (is_ice, output) = match &self.inner.mode {
|
||||
let (is_ice, output) = match &inner.mode {
|
||||
BuildMode::Cargo { args } => {
|
||||
let mut cmd = Command::new("cargo");
|
||||
cmd.args(["build", "--color=always"]);
|
||||
|
|
@ -98,7 +103,7 @@ impl Build {
|
|||
cmd.arg(arg);
|
||||
}
|
||||
|
||||
for env in &self.inner.env {
|
||||
for env in &inner.env {
|
||||
cmd.env(&env.key, &env.value);
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +120,7 @@ impl Build {
|
|||
BuildMode::Script(script_path) => {
|
||||
let mut cmd = Command::new(script_path);
|
||||
|
||||
for env in &self.inner.env {
|
||||
for env in &inner.env {
|
||||
cmd.env(&env.key, &env.value);
|
||||
}
|
||||
|
||||
|
|
@ -128,9 +133,9 @@ impl Build {
|
|||
BuildMode::Rustc => {
|
||||
let mut cmd = Command::new("rustc");
|
||||
cmd.args(["--edition", "2021"]);
|
||||
cmd.arg(&self.inner.input_path);
|
||||
cmd.arg(&inner.input_path);
|
||||
|
||||
for env in &self.inner.env {
|
||||
for env in &inner.env {
|
||||
cmd.env(&env.key, &env.value);
|
||||
}
|
||||
|
||||
|
|
@ -146,7 +151,7 @@ impl Build {
|
|||
}
|
||||
};
|
||||
|
||||
let reproduces_issue = match self.inner.verify {
|
||||
let reproduces_issue = match inner.verify {
|
||||
Verify::None => unreachable!("handled ealier"),
|
||||
Verify::Ice => is_ice,
|
||||
Verify::Custom(func) => func.call(&output),
|
||||
|
|
@ -156,11 +161,14 @@ impl Build {
|
|||
reproduces_issue,
|
||||
no_verify: false,
|
||||
output,
|
||||
allow_color: inner.allow_color,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_diags(&self) -> Result<(Vec<Diagnostic>, Vec<rustfix::Suggestion>)> {
|
||||
let diags = match &self.inner.mode {
|
||||
let inner = &self.inner;
|
||||
|
||||
let diags = match &inner.mode {
|
||||
BuildMode::Cargo { args } => {
|
||||
let mut cmd = Command::new("cargo");
|
||||
cmd.args(["build", "--message-format=json"]);
|
||||
|
|
@ -169,7 +177,7 @@ impl Build {
|
|||
cmd.arg(arg);
|
||||
}
|
||||
|
||||
for env in &self.inner.env {
|
||||
for env in &inner.env {
|
||||
cmd.env(&env.key, &env.value);
|
||||
}
|
||||
|
||||
|
|
@ -189,9 +197,9 @@ impl Build {
|
|||
BuildMode::Rustc => {
|
||||
let mut cmd = std::process::Command::new("rustc");
|
||||
cmd.args(["--edition", "2021", "--error-format=json"]);
|
||||
cmd.arg(&self.inner.input_path);
|
||||
cmd.arg(&inner.input_path);
|
||||
|
||||
for env in &self.inner.env {
|
||||
for env in &inner.env {
|
||||
cmd.env(&env.key, &env.value);
|
||||
}
|
||||
|
||||
|
|
@ -225,6 +233,7 @@ pub struct BuildResult {
|
|||
reproduces_issue: bool,
|
||||
no_verify: bool,
|
||||
output: String,
|
||||
allow_color: bool,
|
||||
}
|
||||
|
||||
impl BuildResult {
|
||||
|
|
@ -245,10 +254,19 @@ impl BuildResult {
|
|||
|
||||
impl Display for BuildResult {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match (self.reproduces_issue, self.no_verify) {
|
||||
use owo_colors::OwoColorize;
|
||||
|
||||
match self.allow_color {
|
||||
false => match (self.reproduces_issue, self.no_verify) {
|
||||
(true, _) => f.write_str("yes"),
|
||||
(false, true) => f.write_str("yes (no-verify)"),
|
||||
(false, false) => f.write_str("no"),
|
||||
},
|
||||
true => match (self.reproduces_issue, self.no_verify) {
|
||||
(true, _) => write!(f, "{}", "yes".green()),
|
||||
(false, true) => write!(f, "{}", "yes (no-verify)".green()),
|
||||
(false, false) => write!(f, "{}", "no".red()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ pub struct Options {
|
|||
#[arg(long)]
|
||||
cargo_args: Option<String>,
|
||||
|
||||
#[arg(long)]
|
||||
no_color: bool,
|
||||
|
||||
#[arg(long)]
|
||||
rustc: bool,
|
||||
#[arg(long)]
|
||||
|
|
@ -74,7 +77,7 @@ pub fn minimize() -> Result<()> {
|
|||
|
||||
let build = build::Build::new(&options);
|
||||
|
||||
let mut minimizer = Minimizer::new_glob_dir(&options.path, build);
|
||||
let mut minimizer = Minimizer::new_glob_dir(options, build);
|
||||
|
||||
minimizer.run_passes([
|
||||
Box::<privatize::Privatize>::default() as Box<dyn Processor>,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
mod files;
|
||||
mod reaper;
|
||||
pub(crate) use self::files::SourceFile;
|
||||
use crate::{build::Build, processor::files::Changes};
|
||||
use self::worklist::Worklist;
|
||||
use crate::{build::Build, processor::files::Changes, Options};
|
||||
use anyhow::{Context, Result};
|
||||
use owo_colors::OwoColorize;
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
collections::{BTreeSet, HashSet},
|
||||
ffi::OsStr,
|
||||
fmt::Debug,
|
||||
mem,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
pub(crate) trait Processor {
|
||||
|
|
@ -47,11 +48,14 @@ pub(crate) enum ProcessState {
|
|||
pub(crate) struct Minimizer {
|
||||
files: Vec<SourceFile>,
|
||||
build: Build,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl Minimizer {
|
||||
pub(crate) fn new_glob_dir(path: &Path, build: Build) -> Self {
|
||||
pub(crate) fn new_glob_dir(options: Options, build: Build) -> Self {
|
||||
let path = &options.path;
|
||||
let walk = walkdir::WalkDir::new(path);
|
||||
|
||||
let files = walk
|
||||
.into_iter()
|
||||
.filter_map(|entry| match entry {
|
||||
|
|
@ -70,7 +74,11 @@ impl Minimizer {
|
|||
})
|
||||
.collect();
|
||||
|
||||
Self { files, build }
|
||||
Self {
|
||||
files,
|
||||
build,
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn run_passes<'a>(
|
||||
|
|
@ -130,7 +138,6 @@ impl Minimizer {
|
|||
) -> Result<()> {
|
||||
let mut checker = PassController::new();
|
||||
loop {
|
||||
debug!(?checker);
|
||||
let file_display = file.path.display();
|
||||
let mut change = file.try_change(changes)?;
|
||||
let mut krate = syn::parse_file(change.before_content())
|
||||
|
|
@ -155,7 +162,15 @@ impl Minimizer {
|
|||
}
|
||||
}
|
||||
ProcessState::NoChange => {
|
||||
if self.options.no_color {
|
||||
info!("{file_display}: After {}: no changes", pass.name());
|
||||
} else {
|
||||
info!(
|
||||
"{file_display}: After {}: {}",
|
||||
pass.name(),
|
||||
"no changes".yellow()
|
||||
);
|
||||
}
|
||||
checker.no_change();
|
||||
}
|
||||
}
|
||||
|
|
@ -197,16 +212,51 @@ enum PassControllerState {
|
|||
committed: BTreeSet<AstPath>,
|
||||
failed: BTreeSet<AstPath>,
|
||||
current: BTreeSet<AstPath>,
|
||||
worklist: Vec<Vec<AstPath>>,
|
||||
worklist: Worklist,
|
||||
},
|
||||
Success,
|
||||
}
|
||||
|
||||
mod worklist {
|
||||
use super::AstPath;
|
||||
|
||||
/// A worklist that ensures that the inner list is never empty.
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Worklist(Vec<Vec<AstPath>>);
|
||||
|
||||
impl Worklist {
|
||||
pub(super) fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
|
||||
pub(super) fn push(&mut self, next: Vec<AstPath>) {
|
||||
if !next.is_empty() {
|
||||
self.0.push(next);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn pop(&mut self) -> Option<Vec<AstPath>> {
|
||||
self.0.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copied from `core` because who needs stable features anyways
|
||||
pub const fn div_ceil(lhs: usize, rhs: usize) -> usize {
|
||||
let d = lhs / rhs;
|
||||
let r = lhs % rhs;
|
||||
if r > 0 && rhs > 0 {
|
||||
d + 1
|
||||
} else {
|
||||
d
|
||||
}
|
||||
}
|
||||
|
||||
fn split_owned<T, From: IntoIterator<Item = T>, A: FromIterator<T>, B: FromIterator<T>>(
|
||||
vec: From,
|
||||
) -> (A, B) {
|
||||
let candidates = vec.into_iter().collect::<Vec<_>>();
|
||||
let half = candidates.len() / 2;
|
||||
let half = div_ceil(candidates.len(), 2);
|
||||
|
||||
let mut candidates = candidates.into_iter();
|
||||
|
||||
|
|
@ -265,21 +315,22 @@ impl PassController {
|
|||
PassControllerState::InitialCollection { candidates } => {
|
||||
let (current, first_worklist_item) = split_owned(mem::take(candidates));
|
||||
|
||||
let mut worklist = Worklist::new();
|
||||
worklist.push(first_worklist_item);
|
||||
|
||||
self.state = PassControllerState::Bisecting {
|
||||
committed: BTreeSet::new(),
|
||||
failed: BTreeSet::new(),
|
||||
current,
|
||||
worklist: vec![first_worklist_item],
|
||||
worklist,
|
||||
};
|
||||
}
|
||||
PassControllerState::Bisecting {
|
||||
committed,
|
||||
committed: _,
|
||||
failed,
|
||||
current,
|
||||
worklist,
|
||||
} => {
|
||||
debug!(?committed, ?current, ?worklist);
|
||||
|
||||
if current.len() == 1 {
|
||||
// We are at a leaf. This is a failure.
|
||||
// FIXME: We should retry the failed ones until a fixpoint is reached.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@ fn unused() {
|
|||
this_is_required_to_error_haha();
|
||||
}
|
||||
fn main() {
|
||||
other::unused();
|
||||
loop {}
|
||||
}
|
||||
mod other;
|
||||
|
|
|
|||
|
|
@ -1 +1,3 @@
|
|||
pub(crate) fn unused() {}
|
||||
pub(crate) fn unused() {
|
||||
loop {}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue