Put Python plugin support in a separate shared object
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Wed, 8 Mar 2017 19:00:07 +0000 (14:00 -0500)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Sun, 28 May 2017 16:57:38 +0000 (12:57 -0400)
This is to make the work of packagers easier for libbabeltrace not
to depend on libpython, even if you need Python plugin support.

With this patch, python.c's constructor tries to open the new Python
plugin provider shared object. If it fails, Python plugin support is
disabled, but shared object plugins can still be loaded.

The new BUILT_IN_PYTHON_PLUGIN_SUPPORT configure variable, if set to 1,
makes the build embed the Python plugin provider into libbabeltrace
(like before this patch).

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
16 files changed:
Makefile.am
configure.ac
include/Makefile.am
include/babeltrace/babeltrace-internal.h
include/babeltrace/plugin/plugin-internal.h
include/babeltrace/plugin/plugin-python-disabled-internal.h [deleted file]
include/babeltrace/plugin/plugin-python-enabled-internal.h [deleted file]
include/babeltrace/plugin/plugin-so-internal.h
include/babeltrace/plugin/python-plugin-provider-internal.h [new file with mode: 0644]
lib/Makefile.am
lib/plugin/Makefile.am
lib/plugin/plugin-python.c [deleted file]
lib/plugin/plugin-so.c
lib/plugin/plugin.c
python-plugin-provider/Makefile.am [new file with mode: 0644]
python-plugin-provider/python-plugin-provider.c [new file with mode: 0644]

index 4b517db724308208f4c699a68daf8cffb9a15841..1d66b1d4ce6c81a88fc8208410e2f2a3b9fb01f8 100644 (file)
@@ -2,7 +2,25 @@ AM_CFLAGS = $(PACKAGE_CFLAGS) -I$(top_srcdir)/include
 
 ACLOCAL_AMFLAGS = -I m4
 
-SUBDIRS = include common types compat lib formats plugins converter bindings tests doc extras
+SUBDIRS =                      \
+       include                 \
+       common                  \
+       types                   \
+       compat
+
+if WITH_PYTHON_PLUGINS
+SUBDIRS += python-plugin-provider
+endif
+
+SUBDIRS +=                     \
+       lib                     \
+       formats                 \
+       plugins                 \
+       converter               \
+       bindings                \
+       tests                   \
+       doc                     \
+       extras
 
 dist_doc_DATA = ChangeLog LICENSE mit-license.txt gpl-2.0.txt \
                std-ext-lib.txt README
index d48be53b6904ec3ac67fa57d569349da5346474a..0f3182142012e86137b8d33cdb85410153754fda 100644 (file)
@@ -376,6 +376,16 @@ AS_IF([test "x$BUILT_IN_PLUGINS" != x], [
 ])
 AM_CONDITIONAL([BUILT_IN_PLUGINS], [test "x$built_in_plugins" = "xyes"])
 
+AC_ARG_VAR([BUILT_IN_PYTHON_PLUGIN_SUPPORT], [Statically-link Python plugin support into the babeltrace binary])
+AS_IF([test "x$BUILT_IN_PYTHON_PLUGIN_SUPPORT" != x], [
+# Built-in plug-ins are only available when the --disable-shared --enable-static options are used.
+  AS_IF([test "x$enable_static" != "xyes"], [AC_MSG_ERROR(--enable-static must be used to bundle Python plugin support in the babeltrace executable)])
+  AS_IF([test "x$enable_shared" = "xyes"], [AC_MSG_ERROR(--disable-shared must be used to bundle Python plugin support in the babeltrace executable)])
+  built_in_python_plugin_support=yes
+  AC_DEFINE([BT_BUILT_IN_PYTHON_PLUGIN_SUPPORT], [1], [Define to 1 to register plug-in attributes in static executable sections])
+])
+AM_CONDITIONAL([BUILT_IN_PYTHON_PLUGIN_SUPPORT], [test "x$built_in_python_plugin_support" = "xyes"])
+
 PKG_CHECK_MODULES(GMODULE, [gmodule-2.0 >= 2.0.0])
 
 LIBS="$LIBS $GMODULE_LIBS"
@@ -496,6 +506,7 @@ AC_CONFIG_FILES([
        plugins/utils/Makefile
        plugins/utils/dummy/Makefile
        plugins/utils/trimmer/Makefile
+       python-plugin-provider/Makefile
        babeltrace.pc
        babeltrace-ctf.pc
 ])
@@ -571,6 +582,10 @@ PPRINT_PROP_BOOL([Python bindings tests], $value)
 test "x$enable_python_plugins" = "xyes" && value=1 || value=0
 PPRINT_PROP_BOOL([Python plugin support], $value)
 
+# built-in Python plugin support enabled/disabled
+test "x$built_in_python_plugin_support" = "xyes" && value=1 || value=0
+PPRINT_PROP_BOOL([Built-in Python plugin support], $value)
+
 # debug info enabled/disabled
 test "x$_enable_debug_info" = "xyes" && value=1 || value=0
 PPRINT_PROP_BOOL([Debug information output], $value)
index ba2ad2bf9b83cbfaa54b845a9d733ae7f75b7159..b4d86eccea81ecec546c50dacbc831f7bf4f5674 100644 (file)
@@ -126,8 +126,6 @@ noinst_HEADERS = \
        babeltrace/mmap-align.h \
        babeltrace/plugin/plugin-internal.h \
        babeltrace/plugin/plugin-so-internal.h \
-       babeltrace/plugin/plugin-python-enabled-internal.h \
-       babeltrace/plugin/plugin-python-disabled-internal.h \
        babeltrace/component/component-class-internal.h \
        babeltrace/component/connection-internal.h \
        babeltrace/component/port-internal.h \
index c3ed6f97246d514f2d919fbf4b4a8beff2384ba2..535f99094970161856927d5adcd6d1fb7753cc0d 100644 (file)
@@ -180,6 +180,9 @@ extern bool babeltrace_verbose, babeltrace_debug;
 #define BT_CTF_MAJOR   1
 #define BT_CTF_MINOR   8
 
+#define __STRINGIFY(x) #x
+#define TOSTRING(x)    __STRINGIFY(x)
+
 struct bt_trace_descriptor;
 struct trace_collection {
        GPtrArray *array;       /* struct bt_trace_descriptor */
index eb72e9445b9101d097a3738120971fd8006bc8d0..9b66836f4889445b851932b5da6af1503a13710e 100644 (file)
@@ -69,10 +69,111 @@ struct bt_plugin {
 
        /* Value depends on the specific plugin type */
        void *spec_data;
+       void (*destroy_spec_data)(struct bt_plugin *);
 };
 
-BT_HIDDEN
-struct bt_plugin *bt_plugin_create_empty(enum bt_plugin_type type);
+static inline
+void bt_plugin_destroy(struct bt_object *obj)
+{
+       struct bt_plugin *plugin;
+
+       assert(obj);
+       plugin = container_of(obj, struct bt_plugin, base);
+
+       if (plugin->destroy_spec_data) {
+               plugin->destroy_spec_data(plugin);
+       }
+
+       if (plugin->comp_classes) {
+               g_ptr_array_free(plugin->comp_classes, TRUE);
+       }
+
+       if (plugin->info.name) {
+               g_string_free(plugin->info.name, TRUE);
+       }
+
+       if (plugin->info.path) {
+               g_string_free(plugin->info.path, TRUE);
+       }
+
+       if (plugin->info.description) {
+               g_string_free(plugin->info.description, TRUE);
+       }
+
+       if (plugin->info.author) {
+               g_string_free(plugin->info.author, TRUE);
+       }
+
+       if (plugin->info.license) {
+               g_string_free(plugin->info.license, TRUE);
+       }
+
+       if (plugin->info.version.extra) {
+               g_string_free(plugin->info.version.extra, TRUE);
+       }
+
+       g_free(plugin);
+}
+
+static inline
+struct bt_plugin *bt_plugin_create_empty(enum bt_plugin_type type)
+{
+       struct bt_plugin *plugin = NULL;
+
+       plugin = g_new0(struct bt_plugin, 1);
+       if (!plugin) {
+               goto error;
+       }
+
+       bt_object_init(plugin, bt_plugin_destroy);
+       plugin->type = type;
+
+       /* Create empty array of component classes */
+       plugin->comp_classes =
+               g_ptr_array_new_with_free_func((GDestroyNotify) bt_put);
+       if (!plugin->comp_classes) {
+               goto error;
+       }
+
+       /* Create empty info */
+       plugin->info.name = g_string_new(NULL);
+       if (!plugin->info.name) {
+               goto error;
+       }
+
+       plugin->info.path = g_string_new(NULL);
+       if (!plugin->info.path) {
+               goto error;
+       }
+
+       plugin->info.description = g_string_new(NULL);
+       if (!plugin->info.description) {
+               goto error;
+       }
+
+       plugin->info.author = g_string_new(NULL);
+       if (!plugin->info.author) {
+               goto error;
+       }
+
+       plugin->info.license = g_string_new(NULL);
+       if (!plugin->info.license) {
+               goto error;
+       }
+
+       plugin->info.version.extra = g_string_new(NULL);
+       if (!plugin->info.version.extra) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       BT_PUT(plugin);
+
+end:
+       return plugin;
+}
 
 static inline
 void bt_plugin_set_path(struct bt_plugin *plugin, const char *path)
diff --git a/include/babeltrace/plugin/plugin-python-disabled-internal.h b/include/babeltrace/plugin/plugin-python-disabled-internal.h
deleted file mode 100644 (file)
index dec6fc1..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef BABELTRACE_PLUGIN_PLUGIN_PYTHON_DISABLED_INTERNAL_H
-#define BABELTRACE_PLUGIN_PLUGIN_PYTHON_DISABLED_INTERNAL_H
-
-/*
- * BabelTrace - Babeltrace Plug-in Interface
- *
- * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-struct bt_plugin;
-
-static inline
-struct bt_plugin **bt_plugin_python_create_all_from_file(const char *path)
-{
-       return NULL;
-}
-
-static inline
-void bt_plugin_python_destroy_spec_data(struct bt_plugin *plugin)
-{
-}
-
-#endif /* BABELTRACE_PLUGIN_PLUGIN_PYTHON_DISABLED_INTERNAL_H */
diff --git a/include/babeltrace/plugin/plugin-python-enabled-internal.h b/include/babeltrace/plugin/plugin-python-enabled-internal.h
deleted file mode 100644 (file)
index b1d0345..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef BABELTRACE_PLUGIN_PLUGIN_PYTHON_ENABLED_INTERNAL_H
-#define BABELTRACE_PLUGIN_PLUGIN_PYTHON_ENABLED_INTERNAL_H
-
-/*
- * BabelTrace - Babeltrace Plug-in Interface
- *
- * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-struct bt_plugin;
-
-BT_HIDDEN
-struct bt_plugin **bt_plugin_python_create_all_from_file(const char *path);
-
-static inline
-void bt_plugin_python_destroy_spec_data(struct bt_plugin *plugin)
-{
-}
-
-#endif /* BABELTRACE_PLUGIN_PLUGIN_PYTHON_ENABLED_INTERNAL_H */
index 4e3bc86629885d11b60694a81e0be9644f81fabc..e2dd51c014102ae22c309e367eaa532d2590c841 100644 (file)
@@ -64,7 +64,4 @@ BT_HIDDEN
 int bt_plugin_so_on_add_component_class(struct bt_plugin *plugin,
                struct bt_component_class *comp_class);
 
-BT_HIDDEN
-void bt_plugin_so_destroy_spec_data(struct bt_plugin *plugin);
-
 #endif /* BABELTRACE_PLUGIN_PLUGIN_SO_INTERNAL_H */
diff --git a/include/babeltrace/plugin/python-plugin-provider-internal.h b/include/babeltrace/plugin/python-plugin-provider-internal.h
new file mode 100644 (file)
index 0000000..acf913f
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef BABELTRACE_PLUGIN_PYTHON_PLUGIN_PROVIDER_INTERNAL_H
+#define BABELTRACE_PLUGIN_PYTHON_PLUGIN_PROVIDER_INTERNAL_H
+
+/* *
+ * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <babeltrace/plugin/plugin.h>
+
+extern
+struct bt_plugin **bt_plugin_python_create_all_from_file(const char *path);
+
+#endif /* BABELTRACE_PLUGIN_PYTHON_PLUGIN_PROVIDER_INTERNAL_H */
index c47d581b8c819c3960bf1a5de0804b9eb545ea8d..0ba33f39f5621a6a8c9e444cdd602531aebecc63 100644 (file)
@@ -34,3 +34,7 @@ libbabeltrace_la_LIBADD = \
        component/libcomponent.la \
        plugin/libplugin.la \
        $(top_builddir)/common/libbabeltrace-common.la
+
+if BUILT_IN_PYTHON_PLUGIN_SUPPORT
+libbabeltrace_la_LIBADD += $(top_builddir)/python-plugin-provider/libbabeltrace-python-plugin-provider.la
+endif
index 355a31f89d72da52ef433bf7c0bf529d371ba28f..467bbc52d92f7adaef0e8ed6195cf2741958c68e 100644 (file)
@@ -1,4 +1,4 @@
-AM_CFLAGS = $(PYTHON_INCLUDE) $(PACKAGE_CFLAGS) -I$(top_srcdir)/include
+AM_CFLAGS = $(PACKAGE_CFLAGS) -I$(top_srcdir)/include
 
 noinst_LTLIBRARIES = libplugin.la
 
@@ -6,8 +6,3 @@ noinst_LTLIBRARIES = libplugin.la
 libplugin_la_SOURCES = \
        plugin.c \
        plugin-so.c
-
-if WITH_PYTHON_PLUGINS
-libplugin_la_SOURCES += plugin-python.c
-libplugin_la_LDFLAGS = $(PYTHON_LIBS)
-endif
diff --git a/lib/plugin/plugin-python.c b/lib/plugin/plugin-python.c
deleted file mode 100644 (file)
index fc897ee..0000000
+++ /dev/null
@@ -1,454 +0,0 @@
-/*
- * plugin-python.c
- *
- * Babeltrace Plugin (Python)
- *
- * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include <babeltrace/babeltrace-internal.h>
-#include <babeltrace/compiler.h>
-#include <babeltrace/ref.h>
-#include <babeltrace/plugin/plugin-internal.h>
-#include <babeltrace/plugin/plugin-internal.h>
-#include <babeltrace/component/component-class-internal.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <Python.h>
-
-#define PYTHON_PLUGIN_FILE_PREFIX      "bt_plugin_"
-#define PYTHON_PLUGIN_FILE_PREFIX_LEN  (sizeof(PYTHON_PLUGIN_FILE_PREFIX) - 1)
-#define PYTHON_PLUGIN_FILE_EXT         ".py"
-#define PYTHON_PLUGIN_FILE_EXT_LEN     (sizeof(PYTHON_PLUGIN_FILE_EXT) - 1)
-
-enum python_state {
-       /* init_python() not called yet */
-       PYTHON_STATE_NOT_INITED,
-
-       /* init_python() called once with success */
-       PYTHON_STATE_FULLY_INITIALIZED,
-
-       /* init_python() called once without success */
-       PYTHON_STATE_CANNOT_INITIALIZE,
-} python_state = PYTHON_STATE_NOT_INITED;
-
-static PyObject *py_try_load_plugin_module_func = NULL;
-
-static
-void print_python_traceback_verbose(void)
-{
-       if (Py_IsInitialized() && PyErr_Occurred() && babeltrace_verbose) {
-               PyErr_Print();
-       }
-}
-
-static
-void pyerr_clear(void)
-{
-       if (Py_IsInitialized()) {
-               PyErr_Clear();
-       }
-}
-
-static
-void init_python(void)
-{
-       PyObject *py_bt2_py_plugin_mod = NULL;
-       const char *dis_python_env;
-       sighandler_t old_sigint = signal(SIGINT, SIG_DFL);
-
-       if (python_state != PYTHON_STATE_NOT_INITED) {
-               goto end;
-       }
-
-       /*
-        * User can disable Python plugin support with the
-        * BABELTRACE_DISABLE_PYTHON_PLUGINS environment variable set to
-        * 1.
-        */
-       dis_python_env = getenv("BABELTRACE_DISABLE_PYTHON_PLUGINS");
-       if (dis_python_env && dis_python_env[0] == '1' &&
-                       dis_python_env[1] == '\0') {
-               printf_verbose("Python plugin support is disabled by BABELTRACE_DISABLE_PYTHON_PLUGINS environment variable\n");
-               python_state = PYTHON_STATE_CANNOT_INITIALIZE;
-               goto end;
-       }
-
-       if (!Py_IsInitialized()) {
-               Py_InitializeEx(0);
-               printf_verbose("Initialized Python:\n%s\n", Py_GetVersion());
-       }
-
-       py_bt2_py_plugin_mod = PyImport_ImportModule("bt2.py_plugin");
-       if (!py_bt2_py_plugin_mod) {
-               printf_verbose("Cannot import bt2.py_plugin Python module\n");
-               python_state = PYTHON_STATE_CANNOT_INITIALIZE;
-               goto end;
-       }
-
-       py_try_load_plugin_module_func =
-               PyObject_GetAttrString(py_bt2_py_plugin_mod, "_try_load_plugin_module");
-       if (!py_try_load_plugin_module_func) {
-               printf_verbose("Cannot get _try_load_plugin_module attribute from bt2.py_plugin Python module\n");
-               python_state = PYTHON_STATE_CANNOT_INITIALIZE;
-               goto end;
-       }
-
-       python_state = PYTHON_STATE_FULLY_INITIALIZED;
-
-end:
-       if (old_sigint != SIG_ERR) {
-               (void) signal(SIGINT, old_sigint);
-       }
-
-       print_python_traceback_verbose();
-       pyerr_clear();
-       Py_XDECREF(py_bt2_py_plugin_mod);
-       return;
-}
-
-__attribute__((destructor)) static
-void fini_python(void) {
-       if (Py_IsInitialized()) {
-               if (py_try_load_plugin_module_func) {
-                       Py_DECREF(py_try_load_plugin_module_func);
-                       py_try_load_plugin_module_func = NULL;
-               }
-
-               Py_Finalize();
-       }
-
-       python_state = PYTHON_STATE_NOT_INITED;
-}
-
-static
-struct bt_plugin *bt_plugin_from_python_plugin_info(PyObject *plugin_info)
-{
-       struct bt_plugin *plugin = NULL;
-       PyObject *py_name = NULL;
-       PyObject *py_author = NULL;
-       PyObject *py_description = NULL;
-       PyObject *py_license = NULL;
-       PyObject *py_version = NULL;
-       PyObject *py_comp_class_addrs = NULL;
-       const char *name = NULL;
-       const char *author = NULL;
-       const char *description = NULL;
-       const char *license = NULL;
-       unsigned int major = 0, minor = 0, patch = 0;
-       const char *version_extra = NULL;
-       int ret;
-
-       assert(plugin_info);
-       assert(python_state == PYTHON_STATE_FULLY_INITIALIZED);
-       py_name = PyObject_GetAttrString(plugin_info, "name");
-       if (!py_name) {
-               printf_verbose("Cannot find `name` attribute in plugin info\n");
-               goto error;
-       }
-
-       py_author = PyObject_GetAttrString(plugin_info, "author");
-       if (!py_author) {
-               printf_verbose("Cannot find `author` attribute in plugin info\n");
-               goto error;
-       }
-
-       py_description = PyObject_GetAttrString(plugin_info, "description");
-       if (!py_description) {
-               printf_verbose("Cannot find `description` attribute in plugin info\n");
-               goto error;
-       }
-
-       py_license = PyObject_GetAttrString(plugin_info, "license");
-       if (!py_license) {
-               printf_verbose("Cannot find `license` attribute in plugin info\n");
-               goto error;
-       }
-
-       py_version = PyObject_GetAttrString(plugin_info, "version");
-       if (!py_version) {
-               printf_verbose("Cannot find `version` attribute in plugin info\n");
-               goto error;
-       }
-
-       py_comp_class_addrs = PyObject_GetAttrString(plugin_info,
-               "comp_class_addrs");
-       if (!py_comp_class_addrs) {
-               printf_verbose("Cannot find `comp_class_addrs` attribute in plugin info\n");
-               goto error;
-       }
-
-       if (PyUnicode_Check(py_name)) {
-               name = PyUnicode_AsUTF8(py_name);
-               if (!name) {
-                       printf_verbose("Cannot decode plugin name string\n");
-                       goto error;
-               }
-       } else {
-               /* Plugin name is mandatory */
-               printf_verbose("Plugin name is not a string\n");
-               goto error;
-       }
-
-       if (PyUnicode_Check(py_author)) {
-               author = PyUnicode_AsUTF8(py_author);
-               if (!author) {
-                       printf_verbose("Cannot decode plugin author string\n");
-                       goto error;
-               }
-       }
-
-       if (PyUnicode_Check(py_description)) {
-               description = PyUnicode_AsUTF8(py_description);
-               if (!description) {
-                       printf_verbose("Cannot decode plugin description string\n");
-                       goto error;
-               }
-       }
-
-       if (PyUnicode_Check(py_license)) {
-               license = PyUnicode_AsUTF8(py_license);
-               if (!license) {
-                       printf_verbose("Cannot decode plugin license string\n");
-                       goto error;
-               }
-       }
-
-       if (PyTuple_Check(py_version)) {
-               if (PyTuple_Size(py_version) >= 3) {
-                       PyObject *py_major = PyTuple_GetItem(py_version, 0);
-                       PyObject *py_minor = PyTuple_GetItem(py_version, 1);
-                       PyObject *py_patch = PyTuple_GetItem(py_version, 2);
-
-                       assert(py_major);
-                       assert(py_minor);
-                       assert(py_patch);
-
-                       if (PyLong_Check(py_major)) {
-                               major = PyLong_AsUnsignedLong(py_major);
-                       }
-
-                       if (PyLong_Check(py_minor)) {
-                               minor = PyLong_AsUnsignedLong(py_minor);
-                       }
-
-                       if (PyLong_Check(py_patch)) {
-                               patch = PyLong_AsUnsignedLong(py_patch);
-                       }
-
-                       if (PyErr_Occurred()) {
-                               /* Overflow error, most probably */
-                               printf_verbose("Invalid plugin version format\n");
-                               goto error;
-                       }
-               }
-
-               if (PyTuple_Size(py_version) >= 4) {
-                       PyObject *py_extra = PyTuple_GetItem(py_version, 3);
-
-                       assert(py_extra);
-
-                       if (PyUnicode_Check(py_extra)) {
-                               version_extra = PyUnicode_AsUTF8(py_extra);
-                               if (!version_extra) {
-                                       printf_verbose("Cannot decode plugin version's extra string\n");
-                                       goto error;
-                               }
-                       }
-               }
-       }
-
-       plugin = bt_plugin_create_empty(BT_PLUGIN_TYPE_PYTHON);
-       if (!plugin) {
-               goto error;
-       }
-
-       bt_plugin_set_name(plugin, name);
-
-       if (description) {
-               bt_plugin_set_description(plugin, description);
-       }
-
-       if (author) {
-               bt_plugin_set_author(plugin, author);
-       }
-
-       if (license) {
-               bt_plugin_set_license(plugin, license);
-       }
-
-       bt_plugin_set_version(plugin, major, minor, patch, version_extra);
-
-       if (PyList_Check(py_comp_class_addrs)) {
-               size_t i;
-
-               for (i = 0; i < PyList_Size(py_comp_class_addrs); i++) {
-                       struct bt_component_class *comp_class;
-                       PyObject *py_comp_class_addr;
-
-                       py_comp_class_addr =
-                               PyList_GetItem(py_comp_class_addrs, i);
-                       assert(py_comp_class_addr);
-                       if (PyLong_Check(py_comp_class_addr)) {
-                               comp_class = (struct bt_component_class *)
-                                       PyLong_AsUnsignedLongLong(py_comp_class_addr);
-                       } else {
-                               printf_verbose("Component class address #%zu: not an integer\n",
-                                       i);
-                               continue;
-                       }
-
-                       ret = bt_plugin_add_component_class(plugin, comp_class);
-                       if (ret < 0) {
-                               printf_verbose("Cannot add component class #%zu\n",
-                                       i);
-                               continue;
-                       }
-               }
-       }
-
-       bt_plugin_freeze(plugin);
-
-       goto end;
-
-error:
-       print_python_traceback_verbose();
-       pyerr_clear();
-       BT_PUT(plugin);
-
-end:
-       Py_XDECREF(py_name);
-       Py_XDECREF(py_author);
-       Py_XDECREF(py_description);
-       Py_XDECREF(py_license);
-       Py_XDECREF(py_version);
-       Py_XDECREF(py_comp_class_addrs);
-       return plugin;
-}
-
-BT_HIDDEN
-struct bt_plugin **bt_plugin_python_create_all_from_file(const char *path)
-{
-       struct bt_plugin **plugins = NULL;
-       PyObject *py_plugin_info = NULL;
-       gchar *basename = NULL;
-       size_t path_len;
-
-       assert(path);
-
-       if (python_state == PYTHON_STATE_CANNOT_INITIALIZE) {
-               /*
-                * We do not even care about the rest of the function
-                * here because we already know Python cannot be fully
-                * initialized.
-                */
-               goto error;
-       }
-
-       path_len = strlen(path);
-
-       /* File name ends with `.py` */
-       if (strncmp(path + path_len - PYTHON_PLUGIN_FILE_EXT_LEN,
-                       PYTHON_PLUGIN_FILE_EXT,
-                       PYTHON_PLUGIN_FILE_EXT_LEN) != 0) {
-               printf_verbose("Skipping non-Python file: `%s`\n",
-                       path);
-               goto error;
-       }
-
-       /* File name starts with `bt_plugin_` */
-       basename = g_path_get_basename(path);
-       if (!basename) {
-               goto error;
-       }
-
-       if (strncmp(basename, PYTHON_PLUGIN_FILE_PREFIX,
-                       PYTHON_PLUGIN_FILE_PREFIX_LEN) != 0) {
-               printf_verbose("Skipping Python file not starting with `%s`: `%s`\n",
-                       PYTHON_PLUGIN_FILE_PREFIX, path);
-               goto error;
-       }
-
-       /*
-        * Initialize Python now.
-        *
-        * This is not done in the library contructor because the
-        * interpreter is somewhat slow to initialize. If you don't
-        * have any potential Python plugins, you don't need to endure
-        * this waiting time everytime you load the library.
-        */
-       init_python();
-       if (python_state != PYTHON_STATE_FULLY_INITIALIZED) {
-               /*
-                * For some reason we cannot initialize Python,
-                * import the required modules, and get the required
-                * attributes from them.
-                */
-               goto error;
-       }
-
-       /*
-        * Call bt2.py_plugin._try_load_plugin_module() with this path
-        * to get plugin info if the plugin is loadable and complete.
-        * This function returns None when there's an error, but just in
-        * case we also manually clear the last Python error state.
-        */
-       py_plugin_info = PyObject_CallFunction(py_try_load_plugin_module_func,
-               "(s)", path);
-       if (!py_plugin_info || py_plugin_info == Py_None) {
-               printf_verbose("Cannot load Python plugin `%s`:\n", path);
-               print_python_traceback_verbose();
-               PyErr_Clear();
-               goto error;
-       }
-
-       /*
-        * Get bt_plugin from plugin info object.
-        *
-        * calloc(2, ...) because a single Python plugin file always
-        * provides a single Babeltrace plugin (second item is the
-        * sentinel).
-        */
-       plugins = calloc(2, sizeof(*plugins));
-       if (!plugins) {
-               goto error;
-       }
-
-       plugins[0] = bt_plugin_from_python_plugin_info(py_plugin_info);
-       if (!plugins[0]) {
-               goto error;
-       }
-
-       bt_plugin_set_path(plugins[0], path);
-       goto end;
-
-error:
-       if (plugins) {
-               BT_PUT(plugins[0]);
-               free(plugins);
-               plugins = NULL;
-       }
-
-end:
-       Py_XDECREF(py_plugin_info);
-       g_free(basename);
-       return plugins;
-}
index 24478f1fc761ab9f02ae8c4b0e9c12d0f7f36665..96f21458c7223eac870cc97880f0aa6c793be328 100644 (file)
@@ -188,7 +188,7 @@ end:
        return shared_lib_handle;
 }
 
-BT_HIDDEN
+static
 void bt_plugin_so_destroy_spec_data(struct bt_plugin *plugin)
 {
        struct bt_plugin_so_spec_data *spec = plugin->spec_data;
@@ -642,6 +642,7 @@ struct bt_plugin *bt_plugin_so_create_empty(
                goto error;
        }
 
+       plugin->destroy_spec_data = bt_plugin_so_destroy_spec_data;
        plugin->spec_data = g_new0(struct bt_plugin_so_spec_data, 1);
        if (!plugin->spec_data) {
                goto error;
index 0e62889030a63f229a08d7811191caa42e9efb9c..83f628cd8ef79fe387360ba3e55b9fcc1c95c515 100644 (file)
@@ -27,6 +27,7 @@
  * SOFTWARE.
  */
 
+#include <babeltrace/babeltrace-internal.h>
 #include <babeltrace/compiler.h>
 #include <babeltrace/ref.h>
 #include <babeltrace/common-internal.h>
 #include <sys/stat.h>
 #include <dirent.h>
 
-#ifdef WITH_PYTHON_PLUGINS
-# include <babeltrace/plugin/plugin-python-enabled-internal.h>
-#else
-# include <babeltrace/plugin/plugin-python-disabled-internal.h>
-#endif
+#define PYTHON_PLUGIN_PROVIDER_FILENAME        "libbabeltrace-python-plugin-provider." G_MODULE_SUFFIX
+#define PYTHON_PLUGIN_PROVIDER_SYM_NAME        bt_plugin_python_create_all_from_file
+#define PYTHON_PLUGIN_PROVIDER_SYM_NAME_STR    TOSTRING(PYTHON_PLUGIN_PROVIDER_SYM_NAME)
 
+#ifdef BT_BUILT_IN_PYTHON_PLUGIN_SUPPORT
+#include <babeltrace/plugin/python-plugin-provider-internal.h>
 static
-void bt_plugin_destroy(struct bt_object *obj)
-{
-       struct bt_plugin *plugin;
-
-       assert(obj);
-       plugin = container_of(obj, struct bt_plugin, base);
-
-       if (plugin->type == BT_PLUGIN_TYPE_SO) {
-               bt_plugin_so_destroy_spec_data(plugin);
-       } else if (plugin->type == BT_PLUGIN_TYPE_PYTHON) {
-               bt_plugin_python_destroy_spec_data(plugin);
-       } else {
-               assert(false);
-       }
-
-       if (plugin->comp_classes) {
-               g_ptr_array_free(plugin->comp_classes, TRUE);
-       }
-
-       if (plugin->info.name) {
-               g_string_free(plugin->info.name, TRUE);
-       }
-
-       if (plugin->info.path) {
-               g_string_free(plugin->info.path, TRUE);
-       }
-
-       if (plugin->info.description) {
-               g_string_free(plugin->info.description, TRUE);
-       }
-
-       if (plugin->info.author) {
-               g_string_free(plugin->info.author, TRUE);
-       }
+struct bt_plugin **(*bt_plugin_python_create_all_from_file_sym)(const char *path) =
+       bt_plugin_python_create_all_from_file;
+#else /* BT_BUILT_IN_PYTHON_PLUGIN_SUPPORT */
+static GModule *python_plugin_provider_module;
+static
+struct bt_plugin **(*bt_plugin_python_create_all_from_file_sym)(const char *path);
 
-       if (plugin->info.license) {
-               g_string_free(plugin->info.license, TRUE);
+__attribute__((constructor)) static
+void init_python_plugin_provider(void) {
+       python_plugin_provider_module =
+               g_module_open(PYTHON_PLUGIN_PROVIDER_FILENAME,
+                       G_MODULE_BIND_LOCAL);
+       if (!python_plugin_provider_module) {
+               printf_warning("Cannot find `%s`: Python plugin support is disabled\n",
+                       PYTHON_PLUGIN_PROVIDER_FILENAME);
+               return;
        }
 
-       if (plugin->info.version.extra) {
-               g_string_free(plugin->info.version.extra, TRUE);
+       if (!g_module_symbol(python_plugin_provider_module,
+                       PYTHON_PLUGIN_PROVIDER_SYM_NAME_STR,
+                       (gpointer) &bt_plugin_python_create_all_from_file_sym)) {
+               printf_warning("Cannot find the Python plugin provider loading symbole: Python plugin support is disabled\n");
        }
-
-       g_free(plugin);
 }
 
-BT_HIDDEN
-struct bt_plugin *bt_plugin_create_empty(enum bt_plugin_type type)
-{
-       struct bt_plugin *plugin = NULL;
-
-       plugin = g_new0(struct bt_plugin, 1);
-       if (!plugin) {
-               goto error;
-       }
-
-       bt_object_init(plugin, bt_plugin_destroy);
-       plugin->type = type;
-
-       /* Create empty array of component classes */
-       plugin->comp_classes =
-               g_ptr_array_new_with_free_func((GDestroyNotify) bt_put);
-       if (!plugin->comp_classes) {
-               goto error;
-       }
-
-       /* Create empty info */
-       plugin->info.name = g_string_new(NULL);
-       if (!plugin->info.name) {
-               goto error;
-       }
-
-       plugin->info.path = g_string_new(NULL);
-       if (!plugin->info.path) {
-               goto error;
-       }
-
-       plugin->info.description = g_string_new(NULL);
-       if (!plugin->info.description) {
-               goto error;
-       }
-
-       plugin->info.author = g_string_new(NULL);
-       if (!plugin->info.author) {
-               goto error;
-       }
-
-       plugin->info.license = g_string_new(NULL);
-       if (!plugin->info.license) {
-               goto error;
-       }
-
-       plugin->info.version.extra = g_string_new(NULL);
-       if (!plugin->info.version.extra) {
-               goto error;
+__attribute__((destructor)) static
+void fini_python_plugin_provider(void) {
+       if (python_plugin_provider_module) {
+               (void) g_module_close(python_plugin_provider_module);
+               python_plugin_provider_module = NULL;
        }
-
-       goto end;
-
-error:
-       BT_PUT(plugin);
-
-end:
-       return plugin;
 }
+#endif
 
 struct bt_plugin **bt_plugin_create_all_from_static(void)
 {
@@ -172,9 +101,12 @@ struct bt_plugin **bt_plugin_create_all_from_file(const char *path)
                goto end;
        }
 
-       plugins = bt_plugin_python_create_all_from_file(path);
-       if (plugins) {
-               goto end;
+       /* Try Python plugins if support is available */
+       if (bt_plugin_python_create_all_from_file_sym) {
+               plugins = bt_plugin_python_create_all_from_file_sym(path);
+               if (plugins) {
+                       goto end;
+               }
        }
 
 end:
diff --git a/python-plugin-provider/Makefile.am b/python-plugin-provider/Makefile.am
new file mode 100644 (file)
index 0000000..f477676
--- /dev/null
@@ -0,0 +1,7 @@
+AM_CFLAGS = $(PYTHON_INCLUDE) $(PACKAGE_CFLAGS) -I$(top_srcdir)/include
+
+lib_LTLIBRARIES = libbabeltrace-python-plugin-provider.la
+
+libbabeltrace_python_plugin_provider_la_SOURCES = python-plugin-provider.c
+libbabeltrace_python_plugin_provider_la_LDFLAGS = \
+       -version-info $(BABELTRACE_LIBRARY_VERSION) $(PYTHON_LIBS)
diff --git a/python-plugin-provider/python-plugin-provider.c b/python-plugin-provider/python-plugin-provider.c
new file mode 100644 (file)
index 0000000..20892fc
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * python-plugin-provider.c
+ *
+ * Babeltrace Python plugin provider
+ *
+ * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <babeltrace/babeltrace-internal.h>
+#include <babeltrace/compiler.h>
+#include <babeltrace/ref.h>
+#include <babeltrace/plugin/plugin-internal.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <Python.h>
+#include <glib.h>
+#include <gmodule.h>
+
+#define PYTHON_PLUGIN_FILE_PREFIX      "bt_plugin_"
+#define PYTHON_PLUGIN_FILE_PREFIX_LEN  (sizeof(PYTHON_PLUGIN_FILE_PREFIX) - 1)
+#define PYTHON_PLUGIN_FILE_EXT         ".py"
+#define PYTHON_PLUGIN_FILE_EXT_LEN     (sizeof(PYTHON_PLUGIN_FILE_EXT) - 1)
+
+enum python_state {
+       /* init_python() not called yet */
+       PYTHON_STATE_NOT_INITED,
+
+       /* init_python() called once with success */
+       PYTHON_STATE_FULLY_INITIALIZED,
+
+       /* init_python() called once without success */
+       PYTHON_STATE_CANNOT_INITIALIZE,
+} python_state = PYTHON_STATE_NOT_INITED;
+
+static PyObject *py_try_load_plugin_module_func = NULL;
+
+static
+void print_python_traceback_verbose(void)
+{
+       if (Py_IsInitialized() && PyErr_Occurred() && babeltrace_verbose) {
+               PyErr_Print();
+       }
+}
+
+static
+void pyerr_clear(void)
+{
+       if (Py_IsInitialized()) {
+               PyErr_Clear();
+       }
+}
+
+static
+void init_python(void)
+{
+       PyObject *py_bt2_py_plugin_mod = NULL;
+       const char *dis_python_env;
+       sighandler_t old_sigint = signal(SIGINT, SIG_DFL);
+
+       if (python_state != PYTHON_STATE_NOT_INITED) {
+               goto end;
+       }
+
+       /*
+        * User can disable Python plugin support with the
+        * BABELTRACE_DISABLE_PYTHON_PLUGINS environment variable set to
+        * 1.
+        */
+       dis_python_env = getenv("BABELTRACE_DISABLE_PYTHON_PLUGINS");
+       if (dis_python_env && dis_python_env[0] == '1' &&
+                       dis_python_env[1] == '\0') {
+               printf_verbose("Python plugin support is disabled by BABELTRACE_DISABLE_PYTHON_PLUGINS environment variable\n");
+               python_state = PYTHON_STATE_CANNOT_INITIALIZE;
+               goto end;
+       }
+
+       if (!Py_IsInitialized()) {
+               Py_InitializeEx(0);
+               printf_verbose("Initialized Python:\n%s\n", Py_GetVersion());
+       }
+
+       py_bt2_py_plugin_mod = PyImport_ImportModule("bt2.py_plugin");
+       if (!py_bt2_py_plugin_mod) {
+               printf_verbose("Cannot import bt2.py_plugin Python module\n");
+               python_state = PYTHON_STATE_CANNOT_INITIALIZE;
+               goto end;
+       }
+
+       py_try_load_plugin_module_func =
+               PyObject_GetAttrString(py_bt2_py_plugin_mod, "_try_load_plugin_module");
+       if (!py_try_load_plugin_module_func) {
+               printf_verbose("Cannot get _try_load_plugin_module attribute from bt2.py_plugin Python module\n");
+               python_state = PYTHON_STATE_CANNOT_INITIALIZE;
+               goto end;
+       }
+
+       python_state = PYTHON_STATE_FULLY_INITIALIZED;
+
+end:
+       if (old_sigint != SIG_ERR) {
+               (void) signal(SIGINT, old_sigint);
+       }
+
+       print_python_traceback_verbose();
+       pyerr_clear();
+       Py_XDECREF(py_bt2_py_plugin_mod);
+       return;
+}
+
+__attribute__((destructor)) static
+void fini_python(void) {
+       if (Py_IsInitialized()) {
+               if (py_try_load_plugin_module_func) {
+                       Py_DECREF(py_try_load_plugin_module_func);
+                       py_try_load_plugin_module_func = NULL;
+               }
+
+               Py_Finalize();
+       }
+
+       python_state = PYTHON_STATE_NOT_INITED;
+}
+
+static
+struct bt_plugin *bt_plugin_from_python_plugin_info(PyObject *plugin_info)
+{
+       struct bt_plugin *plugin = NULL;
+       PyObject *py_name = NULL;
+       PyObject *py_author = NULL;
+       PyObject *py_description = NULL;
+       PyObject *py_license = NULL;
+       PyObject *py_version = NULL;
+       PyObject *py_comp_class_addrs = NULL;
+       const char *name = NULL;
+       const char *author = NULL;
+       const char *description = NULL;
+       const char *license = NULL;
+       unsigned int major = 0, minor = 0, patch = 0;
+       const char *version_extra = NULL;
+       int ret;
+
+       assert(plugin_info);
+       assert(python_state == PYTHON_STATE_FULLY_INITIALIZED);
+       py_name = PyObject_GetAttrString(plugin_info, "name");
+       if (!py_name) {
+               printf_verbose("Cannot find `name` attribute in plugin info\n");
+               goto error;
+       }
+
+       py_author = PyObject_GetAttrString(plugin_info, "author");
+       if (!py_author) {
+               printf_verbose("Cannot find `author` attribute in plugin info\n");
+               goto error;
+       }
+
+       py_description = PyObject_GetAttrString(plugin_info, "description");
+       if (!py_description) {
+               printf_verbose("Cannot find `description` attribute in plugin info\n");
+               goto error;
+       }
+
+       py_license = PyObject_GetAttrString(plugin_info, "license");
+       if (!py_license) {
+               printf_verbose("Cannot find `license` attribute in plugin info\n");
+               goto error;
+       }
+
+       py_version = PyObject_GetAttrString(plugin_info, "version");
+       if (!py_version) {
+               printf_verbose("Cannot find `version` attribute in plugin info\n");
+               goto error;
+       }
+
+       py_comp_class_addrs = PyObject_GetAttrString(plugin_info,
+               "comp_class_addrs");
+       if (!py_comp_class_addrs) {
+               printf_verbose("Cannot find `comp_class_addrs` attribute in plugin info\n");
+               goto error;
+       }
+
+       if (PyUnicode_Check(py_name)) {
+               name = PyUnicode_AsUTF8(py_name);
+               if (!name) {
+                       printf_verbose("Cannot decode plugin name string\n");
+                       goto error;
+               }
+       } else {
+               /* Plugin name is mandatory */
+               printf_verbose("Plugin name is not a string\n");
+               goto error;
+       }
+
+       if (PyUnicode_Check(py_author)) {
+               author = PyUnicode_AsUTF8(py_author);
+               if (!author) {
+                       printf_verbose("Cannot decode plugin author string\n");
+                       goto error;
+               }
+       }
+
+       if (PyUnicode_Check(py_description)) {
+               description = PyUnicode_AsUTF8(py_description);
+               if (!description) {
+                       printf_verbose("Cannot decode plugin description string\n");
+                       goto error;
+               }
+       }
+
+       if (PyUnicode_Check(py_license)) {
+               license = PyUnicode_AsUTF8(py_license);
+               if (!license) {
+                       printf_verbose("Cannot decode plugin license string\n");
+                       goto error;
+               }
+       }
+
+       if (PyTuple_Check(py_version)) {
+               if (PyTuple_Size(py_version) >= 3) {
+                       PyObject *py_major = PyTuple_GetItem(py_version, 0);
+                       PyObject *py_minor = PyTuple_GetItem(py_version, 1);
+                       PyObject *py_patch = PyTuple_GetItem(py_version, 2);
+
+                       assert(py_major);
+                       assert(py_minor);
+                       assert(py_patch);
+
+                       if (PyLong_Check(py_major)) {
+                               major = PyLong_AsUnsignedLong(py_major);
+                       }
+
+                       if (PyLong_Check(py_minor)) {
+                               minor = PyLong_AsUnsignedLong(py_minor);
+                       }
+
+                       if (PyLong_Check(py_patch)) {
+                               patch = PyLong_AsUnsignedLong(py_patch);
+                       }
+
+                       if (PyErr_Occurred()) {
+                               /* Overflow error, most probably */
+                               printf_verbose("Invalid plugin version format\n");
+                               goto error;
+                       }
+               }
+
+               if (PyTuple_Size(py_version) >= 4) {
+                       PyObject *py_extra = PyTuple_GetItem(py_version, 3);
+
+                       assert(py_extra);
+
+                       if (PyUnicode_Check(py_extra)) {
+                               version_extra = PyUnicode_AsUTF8(py_extra);
+                               if (!version_extra) {
+                                       printf_verbose("Cannot decode plugin version's extra string\n");
+                                       goto error;
+                               }
+                       }
+               }
+       }
+
+       plugin = bt_plugin_create_empty(BT_PLUGIN_TYPE_PYTHON);
+       if (!plugin) {
+               goto error;
+       }
+
+       bt_plugin_set_name(plugin, name);
+
+       if (description) {
+               bt_plugin_set_description(plugin, description);
+       }
+
+       if (author) {
+               bt_plugin_set_author(plugin, author);
+       }
+
+       if (license) {
+               bt_plugin_set_license(plugin, license);
+       }
+
+       bt_plugin_set_version(plugin, major, minor, patch, version_extra);
+
+       if (PyList_Check(py_comp_class_addrs)) {
+               size_t i;
+
+               for (i = 0; i < PyList_Size(py_comp_class_addrs); i++) {
+                       struct bt_component_class *comp_class;
+                       PyObject *py_comp_class_addr;
+
+                       py_comp_class_addr =
+                               PyList_GetItem(py_comp_class_addrs, i);
+                       assert(py_comp_class_addr);
+                       if (PyLong_Check(py_comp_class_addr)) {
+                               comp_class = (struct bt_component_class *)
+                                       PyLong_AsUnsignedLongLong(py_comp_class_addr);
+                       } else {
+                               printf_verbose("Component class address #%zu: not an integer\n",
+                                       i);
+                               continue;
+                       }
+
+                       ret = bt_plugin_add_component_class(plugin, comp_class);
+                       if (ret < 0) {
+                               printf_verbose("Cannot add component class #%zu\n",
+                                       i);
+                               continue;
+                       }
+               }
+       }
+
+       bt_plugin_freeze(plugin);
+
+       goto end;
+
+error:
+       print_python_traceback_verbose();
+       pyerr_clear();
+       BT_PUT(plugin);
+
+end:
+       Py_XDECREF(py_name);
+       Py_XDECREF(py_author);
+       Py_XDECREF(py_description);
+       Py_XDECREF(py_license);
+       Py_XDECREF(py_version);
+       Py_XDECREF(py_comp_class_addrs);
+       return plugin;
+}
+
+G_MODULE_EXPORT
+struct bt_plugin **bt_plugin_python_create_all_from_file(const char *path)
+{
+       struct bt_plugin **plugins = NULL;
+       PyObject *py_plugin_info = NULL;
+       gchar *basename = NULL;
+       size_t path_len;
+
+       assert(path);
+
+       if (python_state == PYTHON_STATE_CANNOT_INITIALIZE) {
+               /*
+                * We do not even care about the rest of the function
+                * here because we already know Python cannot be fully
+                * initialized.
+                */
+               goto error;
+       }
+
+       path_len = strlen(path);
+
+       /* File name ends with `.py` */
+       if (strncmp(path + path_len - PYTHON_PLUGIN_FILE_EXT_LEN,
+                       PYTHON_PLUGIN_FILE_EXT,
+                       PYTHON_PLUGIN_FILE_EXT_LEN) != 0) {
+               printf_verbose("Skipping non-Python file: `%s`\n",
+                       path);
+               goto error;
+       }
+
+       /* File name starts with `bt_plugin_` */
+       basename = g_path_get_basename(path);
+       if (!basename) {
+               goto error;
+       }
+
+       if (strncmp(basename, PYTHON_PLUGIN_FILE_PREFIX,
+                       PYTHON_PLUGIN_FILE_PREFIX_LEN) != 0) {
+               printf_verbose("Skipping Python file not starting with `%s`: `%s`\n",
+                       PYTHON_PLUGIN_FILE_PREFIX, path);
+               goto error;
+       }
+
+       /*
+        * Initialize Python now.
+        *
+        * This is not done in the library contructor because the
+        * interpreter is somewhat slow to initialize. If you don't
+        * have any potential Python plugins, you don't need to endure
+        * this waiting time everytime you load the library.
+        */
+       init_python();
+       if (python_state != PYTHON_STATE_FULLY_INITIALIZED) {
+               /*
+                * For some reason we cannot initialize Python,
+                * import the required modules, and get the required
+                * attributes from them.
+                */
+               goto error;
+       }
+
+       /*
+        * Call bt2.py_plugin._try_load_plugin_module() with this path
+        * to get plugin info if the plugin is loadable and complete.
+        * This function returns None when there's an error, but just in
+        * case we also manually clear the last Python error state.
+        */
+       py_plugin_info = PyObject_CallFunction(py_try_load_plugin_module_func,
+               "(s)", path);
+       if (!py_plugin_info || py_plugin_info == Py_None) {
+               printf_verbose("Cannot load Python plugin `%s`:\n", path);
+               print_python_traceback_verbose();
+               PyErr_Clear();
+               goto error;
+       }
+
+       /*
+        * Get bt_plugin from plugin info object.
+        *
+        * calloc(2, ...) because a single Python plugin file always
+        * provides a single Babeltrace plugin (second item is the
+        * sentinel).
+        */
+       plugins = calloc(2, sizeof(*plugins));
+       if (!plugins) {
+               goto error;
+       }
+
+       plugins[0] = bt_plugin_from_python_plugin_info(py_plugin_info);
+       if (!plugins[0]) {
+               goto error;
+       }
+
+       bt_plugin_set_path(plugins[0], path);
+       goto end;
+
+error:
+       if (plugins) {
+               BT_PUT(plugins[0]);
+               free(plugins);
+               plugins = NULL;
+       }
+
+end:
+       Py_XDECREF(py_plugin_info);
+       g_free(basename);
+       return plugins;
+}
This page took 0.041768 seconds and 4 git commands to generate.