Skip to content

Commit

Permalink
Add dynamic linking example
Browse files Browse the repository at this point in the history
This was a little tricky to get working:
- Add a few extra dependencies to the dev-dependencies which are
  required by the two libraries.
- The plugin library itself will not be automatically compiled with the
  binary that loads it, but there's no way in Cargo to fix that without
  making them direct dependencies (which we explicitly don't want)
- For both binaries (executable and library) to use the same bevy
  library instance, it needs to be dynamically linked. The only real way
  (while keeping both as "example" binaries) to do this is to include
  the dynamic library version of bevy as a dev dependency, thereby
  making tests and example use the dynamically-linked bevy.
  • Loading branch information
kleinesfilmroellchen committed Sep 5, 2023
1 parent 1fe7a72 commit af6f9ee
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 0 deletions.
26 changes: 26 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,13 @@ futures-lite = "1.11.3"
crossbeam-channel = "0.5.0"
argh = "0.1.12"

# Needed for the dynamic linking examples
libloading = { version = "0.8" }
parking_lot = "0.12.1"
bevy_dynamic_plugin = { path = "crates/bevy_dynamic_plugin" }
# bevy must be dynamically linked into the examples, or else dynamic plugin libraries get their own private copy of bevy and chaos ensues
bevy = { path = ".", features = ["dynamic_linking"] }

[[example]]
name = "hello_world"
path = "examples/hello_world.rs"
Expand Down Expand Up @@ -2290,6 +2297,25 @@ description = "Demonstrates resizing and responding to resizing a window"
category = "Window"
wasm = true

[[example]]
name = "dynamic_plugin"
path = "examples/ecs/dynamic_plugin.rs"
# remember to use `-Zshare-generics=n` on Windows, see the template .cargo/config_fast_builds
crate-type = ["dylib"]
doc-scrape-examples = false

[package.metadata.example.dynamic_plugin]
name = "Dynamic Plugin"
description = "A dynamic plugin in a shared library loaded by the load_dynamic_plugin example at run time"

[[example]]
name = "load_dynamic_plugin"
path = "examples/ecs/load_dynamic_plugin.rs"

[package.metadata.example.load_dynamic_plugin]
name = "Dynamic Plugin Loading"
description = "Demonstrates loading a dynamic plugin from a shared library"

[profile.wasm-release]
inherits = "release"
opt-level = "z"
Expand Down
18 changes: 18 additions & 0 deletions examples/ecs/dynamic_plugin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//! A dynamic library that contains a dynamically loaded plugin. See the `load_dynamic_plugin` on how to load such a plugin.
use bevy::prelude::*;

/// Derive DynamicPlugin on one main plugin, which will then be loaded by [`bevy_dynamic_plugin::load_dynamic_plugin`].
#[derive(DynamicPlugin)]
struct MyDynamicPlugin;

impl Plugin for MyDynamicPlugin {
fn build(&self, app: &mut App) {
info!("Plugin is being loaded...");
app.add_systems(Update, say_hello);
}
}

fn say_hello() {
info!("Hello from the dynamic plugin!");
}
33 changes: 33 additions & 0 deletions examples/ecs/load_dynamic_plugin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//! Loads a dynamic plugin from the shared library `dynamic_plugin`
use std::path::PathBuf;

use bevy::prelude::*;
use bevy_dynamic_plugin::dynamically_load_plugin;
use libloading::Library;
use parking_lot::Mutex;

/// If the library wasn't stored here, libloading would unload it as soon as we were done with it below.
/// This would cause the program to crash/segfault later when bevy tries to invoke the plugin's systems.
/// Therefore, libraries should be stored such that they outlive the [`App`].
/// A simple method is to use a global mutex.
static LIBRARY: Mutex<Option<Library>> = Mutex::new(None);

fn main() {
let mut app = App::new();
app.add_plugins(DefaultPlugins);

#[cfg(any(target_family = "windows", target_family = "unix"))]
{
// The ending can be the default library ending of the operating system (e.g. .dll, .so).
let plugin_name = PathBuf::from("dynamic_plugin");

let (library, plugin) = unsafe { dynamically_load_plugin(plugin_name) }.unwrap();
app.add_plugins(plugin);
info!("Loaded plugin!");
// Make sure the plugin stays alive by storing it in a global variable:
*LIBRARY.lock() = Some(library);
}

app.run();
}

0 comments on commit af6f9ee

Please sign in to comment.