Интеграция

Как подключить ядро Plugify к своему CMake-проекту и управлять им из приложения.

На этой странице показано, как подключить ядро к CMake-проекту и как приложение создаёт, обновляет и завершает Plugify.

Подключение ядра к проекту

Как подкаталог

Положите исходники ядра в свой проект, например Git-сабмодулем в external/plugify, и подключите их:

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)

Через FetchContent

CMake может скачать ядро при конфигурации проекта:

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 можно указать тег релиза или ветку, например main.

Как установленный пакет

Если ядро собрано и установлено (cmake --install), найдите его через find_package:

CMakeLists.txt
find_package(plugify REQUIRED)

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

Если префикс установки нестандартный, передайте его через -DCMAKE_PREFIX_PATH=<prefix>.

Оба варианта

Проект может дать пользователю выбор между установленным ядром и копией в проекте:

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)

По умолчанию ядру нужен C++23; поставьте CMAKE_CXX_STANDARD своего проекта тоже в 23. Опции CMake ядра перечислены в Инструкции по сборке.

Запуск Plugify в приложении

Короткий путь

MakePlugify создаёт ядро со всеми настройками по умолчанию и заданной базовой папкой. Расширения ищутся в <база>/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() ядра проверяет папки: базовая папка должна существовать, остальные создаются. GetManager().Initialize() загружает расширения. Если одно расширение не загрузилось, остальные всё равно загружаются, а ядро пишет в лог отчёт; ошибка возвращается, только если загрузить не удалось вообще ничего.

Со своими настройками

Plugify::CreateBuilder() позволяет задать конфиг и заменить сервисы, например логгер:

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;
}

Чтобы читать настройки из файла .pconfig, вызовите WithConfigFile("plugify.pconfig"). Формат файла описан в Архитектуре.

Перезагрузка

Перезагрузить отдельный плагин ядро не умеет. Чтобы перезагрузить всё, вызовите GetManager().Terminate(), а затем GetManager().Initialize(): манифесты читаются заново, а расширения загружаются с диска.

Хорошие практики

  • Собирайте ядро, языковые модули и хост совместимыми компиляторами с одной стандартной библиотекой C++.
  • Вызывайте Initialize, Update и Terminate из одного потока, того, который создал ядро.
  • Проверяйте каждый Result: текст ошибки говорит, что пошло не так.
  • Отключите лишнее: PLUGIFY_BUILD_TESTS и PLUGIFY_BUILD_DOCS.

Устранение неполадок

  • Расширение не запустилось. Посмотрите отчёт, который ядро пишет в лог после загрузки: в нём причина по каждому незапущенному расширению. logging.printReport добавляет ещё и время каждой стадии.
  • «Validation failed: baseDir does not exist». Создайте базовую папку или передайте существующий путь. Относительные пути отсчитываются от текущей папки процесса.
  • Ошибки сборки. Проверьте, что компилятор поддерживает C++23, а CMake версии 3.14 или новее.