plugin_group! macro (adopted) (#14339)

# Objective

- Adopted from #11460.
- Closes #7332.
- The documentation for `DefaultPlugins` and `MinimalPlugins` frequently
goes out of date because it is not .

## Solution

- Create a macro, `plugin_group!`, to automatically create
`PluginGroup`s and document them.

## Testing

- Run `cargo-expand` on the generated code for `DefaultPlugins` and
`MinimalPlugins`.
- Try creating a custom plugin group with the macro.

---

## Showcase

- You can now define custom `PluginGroup`s using the `plugin_group!`
macro.

```rust
plugin_group! {
    /// My really cool plugic group!
    pub struct MyPluginGroup {
        physics:::PhysicsPlugin,
        rendering:::RenderingPlugin,
        ui:::UiPlugin,
    }
}
```

<details>
  <summary>Expanded output</summary>

```rust
/// My really cool plugic group!
///
/// - [`PhysicsPlugin`](physics::PhysicsPlugin)
/// - [`RenderingPlugin`](rendering::RenderingPlugin)
/// - [`UiPlugin`](ui::UiPlugin)
pub struct MyPluginGroup;
impl ::bevy_app::PluginGroup for MyPluginGroup {
    fn build(self) -> ::bevy_app::PluginGroupBuilder {
        let mut group = ::bevy_app::PluginGroupBuilder::start::<Self>();
        {
            const _: () = {
                const fn check_default<T: Default>() {}
                check_default::<physics::PhysicsPlugin>();
            };
            group = group.add(<physics::PhysicsPlugin>::default());
        }
        {
            const _: () = {
                const fn check_default<T: Default>() {}
                check_default::<rendering::RenderingPlugin>();
            };
            group = group.add(<rendering::RenderingPlugin>::default());
        }
        {
            const _: () = {
                const fn check_default<T: Default>() {}
                check_default::<ui::UiPlugin>();
            };
            group = group.add(<ui::UiPlugin>::default());
        }
        group
    }
}
```

</details>

---------

Co-authored-by: Doonv <58695417+doonv@users.noreply.github.com>
Co-authored-by: Mateusz Wachowiak <mateusz_wachowiak@outlook.com>
This commit is contained in:
BD103 2024-07-15 21:14:33 -04:00 committed by GitHub
parent 9a1a84dd22
commit c3057d4353
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 232 additions and 184 deletions

View file

@ -103,6 +103,7 @@ pub enum AccessibilitySystem {
}
/// Plugin managing non-GUI aspects of integrating with accessibility APIs.
#[derive(Default)]
pub struct AccessibilityPlugin;
impl Plugin for AccessibilityPlugin {

View file

@ -2,7 +2,159 @@ use crate::{App, AppError, Plugin};
use bevy_utils::{tracing::debug, tracing::warn, TypeIdMap};
use std::any::TypeId;
/// A macro for generating a well-documented [`PluginGroup`] from a list of [`Plugin`] paths.
///
/// Every plugin must implement the [`Default`] trait.
///
/// # Example
///
/// ```
/// # use bevy_app::*;
/// #
/// # mod velocity {
/// # use bevy_app::*;
/// # #[derive(Default)]
/// # pub struct VelocityPlugin;
/// # impl Plugin for VelocityPlugin { fn build(&self, _: &mut App) {} }
/// # }
/// #
/// # mod collision {
/// # pub mod capsule {
/// # use bevy_app::*;
/// # #[derive(Default)]
/// # pub struct CapsuleCollisionPlugin;
/// # impl Plugin for CapsuleCollisionPlugin { fn build(&self, _: &mut App) {} }
/// # }
/// # }
/// #
/// # #[derive(Default)]
/// # pub struct TickratePlugin;
/// # impl Plugin for TickratePlugin { fn build(&self, _: &mut App) {} }
/// #
/// # mod features {
/// # use bevy_app::*;
/// # #[derive(Default)]
/// # pub struct ForcePlugin;
/// # impl Plugin for ForcePlugin { fn build(&self, _: &mut App) {} }
/// # }
/// #
/// # mod web {
/// # use bevy_app::*;
/// # #[derive(Default)]
/// # pub struct WebCompatibilityPlugin;
/// # impl Plugin for WebCompatibilityPlugin { fn build(&self, _: &mut App) {} }
/// # }
/// #
/// # mod internal {
/// # use bevy_app::*;
/// # #[derive(Default)]
/// # pub struct InternalPlugin;
/// # impl Plugin for InternalPlugin { fn build(&self, _: &mut App) {} }
/// # }
/// #
/// plugin_group! {
/// /// Doc comments and annotations are supported: they will be added to the generated plugin
/// /// group.
/// #[derive(Debug)]
/// pub struct PhysicsPlugins {
/// // If referencing a plugin within the same module, you must prefix it with a colon `:`.
/// :TickratePlugin,
/// // If referencing a plugin within a different module, there must be three colons `:::`
/// // between the final module and the plugin name.
/// collision::capsule:::CapsuleCollisionPlugin,
/// velocity:::VelocityPlugin,
/// // If you feature-flag a plugin, it will be automatically documented. There can only be
/// // one automatically documented feature flag, and it must be first. All other
/// // `#[cfg()]` attributes must be wrapped by `#[custom()]`.
/// #[cfg(feature = "external_forces")]
/// features:::ForcePlugin,
/// // More complicated `#[cfg()]`s and annotations are not supported by automatic doc
/// // generation, in which case you must wrap it in `#[custom()]`.
/// #[custom(cfg(target_arch = "wasm32"))]
/// web:::WebCompatibilityPlugin,
/// // You can hide plugins from documentation. Due to macro limitations, hidden plugins
/// // must be last.
/// #[doc(hidden)]
/// internal:::InternalPlugin
/// }
/// /// You may add doc comments after the plugin group as well. They will be appended after
/// /// the documented list of plugins.
/// }
/// ```
#[macro_export]
macro_rules! plugin_group {
{
$(#[$group_meta:meta])*
$vis:vis struct $group:ident {
$(
$(#[cfg(feature = $plugin_feature:literal)])?
$(#[custom($plugin_meta:meta)])*
$($plugin_path:ident::)* : $plugin_name:ident
),*
$(
$(,)?$(
#[doc(hidden)]
$(#[cfg(feature = $hidden_plugin_feature:literal)])?
$(#[custom($hidden_plugin_meta:meta)])*
$($hidden_plugin_path:ident::)* : $hidden_plugin_name:ident
),+
)?
$(,)?
}
$($(#[doc = $post_doc:literal])+)?
} => {
$(#[$group_meta])*
///
$(#[doc = concat!(
" - [`", stringify!($plugin_name), "`](" $(, stringify!($plugin_path), "::")*, stringify!($plugin_name), ")"
$(, " - with feature `", $plugin_feature, "`")?
)])*
$(
///
$(#[doc = $post_doc])+
)?
$vis struct $group;
impl $crate::PluginGroup for $group {
fn build(self) -> $crate::PluginGroupBuilder {
let mut group = $crate::PluginGroupBuilder::start::<Self>();
$(
$(#[cfg(feature = $plugin_feature)])?
$(#[$plugin_meta])*
{
const _: () = {
const fn check_default<T: Default>() {}
check_default::<$($plugin_path::)*$plugin_name>();
};
group = group.add(<$($plugin_path::)*$plugin_name>::default());
}
)*
$($(
$(#[cfg(feature = $hidden_plugin_feature)])?
$(#[$hidden_plugin_meta])*
{
const _: () = {
const fn check_default<T: Default>() {}
check_default::<$($hidden_plugin_path::)*$hidden_plugin_name>();
};
group = group.add(<$($hidden_plugin_path::)*$hidden_plugin_name>::default());
}
)+)?
group
}
}
};
}
/// Combines multiple [`Plugin`]s into a single unit.
///
/// If you want an easier, but slightly more restrictive, method of implementing this trait, you
/// may be interested in the [`plugin_group!`] macro.
pub trait PluginGroup: Sized {
/// Configures the [`Plugin`]s that are to be added.
fn build(self) -> PluginGroupBuilder;

View file

@ -18,6 +18,7 @@ use std::time::Duration;
/// This plugin is included within `DefaultPlugins` and `MinimalPlugins` when the `bevy_ci_testing`
/// feature is enabled. It is recommended to only used this plugin during testing (manual or
/// automatic), and disable it during regular development and for production builds.
#[derive(Default)]
pub struct CiTestingPlugin;
impl Plugin for CiTestingPlugin {

View file

@ -47,6 +47,7 @@ pub mod states;
///
/// Note: The third method is not recommended, as it requires you to remove the feature before
/// creating a build for release to the public.
#[derive(Default)]
pub struct DevToolsPlugin;
impl Plugin for DevToolsPlugin {

View file

@ -113,6 +113,7 @@ const LINE_JOINT_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(11627807
/// A [`Plugin`] that provides an immediate mode drawing api for visual debugging.
///
/// Requires to be loaded after [`PbrPlugin`](bevy_pbr::PbrPlugin) or [`SpritePlugin`](bevy_sprite::SpritePlugin).
#[derive(Default)]
pub struct GizmoPlugin;
impl Plugin for GizmoPlugin {

View file

@ -1,171 +1,78 @@
use bevy_app::{Plugin, PluginGroup, PluginGroupBuilder};
/// This plugin group will add all the default plugins for a *Bevy* application:
/// * [`PanicHandlerPlugin`](crate::app::PanicHandlerPlugin)
/// * [`LogPlugin`](crate::log::LogPlugin)
/// * [`TaskPoolPlugin`](crate::core::TaskPoolPlugin)
/// * [`TypeRegistrationPlugin`](crate::core::TypeRegistrationPlugin)
/// * [`FrameCountPlugin`](crate::core::FrameCountPlugin)
/// * [`TimePlugin`](crate::time::TimePlugin)
/// * [`TransformPlugin`](crate::transform::TransformPlugin)
/// * [`HierarchyPlugin`](crate::hierarchy::HierarchyPlugin)
/// * [`DiagnosticsPlugin`](crate::diagnostic::DiagnosticsPlugin)
/// * [`InputPlugin`](crate::input::InputPlugin)
/// * [`WindowPlugin`](crate::window::WindowPlugin)
/// * [`AccessibilityPlugin`](crate::a11y::AccessibilityPlugin)
/// * [`AssetPlugin`](crate::asset::AssetPlugin) - with feature `bevy_asset`
/// * [`ScenePlugin`](crate::scene::ScenePlugin) - with feature `bevy_scene`
/// * [`WinitPlugin`](crate::winit::WinitPlugin) - with feature `bevy_winit`
/// * [`RenderPlugin`](crate::render::RenderPlugin) - with feature `bevy_render`
/// * [`ImagePlugin`](crate::render::texture::ImagePlugin) - with feature `bevy_render`
/// * [`PipelinedRenderingPlugin`](crate::render::pipelined_rendering::PipelinedRenderingPlugin) - with feature `bevy_render` when not targeting `wasm32`
/// * [`CorePipelinePlugin`](crate::core_pipeline::CorePipelinePlugin) - with feature `bevy_core_pipeline`
/// * [`SpritePlugin`](crate::sprite::SpritePlugin) - with feature `bevy_sprite`
/// * [`TextPlugin`](crate::text::TextPlugin) - with feature `bevy_text`
/// * [`UiPlugin`](crate::ui::UiPlugin) - with feature `bevy_ui`
/// * [`PbrPlugin`](crate::pbr::PbrPlugin) - with feature `bevy_pbr`
/// * [`GltfPlugin`](crate::gltf::GltfPlugin) - with feature `bevy_gltf`
/// * [`AudioPlugin`](crate::audio::AudioPlugin) - with feature `bevy_audio`
/// * [`GilrsPlugin`](crate::gilrs::GilrsPlugin) - with feature `bevy_gilrs`
/// * [`AnimationPlugin`](crate::animation::AnimationPlugin) - with feature `bevy_animation`
/// * [`GizmoPlugin`](crate::gizmos::GizmoPlugin) - with feature `bevy_gizmos`
/// * [`StatesPlugin`](crate::state::app::StatesPlugin) - with feature `bevy_state`
/// * [`DevToolsPlugin`](crate::dev_tools::DevToolsPlugin) - with feature `bevy_dev_tools`
/// * [`CiTestingPlugin`](crate::dev_tools::ci_testing::CiTestingPlugin) - with feature `bevy_ci_testing`
///
/// [`DefaultPlugins`] obeys *Cargo* *feature* flags. Users may exert control over this plugin group
/// by disabling `default-features` in their `Cargo.toml` and enabling only those features
/// that they wish to use.
///
/// [`DefaultPlugins`] contains all the plugins typically required to build
/// a *Bevy* application which includes a *window* and presentation components.
/// For *headless* cases without a *window* or presentation, see [`MinimalPlugins`].
pub struct DefaultPlugins;
impl PluginGroup for DefaultPlugins {
fn build(self) -> PluginGroupBuilder {
let mut group = PluginGroupBuilder::start::<Self>();
group = group
.add(bevy_app::PanicHandlerPlugin)
.add(bevy_log::LogPlugin::default())
.add(bevy_core::TaskPoolPlugin::default())
.add(bevy_core::TypeRegistrationPlugin)
.add(bevy_core::FrameCountPlugin)
.add(bevy_time::TimePlugin)
.add(bevy_transform::TransformPlugin)
.add(bevy_hierarchy::HierarchyPlugin)
.add(bevy_diagnostic::DiagnosticsPlugin)
.add(bevy_input::InputPlugin)
.add(bevy_window::WindowPlugin::default())
.add(bevy_a11y::AccessibilityPlugin);
#[cfg(not(target_arch = "wasm32"))]
{
group = group.add(bevy_app::TerminalCtrlCHandlerPlugin);
}
use bevy_app::{plugin_group, Plugin};
plugin_group! {
/// This plugin group will add all the default plugins for a *Bevy* application:
pub struct DefaultPlugins {
bevy_app:::PanicHandlerPlugin,
bevy_log:::LogPlugin,
bevy_core:::TaskPoolPlugin,
bevy_core:::TypeRegistrationPlugin,
bevy_core:::FrameCountPlugin,
bevy_time:::TimePlugin,
bevy_transform:::TransformPlugin,
bevy_hierarchy:::HierarchyPlugin,
bevy_diagnostic:::DiagnosticsPlugin,
bevy_input:::InputPlugin,
bevy_window:::WindowPlugin,
bevy_a11y:::AccessibilityPlugin,
#[custom(cfg(not(target_arch = "wasm32")))]
bevy_app:::TerminalCtrlCHandlerPlugin,
#[cfg(feature = "bevy_asset")]
{
group = group.add(bevy_asset::AssetPlugin::default());
}
bevy_asset:::AssetPlugin,
#[cfg(feature = "bevy_scene")]
{
group = group.add(bevy_scene::ScenePlugin);
}
bevy_scene:::ScenePlugin,
#[cfg(feature = "bevy_winit")]
{
group = group.add::<bevy_winit::WinitPlugin>(bevy_winit::WinitPlugin::default());
}
bevy_winit:::WinitPlugin,
#[cfg(feature = "bevy_render")]
{
group = group
.add(bevy_render::RenderPlugin::default())
// NOTE: Load this after renderer initialization so that it knows about the supported
// compressed texture formats
.add(bevy_render::texture::ImagePlugin::default());
#[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))]
{
group = group.add(bevy_render::pipelined_rendering::PipelinedRenderingPlugin);
}
}
#[cfg(feature = "bevy_core_pipeline")]
{
group = group.add(bevy_core_pipeline::CorePipelinePlugin);
}
#[cfg(feature = "bevy_sprite")]
{
group = group.add(bevy_sprite::SpritePlugin);
}
#[cfg(feature = "bevy_text")]
{
group = group.add(bevy_text::TextPlugin);
}
#[cfg(feature = "bevy_ui")]
{
group = group.add(bevy_ui::UiPlugin);
}
#[cfg(feature = "bevy_pbr")]
{
group = group.add(bevy_pbr::PbrPlugin::default());
}
bevy_render:::RenderPlugin,
// NOTE: Load this after renderer initialization so that it knows about the supported
// compressed texture formats
// compressed texture formats.
#[cfg(feature = "bevy_render")]
bevy_render::texture:::ImagePlugin,
#[cfg(feature = "bevy_render")]
#[custom(cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded")))]
bevy_render::pipelined_rendering:::PipelinedRenderingPlugin,
#[cfg(feature = "bevy_core_pipeline")]
bevy_core_pipeline:::CorePipelinePlugin,
#[cfg(feature = "bevy_sprite")]
bevy_sprite:::SpritePlugin,
#[cfg(feature = "bevy_text")]
bevy_text:::TextPlugin,
#[cfg(feature = "bevy_ui")]
bevy_ui:::UiPlugin,
#[cfg(feature = "bevy_pbr")]
bevy_pbr:::PbrPlugin,
// NOTE: Load this after renderer initialization so that it knows about the supported
// compressed texture formats.
#[cfg(feature = "bevy_gltf")]
{
group = group.add(bevy_gltf::GltfPlugin::default());
}
bevy_gltf:::GltfPlugin,
#[cfg(feature = "bevy_audio")]
{
group = group.add(bevy_audio::AudioPlugin::default());
}
bevy_audio:::AudioPlugin,
#[cfg(feature = "bevy_gilrs")]
{
group = group.add(bevy_gilrs::GilrsPlugin);
}
bevy_gilrs:::GilrsPlugin,
#[cfg(feature = "bevy_animation")]
{
group = group.add(bevy_animation::AnimationPlugin);
}
bevy_animation:::AnimationPlugin,
#[cfg(feature = "bevy_gizmos")]
{
group = group.add(bevy_gizmos::GizmoPlugin);
}
bevy_gizmos:::GizmoPlugin,
#[cfg(feature = "bevy_state")]
{
group = group.add(bevy_state::app::StatesPlugin);
}
bevy_state::app:::StatesPlugin,
#[cfg(feature = "bevy_dev_tools")]
{
group = group.add(bevy_dev_tools::DevToolsPlugin);
}
bevy_dev_tools:::DevToolsPlugin,
#[cfg(feature = "bevy_ci_testing")]
{
group = group.add(bevy_dev_tools::ci_testing::CiTestingPlugin);
}
group = group.add(IgnoreAmbiguitiesPlugin);
group
bevy_dev_tools::ci_testing:::CiTestingPlugin,
#[doc(hidden)]
:IgnoreAmbiguitiesPlugin,
}
/// [`DefaultPlugins`] obeys *Cargo* *feature* flags. Users may exert control over this plugin group
/// by disabling `default-features` in their `Cargo.toml` and enabling only those features
/// that they wish to use.
///
/// [`DefaultPlugins`] contains all the plugins typically required to build
/// a *Bevy* application which includes a *window* and presentation components.
/// For *headless* cases without a *window* or presentation, see [`MinimalPlugins`].
}
#[derive(Default)]
struct IgnoreAmbiguitiesPlugin;
impl Plugin for IgnoreAmbiguitiesPlugin {
@ -185,39 +92,23 @@ impl Plugin for IgnoreAmbiguitiesPlugin {
}
}
/// This plugin group will add the minimal plugins for a *Bevy* application:
/// * [`TaskPoolPlugin`](crate::core::TaskPoolPlugin)
/// * [`TypeRegistrationPlugin`](crate::core::TypeRegistrationPlugin)
/// * [`FrameCountPlugin`](crate::core::FrameCountPlugin)
/// * [`TimePlugin`](crate::time::TimePlugin)
/// * [`ScheduleRunnerPlugin`](crate::app::ScheduleRunnerPlugin)
/// * [`CiTestingPlugin`](crate::dev_tools::ci_testing::CiTestingPlugin) - with feature `bevy_ci_testing`
///
/// This group of plugins is intended for use for minimal, *headless* programs
/// see the [*Bevy* *headless* example](https://github.com/bevyengine/bevy/blob/main/examples/app/headless.rs)
/// and includes a [schedule runner (`ScheduleRunnerPlugin`)](crate::app::ScheduleRunnerPlugin)
/// to provide functionality that would otherwise be driven by a windowed application's
/// *event loop* or *message loop*.
///
/// Windowed applications that wish to use a reduced set of plugins should consider the
/// [`DefaultPlugins`] plugin group which can be controlled with *Cargo* *feature* flags.
pub struct MinimalPlugins;
impl PluginGroup for MinimalPlugins {
fn build(self) -> PluginGroupBuilder {
let mut group = PluginGroupBuilder::start::<Self>();
group = group
.add(bevy_core::TaskPoolPlugin::default())
.add(bevy_core::TypeRegistrationPlugin)
.add(bevy_core::FrameCountPlugin)
.add(bevy_time::TimePlugin)
.add(bevy_app::ScheduleRunnerPlugin::default());
plugin_group! {
/// This plugin group will add the minimal plugins for a *Bevy* application:
pub struct MinimalPlugins {
bevy_core:::TaskPoolPlugin,
bevy_core:::TypeRegistrationPlugin,
bevy_core:::FrameCountPlugin,
bevy_time:::TimePlugin,
bevy_app:::ScheduleRunnerPlugin,
#[cfg(feature = "bevy_ci_testing")]
{
group = group.add(bevy_dev_tools::ci_testing::CiTestingPlugin);
}
group
bevy_dev_tools::ci_testing:::CiTestingPlugin,
}
/// This group of plugins is intended for use for minimal, *headless* programs
/// see the [*Bevy* *headless* example](https://github.com/bevyengine/bevy/blob/main/examples/app/headless.rs)
/// and includes a [schedule runner (`ScheduleRunnerPlugin`)](crate::app::ScheduleRunnerPlugin)
/// to provide functionality that would otherwise be driven by a windowed application's
/// *event loop* or *message loop*.
///
/// Windowed applications that wish to use a reduced set of plugins should consider the
/// [`DefaultPlugins`] plugin group which can be controlled with *Cargo* *feature* flags.
}

View file

@ -211,6 +211,7 @@ impl AppExtStates for App {
}
/// Registers the [`StateTransition`] schedule in the [`MainScheduleOrder`] to enable state processing.
#[derive(Default)]
pub struct StatesPlugin;
impl Plugin for StatesPlugin {