Provide a more elaborate interface for native plugins

With this interface, plugins will be able to specify
their version, register autorun functions and more.
This commit is contained in:
Matthias Koefferlein 2017-06-19 01:02:49 +02:00
parent 0978fb527f
commit 19777e5629
7 changed files with 180 additions and 13 deletions

View File

@ -59,7 +59,8 @@ HEADERS = \
laySaltController.h \
laySignalHandler.h \
layLibraryController.h \
layFontController.h
layFontController.h \
layNativePlugin.h
FORMS = \
ClipDialog.ui \

View File

@ -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<init_func_t> (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<klp_init_func_t> (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<std::string> vv = tl::split (version, ".");
for (std::vector <std::string>::const_iterator p = m_klayout_path.begin (); p != m_klayout_path.end (); ++p) {
std::set<std::string> 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<QString>::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") {

View File

@ -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<PluginDescriptor> &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<PluginDescriptor> m_native_plugins;
};
} // namespace lay

View File

@ -5428,7 +5428,27 @@ HelpAboutDialog::HelpAboutDialog (QWidget *parent)
}
s += "</ul>";
}
if (! lay::Application::instance ()->native_plugins ().empty ()) {
s += "<p>";
s += "<h4>";
s += escape_xml (tl::to_string (QObject::tr ("Native extensions:")));
s += "</h4><ul>";
for (std::vector<lay::PluginDescriptor>::const_iterator pd = lay::Application::instance ()->native_plugins ().begin (); pd != lay::Application::instance ()->native_plugins ().end (); ++pd) {
s += "<li>";
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 += "</li>";
}
s += "</ul>";
}
s += "</body></html>";
std::string t = tl::to_string (QObject::tr ("About ")) + lay::Version::name ();

75
src/lay/layNativePlugin.h Normal file
View File

@ -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

View File

@ -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 += "/";

View File

@ -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