26 namespace plugin_manager
58 LoadedPlugin() =
default;
59 LoadedPlugin(LoadedPlugin&&) =
default;
60 LoadedPlugin& operator=(LoadedPlugin&&) =
default;
61 LoadedPlugin(
const LoadedPlugin&) =
delete;
62 LoadedPlugin& operator=(
const LoadedPlugin&) =
delete;
66 std::unordered_map<std::string, LoadedPlugin> m_loaded_plugins;
69 std::unordered_map<std::string, std::vector<PluginFeature>> m_plugin_features;
72 std::unordered_map<std::string, std::string> m_cli_option_to_plugin_name[2];
75 CallbackHook<void(
bool, std::string
const&, std::string
const&)> m_hook;
78 ProgramOptions m_existing_options(
"existing options");
81 ProgramOptions m_plugin_options(
"plugin options");
87 std::string m_current_loading;
89 bool solve_dependencies(std::string plugin_name, std::set<std::string> dep_file_name)
91 if (plugin_name.empty())
93 log_error(
"core",
"parameter 'plugin_name' is empty");
96 if (dep_file_name.empty())
98 log_debug(
"core",
"no dependency for plugin '{}'", plugin_name);
101 for (
const auto& file_name : dep_file_name)
103 auto dep_plugin_name = std::filesystem::path(file_name).stem().string();
106 if (m_loaded_plugins.find(dep_plugin_name) != m_loaded_plugins.end())
113 if (file_path.empty() || !
load(dep_plugin_name, file_path))
115 log_error(
"core",
"cannot solve dependency '{}' for plugin '{}'", dep_plugin_name, plugin_name);
119 log_debug(
"core",
"solved dependency '{}' for plugin '{}'", dep_plugin_name, plugin_name);
121 log_debug(
"core",
"solved {} dependencies for plugin '{}'", dep_file_name.size(), plugin_name);
129 std::filesystem::path retval;
130 if (plugin_name.empty())
return retval;
140 #if defined(__APPLE__) && defined(__MACH__)
143 if (file_name.string().find(
".so") != std::string::npos)
160 std::set<std::string> names;
161 for (
const auto& it : m_loaded_plugins)
163 names.insert(it.first);
170 auto it = m_plugin_features.find(
name);
171 if (it == m_plugin_features.end())
return std::vector<PluginFeature>();
177 return m_cli_option_to_plugin_name[0];
182 return m_cli_option_to_plugin_name[1];
187 return m_plugin_options;
192 auto directories = (!directory_names.empty()) ? directory_names : m_plugin_folders;
193 for (
const auto& directory : directories)
195 if (!std::filesystem::exists(directory))
199 u32 num_of_loaded_plugins = 0;
205 auto plugin_name = file.path().stem().string();
206 if (
load(plugin_name, file.path()))
208 num_of_loaded_plugins++;
211 log_debug(
"core",
"loaded {} plugins from '{}'", num_of_loaded_plugins, directory.string());
216 bool load(
const std::string& plugin_name,
const std::filesystem::path& file_path_or_empty)
218 if (plugin_name.empty())
220 log_error(
"core",
"parameter 'plugin_name' is empty");
224 std::filesystem::path file_path = file_path_or_empty;
225 if (file_path.empty())
230 if (file_path.empty())
232 log_error(
"core",
"path for plugin '{}' not found", plugin_name);
237 log_info(
"core",
"loading plugin '{}'...", file_path.string());
239 if (m_loaded_plugins.find(plugin_name) != m_loaded_plugins.end())
241 log_debug(
"core",
"plugin '{}' is already loaded", plugin_name);
246 auto lib = std::make_unique<RuntimeLibrary>();
247 if (!lib->load_library(file_path.string()))
254 if (factory ==
nullptr)
256 log_error(
"core",
"file does not seem to be a HAL plugin since it does not contain a factory function 'create_plugin_instance'.");
262 log_error(
"core",
"factory constructor for plugin '{}' returned a nullptr", plugin_name);
267 std::set<std::string> dep_file_name =
instance->get_dependencies();
268 if (!solve_dependencies(plugin_name, dep_file_name))
278 if (plugin ==
nullptr)
285 for (
const auto& cli_option : cli_options.get_options())
287 for (
const auto& flag : std::get<0>(cli_option))
289 if (m_existing_options.is_registered(flag))
291 log_error(
"core",
"command line option '{}' is already used by generic program options -- use another one.", flag);
295 for (
int iplugType = 0; iplugType<2; iplugType++)
296 if (m_cli_option_to_plugin_name[iplugType].find(flag) != m_cli_option_to_plugin_name[iplugType].end())
298 log_error(
"core",
"command line option '{}' is already used by plugin '{}' -- use another option in plugin '{}'.", flag, m_cli_option_to_plugin_name[iplugType][flag], plugin_name);
301 m_cli_option_to_plugin_name[ui_plugin?1:0][flag] = plugin_name;
302 log_debug(
"core",
"registered command line option '{}' for plugin '{}'.", flag, plugin->
get_name());
305 m_plugin_options.add(cli_options);
310 m_current_loading = plugin_name;
318 m_plugin_features[plugin_name].push_back({feif->
get_feature(),
351 m_current_loading.clear();
353 m_loaded_plugins[plugin_name] = LoadedPlugin{std::move(
instance), std::move(lib)};
356 m_hook(
true, plugin_name, file_path.string());
358 log_debug(
"core",
"loaded plugin '{}'.", plugin_name);
364 if (m_loaded_plugins.size() == 0)
366 log_debug(
"core",
"no plugins to unload");
370 for (
const auto&
name : loaded_plugin_names)
377 log_info(
"core",
"unloaded all {} plugins", loaded_plugin_names.size());
381 bool unload(
const std::string& plugin_name)
383 auto loaded_it = m_loaded_plugins.find(plugin_name);
384 if (loaded_it == m_loaded_plugins.end())
386 log_debug(
"core",
"cannot find plugin '{}' to unload it", plugin_name);
390 log_info(
"core",
"unloading plugin '{}'...", plugin_name);
393 auto rt_library = std::move(loaded_it->second.library);
394 auto plugin_inst = std::move(loaded_it->second.instance);
398 auto it = m_cli_option_to_plugin_name[iplugType].begin();
399 while (it != m_cli_option_to_plugin_name[iplugType].end())
401 auto flag = it->first;
402 auto name = it->second;
403 if (
name == plugin_name)
405 m_plugin_options.remove(flag);
406 it = m_cli_option_to_plugin_name[iplugType].erase(it);
415 m_loaded_plugins.erase(loaded_it);
440 auto file_name = rt_library->get_file_name();
441 plugin_inst->on_unload();
450 m_hook(
false, plugin_name, file_name);
452 log_debug(
"core",
"unloaded plugin '{}'", plugin_name);
458 auto it = m_loaded_plugins.find(plugin_name);
459 if (it == m_loaded_plugins.end())
462 log_error(
"core",
"plugin '{}' is not loaded", plugin_name);
466 auto instance = it->second.instance.get();
476 for (
const auto& [_, plugin] : m_loaded_plugins)
478 if (
auto* ui_plugin =
dynamic_cast<UIPluginInterface*
>(plugin.instance.get()); ui_plugin !=
nullptr)
488 if (callback ==
nullptr)
490 log_error(
"core",
"parameter 'callback' is nullptr");
493 return m_hook.add_callback(callback);
498 m_hook.remove_callback(
id);
503 m_existing_options.add(existing_options);
#define LIBRARY_FILE_EXTENSION
#define ALTERNATE_LIBRARY_FILE_EXTENSION
virtual std::string get_name() const =0
virtual ProgramOptions get_cli_options() const =0
AbstractFactoryProvider * factory_provider
std::vector< std::string > get_supported_file_extensions() const
std::string get_description() const
Feature get_feature() const
std::function< std::unique_ptr< T >)> m_factory
#define log_error(channel,...)
#define log_debug(channel,...)
#define log_info(channel,...)
void register_parser(const std::string &name, const ParserFactory &parser_factory, const std::vector< std::string > &supported_file_extensions)
void unregister_parser(const std::string &name)
void unregister_writer(const std::string &name)
void register_writer(const std::string &name, const WriterFactory &writer_factory, const std::vector< std::string > &supported_file_extensions)
void register_parser(const std::string &name, const ParserFactory &parser_factory, const std::vector< std::string > &supported_file_extensions)
void unregister_parser(const std::string &name)
void unregister_writer(const std::string &name)
void register_writer(const std::string &name, const WriterFactory &writer_factory, const std::vector< std::string > &supported_file_extensions)
void add_existing_options_description(const ProgramOptions &existing_options)
u64 add_model_changed_callback(std::function< void(bool, std::string const &, std::string const &)> callback)
bool has_valid_file_extension(std::filesystem::path file_name)
UIPluginInterface * get_ui_plugin()
bool unload(const std::string &plugin_name)
bool load_all_plugins(const std::vector< std::filesystem::path > &directory_names={})
bool load(const std::string &plugin_name, const std::filesystem::path &file_path=std::filesystem::path())
std::set< std::string > get_plugin_names()
std::unordered_map< std::string, std::string > get_cli_plugin_flags()
std::unordered_map< std::string, std::string > get_ui_plugin_flags()
bool unload_all_plugins()
std::filesystem::path get_plugin_path(std::string plugin_name)
ProgramOptions get_cli_plugin_options()
void remove_model_changed_callback(u64 id)
BasePluginInterface * get_plugin_instance(const std::string &plugin_name, bool initialize=true, bool silent=false)
std::vector< PluginFeature > get_plugin_features(std::string name)
std::filesystem::path get_file(std::string file_name, std::vector< std::filesystem::path > path_hints)
bool ends_with(const T &s, const T &end)
std::vector< std::filesystem::path > get_plugin_directories()
std::unique_ptr< BasePluginInterface >(*)() instantiate_plugin_function
std::unique_ptr< RuntimeLibrary > library
std::unique_ptr< BasePluginInterface > instance