diff --git a/release-content/release-notes/pedantic_ci_commands.md b/release-content/release-notes/pedantic_ci_commands.md new file mode 100644 index 0000000000000..727077282bd8f --- /dev/null +++ b/release-content/release-notes/pedantic_ci_commands.md @@ -0,0 +1,75 @@ +--- +title: Pedantic CI commands +authors: ["@hukasu"] +pull_requests: [21145] +--- + +New commands were added to CI tool to allow running clippy with multiple permutations of +features. For a crate it is run with: + +* `--no-default-features` +* Multiple `--no-default-features --features="..."` +* Default features +* Multiple `--all-features --features="..."` + +There are crates that are run with multiple `--all-features` due to them depending +another crate that has a switchable features. Examples are `bevy_image/zstd_c` or +`bevy_image/zstd_rust`, or `bevy_reflect/auto_register_inventory` or `bevy_reflect/auto_register_static`. + +## Commands + +### `clippys` + +This is a meta commands that runs the other clippy permutation commands. + +If you are on the workspace, run `cargo run -p ci -- --build-jobs 4 clippys`. + +### `clippy_android` + +Runs clippy on crates for Android targets. Requires an Android +target. + +If you are on the workspace, run `cargo run -p ci -- --build-jobs 4 --target aarch64-linux-android clippy_android`. + +### `clippy_dlss` + +Runs clippy on crates and features that require the Dlss SDK. + +If you are on the workspace, run `cargo run -p ci -- --build-jobs 4 clippy_dlss`. + +### `bevy_a11y` + +Runs clippy on `bevy_a11y` with multiple feature permutations. + +If you are on the workspace, run `cargo run -p ci -- --build-jobs 4 bevy_a11y`. + +### `bevy_android` + +Runs clippy on `bevy_android` with multiple feature permutations. Requires an Android +target. + +If you are on the workspace, run `cargo run -p ci -- --build-jobs 4 --target aarch64-linux-android bevy_android`. + +### `bevy_animation` + +Runs clippy on `bevy_animation` with multiple feature permutations. + +If you are on the workspace, run `cargo run -p ci -- --build-jobs 4 bevy_animation`. + +### `bevy_anti_alias` + +Runs clippy on `bevy_anti_alias` with multiple feature permutations. + +If you are on the workspace, run `cargo run -p ci -- --build-jobs 4 bevy_anti_alias`. + +### `bevy_app` + +Runs clippy on `bevy_app` with multiple feature permutations. + +If you are on the workspace, run `cargo run -p ci -- --build-jobs 4 bevy_app`. + +### `bevy_ecs` + +Runs clippy on `bevy_ecs` with multiple feature permutations. + +If you are on the workspace, run `cargo run -p ci -- --build-jobs 4 bevy_ecs`. diff --git a/tools/ci/src/args.rs b/tools/ci/src/args.rs index 574d98f192ca3..bdc603d4251bf 100644 --- a/tools/ci/src/args.rs +++ b/tools/ci/src/args.rs @@ -1,11 +1,18 @@ use crate::CI; +/// Android targets +const ANDROID_TARGETS: &[&str] = &[ + "aarch64-linux-android", + // Help expand this +]; + /// Arguments that are available to CI commands. #[derive(Copy, Clone, PartialEq, Eq)] pub struct Args { keep_going: bool, test_threads: Option, build_jobs: Option, + target: Option<&'static str>, } impl Args { @@ -24,6 +31,20 @@ impl Args { self.test_threads .map(|threads| format!("--test-threads={threads}")) } + + #[inline(always)] + pub fn target(&self) -> Option { + self.target.map(|target| format!("--target={target}")) + } + + /// Tests if the target is an android target + pub fn is_android_target(&self) -> bool { + if let Some(target) = &self.target { + ANDROID_TARGETS.contains(target) + } else { + cfg!(target_os = "android") + } + } } impl From<&CI> for Args { @@ -32,6 +53,10 @@ impl From<&CI> for Args { keep_going: value.keep_going, test_threads: value.test_threads, build_jobs: value.build_jobs, + target: value.target.as_ref().map(|string| { + let s: &'static str = string.clone().leak(); + s + }), } } } diff --git a/tools/ci/src/ci.rs b/tools/ci/src/ci.rs index 8b8556d90ac05..ea87616895311 100644 --- a/tools/ci/src/ci.rs +++ b/tools/ci/src/ci.rs @@ -22,6 +22,10 @@ pub struct CI { /// number of build jobs #[argh(option)] pub(crate) build_jobs: Option, + + /// target to compile to + #[argh(option)] + pub(crate) target: Option, } impl CI { @@ -75,6 +79,7 @@ impl CI { let mut cmds = vec![]; cmds.append(&mut commands::FormatCommand::default().prepare(sh, args)); cmds.append(&mut commands::ClippyCommand::default().prepare(sh, args)); + cmds.append(&mut commands::ClippysCommand::default().prepare(sh, args)); cmds.append(&mut commands::TestCommand::default().prepare(sh, args)); cmds.append(&mut commands::TestCheckCommand::default().prepare(sh, args)); cmds.append(&mut commands::IntegrationTestCommand::default().prepare(sh, args)); @@ -105,9 +110,12 @@ enum Commands { Lints(commands::LintsCommand), Doc(commands::DocCommand), Compile(commands::CompileCommand), + Clippys(commands::ClippysCommand), // Actual subcommands Format(commands::FormatCommand), Clippy(commands::ClippyCommand), + ClippyAndroid(commands::ClippyAndroidCommand), + ClippyDlss(commands::ClippyDlssCommand), Test(commands::TestCommand), TestCheck(commands::TestCheckCommand), IntegrationTest(commands::IntegrationTestCommand), @@ -119,6 +127,12 @@ enum Commands { CompileFail(commands::CompileFailCommand), BenchCheck(commands::BenchCheckCommand), ExampleCheck(commands::ExampleCheckCommand), + BevyA11y(commands::BevyA11y), + BevyAndroid(commands::BevyAndroid), + BevyAnimation(commands::BevyAnimation), + BevyAntiAlias(commands::BevyAntiAlias), + BevyApp(commands::BevyApp), + BevyEcs(commands::BevyEcs), } impl Prepare for Commands { @@ -127,9 +141,12 @@ impl Prepare for Commands { Commands::Lints(subcommand) => subcommand.prepare(sh, args), Commands::Doc(subcommand) => subcommand.prepare(sh, args), Commands::Compile(subcommand) => subcommand.prepare(sh, args), + Commands::Clippys(subcommand) => subcommand.prepare(sh, args), Commands::Format(subcommand) => subcommand.prepare(sh, args), Commands::Clippy(subcommand) => subcommand.prepare(sh, args), + Commands::ClippyAndroid(subcommand) => subcommand.prepare(sh, args), + Commands::ClippyDlss(subcommand) => subcommand.prepare(sh, args), Commands::Test(subcommand) => subcommand.prepare(sh, args), Commands::TestCheck(subcommand) => subcommand.prepare(sh, args), Commands::IntegrationTest(subcommand) => subcommand.prepare(sh, args), @@ -141,6 +158,12 @@ impl Prepare for Commands { Commands::CompileFail(subcommand) => subcommand.prepare(sh, args), Commands::BenchCheck(subcommand) => subcommand.prepare(sh, args), Commands::ExampleCheck(subcommand) => subcommand.prepare(sh, args), + Commands::BevyA11y(subcommand) => subcommand.prepare(sh, args), + Commands::BevyAndroid(subcommand) => subcommand.prepare(sh, args), + Commands::BevyAnimation(subcommand) => subcommand.prepare(sh, args), + Commands::BevyAntiAlias(subcommand) => subcommand.prepare(sh, args), + Commands::BevyApp(subcommand) => subcommand.prepare(sh, args), + Commands::BevyEcs(subcommand) => subcommand.prepare(sh, args), } } } diff --git a/tools/ci/src/commands/bevy_a11y.rs b/tools/ci/src/commands/bevy_a11y.rs new file mode 100644 index 0000000000000..17e12bbf01120 --- /dev/null +++ b/tools/ci/src/commands/bevy_a11y.rs @@ -0,0 +1,20 @@ +use crate::{ + args::Args, commands::clippy_permutations::ClippyPermutations, Prepare, PreparedCommand, +}; +use argh::FromArgs; + +/// Check for clippy warnings and errors on `bevy_a11y`. +#[derive(FromArgs, Default)] +#[argh(subcommand, name = "bevy_a11y")] +pub struct BevyA11y {} + +impl Prepare for BevyA11y { + fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec> { + ClippyPermutations { + crate_name: "bevy_a11y", + features: &["bevy_reflect", "serialize", "std", "critical-section"], + all_features_features: &[], + } + .build::(sh, args) + } +} diff --git a/tools/ci/src/commands/bevy_android.rs b/tools/ci/src/commands/bevy_android.rs new file mode 100644 index 0000000000000..6e4774ae6bff8 --- /dev/null +++ b/tools/ci/src/commands/bevy_android.rs @@ -0,0 +1,24 @@ +use crate::{ + args::Args, commands::clippy_permutations::ClippyPermutations, Prepare, PreparedCommand, +}; +use argh::FromArgs; + +/// Check for clippy warnings and errors on `bevy_android`. +#[derive(FromArgs, Default)] +#[argh(subcommand, name = "bevy_android")] +pub struct BevyAndroid {} + +impl Prepare for BevyAndroid { + fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec> { + if !args.is_android_target() { + panic!("BevyAndroid requires an Android target."); + } + + ClippyPermutations { + crate_name: "bevy_android", + features: &[], + all_features_features: &[], + } + .build::(sh, args) + } +} diff --git a/tools/ci/src/commands/bevy_animation.rs b/tools/ci/src/commands/bevy_animation.rs new file mode 100644 index 0000000000000..425790ca9e155 --- /dev/null +++ b/tools/ci/src/commands/bevy_animation.rs @@ -0,0 +1,20 @@ +use crate::{ + args::Args, commands::clippy_permutations::ClippyPermutations, Prepare, PreparedCommand, +}; +use argh::FromArgs; + +/// Check for clippy warnings and errors on `bevy_animation`. +#[derive(FromArgs, Default)] +#[argh(subcommand, name = "bevy_animation")] +pub struct BevyAnimation {} + +impl Prepare for BevyAnimation { + fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec> { + ClippyPermutations { + crate_name: "bevy_animation", + features: &[], + all_features_features: &[], + } + .build::(sh, args) + } +} diff --git a/tools/ci/src/commands/bevy_anti_alias.rs b/tools/ci/src/commands/bevy_anti_alias.rs new file mode 100644 index 0000000000000..c39ccf3c06f13 --- /dev/null +++ b/tools/ci/src/commands/bevy_anti_alias.rs @@ -0,0 +1,25 @@ +use crate::{ + args::Args, commands::clippy_permutations::ClippyPermutations, Prepare, PreparedCommand, +}; +use argh::FromArgs; + +/// Check for clippy warnings and errors on `bevy_anti_alias`. +#[derive(FromArgs, Default)] +#[argh(subcommand, name = "bevy_anti_alias")] +pub struct BevyAntiAlias {} + +impl Prepare for BevyAntiAlias { + fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec> { + ClippyPermutations { + crate_name: "bevy_anti_alias", + features: &[ + "trace", + "smaa_luts bevy_image/zstd_rust", + "smaa_luts bevy_image/zstd_c", + "dlss force_disable_dlss", + ], + all_features_features: &["bevy_image/zstd_rust", "bevy_image/zstd_c"], + } + .build::(sh, args) + } +} diff --git a/tools/ci/src/commands/bevy_app.rs b/tools/ci/src/commands/bevy_app.rs new file mode 100644 index 0000000000000..606400cf57271 --- /dev/null +++ b/tools/ci/src/commands/bevy_app.rs @@ -0,0 +1,35 @@ +use crate::{ + args::Args, commands::clippy_permutations::ClippyPermutations, Prepare, PreparedCommand, +}; +use argh::FromArgs; + +/// Check for clippy warnings and errors on `bevy_app`. +#[derive(FromArgs, Default)] +#[argh(subcommand, name = "bevy_app")] +pub struct BevyApp {} + +impl Prepare for BevyApp { + fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec> { + ClippyPermutations { + crate_name: "bevy_app", + features: &[ + "bevy_reflect", + "reflect_functions", + "reflect_auto_register bevy_reflect/auto_register_inventory", + "reflect_auto_register bevy_reflect/auto_register_static", + "trace", + "bevy_debug_stepping", + "error_panic_hook", + "std", + "critical-section", + "web", + "hotpatching", + ], + all_features_features: &[ + "bevy_reflect/auto_register_inventory", + "bevy_reflect/auto_register_static", + ], + } + .build::(sh, args) + } +} diff --git a/tools/ci/src/commands/bevy_ecs.rs b/tools/ci/src/commands/bevy_ecs.rs new file mode 100644 index 0000000000000..099c7c95ca1b3 --- /dev/null +++ b/tools/ci/src/commands/bevy_ecs.rs @@ -0,0 +1,38 @@ +use crate::{ + args::Args, commands::clippy_permutations::ClippyPermutations, Prepare, PreparedCommand, +}; +use argh::FromArgs; + +/// Check for clippy warnings and errors on `bevy_ecs`. +#[derive(FromArgs, Default)] +#[argh(subcommand, name = "bevy_ecs")] +pub struct BevyEcs {} + +impl Prepare for BevyEcs { + fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec> { + ClippyPermutations { + crate_name: "bevy_ecs", + features: &[ + //"multi_threaded", + "serialize", + "bevy_reflect", + "reflect_functions", + "reflect_auto_register bevy_reflect/auto_register_inventory", + "reflect_auto_register bevy_reflect/auto_register_static", + "backtrace", + "trace", + "detailed_trace", + "track_location", + "async_executor", + "std", + "critical-section", + "hotpatching", + ], + all_features_features: &[ + "bevy_reflect/auto_register_inventory", + "bevy_reflect/auto_register_static", + ], + } + .build::(sh, args) + } +} diff --git a/tools/ci/src/commands/clippy_android.rs b/tools/ci/src/commands/clippy_android.rs new file mode 100644 index 0000000000000..e90bea60db608 --- /dev/null +++ b/tools/ci/src/commands/clippy_android.rs @@ -0,0 +1,21 @@ +use crate::{args::Args, commands::BevyAndroid, Prepare, PreparedCommand}; +use argh::FromArgs; + +/// Check for clippy warnings and errors for Android targets. +#[derive(FromArgs, Default)] +#[argh(subcommand, name = "clippy_android")] +pub struct ClippyAndroidCommand {} + +impl Prepare for ClippyAndroidCommand { + fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec> { + if !args.is_android_target() { + panic!("ClippyAndroidCommand requires an Android target."); + } + + let mut commands = Vec::new(); + + commands.append(&mut BevyAndroid::default().prepare(sh, args)); + + commands + } +} diff --git a/tools/ci/src/commands/clippy_dlss.rs b/tools/ci/src/commands/clippy_dlss.rs new file mode 100644 index 0000000000000..3256f329db83b --- /dev/null +++ b/tools/ci/src/commands/clippy_dlss.rs @@ -0,0 +1,22 @@ +use crate::{args::Args, Prepare, PreparedCommand}; +use argh::FromArgs; +use xshell::cmd; + +/// Check for clippy warnings and errors for crates/features that require Dlss SDK. +#[derive(FromArgs, Default)] +#[argh(subcommand, name = "clippy_dlss")] +pub struct ClippyDlssCommand {} + +impl Prepare for ClippyDlssCommand { + fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec> { + let jobs = args.build_jobs(); + + vec![PreparedCommand::new::( + cmd!( + sh, + "cargo clippy -p bevy_anti_alias --no-default-features --features=dlss {jobs...} -- -Dwarnings" + ), + "Please fix clippy errors in output above.", + )] + } +} diff --git a/tools/ci/src/commands/clippy_permutations.rs b/tools/ci/src/commands/clippy_permutations.rs new file mode 100644 index 0000000000000..6000c194767a4 --- /dev/null +++ b/tools/ci/src/commands/clippy_permutations.rs @@ -0,0 +1,79 @@ +use crate::{args::Args, PreparedCommand}; +use argh::SubCommand; +use xshell::cmd; + +/// Prepares the clippy permutations of a crate. +pub(super) struct ClippyPermutations { + /// Crate name + pub crate_name: &'static str, + /// Features used when running clippy with no default features + pub features: &'static [&'static str], + /// Features used when running clippy with all features + pub all_features_features: &'static [&'static str], +} + +impl ClippyPermutations { + pub fn build<'a, T: SubCommand>( + self, + sh: &'a xshell::Shell, + args: Args, + ) -> Vec> { + let jobs = args.build_jobs(); + let jobs_ref = jobs.as_ref(); + let target = args.target(); + let target_ref = target.as_ref(); + let crate_name = self.crate_name; + + let mut permutations = + Vec::with_capacity(2 + self.features.len() + self.all_features_features.len().min(1)); + + // No default features + permutations.push(PreparedCommand::new::( + cmd!( + sh, + "cargo clippy -p {crate_name} --no-default-features {jobs_ref...} {target_ref...} -- -Dwarnings" + ), + "Please fix clippy errors in output above.", + )); + // Feature permutations + for feature in self.features { + permutations.push(PreparedCommand::new::( + cmd!( + sh, + "cargo clippy -p {crate_name} --no-default-features --features={feature} {jobs_ref...} {target_ref...} -- -Dwarnings" + ), + "Please fix clippy errors in output above.", + )); + } + // Default features + permutations.push(PreparedCommand::new::( + cmd!( + sh, + "cargo clippy -p {crate_name} {jobs_ref...} {target_ref...} -- -Dwarnings" + ), + "Please fix clippy errors in output above.", + )); + // All features + if self.all_features_features.is_empty() { + permutations.push(PreparedCommand::new::( + cmd!( + sh, + "cargo clippy -p {crate_name} --all-features {jobs_ref...} {target_ref...} -- -Dwarnings" + ), + "Please fix clippy errors in output above.", + )); + } else { + for feature in self.all_features_features { + permutations.push(PreparedCommand::new::( + cmd!( + sh, + "cargo clippy -p {crate_name} --all-features --features={feature} {jobs_ref...} {target_ref...} -- -Dwarnings" + ), + "Please fix clippy errors in output above.", + )); + } + } + + permutations + } +} diff --git a/tools/ci/src/commands/clippys.rs b/tools/ci/src/commands/clippys.rs new file mode 100644 index 0000000000000..fa0f85405cabb --- /dev/null +++ b/tools/ci/src/commands/clippys.rs @@ -0,0 +1,21 @@ +use crate::{args::Args, commands::*, Prepare, PreparedCommand}; +use argh::FromArgs; + +/// Check for clippy warnings and errors running multiple permutations of features. +#[derive(FromArgs, Default)] +#[argh(subcommand, name = "clippys")] +pub struct ClippysCommand {} + +impl Prepare for ClippysCommand { + fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec> { + let mut commands = Vec::new(); + + commands.append(&mut BevyA11y::default().prepare(sh, args)); + commands.append(&mut BevyAnimation::default().prepare(sh, args)); + commands.append(&mut BevyAntiAlias::default().prepare(sh, args)); + commands.append(&mut BevyApp::default().prepare(sh, args)); + commands.append(&mut BevyEcs::default().prepare(sh, args)); + + commands + } +} diff --git a/tools/ci/src/commands/mod.rs b/tools/ci/src/commands/mod.rs index 9247ab201627b..dc8e80f981478 100644 --- a/tools/ci/src/commands/mod.rs +++ b/tools/ci/src/commands/mod.rs @@ -1,5 +1,14 @@ pub use bench_check::*; +pub use bevy_a11y::*; +pub use bevy_android::*; +pub use bevy_animation::*; +pub use bevy_anti_alias::*; +pub use bevy_app::*; +pub use bevy_ecs::*; pub use clippy::*; +pub use clippy_android::*; +pub use clippy_dlss::*; +pub use clippys::*; pub use compile::*; pub use compile_check::*; pub use compile_fail::*; @@ -16,7 +25,17 @@ pub use test::*; pub use test_check::*; mod bench_check; +mod bevy_a11y; +mod bevy_android; +mod bevy_animation; +mod bevy_anti_alias; +mod bevy_app; +mod bevy_ecs; mod clippy; +mod clippy_android; +mod clippy_dlss; +mod clippy_permutations; +mod clippys; mod compile; mod compile_check; mod compile_fail;