26 namespace plugin_manager
31 std::unordered_map<std::string, std::tuple<std::unique_ptr<BasePluginInterface>, std::unique_ptr<RuntimeLibrary>>> m_loaded_plugins;
34 std::unordered_map<std::string, std::vector<PluginFeature>> m_plugin_features;
37 std::unordered_map<std::string, std::string> m_cli_option_to_plugin_name[2];
40 CallbackHook<void(
bool, std::string
const&, std::string
const&)> m_hook;
43 ProgramOptions m_existing_options(
"existing options");
46 ProgramOptions m_plugin_options(
"plugin options");
52 std::string m_current_loading;
54 bool solve_dependencies(std::string plugin_name, std::set<std::string> dep_file_name)
56 if (plugin_name.empty())
58 log_error(
"core",
"parameter 'plugin_name' is empty");
61 if (dep_file_name.empty())
63 log_debug(
"core",
"no dependency for plugin '{}'", plugin_name);
66 for (
const auto& file_name : dep_file_name)
68 auto dep_plugin_name = std::filesystem::path(file_name).stem().string();
71 if (m_loaded_plugins.find(dep_plugin_name) != m_loaded_plugins.end())
78 if (file_path.empty() || !
load(dep_plugin_name, file_path))
80 log_error(
"core",
"cannot solve dependency '{}' for plugin '{}'", dep_plugin_name, plugin_name);
84 log_debug(
"core",
"solved dependency '{}' for plugin '{}'", dep_plugin_name, plugin_name);
86 log_debug(
"core",
"solved {} dependencies for plugin '{}'", dep_file_name.size(), plugin_name);
94 std::filesystem::path retval;
95 if (plugin_name.empty())
return retval;
105 #if defined(__APPLE__) && defined(__MACH__)
108 if (file_name.string().find(
".so") != std::string::npos)
125 std::set<std::string> names;
126 for (
const auto& it : m_loaded_plugins)
128 names.insert(it.first);
135 auto it = m_plugin_features.find(
name);
136 if (it == m_plugin_features.end())
return std::vector<PluginFeature>();
142 return m_cli_option_to_plugin_name[0];
147 return m_cli_option_to_plugin_name[1];
152 return m_plugin_options;
157 auto directories = (!directory_names.empty()) ? directory_names : m_plugin_folders;
158 for (
const auto& directory : directories)
160 if (!std::filesystem::exists(directory))
164 u32 num_of_loaded_plugins = 0;
170 auto plugin_name = file.path().stem().string();
171 if (
load(plugin_name, file.path()))
173 num_of_loaded_plugins++;
176 log_debug(
"core",
"loaded {} plugins from '{}'", num_of_loaded_plugins, directory.string());
181 bool load(
const std::string& plugin_name,
const std::filesystem::path& file_path_or_empty)
183 if (plugin_name.empty())
185 log_error(
"core",
"parameter 'plugin_name' is empty");
189 std::filesystem::path file_path = file_path_or_empty;
190 if (file_path.empty())
195 if (file_path.empty())
197 log_error(
"core",
"path for plugin '{}' not found", plugin_name);
202 log_info(
"core",
"loading plugin '{}'...", file_path.string());
204 if (m_loaded_plugins.find(plugin_name) != m_loaded_plugins.end())
206 log_debug(
"core",
"plugin '{}' is already loaded", plugin_name);
211 auto lib = std::make_unique<RuntimeLibrary>();
212 if (!lib->load_library(file_path.string()))
219 if (factory ==
nullptr)
221 log_error(
"core",
"file does not seem to be a HAL plugin since it does not contain a factory function 'create_plugin_instance'.");
224 auto instance = factory();
225 if (instance ==
nullptr)
227 log_error(
"core",
"factory constructor for plugin '{}' returned a nullptr", plugin_name);
232 std::set<std::string> dep_file_name = instance->get_dependencies();
233 if (!solve_dependencies(plugin_name, dep_file_name))
243 if (plugin ==
nullptr)
250 for (
const auto& cli_option : cli_options.get_options())
252 for (
const auto& flag : std::get<0>(cli_option))
254 if (m_existing_options.is_registered(flag))
256 log_error(
"core",
"command line option '{}' is already used by generic program options -- use another one.", flag);
260 for (
int iplugType = 0; iplugType<2; iplugType++)
261 if (m_cli_option_to_plugin_name[iplugType].find(flag) != m_cli_option_to_plugin_name[iplugType].end())
263 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);
266 m_cli_option_to_plugin_name[ui_plugin?1:0][flag] = plugin_name;
267 log_debug(
"core",
"registered command line option '{}' for plugin '{}'.", flag, plugin->
get_name());
270 m_plugin_options.add(cli_options);
273 instance->initialize_logging();
275 m_current_loading = plugin_name;
283 m_plugin_features[plugin_name].push_back({feif->
get_feature(),
316 m_current_loading.clear();
318 m_loaded_plugins[plugin_name] = std::make_tuple(std::move(instance), std::move(lib));
321 m_hook(
true, plugin_name, file_path.string());
323 log_debug(
"core",
"loaded plugin '{}'.", plugin_name);
329 if (m_loaded_plugins.size() == 0)
331 log_debug(
"core",
"no plugins to unload");
335 for (
const auto&
name : loaded_plugin_names)
342 log_info(
"core",
"unloaded all {} plugins", loaded_plugin_names.size());
346 bool unload(
const std::string& plugin_name)
348 auto loaded_it = m_loaded_plugins.find(plugin_name);
349 if (loaded_it == m_loaded_plugins.end())
351 log_debug(
"core",
"cannot find plugin '{}' to unload it", plugin_name);
355 log_info(
"core",
"unloading plugin '{}'...", plugin_name);
358 auto rt_library = std::move(std::get<1>(loaded_it->second));
359 auto plugin_inst = std::move(std::get<0>(loaded_it->second));
363 auto it = m_cli_option_to_plugin_name[iplugType].begin();
364 while (it != m_cli_option_to_plugin_name[iplugType].end())
366 auto flag = it->first;
367 auto name = it->second;
368 if (
name == plugin_name)
370 m_plugin_options.remove(flag);
371 it = m_cli_option_to_plugin_name[iplugType].erase(it);
380 m_loaded_plugins.erase(loaded_it);
405 auto file_name = rt_library->get_file_name();
406 plugin_inst->on_unload();
415 m_hook(
false, plugin_name, file_name);
417 log_debug(
"core",
"unloaded plugin '{}'", plugin_name);
423 auto it = m_loaded_plugins.find(plugin_name);
424 if (it == m_loaded_plugins.end())
427 log_error(
"core",
"plugin '{}' is not loaded", plugin_name);
431 auto instance = std::get<0>(it->second).get();
434 instance->initialize();
441 if (callback ==
nullptr)
443 log_error(
"core",
"parameter 'callback' is nullptr");
446 return m_hook.add_callback(callback);
451 m_hook.remove_callback(
id);
456 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)
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