feat: implement basic suggest-tests tool
This commit is contained in:
parent
7cd6f55323
commit
1e95cddc74
13
Cargo.lock
13
Cargo.lock
@ -3459,9 +3459,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.16.0"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "opener"
|
||||
@ -6097,6 +6097,15 @@ version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "suggest-tests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"build_helper",
|
||||
"glob",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.102"
|
||||
|
@ -44,6 +44,7 @@ members = [
|
||||
"src/tools/lld-wrapper",
|
||||
"src/tools/collect-license-metadata",
|
||||
"src/tools/generate-copyright",
|
||||
"src/tools/suggest-tests",
|
||||
]
|
||||
|
||||
exclude = [
|
||||
|
@ -591,6 +591,7 @@ pub enum Kind {
|
||||
Install,
|
||||
Run,
|
||||
Setup,
|
||||
Suggest,
|
||||
}
|
||||
|
||||
impl Kind {
|
||||
@ -610,6 +611,7 @@ pub fn parse(string: &str) -> Option<Kind> {
|
||||
"install" => Kind::Install,
|
||||
"run" | "r" => Kind::Run,
|
||||
"setup" => Kind::Setup,
|
||||
"suggest" => Kind::Suggest,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@ -629,6 +631,7 @@ pub fn as_str(&self) -> &'static str {
|
||||
Kind::Install => "install",
|
||||
Kind::Run => "run",
|
||||
Kind::Setup => "setup",
|
||||
Kind::Suggest => "suggest",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -709,6 +712,7 @@ macro_rules! describe {
|
||||
test::CrateRustdoc,
|
||||
test::CrateRustdocJsonTypes,
|
||||
test::CrateJsonDocLint,
|
||||
test::SuggestTestsCrate,
|
||||
test::Linkcheck,
|
||||
test::TierCheck,
|
||||
test::ReplacePlaceholderTest,
|
||||
@ -827,7 +831,7 @@ macro_rules! describe {
|
||||
Kind::Setup => describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode),
|
||||
Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std),
|
||||
// special-cased in Build::build()
|
||||
Kind::Format => vec![],
|
||||
Kind::Format | Kind::Suggest => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@ -891,6 +895,7 @@ pub fn new(build: &Build) -> Builder<'_> {
|
||||
Subcommand::Run { ref paths, .. } => (Kind::Run, &paths[..]),
|
||||
Subcommand::Clean { ref paths, .. } => (Kind::Clean, &paths[..]),
|
||||
Subcommand::Format { .. } => (Kind::Format, &[][..]),
|
||||
Subcommand::Suggest { .. } => (Kind::Suggest, &[][..]),
|
||||
Subcommand::Setup { profile: ref path } => (
|
||||
Kind::Setup,
|
||||
path.as_ref().map_or([].as_slice(), |path| std::slice::from_ref(path)),
|
||||
@ -900,6 +905,21 @@ pub fn new(build: &Build) -> Builder<'_> {
|
||||
Self::new_internal(build, kind, paths.to_owned())
|
||||
}
|
||||
|
||||
/// Creates a new standalone builder for use outside of the normal process
|
||||
pub fn new_standalone(
|
||||
build: &mut Build,
|
||||
kind: Kind,
|
||||
paths: Vec<PathBuf>,
|
||||
stage: Option<u32>,
|
||||
) -> Builder<'_> {
|
||||
// FIXME: don't mutate `build`
|
||||
if let Some(stage) = stage {
|
||||
build.config.stage = stage;
|
||||
}
|
||||
|
||||
Self::new_internal(build, kind, paths.to_owned())
|
||||
}
|
||||
|
||||
pub fn execute_cli(&self) {
|
||||
self.run_step_descriptions(&Builder::get_step_descriptions(self.kind), &self.paths);
|
||||
}
|
||||
|
@ -56,8 +56,7 @@ pub enum DryRun {
|
||||
/// filled out from the decoded forms of the structs below. For documentation
|
||||
/// each field, see the corresponding fields in
|
||||
/// `config.example.toml`.
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(test, derive(Clone))]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Config {
|
||||
pub changelog_seen: Option<usize>,
|
||||
pub ccache: Option<String>,
|
||||
@ -240,23 +239,20 @@ pub struct Config {
|
||||
pub initial_rustfmt: RefCell<RustfmtState>,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
#[cfg_attr(test, derive(Clone))]
|
||||
#[derive(Default, Deserialize, Clone)]
|
||||
pub struct Stage0Metadata {
|
||||
pub compiler: CompilerMetadata,
|
||||
pub config: Stage0Config,
|
||||
pub checksums_sha256: HashMap<String, String>,
|
||||
pub rustfmt: Option<RustfmtMetadata>,
|
||||
}
|
||||
#[derive(Default, Deserialize)]
|
||||
#[cfg_attr(test, derive(Clone))]
|
||||
#[derive(Default, Deserialize, Clone)]
|
||||
pub struct CompilerMetadata {
|
||||
pub date: String,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
#[cfg_attr(test, derive(Clone))]
|
||||
#[derive(Default, Deserialize, Clone)]
|
||||
pub struct Stage0Config {
|
||||
pub dist_server: String,
|
||||
pub artifacts_server: String,
|
||||
@ -264,8 +260,7 @@ pub struct Stage0Config {
|
||||
pub git_merge_commit_email: String,
|
||||
pub nightly_branch: String,
|
||||
}
|
||||
#[derive(Default, Deserialize)]
|
||||
#[cfg_attr(test, derive(Clone))]
|
||||
#[derive(Default, Deserialize, Clone)]
|
||||
pub struct RustfmtMetadata {
|
||||
pub date: String,
|
||||
pub version: String,
|
||||
@ -443,8 +438,7 @@ fn eq(&self, other: &&str) -> bool {
|
||||
}
|
||||
|
||||
/// Per-target configuration stored in the global configuration structure.
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(test, derive(Clone))]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Target {
|
||||
/// Some(path to llvm-config) if using an external LLVM.
|
||||
pub llvm_config: Option<PathBuf>,
|
||||
@ -1396,7 +1390,8 @@ fn parse_inner<'a>(args: &[String], get_toml: impl 'a + Fn(&Path) -> TomlConfig)
|
||||
| Subcommand::Fix { .. }
|
||||
| Subcommand::Run { .. }
|
||||
| Subcommand::Setup { .. }
|
||||
| Subcommand::Format { .. } => flags.stage.unwrap_or(0),
|
||||
| Subcommand::Format { .. }
|
||||
| Subcommand::Suggest { .. } => flags.stage.unwrap_or(0),
|
||||
};
|
||||
|
||||
// CI should always run stage 2 builds, unless it specifically states otherwise
|
||||
@ -1421,7 +1416,8 @@ fn parse_inner<'a>(args: &[String], get_toml: impl 'a + Fn(&Path) -> TomlConfig)
|
||||
| Subcommand::Fix { .. }
|
||||
| Subcommand::Run { .. }
|
||||
| Subcommand::Setup { .. }
|
||||
| Subcommand::Format { .. } => {}
|
||||
| Subcommand::Format { .. }
|
||||
| Subcommand::Suggest { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,8 +84,7 @@ pub struct Flags {
|
||||
pub free_args: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(Clone))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Subcommand {
|
||||
Build {
|
||||
paths: Vec<PathBuf>,
|
||||
@ -149,6 +148,9 @@ pub enum Subcommand {
|
||||
Setup {
|
||||
profile: Option<PathBuf>,
|
||||
},
|
||||
Suggest {
|
||||
run: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for Subcommand {
|
||||
@ -183,6 +185,7 @@ pub fn parse(args: &[String]) -> Flags {
|
||||
install Install distribution artifacts
|
||||
run, r Run tools contained in this repository
|
||||
setup Create a config.toml (making it easier to use `x.py` itself)
|
||||
suggest Suggest a subset of tests to run, based on modified files
|
||||
|
||||
To learn more about a subcommand, run `./x.py <subcommand> -h`",
|
||||
);
|
||||
@ -349,6 +352,9 @@ pub fn parse(args: &[String]) -> Flags {
|
||||
Kind::Run => {
|
||||
opts.optmulti("", "args", "arguments for the tool", "ARGS");
|
||||
}
|
||||
Kind::Suggest => {
|
||||
opts.optflag("", "run", "run suggested tests");
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
@ -565,7 +571,7 @@ pub fn parse(args: &[String]) -> Flags {
|
||||
Profile::all_for_help(" ").trim_end()
|
||||
));
|
||||
}
|
||||
Kind::Bench | Kind::Clean | Kind::Dist | Kind::Install => {}
|
||||
Kind::Bench | Kind::Clean | Kind::Dist | Kind::Install | Kind::Suggest => {}
|
||||
};
|
||||
// Get any optional paths which occur after the subcommand
|
||||
let mut paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>();
|
||||
@ -626,6 +632,7 @@ pub fn parse(args: &[String]) -> Flags {
|
||||
Kind::Format => Subcommand::Format { check: matches.opt_present("check"), paths },
|
||||
Kind::Dist => Subcommand::Dist { paths },
|
||||
Kind::Install => Subcommand::Install { paths },
|
||||
Kind::Suggest => Subcommand::Suggest { run: matches.opt_present("run") },
|
||||
Kind::Run => {
|
||||
if paths.is_empty() {
|
||||
println!("\nrun requires at least a path!\n");
|
||||
@ -734,6 +741,7 @@ pub fn kind(&self) -> Kind {
|
||||
Subcommand::Install { .. } => Kind::Install,
|
||||
Subcommand::Run { .. } => Kind::Run,
|
||||
Subcommand::Setup { .. } => Kind::Setup,
|
||||
Subcommand::Suggest { .. } => Kind::Suggest,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,7 @@
|
||||
mod run;
|
||||
mod sanity;
|
||||
mod setup;
|
||||
mod suggest;
|
||||
mod tarball;
|
||||
mod test;
|
||||
mod tool;
|
||||
@ -189,6 +190,7 @@ pub enum GitRepo {
|
||||
/// although most functions are implemented as free functions rather than
|
||||
/// methods specifically on this structure itself (to make it easier to
|
||||
/// organize).
|
||||
#[derive(Clone)]
|
||||
pub struct Build {
|
||||
/// User-specified configuration from `config.toml`.
|
||||
config: Config,
|
||||
@ -242,7 +244,7 @@ pub struct Build {
|
||||
metrics: metrics::BuildMetrics,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct Crate {
|
||||
name: Interned<String>,
|
||||
deps: HashSet<Interned<String>>,
|
||||
@ -656,13 +658,20 @@ pub fn build(&mut self) {
|
||||
job::setup(self);
|
||||
}
|
||||
|
||||
if let Subcommand::Format { check, paths } = &self.config.cmd {
|
||||
return format::format(&builder::Builder::new(&self), *check, &paths);
|
||||
}
|
||||
|
||||
// Download rustfmt early so that it can be used in rust-analyzer configs.
|
||||
let _ = &builder::Builder::new(&self).initial_rustfmt();
|
||||
|
||||
// hardcoded subcommands
|
||||
match &self.config.cmd {
|
||||
Subcommand::Format { check, paths } => {
|
||||
return format::format(&builder::Builder::new(&self), *check, &paths);
|
||||
}
|
||||
Subcommand::Suggest { run } => {
|
||||
return suggest::suggest(&builder::Builder::new(&self), *run);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
{
|
||||
let builder = builder::Builder::new(&self);
|
||||
if let Some(path) = builder.paths.get(0) {
|
||||
|
72
src/bootstrap/suggest.rs
Normal file
72
src/bootstrap/suggest.rs
Normal file
@ -0,0 +1,72 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{
|
||||
builder::{Builder, Kind},
|
||||
tool::Tool,
|
||||
};
|
||||
|
||||
/// Suggests a list of possible `x.py` commands to run based on modified files in branch.
|
||||
pub fn suggest(builder: &Builder<'_>, run: bool) {
|
||||
let suggestions =
|
||||
builder.tool_cmd(Tool::SuggestTests).output().expect("failed to run `suggest-tests` tool");
|
||||
|
||||
if !suggestions.status.success() {
|
||||
println!("failed to run `suggest-tests` tool ({})", suggestions.status);
|
||||
println!(
|
||||
"`suggest_tests` stdout:\n{}`suggest_tests` stderr:\n{}",
|
||||
String::from_utf8(suggestions.stdout).unwrap(),
|
||||
String::from_utf8(suggestions.stderr).unwrap()
|
||||
);
|
||||
panic!("failed to run `suggest-tests`");
|
||||
}
|
||||
|
||||
let suggestions = String::from_utf8(suggestions.stdout).unwrap();
|
||||
let suggestions = suggestions
|
||||
.lines()
|
||||
.map(|line| {
|
||||
let mut sections = line.split_ascii_whitespace();
|
||||
|
||||
// this code expects one suggestion per line in the following format:
|
||||
// <x_subcommand> {some number of flags} [optional stage number]
|
||||
let cmd = sections.next().unwrap();
|
||||
let stage = sections.next_back().map(|s| str::parse(s).ok()).flatten();
|
||||
let paths: Vec<PathBuf> = sections.map(|p| PathBuf::from_str(p).unwrap()).collect();
|
||||
|
||||
(cmd, stage, paths)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !suggestions.is_empty() {
|
||||
println!("==== SUGGESTIONS ====");
|
||||
for sug in &suggestions {
|
||||
print!("x {} ", sug.0);
|
||||
if let Some(stage) = sug.1 {
|
||||
print!("--stage {stage} ");
|
||||
}
|
||||
|
||||
for path in &sug.2 {
|
||||
print!("{} ", path.display());
|
||||
}
|
||||
println!();
|
||||
}
|
||||
println!("=====================");
|
||||
} else {
|
||||
println!("No suggestions found!");
|
||||
return;
|
||||
}
|
||||
|
||||
if run {
|
||||
for sug in suggestions {
|
||||
let mut build = builder.build.clone();
|
||||
|
||||
let builder =
|
||||
Builder::new_standalone(&mut build, Kind::parse(&sug.0).unwrap(), sug.2, sug.1);
|
||||
|
||||
builder.execute_cli()
|
||||
}
|
||||
} else {
|
||||
println!("help: consider using the `--run` flag to automatically run suggested tests");
|
||||
}
|
||||
}
|
@ -128,6 +128,42 @@ fn run(self, builder: &Builder<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SuggestTestsCrate {
|
||||
host: TargetSelection,
|
||||
}
|
||||
|
||||
impl Step for SuggestTestsCrate {
|
||||
type Output = ();
|
||||
const ONLY_HOSTS: bool = true;
|
||||
const DEFAULT: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
run.path("src/tools/suggest-tests")
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig<'_>) {
|
||||
run.builder.ensure(SuggestTestsCrate { host: run.target });
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder<'_>) {
|
||||
let bootstrap_host = builder.config.build;
|
||||
let compiler = builder.compiler(0, bootstrap_host);
|
||||
|
||||
let suggest_tests = tool::prepare_tool_cargo(
|
||||
builder,
|
||||
compiler,
|
||||
Mode::ToolBootstrap,
|
||||
bootstrap_host,
|
||||
"test",
|
||||
"src/tools/suggest-tests",
|
||||
SourceType::InTree,
|
||||
&[],
|
||||
);
|
||||
add_flags_and_try_run_tests(builder, &mut suggest_tests.into());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Linkcheck {
|
||||
host: TargetSelection,
|
||||
|
@ -433,6 +433,7 @@ fn run(self, builder: &Builder<'_>) -> PathBuf {
|
||||
ReplaceVersionPlaceholder, "src/tools/replace-version-placeholder", "replace-version-placeholder";
|
||||
CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
|
||||
GenerateCopyright, "src/tools/generate-copyright", "generate-copyright";
|
||||
SuggestTests, "src/tools/suggest-tests", "suggest-tests";
|
||||
);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
|
||||
|
9
src/tools/suggest-tests/Cargo.toml
Normal file
9
src/tools/suggest-tests/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "suggest-tests"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
glob = "0.3.0"
|
||||
build_helper = { version = "0.1.0", path = "../build_helper" }
|
||||
once_cell = "1.17.1"
|
23
src/tools/suggest-tests/src/dynamic_suggestions.rs
Normal file
23
src/tools/suggest-tests/src/dynamic_suggestions.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::Suggestion;
|
||||
|
||||
type DynamicSuggestion = fn(&Path) -> Vec<Suggestion>;
|
||||
|
||||
pub(crate) const DYNAMIC_SUGGESTIONS: &[DynamicSuggestion] = &[|path: &Path| -> Vec<Suggestion> {
|
||||
if path.starts_with("compiler/") || path.starts_with("library/") {
|
||||
let path = path.components().take(2).collect::<Vec<_>>();
|
||||
|
||||
vec![Suggestion::with_single_path(
|
||||
"test",
|
||||
None,
|
||||
&format!(
|
||||
"{}/{}",
|
||||
path[0].as_os_str().to_str().unwrap(),
|
||||
path[1].as_os_str().to_str().unwrap()
|
||||
),
|
||||
)]
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}];
|
96
src/tools/suggest-tests/src/lib.rs
Normal file
96
src/tools/suggest-tests/src/lib.rs
Normal file
@ -0,0 +1,96 @@
|
||||
use std::{
|
||||
fmt::{self, Display},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use dynamic_suggestions::DYNAMIC_SUGGESTIONS;
|
||||
use glob::Pattern;
|
||||
use static_suggestions::STATIC_SUGGESTIONS;
|
||||
|
||||
mod dynamic_suggestions;
|
||||
mod static_suggestions;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
macro_rules! sug {
|
||||
($cmd:expr) => {
|
||||
Suggestion::new($cmd, None, &[])
|
||||
};
|
||||
|
||||
($cmd:expr, $paths:expr) => {
|
||||
Suggestion::new($cmd, None, $paths.as_slice())
|
||||
};
|
||||
|
||||
($cmd:expr, $stage:expr, $paths:expr) => {
|
||||
Suggestion::new($cmd, Some($stage), $paths.as_slice())
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use sug;
|
||||
|
||||
pub fn get_suggestions<T: AsRef<str>>(modified_files: &[T]) -> Vec<Suggestion> {
|
||||
let mut suggestions = Vec::new();
|
||||
|
||||
// static suggestions
|
||||
for sug in STATIC_SUGGESTIONS.iter() {
|
||||
let glob = Pattern::new(&sug.0).expect("Found invalid glob pattern!");
|
||||
|
||||
for file in modified_files {
|
||||
if glob.matches(file.as_ref()) {
|
||||
suggestions.extend_from_slice(&sug.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dynamic suggestions
|
||||
for sug in DYNAMIC_SUGGESTIONS {
|
||||
for file in modified_files {
|
||||
let sugs = sug(Path::new(file.as_ref()));
|
||||
|
||||
suggestions.extend_from_slice(&sugs);
|
||||
}
|
||||
}
|
||||
|
||||
suggestions.sort();
|
||||
suggestions.dedup();
|
||||
|
||||
suggestions
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
|
||||
pub struct Suggestion {
|
||||
pub cmd: String,
|
||||
pub stage: Option<u32>,
|
||||
pub paths: Vec<String>,
|
||||
}
|
||||
|
||||
impl Suggestion {
|
||||
pub fn new(cmd: &str, stage: Option<u32>, paths: &[&str]) -> Self {
|
||||
Self { cmd: cmd.to_owned(), stage, paths: paths.iter().map(|p| p.to_string()).collect() }
|
||||
}
|
||||
|
||||
pub fn with_single_path(cmd: &str, stage: Option<u32>, path: &str) -> Self {
|
||||
Self::new(cmd, stage, &[path])
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Suggestion {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(f, "{} ", self.cmd)?;
|
||||
|
||||
for path in &self.paths {
|
||||
write!(f, "{} ", path)?;
|
||||
}
|
||||
|
||||
if let Some(stage) = self.stage {
|
||||
write!(f, "{}", stage)?;
|
||||
} else {
|
||||
// write a sentinel value here (in place of a stage) to be consumed
|
||||
// by the shim in bootstrap, it will be read and ignored.
|
||||
write!(f, "N/A")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
27
src/tools/suggest-tests/src/main.rs
Normal file
27
src/tools/suggest-tests/src/main.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use std::process::ExitCode;
|
||||
|
||||
use build_helper::git::get_git_modified_files;
|
||||
use suggest_tests::get_suggestions;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let modified_files = get_git_modified_files(None, &Vec::new());
|
||||
let modified_files = match modified_files {
|
||||
Ok(Some(files)) => files,
|
||||
Ok(None) => {
|
||||
eprintln!("git error");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Could not get modified files from git: \"{err}\"");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
|
||||
let suggestions = get_suggestions(&modified_files);
|
||||
|
||||
for sug in &suggestions {
|
||||
println!("{sug}");
|
||||
}
|
||||
|
||||
ExitCode::SUCCESS
|
||||
}
|
24
src/tools/suggest-tests/src/static_suggestions.rs
Normal file
24
src/tools/suggest-tests/src/static_suggestions.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use crate::{sug, Suggestion};
|
||||
|
||||
// FIXME: perhaps this could use `std::lazy` when it is stablizied
|
||||
macro_rules! static_suggestions {
|
||||
($( $glob:expr => [ $( $suggestion:expr ),* ] ),*) => {
|
||||
pub(crate) const STATIC_SUGGESTIONS: ::once_cell::unsync::Lazy<Vec<(&'static str, Vec<Suggestion>)>>
|
||||
= ::once_cell::unsync::Lazy::new(|| vec![ $( ($glob, vec![ $($suggestion),* ]) ),*]);
|
||||
}
|
||||
}
|
||||
|
||||
static_suggestions! {
|
||||
"*.md" => [
|
||||
sug!("test", 0, ["linkchecker"])
|
||||
],
|
||||
|
||||
"compiler/*" => [
|
||||
sug!("check"),
|
||||
sug!("test", 1, ["src/test/ui", "src/test/run-make"])
|
||||
],
|
||||
|
||||
"src/librustdoc/*" => [
|
||||
sug!("test", 1, ["rustdoc"])
|
||||
]
|
||||
}
|
21
src/tools/suggest-tests/src/tests.rs
Normal file
21
src/tools/suggest-tests/src/tests.rs
Normal file
@ -0,0 +1,21 @@
|
||||
macro_rules! sugg_test {
|
||||
( $( $name:ident: $paths:expr => $suggestions:expr ),* ) => {
|
||||
$(
|
||||
#[test]
|
||||
fn $name() {
|
||||
let suggestions = crate::get_suggestions(&$paths).into_iter().map(|s| s.to_string()).collect::<Vec<_>>();
|
||||
assert_eq!(suggestions, $suggestions);
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
sugg_test! {
|
||||
test_error_code_docs: ["compiler/rustc_error_codes/src/error_codes/E0000.md"] =>
|
||||
["check N/A", "test compiler/rustc_error_codes N/A", "test linkchecker 0", "test src/test/ui src/test/run-make 1"],
|
||||
|
||||
test_rustdoc: ["src/librustdoc/src/lib.rs"] => ["test rustdoc 1"],
|
||||
|
||||
test_rustdoc_and_libstd: ["src/librustdoc/src/lib.rs", "library/std/src/lib.rs"] =>
|
||||
["test library/std N/A", "test rustdoc 1"]
|
||||
}
|
Loading…
Reference in New Issue
Block a user