Commit 84a15937 authored by Garrett Regier's avatar Garrett Regier
Browse files

Implemented a Lua plugin loader using the LGI bindings

parent 1b5f91bf
......@@ -78,6 +78,7 @@ Makefile.in
/tests/*/vgdump-*
/tests/libpeas/engine
/tests/libpeas/extension-c
/tests/libpeas/extension-lua51
/tests/libpeas/extension-python
/tests/libpeas/extension-python3
/tests/libpeas/extension-set
......
......@@ -20,11 +20,11 @@ GInterfaces the plugin writer will be able to implement as his plugin requires.
On-demand programming language support
--------------------------------------
libpeas comes with a set of supported languages (currently, C, Python 2 and
Python 3). Those languages are supported through “loaders” which are loaded
on demand. What it means is that you only pay for what you use: if you have no
Python plugin, the Python interpreter won't be loaded in memory. Of course, the
same goes for the C loader.
libpeas comes with a set of supported languages (currently, C, Lua 5.1,
Python 2 and Python 3). Those languages are supported through “loaders” which
are loaded on demand. What it means is that you only pay for what you use: if
you have no Python plugin, the Python interpreter won't be loaded in memory.
Of course, the same goes for the C loader.
Damn simple to use (or at least we try hard)
--------------------------------------------
......@@ -82,7 +82,7 @@ Sample code
-----------
The libpeas package contains a sample application called peas-demo, and sample
plugins written in C and Python.
plugins written in C, Lua and Python.
The global idea is this one: you create a new PeasEngine instance and give it
the information needed for it to find your plugins. Then you load some plugins
......
......@@ -231,6 +231,130 @@ fi
AM_CONDITIONAL([ENABLE_GLADE_CATALOG],[test "x$found_glade_catalog" = "xyes"])
dnl ================================================================
dnl Lua
dnl ================================================================
LUA51_REQUIRED=5.1.0
LUAJIT_REQUIRED=2.0
LGI_MAJOR_VERSION=0
LGI_MINOR_VERSION=8
LGI_MICRO_VERSION=0
LGI_REQUIRED=$LGI_MAJOR_VERSION.$LGI_MINOR_VERSION.$LGI_MICRO_VERSION
AC_DEFINE_UNQUOTED(LGI_MAJOR_VERSION, [$LGI_MAJOR_VERSION], [LGI major version.])
AC_DEFINE_UNQUOTED(LGI_MINOR_VERSION, [$LGI_MINOR_VERSION], [LGI minor version.])
AC_DEFINE_UNQUOTED(LGI_MICRO_VERSION, [$LGI_MICRO_VERSION], [LGI micro version.])
dnl
dnl Test program for LGI version
dnl
m4_define([peas_lgi_version_test], [
#include <lauxlib.h>
#include <lualib.h>
#include "peas-plugin-loader-lua-utils.c"
int main(int argc, char **argv)
{
lua_State *L;
gboolean success;
L = luaL_newstate ();
luaL_openlibs (L);
success = (peas_lua_utils_require (L, "lgi") &&
peas_lua_utils_check_version (L,
LGI_MAJOR_VERSION,
LGI_MINOR_VERSION,
LGI_MICRO_VERSION));
lua_close (L);
return success ? 0 : 1;
}])
AC_ARG_ENABLE(lua5.1,
AS_HELP_STRING([--enable-lua5.1],[Enable Lua 5.1 support]),
[enable_lua51=$enableval],
[enable_lua51=auto])
AC_ARG_ENABLE(luajit,
AS_HELP_STRING([--enable-luajit],[Enable LuaJIT for Lua 5.1 support]),
[enable_luajit=$enableval],
[enable_luajit=auto])
AC_MSG_CHECKING([for Lua 5.1 availability.])
if test "x$enable_lua51" = "xno"; then
found_lua51="no (disabled, use --enable-lua5.1 to enable)"
AC_MSG_RESULT([$found_lua51])
else
if test "x$enable_luajit" != "xno"; then
PKG_CHECK_EXISTS([luajit >= $LUAJIT_REQUIRED], [
found_lua51=yes
with_lua51=luajit
], [
found_lua51=no
])
if test "x$enable_luajit" = "xyes" -a "x$found_lua51" = "xno"; then
AC_MSG_ERROR([You need to have LuaJIT >= $LUAJIT_REQUIRED
installed to build libpeas])
fi
fi
if test "x$found_lua51" != "xyes"; then
PKG_CHECK_EXISTS([lua5.1 >= $LUA51_REQUIRED], [
found_lua51=yes
with_lua51=lua5.1
], [
found_lua51=no
])
if test "x$enable_lua51" = "xyes" -a "x$found_lua51" = "xno"; then
AC_MSG_ERROR([You need to have Lua 5.1 >= $LUA51_REQUIRED
installed to build libpeas])
fi
fi
if test "x$found_lua51" != "xyes"; then
AC_MSG_RESULT([$found_lua51])
else
AC_MSG_RESULT([$found_lua51 ($with_lua51)])
LUA51_CFLAGS=`$PKG_CONFIG --cflags $with_lua51`
LUA51_LIBS=`$PKG_CONFIG --libs $with_lua51`
AC_SUBST(LUA51_CFLAGS)
AC_SUBST(LUA51_LIBS)
AC_MSG_CHECKING(for LGI availability in Lua 5.1.)
peas_save_CFLAGS="$CFLAGS"
peas_save_LIBS="$LIBS"
CFLAGS="$PEAS_CFLAGS $LUA51_CFLAGS -Iloaders/lua5.1"
LIBS="$PEAS_LIBS $LUA51_LIBS"
AC_TRY_RUN(peas_lgi_version_test(0),
found_lua51=yes,
found_lua51=no,
found_lua51=yes)
CFLAGS="$peas_save_CFLAGS"
LIBS="$peas_save_LIBS"
if test "x$enable_lua51" = "xyes" -a "x$found_lua51" = "xno"; then
AC_MSG_ERROR([You need to have LGI >= $LGI_REQUIRED
installed to build libpeas])
fi
if test "x$found_lua51" = "xyes"; then
AC_DEFINE(ENABLE_LUA51, 1, [Define to compile with Lua support])
fi
AC_MSG_RESULT([$found_lua51])
fi
fi
AM_CONDITIONAL([ENABLE_LUA51], [test "x$found_lua51" = "xyes"])
dnl ================================================================
dnl Python
dnl ================================================================
......@@ -290,7 +414,7 @@ else
fi
if test "x$enable_python2" = "xyes" -a "x$found_python2" != "xyes"; then
AC_MSG_ERROR([You need to have Python 2 and PyGobject installed to build libpeas])
AC_MSG_ERROR([You need to have Python 2 and PyGObject installed to build libpeas])
fi
AC_MSG_RESULT([$found_python2])
......@@ -393,6 +517,7 @@ docs/reference/version.xml
libpeas/Makefile
libpeas-gtk/Makefile
loaders/Makefile
loaders/lua5.1/Makefile
loaders/python/Makefile
loaders/python3/Makefile
data/Makefile
......@@ -403,6 +528,7 @@ data/libpeas-gtk-1.0.pc
peas-demo/Makefile
peas-demo/plugins/Makefile
peas-demo/plugins/helloworld/Makefile
peas-demo/plugins/luahello/Makefile
peas-demo/plugins/pythonhello/Makefile
peas-demo/plugins/secondtime/Makefile
po/Makefile.in
......@@ -410,6 +536,7 @@ tests/Makefile
tests/libpeas/Makefile
tests/libpeas/plugins/Makefile
tests/libpeas/plugins/extension-c/Makefile
tests/libpeas/plugins/extension-lua/Makefile
tests/libpeas/plugins/extension-python/Makefile
tests/libpeas/introspection/Makefile
tests/libpeas/testing/Makefile
......@@ -443,6 +570,7 @@ Configuration:
Languages support:
Lua 5.1 support : ${found_lua51}
Python 2 support : ${found_python2}
Python 3 support : ${found_python3}
"
......@@ -589,6 +589,7 @@ static PeasPluginLoader *
get_plugin_loader (PeasEngine *engine,
gint loader_id)
{
gint i, j;
LoaderInfo *loader_info;
const gchar *loader_name;
gchar *module_name, *module_dir;
......@@ -615,6 +616,15 @@ get_plugin_loader (PeasEngine *engine,
module_name = g_strconcat (loader_name, "loader", NULL);
module_dir = peas_dirs_get_plugin_loaders_dir ();
/* Remove '.'s from the module name */
for (i = 0, j = 0; module_name[i] != '\0'; ++i)
{
if (module_name[i] != '.')
module_name[j++] = module_name[i];
}
module_name[j] = '\0';
loader_info->module = load_module (module_name, module_dir);
if (loader_info->module == NULL)
......@@ -667,7 +677,7 @@ get_plugin_loader (PeasEngine *engine,
*
* Enable a loader, enables a loader for plugins.
* The C plugin loader is always enabled. The other plugin
* loaders are: python and python3.
* loaders are: lua5.1 and python and python3.
*
* For instance, the following code will enable python plugins
* to be loaded:
......
......@@ -29,7 +29,8 @@
#include "peas-utils.h"
static const gchar *all_plugin_loaders[] = {"c", "python", "python3"};
static const gchar *all_plugin_loaders[] = {"c", "lua5.1",
"python", "python3"};
G_STATIC_ASSERT (G_N_ELEMENTS (all_plugin_loaders) == PEAS_UTILS_N_LOADERS);
static void
......
......@@ -24,7 +24,7 @@
#include <glib-object.h>
#define PEAS_UTILS_N_LOADERS 3
#define PEAS_UTILS_N_LOADERS 4
gboolean peas_utils_valist_to_parameter_list (GType iface_type,
const gchar *first_property,
......
SUBDIRS =
if ENABLE_LUA51
SUBDIRS += lua5.1
endif
if ENABLE_PYTHON2
SUBDIRS += python
endif
......
# Lua 5.1 plugin loader
loaderdir = $(libdir)/libpeas-1.0/loaders
AM_CPPFLAGS = \
-I$(top_srcdir) \
$(PEAS_CFLAGS) \
$(GCOV_CFLAGS) \
$(WARN_CFLAGS) \
$(DISABLE_DEPRECATED) \
$(LUA51_CFLAGS)
loader_LTLIBRARIES = liblua51loader.la
liblua51loader_la_SOURCES = \
peas-plugin-loader-lua.c \
peas-plugin-loader-lua.h \
peas-plugin-loader-lua-utils.c \
peas-plugin-loader-lua-utils.h
liblua51loader_la_LDFLAGS = \
$(LOADER_LIBTOOL_FLAGS) \
$(GCOV_LDFLAGS)
liblua51loader_la_LIBADD = \
$(top_builddir)/libpeas/libpeas-1.0.la \
$(PEAS_LIBS) \
$(LUA51_LIBS)
gcov_sources = $(liblua51loader_la_SOURCES)
include $(top_srcdir)/Makefile.gcov
/*
* peas-plugin-loader-lua-utils.c
* This file is part of libpeas
*
* Copyright (C) 2014 - Garrett Regier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "peas-plugin-loader-lua-utils.h"
#include <string.h>
#include <lauxlib.h>
#include <lualib.h>
gboolean
peas_lua_utils_require (lua_State *L,
const gchar *name)
{
luaL_checkstack (L, 2, "");
lua_getglobal (L, "require");
lua_pushstring (L, name);
if (lua_pcall (L, 1, 1, 0) != 0)
{
g_warning ("Error failed to load Lua module '%s': %s",
name, lua_tostring (L, -1));
/* Pop error */
lua_pop (L, 1);
return FALSE;
}
if (!lua_istable (L, -1))
{
g_warning ("Error invalid Lua module for '%s': "
"expected table, got: %s",
name, lua_tostring (L, -1));
/* Pop the module's table */
lua_pop (L, 1);
return FALSE;
}
return TRUE;
}
gboolean
peas_lua_utils_check_version (lua_State *L,
guint req_major,
guint req_minor,
guint req_micro)
{
const gchar *version_str;
gchar **version_str_parts;
gint n_version_parts;
gint *version_parts;
gint i;
gboolean success = FALSE;
lua_getfield (L, -1, "_VERSION");
version_str = lua_tostring (L, -1);
version_str_parts = g_strsplit (version_str, ".", 0);
n_version_parts = g_strv_length (version_str_parts);
version_parts = g_newa (gint, n_version_parts);
for (i = 0; i < n_version_parts; ++i)
{
gchar *end;
version_parts[i] = g_ascii_strtoll (version_str_parts[i], &end, 10);
if (*end != '\0' ||
version_parts[i] < 0 ||
version_parts[i] == G_MAXINT64)
{
g_warning ("Invalid version string: %s", version_str);
goto error;
}
}
if (n_version_parts < 3 ||
version_parts[0] != req_major ||
version_parts[1] < req_minor ||
(version_parts[1] == req_minor && version_parts[2] < req_micro))
{
g_warning ("Version mismatch %d.%d.%d is required, found %s",
req_major, req_minor, req_micro, version_str);
goto error;
}
success = TRUE;
error:
/* Pop _VERSION */
lua_pop (L, 1);
g_strfreev (version_str_parts);
return success;
}
/*
* peas-plugin-loader-lua-utils.h
* This file is part of libpeas
*
* Copyright (C) 2014 - Garrett Regier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __PEAS_PLUGIN_LOADER_LUA_UTILS_H__
#define __PEAS_PLUGIN_LOADER_LUA_UTILS_H__
#include <glib.h>
#include <lua.h>
G_BEGIN_DECLS
gboolean peas_lua_utils_require (lua_State *L,
const gchar *name);
gboolean peas_lua_utils_check_version (lua_State *L,
guint req_major,
guint req_minor,
guint req_micro);
G_END_DECLS
#endif /* __PEAS_PLUGIN_LOADER_LUA_UTILS_H__ */
/*
* peas-plugin-loader-lua.c
* This file is part of libpeas
*
* Copyright (C) 2014 - Garrett Regier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "peas-plugin-loader-lua.h"
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include "peas-plugin-loader-lua-utils.h"
struct _PeasPluginLoaderLuaPrivate {
lua_State *L;
};
G_DEFINE_TYPE (PeasPluginLoaderLua, peas_plugin_loader_lua, PEAS_TYPE_PLUGIN_LOADER)
G_MODULE_EXPORT void
peas_register_types (PeasObjectModule *module)
{
peas_object_module_register_extension_type (module,
PEAS_TYPE_PLUGIN_LOADER,
PEAS_TYPE_PLUGIN_LOADER_LUA);
}
static gboolean
_lua_add_package_path (lua_State *L,
const gchar *package_path)
{
luaL_checkstack (L, 3, "");
lua_getglobal (L, "package");
lua_getfield (L, -1, "path");
if (!lua_isstring (L, -1))
{
g_warning ("Invalid Lua package.path: %s", lua_tostring (L, -1));
/* Pop path and package */
lua_pop (L, 2);
return FALSE;
}
/* ";package_path/?.lua;package_path/?/init.lua" */
lua_pushliteral (L, ";");
lua_pushstring (L, package_path);
lua_pushliteral (L, G_DIR_SEPARATOR_S "?.lua;");
lua_pushstring (L, package_path);
lua_pushliteral (L, G_DIR_SEPARATOR_S "?" G_DIR_SEPARATOR_S "init.lua");
lua_concat (L, 5);
if (strstr (lua_tostring (L, -2), lua_tostring (L, -1)) != NULL)
{
/* Pop new path and path */
lua_pop (L, 2);
}
else
{
/* Update package.path */
lua_concat (L, 2);
lua_setfield (L, -2, "path");
}
/* Pop package */
lua_pop (L, 1);
return TRUE;
}
static gboolean
_lua_pushinstance (lua_State *L,
const gchar *namespace_,
const gchar *name,
GType gtype,
gpointer instance)
{
luaL_checkstack (L, 3, "");
if (!peas_lua_utils_require (L, "lgi"))
return FALSE;
lua_getfield (L, -1, namespace_);
lua_getfield (L, -1, name);
/* Remove the namespace and lgi's module table */
lua_replace (L, -2);
lua_replace (L, -2);
lua_pushlightuserdata (L, instance);
lua_pushboolean (L, FALSE);
/* new(addr[, already_own[, no_sink]]) */
if (lua_pcall (L, 2, 1, 0) != 0)
{
g_warning ("Failed to create Lua object of type '%s': %s",
g_type_name (gtype), lua_tostring (L, -1));
/* Pop the error */
lua_pop (L, 1);
return FALSE;
}
/* Check that the Lua object was created correctly */
lua_getfield (L, -1, "_native");
g_assert (lua_islightuserdata (L, -1));
g_assert (lua_touserdata (L, -1) == instance);
lua_pop (L, 1);
return TRUE;
}
static GType
_lua_get_gtype (lua_State *L,
int index)
{
GType gtype = G_TYPE_INVALID;
luaL_checkstack (L, 1, "");
lua_getfield (L, index, "_gtype");
if (lua_type (L, -1) == LUA_TLIGHTUSERDATA)
gtype = (GType) lua_touserdata (L, -1);
/* Pop _gtype */
lua_pop (L, 1);
return gtype;
}
static GType
_lua_find_extension_type (lua_State *L,
PeasPluginInfo *info,
GType exten_type)
{
GType found_type = G_TYPE_INVALID;
luaL_checkstack (L, 3, "");
/* Get the module's table */
lua_pushlightuserdata (L, info);
lua_rawget (L, LUA_REGISTRYINDEX);
/* Must always have a valid key */