From 19777e56290ac850b07b311258bfca3613b6ddcc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 19 Jun 2017 01:02:49 +0200 Subject: [PATCH] Provide a more elaborate interface for native plugins With this interface, plugins will be able to specify their version, register autorun functions and more. --- src/lay/lay.pro | 3 +- src/lay/layApplication.cc | 60 ++++++++++++++++++++++---- src/lay/layApplication.h | 27 ++++++++++++ src/lay/layMainWindow.cc | 22 +++++++++- src/lay/layNativePlugin.h | 75 +++++++++++++++++++++++++++++++++ src/lay/laySaltGrain.cc | 4 +- src/lay/laySaltManagerDialog.cc | 2 +- 7 files changed, 180 insertions(+), 13 deletions(-) create mode 100644 src/lay/layNativePlugin.h diff --git a/src/lay/lay.pro b/src/lay/lay.pro index 079155bfd..efefe08c1 100644 --- a/src/lay/lay.pro +++ b/src/lay/lay.pro @@ -59,7 +59,8 @@ HEADERS = \ laySaltController.h \ laySignalHandler.h \ layLibraryController.h \ - layFontController.h + layFontController.h \ + layNativePlugin.h FORMS = \ ClipDialog.ui \ diff --git a/src/lay/layApplication.cc b/src/lay/layApplication.cc index f8d43705c..ddea21698 100644 --- a/src/lay/layApplication.cc +++ b/src/lay/layApplication.cc @@ -27,6 +27,7 @@ #include "layConfig.h" #include "layMainWindow.h" #include "layMacroEditorDialog.h" +#include "layNativePlugin.h" #include "layVersion.h" #include "layMacro.h" #include "laySignalHandler.h" @@ -164,23 +165,47 @@ static void ui_exception_handler_def (QWidget *parent) static Application *ms_instance = 0; -static void load_plugin (const std::string &pp) +static PluginDescriptor load_plugin (const std::string &pp) { + PluginDescriptor desc; + desc.path = pp; + + klp_init_func_t init_func = 0; + static const char *init_func_name = "klp_init"; + + // NOTE: since we are using a different suffix ("*.klp"), we can't use QLibrary. #ifdef _WIN32 // there is no "dlopen" on mingw, so we need to emulate it. HINSTANCE handle = LoadLibraryW ((const wchar_t *) tl::to_qstring (pp).constData ()); if (! handle) { throw tl::Exception (tl::to_string (QObject::tr ("Unable to load plugin: %s with error message: %s ")), pp, GetLastError ()); } + init_func = reinterpret_cast (GetProcAddress (handle, init_func_name)); #else void *handle; handle = dlopen (tl::string_to_system (pp).c_str (), RTLD_LAZY); if (! handle) { throw tl::Exception (tl::to_string (QObject::tr ("Unable to load plugin: %s")), pp); } + init_func = reinterpret_cast (dlsym (handle, init_func_name)); #endif + // If present, call the initialization function to fetch some details from the plugin + if (init_func) { + const char *version = 0; + const char *description = 0; + (*init_func) (&desc.autorun, &desc.autorun_early, &version, &description); + if (version) { + desc.version = version; + } + if (description) { + desc.description = description; + } + } + tl::log << "Loaded plugin '" << pp << "'"; + + return desc; } Application::Application (int &argc, char **argv, bool non_ui_mode) @@ -301,6 +326,9 @@ Application::Application (int &argc, char **argv, bool non_ui_mode) // - in one of the Salt packages // - in one of the Salt packages, in a folder named after the architecture + std::string version = lay::Version::version (); + std::vector vv = tl::split (version, "."); + for (std::vector ::const_iterator p = m_klayout_path.begin (); p != m_klayout_path.end (); ++p) { std::set modules; @@ -310,12 +338,27 @@ Application::Application (int &argc, char **argv, bool non_ui_mode) klp_paths.push_back (QDir (klp_paths.back ()).filePath (tl::to_qstring (tl::arch_string ()))); lay::Salt salt; - // TODO: this is code duplicated from the SaltController. But this one - // is initialized much later. salt.add_location (tl::to_string (QDir (tl::to_qstring (*p)).filePath (QString::fromUtf8 ("salt")))); + + // Build the search path for the *.klp files. The search priority is for example: + // salt/mypackage/x86_64-linux-gcc-0.25.1 + // salt/mypackage/x86_64-linux-gcc-0.25 + // salt/mypackage/x86_64-linux-gcc-0 + // salt/mypackage/x86_64-linux-gcc + // salt/mypackage + for (lay::Salt::flat_iterator g = salt.begin_flat (); g != salt.end_flat (); ++g) { + QDir dir = QDir (tl::to_qstring ((*g)->path ())); + klp_paths.push_back (dir.filePath (tl::to_qstring (tl::arch_string () + "-" + lay::Version::version()))); + if (vv.size () >= 2) { + klp_paths.push_back (dir.filePath (tl::to_qstring (tl::arch_string () + "-" + vv[0] + "." + vv[1]))); + } + if (vv.size () >= 1) { + klp_paths.push_back (dir.filePath (tl::to_qstring (tl::arch_string () + "-" + vv[0]))); + } + klp_paths.push_back (dir.filePath (tl::to_qstring (tl::arch_string () + "-" + tl::to_string (lay::Version::version ())))); + klp_paths.push_back (dir.filePath (tl::to_qstring (tl::arch_string ()))); klp_paths.push_back (tl::to_qstring ((*g)->path ())); - klp_paths.push_back (QDir (klp_paths.back ()).filePath (tl::to_qstring (tl::arch_string ()))); } for (std::vector::const_iterator p = klp_paths.begin (); p != klp_paths.end (); ++p) { @@ -330,9 +373,10 @@ Application::Application (int &argc, char **argv, bool non_ui_mode) QFileInfo klp_file (*p, *im); if (klp_file.exists () && klp_file.isReadable ()) { std::string m = tl::to_string (klp_file.absoluteFilePath ()); - if (modules.find (m) == modules.end ()) { - load_plugin (m); - modules.insert (m); + std::string mn = tl::to_string (klp_file.fileName ()); + if (modules.find (mn) == modules.end ()) { + m_native_plugins.push_back (load_plugin (m)); + modules.insert (mn); } } } @@ -472,7 +516,7 @@ Application::Application (int &argc, char **argv, bool non_ui_mode) } else if (a == "-p" && (i + 1) < argc) { - load_plugin (args [++i]); + m_native_plugins.push_back (load_plugin (args [++i])); } else if (a == "-s") { diff --git a/src/lay/layApplication.h b/src/lay/layApplication.h index 3186c7c56..79ac535f5 100644 --- a/src/lay/layApplication.h +++ b/src/lay/layApplication.h @@ -59,6 +59,24 @@ class ProgressReporter; class ProgressBar; class MacroCollection; +/** + * @brief A tiny struct describing a native plugin + */ +struct PluginDescriptor +{ + typedef void (*runner_func_t) (); + + runner_func_t autorun; + runner_func_t autorun_early; + std::string version; + std::string path; + std::string description; + + PluginDescriptor () + : autorun (0), autorun_early (0) + { } +}; + /** * @brief The basic application object * @@ -293,6 +311,14 @@ public: return m_klayout_path; } + /** + * @brief Gets the native plugin descriptors + */ + const std::vector &native_plugins () const + { + return m_native_plugins; + } + private: void shutdown (); void finish (); @@ -343,6 +369,7 @@ private: lay::ProgressBar *mp_pb; lay::PluginRoot *mp_plugin_root; gtf::Recorder *mp_recorder; + std::vector m_native_plugins; }; } // namespace lay diff --git a/src/lay/layMainWindow.cc b/src/lay/layMainWindow.cc index 6da478457..49826e749 100644 --- a/src/lay/layMainWindow.cc +++ b/src/lay/layMainWindow.cc @@ -5428,7 +5428,27 @@ HelpAboutDialog::HelpAboutDialog (QWidget *parent) } s += ""; } - + + if (! lay::Application::instance ()->native_plugins ().empty ()) { + s += "

"; + s += "

"; + s += escape_xml (tl::to_string (QObject::tr ("Native extensions:"))); + s += "

    "; + for (std::vector::const_iterator pd = lay::Application::instance ()->native_plugins ().begin (); pd != lay::Application::instance ()->native_plugins ().end (); ++pd) { + s += "
  • "; + if (! pd->description.empty ()) { + s += escape_xml (pd->description); + } else { + s += escape_xml (pd->path); + } + if (! pd->version.empty ()) { + s += " (" + escape_xml (pd->version) + ")"; + } + s += "
  • "; + } + s += "
"; + } + s += ""; std::string t = tl::to_string (QObject::tr ("About ")) + lay::Version::name (); diff --git a/src/lay/layNativePlugin.h b/src/lay/layNativePlugin.h new file mode 100644 index 000000000..38abcaa04 --- /dev/null +++ b/src/lay/layNativePlugin.h @@ -0,0 +1,75 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2017 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_layNativePlugin +#define HDR_layNativePlugin + +/** + * @brief A struct to hold the data of the plugin + * + * Use it like this: + * + * @code + * static NativePlugin plugin_desc = { + * 0, // (void (*)()) pointer to autorun function or 0 if not present + * 0, // (void (*)()) pointer to early autorun function or 0 if not present + * "1.0", // (const char *) version information - should be given at least + * 0 // (const char *) description or 0/empty if no description is given + * }; + * DECLARE_NATIVE_PLUGIN (plugin_desc); + * @endcode + */ +struct NativePlugin { + void (*autorun) (); + void (*autorun_early) (); + const char *version; + const char *description; +}; + +/** + * @brief A typedef for the initialization function a plugin is supposed to expose. + */ +typedef void (*klp_init_func_t) (void (**autorun) (), void (**autorun_early) (), const char **version, const char **description); + +# if defined _WIN32 || defined __CYGWIN__ +# define _INIT_PUBLIC __declspec(dllexport) +# else +# if __GNUC__ >= 4 +# define _INIT_PUBLIC __attribute__ ((visibility ("default"))) +# else +# define _INIT_PUBLIC +# endif + +# endif + +#define DECLARE_NATIVE_PLUGIN(desc) \ + extern "C" { \ + _INIT_PUBLIC void klp_init (void (**autorun) (), void (**autorun_early) (), const char **version, const char **description) { \ + *autorun = desc.autorun; \ + *autorun_early = desc.autorun_early; \ + *version = desc.version; \ + *description = desc.description; \ + } \ + } + +#endif + diff --git a/src/lay/laySaltGrain.cc b/src/lay/laySaltGrain.cc index 5f988b234..4047b12a3 100644 --- a/src/lay/laySaltGrain.cc +++ b/src/lay/laySaltGrain.cc @@ -251,7 +251,7 @@ SaltGrain::valid_name (const std::string &n) tl::Extractor ex (n); std::string s; - if (! ex.try_read_word (s, "_")) { + if (! ex.try_read_word (s, "_.")) { return false; } res += s; @@ -260,7 +260,7 @@ SaltGrain::valid_name (const std::string &n) if (! ex.test ("/")) { return false; } - if (! ex.try_read_word (s, "_")) { + if (! ex.try_read_word (s, "_.")) { return false; } res += "/"; diff --git a/src/lay/laySaltManagerDialog.cc b/src/lay/laySaltManagerDialog.cc index 6e07bb979..d16ee2b37 100644 --- a/src/lay/laySaltManagerDialog.cc +++ b/src/lay/laySaltManagerDialog.cc @@ -86,7 +86,7 @@ public: if (name.empty ()) { name_alert->error () << tr ("Name must not be empty"); } else if (! SaltGrain::valid_name (name)) { - name_alert->error () << tr ("Name is not valid (must be composed of letters, digits or underscores.\nGroups and names need to be separated with slashes."); + name_alert->error () << tr ("Name is not valid (must be composed of letters, digits, dots or underscores.\nGroups and names need to be separated with slashes."); } else { // check, if this name does not exist yet