|
20.2 A Loadable Module
A feature of the Sic interpreter is that it will use the `unknown'
built-in to handle any command line which is not handled by any of the
other registered built-in callback functions. This mechanism is very
powerful, and allows me to lookup unhandled built-ins in the user's
`PATH', for instance.
Before adding any modules to the project, I have created a separate
subdirectory, `modules', to put the module source code into. Not
forgetting to list this new subdirectory in the AC_OUTPUT macro
in `configure.in', and the SUBDIRS macro in the top level
`Makefile.am', a new `Makefile.am' is needed to build the
loadable modules:
|
## Makefile.am -- Process this file with automake to produce Makefile.in
INCLUDES = -I$(top_builddir) -I$(top_srcdir) \
-I$(top_builddir)/sic -I$(top_srcdir)/sic \
-I$(top_builddir)/src -I$(top_srcdir)/src
pkglib_LTLIBRARIES = unknown.la
|
pkglibdir is a Sic specific directory where modules will be
installed, See section Installing and Uninstalling Configured Packages.
For a library to be maximally portable, it should be written so that it
does not require back-linking(47) to
resolve its own symbols. That is, if at all possible you should design
all of your libraries (not just dynamic modules) so that all of their
symbols can be resolved at linktime. Sometimes, it is impossible or
undesirable to architect your libraries and modules in this way. In
that case you sacrifice the portability of your project to platforms
such as AIX and Windows.
The key to building modules with libtool is in the options that are
specified when the module is linked. This is doubly true when the
module must work with libltdl's dlpreopening mechanism.
|
unknown_la_SOURCES = unknown.c
unknown_la_LDFLAGS = -no-undefined -module -avoid-version
unknown_la_LIBADD = $(top_builddir)/sic/libsic.la
|
Sic modules are built without a `lib' prefix (`-module'),
and without version suffixes (`-avoid-version'). All of the
undefined symbols are resolved at linktime by `libsic.la', hence
`-no-undefined'.
Having added `ltdl.c' to the `sic' subdirectory, and called
the AC_LIB_LTDL macro in `configure.in', `libsic.la'
cannot build correctly on those architectures which do not support
back-linking. This is because `ltdl.c' simply abstracts the native
dlopen API with a common interface, and that local interface
often requires that a special library be linked -- `-ldl' on linux,
for example. AC_LIB_LTDL probes the system to determine the name
of any such dlopen library, and allows you to depend on it in a portable
way by using the configure substitution macro, `@LIBADD_DL@'. If
I were linking a libtool compiled libltdl at this
juncture, the system library details would have already been taken care
of. In this project, I have bypassed that mechanism by compiling and
linking `ltdl.c' myself, so I have altered `sic/Makefile.am'
to use `@LIBADD_DL@':
|
lib_LTLIBRARIES = libcommon.la libsic.la
libsic_la_LIBADD = $(top_builddir)/replace/libreplace.la \
libcommon.la @LIBADD_DL@
libsic_la_SOURCES = builtin.c error.c eval.c list.c ltdl.c \
module.c sic.c syntax.c
|
Having put all this infrastructure in place, the code for the
`unknown' module is a breeze (helper functions omitted for
brevity):
|
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/types.h>
#include <sys/wait.h>
#include <sic/module.h>
#define builtin_table unknown_LTX_builtin_table
static char *path_find (const char *command);
static int path_execute (Sic *sic, const char *path, char *const argv[]);
/* Generate prototype. */
SIC_BUILTIN (builtin_unknown);
Builtin builtin_table[] = {
{ "unknown", builtin_unknown, 0, -1 },
{ 0, 0, -1, -1 }
};
BUILTIN_DECLARATION(unknown)
{
char *path = path_find (argv[0]);
int status = SIC_ERROR;
if (!path)
sic_result_append (sic, "command \"", argv[0], "\" not found",
NULL);
else if (path_execute (sic, path, argv) != SIC_OKAY)
sic_result_append (sic, "command \"", argv[0],"\" failed: ",
strerror (errno), NULL);
else
status = SIC_OKAY;
return status;
}
|
In the first instance, notice that I have used the preprocessor to
redefine the entry point functions to be compatible with libltdls
dlpreopen , hence the unknown_LTX_builtin_table
cpp macro. The `unknown' handler function itself looks
for a suitable executable in the user's path, and if something suitable
is found, executes it.
Notice that Libtool doesn't relink dependent libraries (`libsic'
depends on `libcommon', for example) on my GNU/Linux system,
since they are not required for the static library in any case, and
because the dependencies are also encoded directly into the shared
archive, `libsic.so', by the original link. On the other hand,
Libtool will relink the dependent libraries if that is necessary
for the target host.
|
$ make
/bin/sh ../libtool --mode=compile gcc -DHAVE_CONFIG_H -I. -I. -I.. \
-I.. -I.. -I../sic -I../sic -I../src -I../src -g -O2 -c unknown.c
mkdir .libs
gcc -DHAVE_CONFIG_H -I. -I. -I.. -I.. -I.. -I../sic -I../sic -I../src \
-I../src -g -O2 -Wp,-MD,.deps/unknown.pp -c unknown.c -fPIC -DPIC \
-o .libs/unknown.lo
gcc -DHAVE_CONFIG_H -I. -I. -I.. -I.. -I.. -I../sic -I../sic -I../src \
I../src -g -O2 -Wp,-MD,.deps/unknown.pp -c unknown.c -o unknown.o \
>/dev/null 2>&1
mv -f .libs/unknown.lo unknown.lo
/bin/sh ../libtool --mode=link gcc -g -O2 -o unknown.la -rpath \
/usr/local/lib/sic -no-undefined -module -avoid-version unknown.lo \
../sic/libsic.la
rm -fr .libs/unknown.la .libs/unknown.* .libs/unknown.*
gcc -shared unknown.lo -L/tmp/sic/sic/.libs ../sic/.libs/libsic.so \
-lc -Wl,-soname -Wl,unknown.so -o .libs/unknown.so
ar cru .libs/unknown.a unknown.o
creating unknown.la
(cd .libs && rm -f unknown.la && ln -s ../unknown.la unknown.la)
$ ./libtool --mode=execute ldd ./unknown.la
libsic.so.0 => /tmp/sic/.libs/libsic.so.0 (0x40002000)
libc.so.6 => /lib/libc.so.6 (0x4000f000)
libcommon.so.0 => /tmp/sic/.libs/libcommon.so.0 (0x400ec000)
libdl.so.2 => /lib/libdl.so.2 (0x400ef000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x80000000)
|
After compiling the rest of the tree, I can now use the `unknown'
module:
|
$ SIC_MODULE_PATH=`cd ../modules; pwd` ./sic
] echo hello!
command "echo" not found.
] load unknown
] echo hello!
hello!
] unload unknown
] echo hello!
command "echo" not found.
] exit
$
|
|