/******************************************************************************** * Copyright (C) 2017-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * * * This software is distributed under the terms of the * * GNU Lesser General Public Licence (LGPL) version 3, * * copied verbatim in the file "LICENSE" * ********************************************************************************/ #ifndef FAIR_MQ_PLUGINMANAGER_H #define FAIR_MQ_PLUGINMANAGER_H #include #include #include #if FAIRMQ_HAS_STD_FILESYSTEM #include namespace fs = std::filesystem; #else #define BOOST_FILESYSTEM_VERSION 3 #define BOOST_FILESYSTEM_NO_DEPRECATED #include namespace fs = ::boost::filesystem; #endif #include #include #include #include #include #include #include #include #include #include #include #include // forward namespace fair::mq { /** * @class PluginManager PluginManager.h * @brief manages and owns plugin instances * * The plugin manager is responsible for the whole plugin lifecycle. It * facilitates two plugin mechanisms: * A prelinked dynamic plugins (shared libraries) * B dynamic plugins (shared libraries) * C static plugins (builtin) */ class PluginManager { public: using PluginFactory = std::unique_ptr(PluginServices&); PluginManager(); PluginManager(const PluginManager&) = delete; PluginManager(PluginManager&&) = delete; PluginManager& operator=(const PluginManager&) = delete; PluginManager& operator=(PluginManager&&) = delete; PluginManager(const std::vector& args); ~PluginManager() { LOG(debug) << "Shutting down Plugin Manager"; } auto SetSearchPaths(const std::vector&) -> void; auto AppendSearchPath(const fs::path&) -> void; auto PrependSearchPath(const fs::path&) -> void; auto SearchPaths() const -> const std::vector& { return fSearchPaths; } struct BadSearchPath : std::invalid_argument { using std::invalid_argument::invalid_argument; }; auto SearchPluginFile(const std::string&) const -> fs::path; struct PluginNotFound : std::runtime_error { using std::runtime_error::runtime_error; }; auto LoadPlugin(const std::string& pluginName) -> void; auto LoadPlugins(const std::vector& pluginNames) -> void { for(const auto& pluginName : pluginNames) { LoadPlugin(pluginName); } } struct PluginLoadError : std::runtime_error { using std::runtime_error::runtime_error; }; auto InstantiatePlugins() -> void; struct PluginInstantiationError : std::runtime_error { using std::runtime_error::runtime_error; }; static auto ProgramOptions() -> boost::program_options::options_description; struct ProgramOptionsParseError : std::runtime_error { using std::runtime_error::runtime_error; }; static auto LibPrefix() -> const std::string& { return fgkLibPrefix; } auto ForEachPlugin(std::function func) -> void { for(const auto& p : fPluginOrder) { func(*fPlugins[p]); } } auto ForEachPluginProgOptions(std::function func) const -> void { for(const auto& pair : fPluginProgOptions) { func(pair.second); } } template auto EmplacePluginServices(Args&&... args) -> void { fPluginServices = std::make_unique(std::forward(args)...); } auto WaitForPluginsToReleaseDeviceControl() -> void { fPluginServices->WaitForReleaseDeviceControl(); } private: static auto ValidateSearchPath(const fs::path&) -> void; auto LoadPluginPrelinkedDynamic(const std::string& pluginName) -> void; auto LoadPluginDynamic(const std::string& pluginName) -> void; auto LoadPluginStatic(const std::string& pluginName) -> void; #if FAIRMQ_HAS_STD_FILESYSTEM template static auto AdaptPathType(T&& path) { if constexpr(std::is_same_v) { return boost::filesystem::path(std::forward(path)); } else { return std::forward(path); } } #endif template auto LoadSymbols(const std::string& pluginName, FirstArg&& farg, Args&&... args) -> void { using namespace boost::dll; using fair::mq::tools::ToString; #if FAIRMQ_HAS_STD_FILESYSTEM auto lib = shared_library{AdaptPathType(std::forward(farg)), std::forward(args)...}; #else auto lib = shared_library{std::forward(farg), std::forward(args)...}; #endif fgDLLKeepAlive.push_back(lib); fPluginFactories[pluginName] = import_alias( shared_library{lib}, ToString("make_", pluginName, "_plugin") ); try { fPluginProgOptions.insert({ pluginName, lib.get_alias(ToString("get_", pluginName, "_plugin_progoptions"))().value() }); } catch (const boost::bad_optional_access& e) { /* just ignore, if no prog options are declared */ } } auto InstantiatePlugin(const std::string& pluginName) -> void; static const std::string fgkLibPrefix; static const std::string fgkLibPrefixAlt; std::vector fSearchPaths; static std::vector fgDLLKeepAlive; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) std::map> fPluginFactories; std::unique_ptr fPluginServices; std::map> fPlugins; std::vector fPluginOrder; std::map fPluginProgOptions; }; /* class PluginManager */ } // namespace fair::mq #endif /* FAIR_MQ_PLUGINMANAGER_H */