From 7421f8c9a03a1166728a1f49e4bd357c7f72248a Mon Sep 17 00:00:00 2001 From: Ivan Pelipenko Date: Fri, 2 Oct 2020 14:23:05 +0300 Subject: [PATCH] first try --- libs/main/system/pilibrary.h | 3 + libs/main/system/piplugin.cpp | 162 ++++++++++++++++++++++++++++++++++ libs/main/system/piplugin.h | 158 +++++++++++++++++++++++++++++++++ 3 files changed, 323 insertions(+) create mode 100644 libs/main/system/piplugin.cpp create mode 100644 libs/main/system/piplugin.h diff --git a/libs/main/system/pilibrary.h b/libs/main/system/pilibrary.h index 581fa5d1..17b61dc3 100644 --- a/libs/main/system/pilibrary.h +++ b/libs/main/system/pilibrary.h @@ -1,3 +1,6 @@ +/*! \file pilibrary.h +* \brief Dynamic library +*/ /* PIP - Platform Independent Primitives Dynamic library diff --git a/libs/main/system/piplugin.cpp b/libs/main/system/piplugin.cpp new file mode 100644 index 00000000..b651d6b9 --- /dev/null +++ b/libs/main/system/piplugin.cpp @@ -0,0 +1,162 @@ +/* + PIP - Platform Independent Primitives + Plugin helpers + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef PIP_FREERTOS + +#include "piplugin.h" +#include "pifile.h" +#include "piincludes_p.h" + +#define STR_WF(s) #s +#define STR(s) STR_WF(s) + + +PIPluginInfo * PIPluginInfo::instance() { + static PIPluginInfo ret; + return &ret; +} + + + + +PIPluginLoader::PIPluginLoader(const PIString & name) { + func_loader_version = nullptr; + func_plugin_info = nullptr; + func_static_merge = nullptr; + plugin_info = nullptr; + loaded = false; + if (!name.isEmpty()) + load(name); +} + + +PIPluginLoader::~PIPluginLoader() { + lib.unload(); +} + + +bool PIPluginLoader::load(const PIString & name) { + unload(); + if (!lib.load(findLibrary(name))) return false; + piCout << "loading" << lib.path() << "..."; + func_loader_version = (FunctionLoaderVersion)lib.resolve(STR(__PIP_PLUGIN_LOADER_VERSION_FUNC__)); + if (!func_loader_version) { + piCout << "Load plugin \"" << name << "\" error: can`t find" << STR(__PIP_PLUGIN_LOADER_VERSION_FUNC__); + unload(); + return false; + } + if (__PIP_PLUGIN_LOADER_VERSION__ != func_loader_version()) { + piCout << "Load plugin \"" << name << "\" error: invalid loader version: plugin" << __PIP_PLUGIN_LOADER_VERSION__ << "!=" << func_loader_version(); + unload(); + return false; + } + func_plugin_info = (FunctionPluginInfo)lib.resolve(STR(__PIP_PLUGIN_PLUGIN_INFO_FUNC__)); + if (!func_plugin_info) { + piCout << "Load plugin \"" << name << "\" error: can`t find" << STR(__PIP_PLUGIN_PLUGIN_INFO_FUNC__); + unload(); + return false; + } + plugin_info = func_plugin_info(); + if (!plugin_info) { + piCout << "Load plugin \"" << name << "\" error: null PIPluginInfo"; + unload(); + return false; + } + if (PIPluginInfo::instance()->user_version.size_s() > 1) { + PIString pversion = plugin_info->user_version, lversion = PIPluginInfo::instance()->user_version; + if (pversion != lversion) { + piCout << "Load plugin \"" << name << "\" error: invalid user version: plugin" << pversion << "!=" << lversion; + unload(); + return false; + } + } + func_static_merge = (FunctionStaticMerge)lib.resolve(STR(__PIP_PLUGIN_STATIC_MERGE_FUNC__)); + if (func_static_merge) { + piCout << PIPluginInfo::instance()->static_sections.keys() << plugin_info->static_sections.keys(); + auto it = PIPluginInfo::instance()->static_sections.makeIterator(); + while (it.next()) { + if (!plugin_info->static_sections.contains(it.key())) + continue; + func_static_merge(it.key(), plugin_info->static_sections.value(it.key()), it.value()); + } + } + loaded = true; + return true; +} + + +void PIPluginLoader::unload() { + lib.unload(); + plugin_info = nullptr; + loaded = false; +} + + +PIString PIPluginLoader::libPath() { + return lib.path(); +} + + +void * PIPluginLoader::resolve(const char * name) { + if (!loaded) return nullptr; + return lib.resolve(name); +} + + +void PIPluginLoader::mergeStatic() { + if (!loaded || !func_static_merge || !plugin_info) return; + auto it = PIPluginInfo::instance()->static_sections.makeIterator(); + while (it.next()) { + if (!plugin_info->static_sections.contains(it.key())) + continue; + func_static_merge(it.key(), it.value(), plugin_info->static_sections.value(it.key())); + } +} + + +PIString PIPluginLoader::findLibrary(const PIString & path) { + static const PIStringList prefixes({"", "lib"}); + static const PIStringList suffixes({"", libExtension()}); + PIFile::FileInfo fi(path); + PIString dir = fi.dir(), name = fi.name(); + piForeachC (PIString & p, prefixes) { + piForeachC (PIString & s, suffixes) { + PIString fn = dir + p + name + s; + if (PIFile::isExists(fn)) + return fn; + } + } + return PIString(); +} + + +PIString PIPluginLoader::libExtension() { + return +#ifdef WINDOWS + ".dll" +#elif defined(MAC_OS) + ".dylib" +#else + ".so" +#endif + ; +} + + +#endif // PIP_FREERTOS diff --git a/libs/main/system/piplugin.h b/libs/main/system/piplugin.h new file mode 100644 index 00000000..7ed670d2 --- /dev/null +++ b/libs/main/system/piplugin.h @@ -0,0 +1,158 @@ +/*! \file piplugin.h +* \brief Plugin helpers +*/ +/* + PIP - Platform Independent Primitives + Plugin helpers + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ +# +#ifndef PIPLUGIN_H +#define PIPLUGIN_H + +#ifndef PIP_FREERTOS + +#include "pilibrary.h" +#include "pistringlist.h" + +#ifdef DOXYGEN + + +//! Set user version to check it while loading +#define PIP_PLUGIN_SET_USER_VERSION(v) + +//! Add pointer to future merge with plugin. Type is integer +#define PIP_PLUGIN_ADD_STATIC_SECTION(type, ptr) + +//! Declare plugin export functions +#define PIP_PLUGIN + +//! Declare function to merge static sections. This is functions +//! with 3 arguments: (int type, void * from, void * to). +//! This function invoked first while loading plugin with +//! "from" - plugin scope, "to" - application scope, and second +//! (optionally) on \a PIPluginLoader::mergeStatic() method with +//! "from" - application scope, "to" - plugin scope. So with macro +//! you can merge application and plugin static data. +#define PIP_PLUGIN_STATIC_SECTION_MERGE + + +#else + + +#ifdef WINDOWS +# define PIP_PLUGIN_EXPORT __declspec(dllexport) +#else +# define PIP_PLUGIN_EXPORT +#endif + +#define __PIP_PLUGIN_LOADER_VERSION_FUNC__ pip_loader_version +#define __PIP_PLUGIN_PLUGIN_INFO_FUNC__ pip_plugin_info +#define __PIP_PLUGIN_STATIC_MERGE_FUNC__ pip_merge_static +#define __PIP_PLUGIN_LOADER_VERSION__ 1 + +#define PIP_PLUGIN_SET_USER_VERSION(v) \ + STATIC_INITIALIZER_BEGIN \ + PIPluginInfo::instance()->setUserVersion(v); \ + STATIC_INITIALIZER_END + +#define PIP_PLUGIN_ADD_STATIC_SECTION(type, ptr) \ + STATIC_INITIALIZER_BEGIN \ + PIPluginInfo::instance()->setStaticSection(type, ptr); \ + STATIC_INITIALIZER_END + +#define PIP_PLUGIN \ + extern "C" { \ + PIP_PLUGIN_EXPORT int __PIP_PLUGIN_LOADER_VERSION_FUNC__() {return __PIP_PLUGIN_LOADER_VERSION__;} \ + PIP_PLUGIN_EXPORT PIPluginInfo * __PIP_PLUGIN_PLUGIN_INFO_FUNC__() {return PIPluginInfo::instance();} \ + } + +#define PIP_PLUGIN_STATIC_SECTION_MERGE \ + extern "C" { \ + PIP_PLUGIN_EXPORT void __PIP_PLUGIN_STATIC_MERGE_FUNC__(int type, void * from, void * to); \ + } \ + void __PIP_PLUGIN_STATIC_MERGE_FUNC__(int type, void * from, void * to) + + +#endif + + + +class PIP_EXPORT PIPluginInfo { +public: + PIPluginInfo() {} + + void setUserVersion(const PIString & v) {user_version = v;} + void setStaticSection(int type, void * ptr) {static_sections[type] = ptr;} + + static PIPluginInfo * instance(); + + PIString user_version; + PIMap static_sections; +}; + + + + +class PIP_EXPORT PIPluginLoader { +public: + typedef int(*FunctionLoaderVersion)(); + typedef PIPluginInfo*(*FunctionPluginInfo)(); + typedef void(*FunctionStaticMerge)(int, void *, void *); + + //! Contruscts loader with filename "name" + PIPluginLoader(const PIString & name = PIString()); + + //! Destructor + ~PIPluginLoader(); + + + //! Load plugin with base filename "name". Loader try prefix "lib" + //! and suffix ".dll", ".so" or ".dylib", depends on platform + bool load(const PIString & name); + + //! Unload plugin and free library + void unload(); + + //! Returns loaded plugin library path + PIString libPath(); + + //! Resolve symbol "name" from plugin library + void * resolve(const char * name); + + //! Invoke plugin PIP_PLUGIN_STATIC_SECTION_MERGE function + //! on every common type, with "from" - application scope, + //! "to" - plugin scope + void mergeStatic(); + +private: + NO_COPY_CLASS(PIPluginLoader) + + PIString findLibrary(const PIString & path); + static PIString libExtension(); + + PILibrary lib; + FunctionLoaderVersion func_loader_version; + FunctionPluginInfo func_plugin_info; + FunctionStaticMerge func_static_merge; + PIPluginInfo * plugin_info; + bool loaded; + +}; + + +#endif // PIP_FREERTOS +#endif // PIPLUGIN_H