Commit 808964a0 authored by Øyvind Kolås's avatar Øyvind Kolås

added dynamic loading of operations

parent 81e6a304
2006-07-06 Øyvind Kolås <pippin@gimp.org>
Added a framework for dynamically loading GEGL operations based on
gmodule. Copied from GIMPs module.
* configure.in: added gegl/module/Makefile to generated files.
* gegl/Makefile.am: added module dir
* gegl/gegl-init.c: (gegl_init): load modules upon library
initialization.
* gegl/module/Makefile.am,
* gegl/module/gegldatafiles.c,
* gegl/module/gegldatafiles.h,
* gegl/module/geglmodule.c,
* gegl/module/geglmodule.h,
* gegl/module/geglmoduledb.c,
* gegl/module/geglmoduledb.h,
* gegl/module/geglmoduletypes.h: New files.
2006-07-06 Øyvind Kolås <pippin@gimp.org>
* gegl/gegl-node.c: (gegl_node_get), (gegl_node_get_valist),
......
......@@ -211,6 +211,7 @@ gil/gil/Makefile
gil/tests/Makefile
gegl/Makefile
gegl/buffer/Makefile
gegl/module/Makefile
tools/Makefile
tools/testsuite/Makefile
tests/Makefile
......
noinst_PROGRAMS =
SUBDIRS = buffer
bin_PROGRAMS = gegl
SUBDIRS = buffer module
libbuffer = $(top_builddir)/gegl/buffer/libbuffer.la
libmodule = $(top_builddir)/gegl/module/libmodule.la
lib_LTLIBRARIES = libgegl-1.0.la
......@@ -28,6 +31,11 @@ GEGL_sources = \
gegl-visitor.c \
gegl-xml.c
gegl_SOURCES = \
gegl.c \
gegl-options.c \
gegl-options.h
GEGL_public_headers = \
gegl-connection.h \
gegl-graph.h \
......@@ -47,7 +55,7 @@ GEGL_public_headers = \
libgegl_1_0_la_SOURCES = $(GEGL_sources) $(GEGL_public_headers)
libgegl_1_0_la_LIBADD = $(libbuffer)
libgegl_1_0_la_LIBADD = $(libbuffer) $(libmodule)
INCLUDES = \
-I$(top_srcdir) \
......@@ -57,12 +65,14 @@ AM_LDFLAGS = \
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
-no-undefined \
@DEP_LIBS@ @BABL_LIBS@
gegl_LDFLAGS = \
libgegl-1.0.la
libgeglincludedir = $(includedir)/gegl-1.0
libgeglsubincludedir = $(includedir)/gegl-1.0/gegl
libgeglinclude_HEADERS = gegl.h
libgeglinclude_HEADERS = gegl.h gegl-module.h
libgeglsubinclude_HEADERS = $(GEGL_public_headers)
......
......@@ -24,6 +24,9 @@
#include "gegl-types.h"
#include "gegl-init.h"
#include "buffer/gegl-buffer-allocator.h"
#include "module/geglmodule.h"
#include "module/geglmoduledb.h"
#include <stdlib.h>
static gboolean gegl_initialized = FALSE;
......@@ -31,10 +34,34 @@ void
gegl_init (int *argc,
char ***argv)
{
static GeglModuleDB *module_db = NULL;
if (gegl_initialized)
return;
g_type_init ();
babl_init ();
if (!module_db)
{
gchar *load_inhibit = g_strdup ("");
gchar *module_path;
if (getenv ("GEGL_PATH"))
{
module_path = g_strdup (getenv ("GEGL_PATH"));
}
else
module_path = g_strdup ("/usr/local/lib/gegl-operations/"
":/home/pippin/src/gegl-operations-base/");
module_db = gegl_module_db_new (FALSE);
gegl_module_db_set_load_inhibit (module_db, load_inhibit);
gegl_module_db_load (module_db, module_path);
g_free (module_path);
g_free (load_inhibit);
}
gegl_initialized = TRUE;
}
......
noinst_LTLIBRARIES = libmodule.la
BUFFER_sources = \
gegldatafiles.c \
geglmodule.c \
geglmoduledb.c
BUFFER_headers = \
gegldatafiles.h \
geglmoduledb.h \
geglmodule.h \
geglmoduletypes.h
libmodule_la_SOURCES = $(BUFFER_sources) $(BUFFER_headers)
libmoduleinclude_HEADERS = $(BUFFER_headers)
libmoduleincludedir = $(includedir)/gegl-1.0/gegl/module
INCLUDES = \
-I$(top_srcdir) \
-I$(top_srcdir)/gegl \
@DEP_CFLAGS@
/* this file is part of GEGL
*
* Datafiles module copyight (C) 1996 Federico Mena Quintero
* federico@nuclecu.unam.mx
*
* This library 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 2 of the License, or (at your option) any later version.
*
* This library 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 library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Datafiles module copyight (C) 1996 Federico Mena Quintero
* federico@nuclecu.unam.mx
*/
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <glib-object.h>
#include <glib/gstdio.h>
#ifdef G_OS_WIN32
#include "geglwin32-io.h"
#endif /* G_OS_WIN32 */
/*
#include "geglbasetypes.h"*/
#include "gegldatafiles.h"
gboolean
gegl_datafiles_check_extension (const gchar *filename,
const gchar *extension)
{
gint name_len;
gint ext_len;
g_return_val_if_fail (filename != NULL, FALSE);
g_return_val_if_fail (extension != NULL, FALSE);
name_len = strlen (filename);
ext_len = strlen (extension);
if (! (name_len && ext_len && (name_len > ext_len)))
return FALSE;
return (g_ascii_strcasecmp (&filename[name_len - ext_len], extension) == 0);
}
/**
* gegl_path_parse:
* @path: A list of directories separated by #G_SEARCHPATH_SEPARATOR.
* @max_paths: The maximum number of directories to return.
* @check: %TRUE if you want the directories to be checked.
* @check_failed: Returns a #GList of path elements for which the
* check failed.
*
* Returns: A #GList of all directories in @path.
**/
static GList *
gegl_path_parse (const gchar *path,
gint max_paths,
gboolean check,
GList **check_failed)
{
const gchar *home;
gchar **patharray;
GList *list = NULL;
GList *fail_list = NULL;
gint i;
gboolean exists = TRUE;
if (!path || !*path || max_paths < 1 || max_paths > 256)
return NULL;
home = g_get_home_dir ();
patharray = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, max_paths);
for (i = 0; i < max_paths; i++)
{
GString *dir;
if (! patharray[i])
break;
#ifndef G_OS_WIN32
if (*patharray[i] == '~')
{
dir = g_string_new (home);
g_string_append (dir, patharray[i] + 1);
}
else
#endif
{
dir = g_string_new (patharray[i]);
}
if (check)
exists = g_file_test (dir->str, G_FILE_TEST_IS_DIR);
if (exists)
list = g_list_prepend (list, g_strdup (dir->str));
else if (check_failed)
fail_list = g_list_prepend (fail_list, g_strdup (dir->str));
g_string_free (dir, TRUE);
}
g_strfreev (patharray);
list = g_list_reverse (list);
if (check && check_failed)
{
fail_list = g_list_reverse (fail_list);
*check_failed = fail_list;
}
return list;
}
/**
* gegl_path_free:
* @path: A list of directories as returned by gegl_path_parse().
*
* This function frees the memory allocated for the list and the strings
* it contains.
**/
void
gegl_path_free (GList *path)
{
g_list_foreach (path, (GFunc) g_free, NULL);
g_list_free (path);
}
void
gegl_datafiles_read_directories (const gchar *path_str,
GFileTest flags,
GeglDatafileLoaderFunc loader_func,
gpointer user_data)
{
GeglDatafileData file_data;
struct stat filestat;
gchar *local_path;
GList *path;
GList *list;
gchar *filename;
gint err;
GDir *dir;
const gchar *dir_ent;
g_return_if_fail (path_str != NULL);
g_return_if_fail (loader_func != NULL);
local_path = g_strdup (path_str);
path = gegl_path_parse (local_path, 16, TRUE, NULL);
for (list = path; list; list = g_list_next (list))
{
const gchar *dirname = list->data;
dir = g_dir_open (dirname, 0, NULL);
if (dir)
{
while ((dir_ent = g_dir_read_name (dir)))
{
filename = g_build_filename (dirname, dir_ent, NULL);
err = g_stat (filename, &filestat);
file_data.filename = filename;
file_data.dirname = dirname;
file_data.basename = dir_ent;
file_data.atime = filestat.st_atime;
file_data.mtime = filestat.st_mtime;
file_data.ctime = filestat.st_ctime;
if (! err)
{
if (/*(flags & G_FILE_TEST_IS_DIR) &&*/
S_ISDIR (filestat.st_mode))
{
gegl_datafiles_read_directories (filename,
flags,
loader_func,
user_data);
}
else if (flags & G_FILE_TEST_EXISTS)
{
(* loader_func) (&file_data, user_data);
}
else if ((flags & G_FILE_TEST_IS_REGULAR) &&
S_ISREG (filestat.st_mode))
{
(* loader_func) (&file_data, user_data);
}
#ifndef G_OS_WIN32
else if ((flags & G_FILE_TEST_IS_SYMLINK) &&
S_ISLNK (filestat.st_mode))
{
(* loader_func) (&file_data, user_data);
}
#endif
else if ((flags & G_FILE_TEST_IS_EXECUTABLE) &&
(((filestat.st_mode & S_IXUSR) &&
!S_ISDIR (filestat.st_mode)) ||
(S_ISREG (filestat.st_mode) /*&&
is_script (filename)*/)))
{
(* loader_func) (&file_data, user_data);
}
}
g_free (filename);
}
g_dir_close (dir);
}
}
gegl_path_free (path);
g_free (local_path);
}
/* this file is part of GEGL
*
* Datafiles module copyight (C) 1996 Federico Mena Quintero
* federico@nuclecu.unam.mx
*
* This library 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 2 of the License, or (at your option) any later version.
*
* This library 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 library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Datafiles module copyight (C) 1996 Federico Mena Quintero
* federico@nuclecu.unam.mx
*/
#ifndef __GEGL_DATAFILES_H__
#define __GEGL_DATAFILES_H__
#include <time.h>
G_BEGIN_DECLS
typedef struct _GeglDatafileData GeglDatafileData;
struct _GeglDatafileData
{
const gchar *filename;
const gchar *dirname;
const gchar *basename;
time_t atime;
time_t mtime;
time_t ctime;
};
typedef void (* GeglDatafileLoaderFunc) (const GeglDatafileData *file_data,
gpointer user_data);
gboolean gegl_datafiles_check_extension (const gchar *filename,
const gchar *extension);
void gegl_datafiles_read_directories (const gchar *path_str,
GFileTest flags,
GeglDatafileLoaderFunc loader_func,
gpointer user_data);
G_END_DECLS
#endif /* __GEGL_DATAFILES_H__ */
/* This file is part of GEGL
*
* This library 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 2 of the License, or (at your option) any later version.
*
* This library 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 library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* gimpmodule.c: * (C) 1999 Austin Donnelly <austin@gegl.org>
*/
#include <string.h>
#include <glib-object.h>
#include "geglmodule.h"
/* define hacks to make it compile with as small modifications as possible
* from geglmodule
*/
#define _(string) (string)
#define N_(string) (string)
#define gegl_filename_to_utf8(filename) (filename)
#define gettext(string) (string)
enum
{
MODIFIED,
LAST_SIGNAL
};
static void gegl_module_finalize (GObject *object);
static gboolean gegl_module_load (GTypeModule *module);
static void gegl_module_unload (GTypeModule *module);
static gboolean gegl_module_open (GeglModule *module);
static gboolean gegl_module_close (GeglModule *module);
static void gegl_module_set_last_error (GeglModule *module,
const gchar *error_str);
G_DEFINE_TYPE (GeglModule, gegl_module, G_TYPE_TYPE_MODULE)
#define parent_class gegl_module_parent_class
static guint module_signals[LAST_SIGNAL];
static void
gegl_module_class_init (GeglModuleClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (klass);
module_signals[MODIFIED] =
g_signal_new ("modified",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GeglModuleClass, modified),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
object_class->finalize = gegl_module_finalize;
module_class->load = gegl_module_load;
module_class->unload = gegl_module_unload;
klass->modified = NULL;
}
static void
gegl_module_init (GeglModule *module)
{
module->filename = NULL;
module->verbose = FALSE;
module->state = GEGL_MODULE_STATE_ERROR;
module->on_disk = FALSE;
module->load_inhibit = FALSE;
module->module = NULL;
module->info = NULL;
module->last_module_error = NULL;
module->query_module = NULL;
module->register_module = NULL;
}
static void
gegl_module_finalize (GObject *object)
{
GeglModule *module = GEGL_MODULE (object);
if (module->info)
{
gegl_module_info_free (module->info);
module->info = NULL;
}
if (module->last_module_error)
{
g_free (module->last_module_error);
module->last_module_error = NULL;
}
if (module->filename)
{
g_free (module->filename);
module->filename = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
gegl_module_load (GTypeModule *module)
{
GeglModule *gegl_module = GEGL_MODULE (module);
gpointer func;
g_return_val_if_fail (gegl_module->filename != NULL, FALSE);
g_return_val_if_fail (gegl_module->module == NULL, FALSE);
if (gegl_module->verbose)
g_print ("Loading module '%s'\n",
gegl_filename_to_utf8 (gegl_module->filename));
if (! gegl_module_open (gegl_module))
return FALSE;
if (! gegl_module_query_module (gegl_module))
return FALSE;
/* find the gegl_module_register symbol */
if (! g_module_symbol (gegl_module->module, "gegl_module_register", &func))
{
gegl_module_set_last_error (gegl_module,
"Missing gegl_module_register() symbol");
g_message (_("Module '%s' load error: %s"),
gegl_filename_to_utf8 (gegl_module->filename),
gegl_module->last_module_error);
gegl_module_close (gegl_module);
gegl_module->state = GEGL_MODULE_STATE_ERROR;
return FALSE;
}
gegl_module->register_module = func;
if (! gegl_module->register_module (module))
{
gegl_module_set_last_error (gegl_module,
"gegl_module_register() returned FALSE");
g_message (_("Module '%s' load error: %s"),
gegl_filename_to_utf8 (gegl_module->filename),
gegl_module->last_module_error);
gegl_module_close (gegl_module);
gegl_module->state = GEGL_MODULE_STATE_LOAD_FAILED;
return FALSE;
}
gegl_module->state = GEGL_MODULE_STATE_LOADED;
return TRUE;
}
static void
gegl_module_unload (GTypeModule *module)
{
GeglModule *gegl_module = GEGL_MODULE (module);
g_return_if_fail (gegl_module->module != NULL);
if (gegl_module->verbose)
g_print ("Unloading module '%s'\n",
gegl_filename_to_utf8 (gegl_module->filename));
gegl_module_close (gegl_module);
}
/* public functions */
/**
* gegl_module_new:
* @filename: The filename of a loadable module.
* @load_inhibit: Pass %TRUE to exclude this module from auto-loading.
* @verbose: Pass %TRUE to enable debugging output.
*
* Creates a new #GeglModule instance.
*
* Return value: The new #GeglModule object.
**/
GeglModule *
gegl_module_new (const gchar *filename,
gboolean load_inhibit,
gboolean verbose)
{
GeglModule *module;
g_return_val_if_fail (filename != NULL, NULL);
module = g_object_new (GEGL_TYPE_MODULE, NULL);
module->filename = g_strdup (filename);
module->load_inhibit = load_inhibit ? TRUE : FALSE;
module->verbose = verbose ? TRUE : FALSE;
module->on_disk = TRUE;
if (! module->load_inhibit)
{
if (gegl_module_load (G_TYPE_MODULE (module)))
gegl_module_unload (G_TYPE_MODULE (module));
}
else
{
if (verbose)
g_print ("Skipping module '%s'\n",
gegl_filename_to_utf8 (filename));
module->state = GEGL_MODULE_STATE_NOT_LOADED;
}
return module;
}
/**
* gegl_module_query_module:
* @module: A #GeglModule.
*
* Queries the module without actually registering any of the types it
* may implement. After successful query, the @info field of the
* #GeglModule struct will be available for further inspection.
*
* Return value: %TRUE on success.
**/
gboolean
gegl_module_query_module (GeglModule *module)
{
const GeglModuleInfo *info;
gboolean close_module = FALSE;
gpointer func;
g_return_val_if_fail (GEGL_IS_MODULE (module), FALSE);
if (! module->module)
{
if (! gegl_module_open (module))
return FALSE;
close_module = TRUE;
}
/* find the gegl_module_query symbol */
if (! g_module_symbol (module->module, "gegl_module_query", &func))
{
gegl_module_set_last_error (module,
"Missing gegl_module_query() symbol");
g_message (_("Module '%s' load error: %s"),
gegl_filename_to_utf8 (module->filename),
module->last_module_error);
gegl_module_close (module);
module->state = GEGL_MODULE_STATE_ERROR;
return FALSE;
}
module->query_module = func;
if (module->info)
{
gegl_module_info_free (module->info);
module->info = NULL;
}
info = module->query_module (G_TYPE_MODULE (module));
if (! info || info->abi_version != GEGL_MODULE_ABI_VERSION)
{
gegl_module_set_last_error (module,
info ?
"module ABI version does not match" :