Integrating

How to add the Plugify core to your CMake project and drive it from your application.

This page shows how to add the core to a CMake project and how an application creates, updates and shuts down Plugify.

Adding the core to your project

As a subdirectory

Put the source tree of the core into your project, for example as a Git submodule in external/plugify, and add it:

CMakeLists.txt
set(PLUGIFY_BUILD_TESTS OFF CACHE INTERNAL "")
add_subdirectory(external/plugify)

add_executable(my_app main.cpp)
target_link_libraries(my_app PRIVATE plugify::plugify)

With FetchContent

CMake can download the core while configuring your project:

CMakeLists.txt
set(PLUGIFY_BUILD_TESTS OFF CACHE INTERNAL "")

include(FetchContent)
FetchContent_Declare(
    plugify
    GIT_REPOSITORY https://github.com/untrustedmodders/plugify.git
    GIT_TAG v3.1.0
)
FetchContent_MakeAvailable(plugify)

add_executable(my_app main.cpp)
target_link_libraries(my_app PRIVATE plugify::plugify)

GIT_TAG takes a release tag or a branch name such as main.

As an installed package

If the core is built and installed (cmake --install), find it with find_package:

CMakeLists.txt
find_package(plugify REQUIRED)

add_executable(my_app main.cpp)
target_link_libraries(my_app PRIVATE plugify::plugify)

Pass the install prefix with -DCMAKE_PREFIX_PATH=<prefix> if it is not a standard location.

Supporting both

A project can let the user choose between an installed core and a bundled copy:

CMakeLists.txt
option(MY_APP_USE_EXTERNAL_PLUGIFY "Use an installed Plugify" OFF)

if(MY_APP_USE_EXTERNAL_PLUGIFY)
    find_package(plugify REQUIRED)
else()
    set(PLUGIFY_BUILD_TESTS OFF CACHE INTERNAL "")
    add_subdirectory(external/plugify)
endif()

target_link_libraries(my_app PRIVATE plugify::plugify)

The core needs C++23 by default; set CMAKE_CXX_STANDARD of your project to 23 as well. The CMake options of the core are listed in Build Instructions.

Running Plugify in your application

The short way

MakePlugify creates the core with all defaults and the given base folder. Extensions are looked for in <base>/extensions.

main.cpp
#include <plugify/plugify.hpp>
#include <iostream>

int main() {
    auto result = plugify::MakePlugify("./app");
    if (!result) {
        std::cerr << "Failed to create Plugify: " << result.error() << '\n';
        return 1;
    }

    auto plugify = result.value();
    if (auto init = plugify->Initialize(); !init) {
        std::cerr << "Failed to initialize: " << init.error() << '\n';
        return 1;
    }

    // Find, check and load modules and plugins
    const auto& manager = plugify->GetManager();
    if (auto load = manager.Initialize(); !load) {
        std::cerr << "Failed to load extensions: " << load.error() << '\n';
    }

    bool running = true;
    while (running) {
        plugify->Update();
        // Your application logic here
    }

    plugify->Terminate();
    return 0;
}

Initialize() of the core checks the folders: the base folder must exist, the others are created. GetManager().Initialize() loads the extensions. If one extension fails, the others are still loaded and the core logs a report; an error is returned only when nothing can be loaded at all.

With your own settings

Plugify::CreateBuilder() lets you set the config and replace services, for example the logger:

main.cpp
#include <plugify/plugify.hpp>
#include <iostream>
#include <memory>

class MyLogger final : public plugify::ILogger {
public:
    void Log(std::string_view message, plugify::Severity severity, const plugify::Location&) override {
        if (severity >= _level) {
            std::cout << "[plugify] " << message << '\n';
        }
    }
    void SetLogLevel(plugify::Severity severity) override { _level = severity; }
    plugify::Severity GetLogLevel() override { return _level; }
    void Flush() override { std::cout.flush(); }

private:
    plugify::Severity _level = plugify::Severity::Info;
};

int main() {
    plugify::Config config;
    config.paths.baseDir = "./app";
    config.paths.extensionsDir = "extensions";
    config.logging.severity = plugify::Severity::Info;
    config.logging.printReport = true;

    auto result = plugify::Plugify::CreateBuilder()
        .WithConfig(config)
        .WithLogger(std::make_shared<MyLogger>())
        .Build();
    if (!result) {
        std::cerr << "Failed: " << result.error() << '\n';
        return 1;
    }

    auto plugify = result.value();
    if (auto init = plugify->Initialize(); !init) {
        std::cerr << "Failed to initialize: " << init.error() << '\n';
        return 1;
    }
    if (auto load = plugify->GetManager().Initialize(); !load) {
        std::cerr << "Failed to load extensions: " << load.error() << '\n';
    }

    // Services registered in the core can be resolved by the host as well
    auto logger = plugify->GetServices().Resolve<plugify::ILogger>();
    logger->Log("Application started", plugify::Severity::Info);

    bool running = true;
    while (running) {
        plugify->Update();
    }

    plugify->Terminate();
    return 0;
}

To read the settings from a .pconfig file instead, call WithConfigFile("plugify.pconfig"). The file format is described in Architecture.

Reloading

The core cannot reload a single plugin. To reload everything, call GetManager().Terminate() and then GetManager().Initialize(): the manifests are read again and the extensions are loaded from disk.

Good practice

  • Build the core, the language modules and the host with compatible compilers and the same C++ standard library.
  • Call Initialize, Update and Terminate from one thread, the one that created the core.
  • Check every Result: the error text says what went wrong.
  • Turn off what you do not need: PLUGIFY_BUILD_TESTS and PLUGIFY_BUILD_DOCS.

Troubleshooting

  • An extension did not start. Look at the report the core logs after loading: it gives the reason for every extension that did not start. Set logging.printReport to also get timings.
  • "Validation failed: baseDir does not exist". Create the base folder or pass a path that exists. Relative paths are taken from the current folder of the process.
  • Build errors. Check that your compiler supports C++23 and that CMake is 3.14 or later.