#include "Libraries/Async/Async.h"
#include "Libraries/FileSystemWatcher/FileSystemWatcher.h"
namespace SC
{
struct IPluginContract
{
static constexpr auto InterfaceHash =
PluginHash(
"IPluginContract");
};
struct PluginClient : public IPluginContract
{
PluginClient()
{
IPluginContract::onDraw = []()
{
};
}
bool init() { return true; }
bool close() { return true; }
};
SC_PLUGIN_DEFINE(PluginClient);
SC_PLUGIN_EXPORT_INTERFACES(PluginClient, IPluginContract);
struct PluginHost
{
{
eventLoop = &loop;
SC_TRY(PluginCompiler::findBestCompiler(compiler));
SC_TRY(PluginSysroot::findBestSysroot(compiler.
type, sysroot));
fileSystemWatcherRunner.init(*eventLoop);
SC_TRY(fileSystemWatcher.init(fileSystemWatcherRunner));
watcher.notifyCallback.bind<PluginHost, &PluginHost::onFileChanged>(*this);
SC_TRY(fileSystemWatcher.watch(watcher, pluginsPath.view()));
}
{
SC_TRY(fileSystemWatcher.close());
eventLoop = nullptr;
}
{
SC_TRY(PluginScanner::scanDirectory(pluginsPath.view(), definitions, fileStorage, definitionsSpan))
}
{
SC_TRY(registry.
loadPlugin(identifier, compiler, sysroot, executablePath.view(),
PluginRegistry::LoadMode::Reload));
}
void draw()
{
if (contract)
{
contract->onDraw();
}
}
private:
IPluginContract* contract = nullptr;
{
auto reload = [this](const PluginIdentifier& plugin) { (void)load(plugin.view()); };
}
};
}
constexpr unsigned int PluginHash(const char(&str)[N])
Compute compile time FNV hash for a char array.
Definition PluginHash.h:33
A structure to describe file stats.
Definition FileSystem.h:18
TimeMs modifiedTime
Time when file was last modified.
Definition FileSystem.h:20
Result setLastModifiedTime(StringSpan file, TimeMs time)
Change last modified time of a given file.
Result getFileStat(StringSpan file, FileStat &fileStat)
Obtains stats (size, modified time) about a file.
Compiles a plugin to a dynamic library.
Definition Plugin.h:172
FixedVector< StringPath, 8 > includePaths
Path to include directories used to compile plugin.
Definition Plugin.h:203
Type type
Compile Type.
Definition Plugin.h:199
Plugin description, category, dependencies, files and directory location.
Definition Plugin.h:101
PluginIdentity identity
Uniquely identifier a plugin.
Definition Plugin.h:102
FixedString< 256 > description
Long description of plugin.
Definition Plugin.h:104
FixedVector< PluginIdentifier, 8 > dependencies
Dependencies necessary to load this plugin.
Definition Plugin.h:108
FixedString< 64 > category
Category where plugin belongs to.
Definition Plugin.h:105
FixedVector< PluginBuildOption, 8 > build
Build options.
Definition Plugin.h:109
A plugin dynamic library loaded from a SC::PluginRegistry.
Definition Plugin.h:249
SystemDynamicLibrary dynamicLibrary
System handle of plugin's dynamic library.
Definition Plugin.h:251
bool queryInterface(T *&outInterface) const
Try to obtain a given interface as exported by a plugin through SC_PLUGIN_EXPORT_INTERFACES macro.
Definition Plugin.h:262
PluginIdentifier identifier
Unique string identifying the plugin.
Definition Plugin.h:89
FixedString< 64 > name
Plugin name.
Definition Plugin.h:90
FixedString< 16 > version
Plugin version (x.y.z)
Definition Plugin.h:91
Holds a registry of plugins, loading and compiling them on the fly.
Definition Plugin.h:287
void init(Span< PluginDynamicLibrary > librariesStorage)
Init a PluginRegistry with some given storage.
Result unloadPlugin(const StringView identifier)
Unloads an already loaded plugin by its identifier.
Result loadPlugin(const StringView identifier, const PluginCompiler &compiler, const PluginSysroot &sysroot, StringView executablePath, LoadMode loadMode=LoadMode::Load)
Loads a plugin with given identifier, compiling it with given PluginCompiler.
Result removeAllBuildProducts(const StringView identifier)
Removes all temporary build products of the Plugin with given identifier.
Result replaceDefinitions(Span< PluginDefinition > &&definitions)
Appends the definitions to registry.
PluginDynamicLibrary * findPlugin(const StringView identifier)
Find a PluginDynamicLibrary in the registry with a given identifier.
void getPluginsToReloadBecauseOf(StringView relativePath, Time::Milliseconds tolerance, Function< void(const PluginIdentifier &)> onPlugin)
Enumerates all plugins that must be reloaded when relativePath is modified.
Holds include and library paths for a system toolchain, used to let plugins link to libc and libc++.
Definition Plugin.h:224
StringPath executableFile
Path to current executable.
Definition Testing.h:51
StringPath libraryRootDirectory
Path to sources directory for library.
Definition Testing.h:50
Type-safe wrapper of uint64 used to represent milliseconds.
Definition Time.h:43
The main use case is for now splitting applications in smaller pieces that can be hot-reloaded at runtime.
A secondary use case could be allowing customization on a delivered application (mainly on Desktop systems). Plugins are always meant to be delivered in source code form (.cpp
) and they're compiled on the fly. A plugin is made of a single .cpp
file and it declares itself through a special comment in the source code. Such comment can declare the name, the version, a description / category and a list of dependencies.
A plugin can be modified, unloaded, re-compiled and re-loaded to provide additional functionality.
The list of dependencies makes it possible to find recursive dependencies and unload them before unload a plugin.
As of today this is all implemented using native dynamic library mechanisms that are being loaded directly in the process.
Doing the wrong thing with memory or forgetting to clean everything during shutdown can quickly crash the main executable.
This is the list of videos that have been recorded showing some usages of the library:
There are plans to experiment with out of process plugins using some sort of RPC system (like Audio Unit plugins on macOS) and / or experimenting using WASM as a plugin host to eliminate such instability / security issues. Other ideas include redistribute a minimal C++ toolchain (probably a customized clang) that can compile the plugins without needing a system compiler or a sysroot, as all public headers of libraries in this project do not need any system or compiler header.