Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parallel lints #625

Merged
merged 4 commits into from
Jan 13, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ toml = "0.7.6"
directories = "5.0.1"
sha2 = "0.10.6"
rustc_version = "0.4.0"
rayon = "1.8.0"

[dev-dependencies]
assert_cmd = "2.0"
Expand Down
149 changes: 64 additions & 85 deletions src/check_release.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,19 @@
use std::{collections::BTreeMap, env, io::Write, iter::Peekable, sync::Arc, time::Instant};
use std::{collections::BTreeMap, sync::Arc, time::Instant};

use anyhow::Context;
use clap::crate_version;
use itertools::Itertools;
use rayon::prelude::*;
use termcolor::Color;
use termcolor_output::{colored, colored_ln};
use trustfall::{FieldValue, TransparentValue};
use trustfall::TransparentValue;
use trustfall_rustdoc::{VersionedCrate, VersionedIndexedCrate, VersionedRustdocAdapter};

use crate::{
query::{ActualSemverUpdate, RequiredSemverUpdate, SemverQuery},
CrateReport, GlobalConfig, ReleaseType,
};

type QueryResultItem = BTreeMap<Arc<str>, FieldValue>;

struct QueryWithResults<'a> {
name: &'a str,
results: Peekable<Box<dyn Iterator<Item = QueryResultItem> + 'a>>,
}

impl<'a> QueryWithResults<'a> {
fn new(
name: &'a str,
results: Peekable<Box<dyn Iterator<Item = QueryResultItem> + 'a>>,
) -> Self {
Self { name, results }
}
}

fn classify_semver_version_change(
current_version: Option<&str>,
baseline_version: Option<&str>,
Expand Down Expand Up @@ -115,7 +100,6 @@ pub(super) fn run_check_release(
let current = VersionedIndexedCrate::new(&current_crate);
let previous = VersionedIndexedCrate::new(&baseline_crate);
let adapter = VersionedRustdocAdapter::new(&current, Some(&previous))?;
let mut queries_with_errors: Vec<QueryWithResults> = vec![];

let queries_to_run: Vec<_> = queries
.iter()
Expand All @@ -135,56 +119,58 @@ pub(super) fn run_check_release(
)?;
config
.log_verbose(|config| {
config.shell_status(
"Starting",
format_args!(
"{} checks, {} unnecessary",
queries_to_run.len(),
skipped_queries
),
)
let current_num_threads = rayon::current_num_threads();
if current_num_threads == 1 {
config.shell_status(
"Starting",
format_args!(
"{} checks, {} unnecessary",
queries_to_run.len(),
skipped_queries
),
)
} else {
config.shell_status(
"Starting",
format_args!(
"{} checks, {} unnecessary on {current_num_threads} threads",
queries_to_run.len(),
skipped_queries
),
)
}
})
.expect("print failed");

let queries_start_instant = Instant::now();
let mut all_results = queries_to_run
.par_iter()
.map(|&(query_id, semver_query)| {
let start_instant = std::time::Instant::now();
// trustfall::execute_query(...) -> dyn Iterator (without Send)
// thus the result must be collect()'ed
let results = adapter
.run_query(&semver_query.query, semver_query.arguments.clone())?
.collect_vec();
let time_to_decide = start_instant.elapsed();
Ok((
query_id.as_str(),
semver_query.required_update,
time_to_decide,
results,
))
})
.collect::<anyhow::Result<Vec<_>>>()?;

for (query_id, semver_query) in queries_to_run.iter().copied() {
let category = match semver_query.required_update {
RequiredSemverUpdate::Major => "major",
RequiredSemverUpdate::Minor => "minor",
};
// print PASS/FAIL summaries
all_results.retain(|(query_id, required_update, time_to_decide, results)| {
config
obi1kenobi marked this conversation as resolved.
Show resolved Hide resolved
.log_verbose(|config| {
if config.is_stderr_tty() {
colored!(
config.stderr(),
"{}{}{:>12}{} [{:9}] {:^18} {}",
fg!(Some(Color::Cyan)),
bold!(true),
"Running",
reset!(),
"",
category,
query_id,
)?;
config.stderr().flush()?;
}
Ok(())
})
.expect("print failed");

let start_instant = std::time::Instant::now();
let mut results_iter = adapter
.run_query(&semver_query.query, semver_query.arguments.clone())?
.peekable();
let peeked = results_iter.peek();
let time_to_decide = start_instant.elapsed();

if peeked.is_none() {
config
.log_verbose(|config| {
if config.is_stderr_tty() {
write!(config.stderr(), "\r")?;
}
let category = match required_update {
RequiredSemverUpdate::Major => "major",
RequiredSemverUpdate::Minor => "minor",
};
if results.is_empty() {
colored_ln(config.stderr(), |w| {
colored!(
w,
Expand All @@ -198,17 +184,7 @@ pub(super) fn run_check_release(
query_id,
)
})?;
Ok(())
})
.expect("print failed");
} else {
queries_with_errors.push(QueryWithResults::new(query_id.as_str(), results_iter));

config
.log_verbose(|config| {
if config.is_stderr_tty() {
write!(config.stderr(), "\r")?;
}
} else {
colored_ln(config.stderr(), |w| {
colored!(
w,
Expand All @@ -222,22 +198,25 @@ pub(super) fn run_check_release(
query_id,
)
})?;
Ok(())
})
.expect("print failed");
}
}
}
Ok(())
})
.expect("print failed");

!results.is_empty()
});
let results_with_errors = all_results;

if !queries_with_errors.is_empty() {
if !results_with_errors.is_empty() {
config
.shell_print(
"Checked",
format_args!(
"[{:>8.3}s] {} checks; {} passed, {} failed, {} unnecessary",
queries_start_instant.elapsed().as_secs_f32(),
queries_to_run.len(),
queries_to_run.len() - queries_with_errors.len(),
queries_with_errors.len(),
queries_to_run.len() - results_with_errors.len(),
results_with_errors.len(),
skipped_queries,
),
Color::Red,
Expand All @@ -247,8 +226,8 @@ pub(super) fn run_check_release(

let mut required_versions = vec![];

for query_with_results in queries_with_errors {
let semver_query = &queries[query_with_results.name];
for (query_id, _, _, results) in results_with_errors {
let semver_query = &queries[query_id];
required_versions.push(semver_query.required_update);
config
.log_info(|config| {
Expand Down Expand Up @@ -317,7 +296,7 @@ pub(super) fn run_check_release(
})
.expect("print failed");

for semver_violation_result in query_with_results.results {
for semver_violation_result in results {
let pretty_result: BTreeMap<Arc<str>, TransparentValue> = semver_violation_result
.into_iter()
.map(|(k, v)| (k, v.into()))
Expand Down
Loading