Implement new "load" directive.

Provides support for dynamically loadable objects in GNU make, as a
"technology preview".
This commit is contained in:
Paul Smith 2012-10-29 07:05:21 +00:00
parent 2efd6b47bf
commit 7670c84f77
15 changed files with 807 additions and 260 deletions

View File

@ -1,3 +1,15 @@
2012-10-29 Paul Smith <psmith@gnu.org>
New feature: "load" directive for dynamically-loaded objects.
* NEWS: Document new "load" directive.
* doc/make.texi (Extending make): New chapter on extensions to make.
* configure.in: Check for dlopen/dlsym/dlerror and -ldl.
* Makefile.am (make_SOURCES): Add new file load.c.
* make.h: Prototype for load_file().
* main.c (main): Add "load" to .FEATURES if it's available.
* read.c (eval): Parse "load" and "-load" directives.
2012-09-29 Paul Smith <psmith@gnu.org> 2012-09-29 Paul Smith <psmith@gnu.org>
* configure.in: Require a new version of gettext (1.18.1). * configure.in: Require a new version of gettext (1.18.1).

View File

@ -39,7 +39,7 @@ else
endif endif
make_SOURCES = ar.c arscan.c commands.c default.c dir.c expand.c file.c \ make_SOURCES = ar.c arscan.c commands.c default.c dir.c expand.c file.c \
function.c getopt.c getopt1.c implicit.c job.c main.c \ function.c getopt.c getopt1.c implicit.c job.c load.c main.c \
misc.c read.c remake.c rule.c signame.c \ misc.c read.c remake.c rule.c signame.c \
strcache.c variable.c version.c vpath.c hash.c \ strcache.c variable.c version.c vpath.c hash.c \
$(remote) $(remote)

7
NEWS
View File

@ -9,7 +9,7 @@ manual, which is contained in this distribution as the file doc/make.texi.
See the README file and the GNU make manual for instructions for See the README file and the GNU make manual for instructions for
reporting bugs. reporting bugs.
Version 3.82.90 Version 3.99.90
A complete list of bugs fixed in this version is available here: A complete list of bugs fixed in this version is available here:
@ -50,6 +50,11 @@ http://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=101&set
GNU Guile serves as an embedded extension language for make. GNU Guile serves as an embedded extension language for make.
See the "Guile Function" section in the GNU Make manual for details. See the "Guile Function" section in the GNU Make manual for details.
* New feature: Loadable objects
This version of GNU make contains a "technology preview": the ability to
load dynamic objects into the make runtime. These objects can be created by
the user and can add extended functionality, usable by makefiles.
* New function: $(file ...) writes to a file. * New function: $(file ...) writes to a file.
* On failure, the makefile name and linenumber of the recipe that failed are * On failure, the makefile name and linenumber of the recipe that failed are

View File

@ -252,9 +252,7 @@ AS_IF([test "$PATH_SEPARATOR" = ';'],
[Define to 1 if your system requires backslashes or drive specs in pathnames.]) [Define to 1 if your system requires backslashes or drive specs in pathnames.])
]) ])
# See if the user wants to use pmake's "customs" distributed build capability # See if the user wants to use pmake's "customs" distributed build capability
AC_SUBST([REMOTE]) REMOTE=stub AC_SUBST([REMOTE]) REMOTE=stub
use_customs=false use_customs=false
AC_ARG_WITH([customs], AC_ARG_WITH([customs],
@ -280,7 +278,6 @@ AC_ARG_WITH([customs],
AM_CONDITIONAL([USE_CUSTOMS], [test "$use_customs" = true]) AM_CONDITIONAL([USE_CUSTOMS], [test "$use_customs" = true])
# See if the user asked to handle case insensitive file systems. # See if the user asked to handle case insensitive file systems.
AH_TEMPLATE([HAVE_CASE_INSENSITIVE_FS], [Use case insensitive file names]) AH_TEMPLATE([HAVE_CASE_INSENSITIVE_FS], [Use case insensitive file names])
AC_ARG_ENABLE([case-insensitive-file-system], AC_ARG_ENABLE([case-insensitive-file-system],
AC_HELP_STRING([--enable-case-insensitive-file-system], AC_HELP_STRING([--enable-case-insensitive-file-system],
@ -288,7 +285,6 @@ AC_ARG_ENABLE([case-insensitive-file-system],
[AS_IF([test "$enableval" = yes], [AC_DEFINE([HAVE_CASE_INSENSITIVE_FS])])]) [AS_IF([test "$enableval" = yes], [AC_DEFINE([HAVE_CASE_INSENSITIVE_FS])])])
# See if we can handle the job server feature, and if the user wants it. # See if we can handle the job server feature, and if the user wants it.
AC_ARG_ENABLE([job-server], AC_ARG_ENABLE([job-server],
AC_HELP_STRING([--disable-job-server], AC_HELP_STRING([--disable-job-server],
[disallow recursive make communication during -jN]), [disallow recursive make communication during -jN]),
@ -324,11 +320,57 @@ AS_CASE([/$make_cv_job_server/$user_job_server/],
[Define to 1 to enable job server support in GNU make.]) [Define to 1 to enable job server support in GNU make.])
]) ])
# If dl*() functions are supported we can enable the load operation
AC_CHECK_DECLS([dlopen, dlsym, dlerror], [], [],
[[#include <dlfcn.h>]])
AC_ARG_ENABLE([load],
AC_HELP_STRING([--disable-load],
[disable support for the 'load' operation]),
[make_cv_load="$enableval" user_load="$enableval"],
[make_cv_load="yes"])
AS_CASE([/$ac_cv_func_dlopen/$ac_cv_func_dlsym/$ac_cv_func_dlerror/],
[*/no/*], [make_cv_load=no])
AS_CASE([/$make_cv_load/$user_load/],
[*/no/*], [make_cv_load=no],
[AC_DEFINE(MAKE_LOAD, 1,
[Define to 1 to enable 'load' support in GNU make.])
])
# We might need -ldl
AS_IF([test "$make_cv_load" = yes], [
AC_SEARCH_LIBS([dlopen], [dl], [], [make_cv_load=])
])
# If we want load support, we might need to link with export-dynamic.
# See if we can figure it out. Unfortunately this is very difficult.
# For example passing -rdynamic to the SunPRO linker gives a warning
# but succeeds and creates a shared object, not an executable!
AS_IF([test "$make_cv_load" = yes], [
AC_MSG_CHECKING([If the linker accepts -Wl,--export-dynamic])
old_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
AC_LINK_IFELSE([int main(){}],
[AC_MSG_RESULT([yes])
AC_SUBST([AM_LDFLAGS], [-Wl,--export-dynamic])],
[AC_MSG_RESULT([no])
AC_MSG_CHECKING([If the linker accepts -rdynamic])
LDFLAGS="$old_LDFLAGS -rdynamic"
AC_LINK_IFELSE([int main(){}],
[AC_MSG_RESULT([yes])
AC_SUBST([AM_LDFLAGS], [-rdynamic])],
[AC_MSG_RESULT([no])])
])
LDFLAGS="$old_LDFLAGS"
])
# if we have both lstat() and readlink() then we can support symlink # if we have both lstat() and readlink() then we can support symlink
# timechecks. # timechecks.
AS_IF([test "$ac_cv_func_lstat" = yes && test "$ac_cv_func_readlink" = yes], AS_IF([test "$ac_cv_func_lstat" = yes && test "$ac_cv_func_readlink" = yes],
[ AC_DEFINE([MAKE_SYMLINKS], [1], [ AC_DEFINE([MAKE_SYMLINKS], [1],
[Define to 1 to enable symbolic link timestamp checking.]) [Define to 1 to enable symbolic link timestamp checking.])
]) ])
# Find the SCCS commands, so we can include them in our default rules. # Find the SCCS commands, so we can include them in our default rules.
@ -458,6 +500,15 @@ AS_IF([test "x$make_cv_job_server" = xno && test "x$user_job_server" = xyes],
echo echo
]) ])
AS_IF([test "x$make_cv_load" = xno && test "x$user_load" = xyes],
[ echo
echo "WARNING: 'load' support requires a POSIX-ish system that"
echo " supports the dlopen(), dlsym(), and dlerror() functions."
echo " Your system doesn't appear to provide one or more of these."
echo " Disabling 'load' support."
echo
])
# Specify what files are to be created. # Specify what files are to be created.
AC_CONFIG_FILES([Makefile glob/Makefile po/Makefile.in config/Makefile \ AC_CONFIG_FILES([Makefile glob/Makefile po/Makefile.in config/Makefile \
doc/Makefile w32/Makefile]) doc/Makefile w32/Makefile])

View File

@ -100,6 +100,7 @@ Cover art by Etienne Suvasa.
* Implicit Rules:: Use implicit rules to treat many files alike, * Implicit Rules:: Use implicit rules to treat many files alike,
based on their file names. based on their file names.
* Archives:: How @code{make} can update library archives. * Archives:: How @code{make} can update library archives.
* Extending make:: Using extensions to @code{make}.
* Features:: Features GNU @code{make} has over other @code{make}s. * Features:: Features GNU @code{make} has over other @code{make}s.
* Missing:: What GNU @code{make} lacks from other @code{make}s. * Missing:: What GNU @code{make} lacks from other @code{make}s.
* Makefile Conventions:: Conventions for writing makefiles for * Makefile Conventions:: Conventions for writing makefiles for
@ -277,13 +278,7 @@ Functions for Transforming Text
* Flavor Function:: Find out the flavor of a variable. * Flavor Function:: Find out the flavor of a variable.
* Make Control Functions:: Functions that control how make runs. * Make Control Functions:: Functions that control how make runs.
* Shell Function:: Substitute the output of a shell command. * Shell Function:: Substitute the output of a shell command.
* Guile Function:: Call the GNU Guile embedded scripting language. * Guile Function:: Use GNU Guile embedded scripting language.
The @code{guile} Function
* Guile Types:: Converting Guile types to @code{make} strings.
* Guile Interface:: Invoking @code{make} functions from Guile.
* Guile Example:: Example using Guile in @code{make}.
How to Run @code{make} How to Run @code{make}
@ -339,6 +334,21 @@ Implicit Rule for Archive Member Targets
* Archive Symbols:: How to update archive symbol directories. * Archive Symbols:: How to update archive symbol directories.
Extending GNU @code{make}
* Guile Integration:: Using Guile as an embedded scripting language.
* Loading Objects:: Loading dynamic objects as extensions.
GNU Guile Integration
* Guile Types:: Converting Guile types to @code{make} strings.
* Guile Interface:: Invoking @code{make} functions from Guile.
* Guile Example:: Example using Guile in @code{make}.
Loading Dynamic Objects
* load Directive:: Loading dynamic objects as extensions.
@end detailmenu @end detailmenu
@end menu @end menu
@ -6280,7 +6290,11 @@ Supports the @code{undefine} directive. @xref{Undefine Directive}.
@item guile @item guile
Has GNU Guile available as an embedded extension language. Has GNU Guile available as an embedded extension language.
@xref{Guile Function}. @xref{Guile Integration, ,GNU Guile Integration}.
@item load
Supports dynamically loadable objects for creating custom extensions.
@xref{Loading Objects, ,Loading Dynamic Objects}.
@end table @end table
@ -6422,12 +6436,12 @@ endif
or: or:
@example @example
@var{conditional-directive} @var{conditional-directive-one}
@var{text-if-one-is-true} @var{text-if-one-is-true}
else @var{conditional-directive} else @var{conditional-directive-two}
@var{text-if-true} @var{text-if-two-is-true}
else else
@var{text-if-false} @var{text-if-one-and-two-are-false}
endif endif
@end example @end example
@ -6631,7 +6645,7 @@ be substituted.
* Flavor Function:: Find out the flavor of a variable. * Flavor Function:: Find out the flavor of a variable.
* Make Control Functions:: Functions that control how make runs. * Make Control Functions:: Functions that control how make runs.
* Shell Function:: Substitute the output of a shell command. * Shell Function:: Substitute the output of a shell command.
* Guile Function:: Call the GNU Guile embedded scripting language. * Guile Function:: Use GNU Guile embedded scripting language.
@end menu @end menu
@node Syntax of Functions, Text Functions, Functions, Functions @node Syntax of Functions, Text Functions, Functions, Functions
@ -7896,208 +7910,17 @@ exists).@refill
@findex guile @findex guile
@cindex Guile @cindex Guile
GNU make may be built with support for GNU Guile as an embedded If GNU @code{make} is built with support for GNU Guile as an embedded
extension language. You can check the @code{.FEATURES} variable for extension language then the @code{guile} function will be available.
the word @samp{guile} to determine if your version of GNU make The @code{guile} function takes one argument which is first expanded
provides this capability. by @code{make} in the normal fashion, then passed to the GNU Guile
evaluator. The result of the evaluator is converted into a string and
GNU Guile implements the Scheme language. A review of GNU Guile and used as the expansion of the @code{guile} function in the makefile.
the Scheme language and its features is beyond the scope of this See @ref{Guile Integration, ,GNU Guile Integration} for details on
manual: see the documentation for GNU Guile and Scheme. writing extensions to @code{make} in Guile.
If GNU Guile is available as an extension language, there will be one
new @code{make} function available: @code{guile}. The @code{guile}
function takes one argument which is first expanded by @code{make} in
the normal fashion, then passed to the GNU Guile evaluator. The
result of the evaluator is converted into a string and used as the
expansion of the @code{guile} function in the makefile.
Similarly, there are Guile procedures exposed by @code{make} for use
in Guile scripts.
@menu
* Guile Types:: Converting Guile types to @code{make} strings.
* Guile Interface:: Invoking @code{make} functions from Guile.
* Guile Example:: Example using Guile in @code{make}.
@end menu
@node Guile Types, Guile Interface, Guile Function, Guile Function
@subsection Conversion of Guile Types
@cindex convert guile types
@cindex guile, conversion of types
@cindex types, conversion of
There is only one ``data type'' in @code{make}: a string. GNU Guile,
on the other hand, provides a rich variety of different data types.
An important aspect of the interface between @code{make} and GNU Guile
is the conversion of Guile data types into @code{make} strings.
This conversion is relevant in two places: when a makefile invokes the
@code{guile} function to evaluate a Guile expression, the result of
that evaluation must be converted into a make string so it can be
further evaluated by @code{make}. And secondly, when a Guile script
invokes one of the procedures exported by @code{make} the argument
provided to the procedure must be converted into a string.
The conversion of Guile types into @code{make} strings is as below:
@table @code
@item #f
False is converted into the empty string: in @code{make} conditionals
the empty string is considered false.
@item #t
True is converted to the string @samp{#t}: in @code{make} conditionals
any non-empty string is considered true.
@item symbol
@item number
A symbol or number is converted into the string representation of that
symbol or number.
@item character
A printable character is converted to the same character.
@item string
A string containing only printable characters is converted to the same
string.
@item list
A list is converted recursively according to the above rules. This
implies that any structured list will be flattened (that is, a result
of @samp{'(a b (c d) e)} will be converted to the @code{make} string
@samp{a b c d e}).
@item other
Any other Guile type results in an error. In future versions of
@code{make}, other Guile types may be converted.
@end table
The translation of @samp{#f} (to the empty string) and @samp{#t} (to
the non-empty string @samp{#t}) is designed to allow you to use Guile
boolean results directly as @code{make} boolean conditions. For
example:
@example
$(if $(guile (access? "myfile" R_OK)),$(info myfile exists))
@end example
As a consequence of these conversion rules you must consider the
result of your Guile script, as that result will be converted into a
string and parsed by @code{make}. If there is no natural result for
the script (that is, the script exists solely for its side-effects),
you should add @samp{#f} as the final expression in order to avoid
syntax errors in your makefile.
@node Guile Interface, Guile Example, Guile Types, Guile Function
@subsection Interfaces from Guile to @code{make}
@cindex make interface to guile
@cindex make procedures in guile
In addition to the @code{guile} function available in makefiles,
@code{make} exposes some procedures for use in your Guile scripts. At
startup @code{make} creates a new Guile module, @code{gnu make}, and
exports these procedures as public interfaces from that module:
@table @code
@item gmk-expand
This procedure takes a single argument which is converted into a
string. The string is expanded by @code{make} using normal
@code{make} expansion rules. The result of the expansion is converted
into a Guile string and provided as the result of the procedure.
@item gmk-eval
This procedure takes a single argument which is converted into a
string. The string is evaluated by @code{make} as if it were a
makefile. This is the same capability available via the @code{eval}
function (@pxref{Eval Function}). The result of the @code{gmk-eval}
procedure is always the empty string.
@item gmk-var
This procedure takes a single argument which is converted into a
string. The string is assumed to be the name of a @code{make}
variable, which is then expanded. The expansion is converted into a
string and provided as the result of the procedure.
@end table
@node Guile Example, , Guile Interface, Guile Function
@subsection Example Using Guile in @code{make}
@cindex Guile example
@cindex example using Guile
Here is a very simple example using GNU Guile to manage writing to a
file. These Guile procedures simply open a file, allow writing to the
file (one string per line), and close the file. Note that because we
cannot store complex values such as Guile ports in @code{make}
variables, we'll keep the port as a global variable in the Guile
interpreter.
You can create Guile functions easily using @code{define}/@code{endef}
to create a Guile script, then use the @code{guile} function to
internalize it:
@example
@group
define GUILEIO
;; A simple Guile IO library for GNU make
(define MKPORT #f)
(define (mkopen name mode)
(set! MKPORT (open-file name mode))
#f)
(define (mkwrite s)
(display s MKPORT)
(newline MKPORT)
#f)
(define (mkclose)
(close-port MKPORT)
#f)
#f
endef
# Internalize the Guile IO functions
$(guile $(GUILEIO))
@end group
@end example
If you have a significant amount of Guile support code, you might
consider keeping it in a different file (e.g., @file{guileio.scm}) and
then loading it in your makefile using the @code{guile} function:
@example
$(guile (load "guileio.scm"))
@end example
An advantage to this method is that when editing @file{guileio.scm},
your editor will understand that this file contains Scheme syntax
rather than makefile syntax.
Now you can use these Guile functions to create files. Suppose you
need to operate on a very large list, which cannot fit on the command
line, but the utility you're using accepts the list as input as well:
@example
@group
prog: $(PREREQS)
@@$(guile (mkopen "tmp.out" "w")) \
$(foreach X,$^,$(guile (mkwrite "$(X)"))) \
$(guile (mkclose))
$(LINK) < tmp.out
@end group
@end example
A more comprehensive suite of file manipulation procedures is possible
of course. You could, for example, maintain multiple output files at
the same time by choosing a symbol for each one and using it as the
key to a hash table, where the value is a port, then returning the
symbol to be stored in a @code{make} variable.
You can determine whether GNU Guile support is available by checking
the @code{.FEATURES} variable for the word @var{guile}.
@node Running, Implicit Rules, Functions, Top @node Running, Implicit Rules, Functions, Top
@chapter How to Run @code{make} @chapter How to Run @code{make}
@ -10476,7 +10299,7 @@ When the recipe of a pattern rule is executed for @var{t}, the
automatic variables are set corresponding to the target and automatic variables are set corresponding to the target and
prerequisites. @xref{Automatic Variables}. prerequisites. @xref{Automatic Variables}.
@node Archives, Features, Implicit Rules, Top @node Archives, Extending make, Implicit Rules, Top
@chapter Using @code{make} to Update Archive Files @chapter Using @code{make} to Update Archive Files
@cindex archive @cindex archive
@ -10703,7 +10526,345 @@ in the normal way (@pxref{Suffix Rules}). Thus a double-suffix rule
@w{@samp{.@var{x}.a}} produces two pattern rules: @samp{@w{(%.o):} @w{@samp{.@var{x}.a}} produces two pattern rules: @samp{@w{(%.o):}
@w{%.@var{x}}} and @samp{@w{%.a}: @w{%.@var{x}}}.@refill @w{%.@var{x}}} and @samp{@w{%.a}: @w{%.@var{x}}}.@refill
@node Features, Missing, Archives, Top @node Extending make, Features, Archives, Top
@chapter Extending GNU @code{make}
@cindex make extensions
GNU @code{make} provides many advanced capabilities, including many
useful functions. However, it does not contain a complete programming
language and so it has limitations. Sometimes these limitations can be
overcome through use of the @code{shell} function to invoke a separate
program, although this can be inefficient.
In cases where the built-in capabilities of GNU @code{make} are
insufficient to your requirements there are two options for extending
@code{make}. On systems where it's provided, you can utilize GNU
Guile as an embedded scripting language (@pxref{Guile Integration,
,GNU Guile Integration}). On systems which support dynamically
loadable objects, you can write your own extension in any language
(which can be compiled into such an object) and load it to provide
extended capabilities (@pxref{load Directive, ,The @code{load} Directive}).
@menu
* Guile Integration:: Using Guile as an embedded scripting language.
* Loading Objects:: Loading dynamic objects as extensions.
@end menu
@node Guile Integration, Loading Objects, Extending make, Extending make
@section GNU Guile Integration
@cindex Guile
@cindex extensions, Guile
GNU @code{make} may be built with support for GNU Guile as an embedded
extension language. Guile implements the Scheme language. A review
of GNU Guile and the Scheme language and its features is beyond the
scope of this manual: see the documentation for GNU Guile and Scheme.
You can determine if @code{make} contains support for Guile by
examining the @code{.FEATURES} variable; it will contain the word
@var{guile} if Guile support is available.
The Guile integration provides one new @code{make} function: @code{guile}.
The @code{guile} function takes one argument which is first expanded
by @code{make} in the normal fashion, then passed to the GNU Guile
evaluator. The result of the evaluator is converted into a string and
used as the expansion of the @code{guile} function in the makefile.
In addition, GNU @code{make} exposes Guile procedures for use in Guile
scripts.
@menu
* Guile Types:: Converting Guile types to @code{make} strings.
* Guile Interface:: Invoking @code{make} functions from Guile.
* Guile Example:: Example using Guile in @code{make}.
@end menu
@node Guile Types, Guile Interface, Guile Integration, Guile Integration
@subsection Conversion of Guile Types
@cindex convert guile types
@cindex guile, conversion of types
@cindex types, conversion of
There is only one ``data type'' in @code{make}: a string. GNU Guile,
on the other hand, provides a rich variety of different data types.
An important aspect of the interface between @code{make} and GNU Guile
is the conversion of Guile data types into @code{make} strings.
This conversion is relevant in two places: when a makefile invokes the
@code{guile} function to evaluate a Guile expression, the result of
that evaluation must be converted into a make string so it can be
further evaluated by @code{make}. And secondly, when a Guile script
invokes one of the procedures exported by @code{make} the argument
provided to the procedure must be converted into a string.
The conversion of Guile types into @code{make} strings is as below:
@table @code
@item #f
False is converted into the empty string: in @code{make} conditionals
the empty string is considered false.
@item #t
True is converted to the string @samp{#t}: in @code{make} conditionals
any non-empty string is considered true.
@item symbol
@item number
A symbol or number is converted into the string representation of that
symbol or number.
@item character
A printable character is converted to the same character.
@item string
A string containing only printable characters is converted to the same
string.
@item list
A list is converted recursively according to the above rules. This
implies that any structured list will be flattened (that is, a result
of @samp{'(a b (c d) e)} will be converted to the @code{make} string
@samp{a b c d e}).
@item other
Any other Guile type results in an error. In future versions of
@code{make}, other Guile types may be converted.
@end table
The translation of @samp{#f} (to the empty string) and @samp{#t} (to
the non-empty string @samp{#t}) is designed to allow you to use Guile
boolean results directly as @code{make} boolean conditions. For
example:
@example
$(if $(guile (access? "myfile" R_OK)),$(info myfile exists))
@end example
As a consequence of these conversion rules you must consider the
result of your Guile script, as that result will be converted into a
string and parsed by @code{make}. If there is no natural result for
the script (that is, the script exists solely for its side-effects),
you should add @samp{#f} as the final expression in order to avoid
syntax errors in your makefile.
@node Guile Interface, Guile Example, Guile Types, Guile Integration
@subsection Interfaces from Guile to @code{make}
@cindex make interface to guile
@cindex make procedures in guile
In addition to the @code{guile} function available in makefiles,
@code{make} exposes some procedures for use in your Guile scripts. At
startup @code{make} creates a new Guile module, @code{gnu make}, and
exports these procedures as public interfaces from that module:
@table @code
@item gmk-expand
This procedure takes a single argument which is converted into a
string. The string is expanded by @code{make} using normal
@code{make} expansion rules. The result of the expansion is converted
into a Guile string and provided as the result of the procedure.
@item gmk-eval
This procedure takes a single argument which is converted into a
string. The string is evaluated by @code{make} as if it were a
makefile. This is the same capability available via the @code{eval}
function (@pxref{Eval Function}). The result of the @code{gmk-eval}
procedure is always the empty string.
@item gmk-var
This procedure takes a single argument which is converted into a
string. The string is assumed to be the name of a @code{make}
variable, which is then expanded. The expansion is converted into a
string and provided as the result of the procedure.
@end table
@node Guile Example, , Guile Interface, Guile Integration
@subsection Example Using Guile in @code{make}
@cindex Guile example
@cindex example using Guile
Here is a very simple example using GNU Guile to manage writing to a
file. These Guile procedures simply open a file, allow writing to the
file (one string per line), and close the file. Note that because we
cannot store complex values such as Guile ports in @code{make}
variables, we'll keep the port as a global variable in the Guile
interpreter.
You can create Guile functions easily using @code{define}/@code{endef}
to create a Guile script, then use the @code{guile} function to
internalize it:
@example
@group
define GUILEIO
;; A simple Guile IO library for GNU make
(define MKPORT #f)
(define (mkopen name mode)
(set! MKPORT (open-file name mode))
#f)
(define (mkwrite s)
(display s MKPORT)
(newline MKPORT)
#f)
(define (mkclose)
(close-port MKPORT)
#f)
#f
endef
# Internalize the Guile IO functions
$(guile $(GUILEIO))
@end group
@end example
If you have a significant amount of Guile support code, you might
consider keeping it in a different file (e.g., @file{guileio.scm}) and
then loading it in your makefile using the @code{guile} function:
@example
$(guile (load "guileio.scm"))
@end example
An advantage to this method is that when editing @file{guileio.scm},
your editor will understand that this file contains Scheme syntax
rather than makefile syntax.
Now you can use these Guile functions to create files. Suppose you
need to operate on a very large list, which cannot fit on the command
line, but the utility you're using accepts the list as input as well:
@example
@group
prog: $(PREREQS)
@@$(guile (mkopen "tmp.out" "w")) \
$(foreach X,$^,$(guile (mkwrite "$(X)"))) \
$(guile (mkclose))
$(LINK) < tmp.out
@end group
@end example
A more comprehensive suite of file manipulation procedures is possible
of course. You could, for example, maintain multiple output files at
the same time by choosing a symbol for each one and using it as the
key to a hash table, where the value is a port, then returning the
symbol to be stored in a @code{make} variable.
@node Loading Objects, , Guile Integration, Extending make
@section Loading Dynamic Objects
@cindex loading objects
@cindex objects, loading
@cindex extensions, loading
@cartouche
@quotation Warning
The @code{load} directive and extension capability is considered a
``technology preview'' in this release of GNU make. We encourage you
to experiment with this feature and we appreciate any feedback on it.
However we cannot guarantee to maintain backward-compatibility in the
next release.
In particular, for this feature to be useful your extensions will need
to invoke various functions internal to GNU @code{make}. In this
release there is no stable programming interface defined for
@code{make}: any internal function may change or even disappear in
future releases.
@end quotation
@end cartouche
Many operating systems provide a facility for dynamically loading
compiled objects. If your system provides this facility, GNU
@code{make} can make use of it to load dynamic objects at runtime,
providing new capabilities which may then be invoked by your makefile.
The @code{load} directive is used to load a dynamic object. Once the
object is loaded, a ``setup'' function will be invoked to allow the
object to initialize itself and register new facilities with GNU
@code{make}. Typically a dynamic object would create new functions,
for example, and the ``setup'' function would register them with GNU
@code{make}'s function handling system.
@menu
* load Directive:: Loading dynamic objects as extensions.
@end menu
@node load Directive, , Loading Objects, Loading Objects
@subsection The @code{load} Directive
@cindex load directive
@cindex extensions, load directive
Objects are loaded into GNU @code{make} by placing the @code{load}
directive into your makefile. The syntax of the @code{load} directive
is as follows:
@findex load
@example
load @var{object-file} @dots{}
@end example
or:
@example
load @var{object-file}(@var{symbol-name}) @dots{}
@end example
In the first form, the file @var{object-file} is dynamically loaded by
GNU @code{make}. On failure, @code{make} will print a message and
exit. If the load succeeds @code{make} will invoke an initializing
function whose name is created by taking the base file name of
@var{object-file}, up to the first character which is not a valid
symbol name character (alphanumerics and underscores are valid symbol
name characters). To this prefix will be appended the suffix
@code{_gmake_setup}, then this symbol will be invoked.
In the second form, the function @var{symbol-name} will be invoked.
More than one object file may be loaded with a single @code{load}
directive, and both forms of @code{load} arguments may be used in the
same directive.
For example:
@example
load ../mk_funcs.so
@end example
will load the dynamic object @file{../mk_funcs.so}. After the object
is loaded, @code{make} will invoke the function (assumed to be defined
by the shared object) @code{mk_funcs_gmake_setup}.
On the other hand:
@example
load ../mk_funcs.so(init_mk_func)
@end example
will load the dynamic object @file{../mk_funcs.so}. After the object
is loaded, @code{make} will invoke the function @code{init_mk_func}.
Regardless of how many times an object file appears in a @code{load}
directive, it will only be loaded (and it's setup function will only
be invoked) once.
@vindex .LOADED
After an object has been successfully loaded, its file name is
appended to the @code{.LOADED} variable.
@findex -load
If you would prefer that failure to load a dynamic object not be
reported as an error, you can use the @code{-load} directive instead
of @code{load}. GNU @code{make} will not fail and no message will be
generated if an object fails to load. The failed object is not added
to the @code{.LOADED} variable, which can then be consulted to
determine if the load was successful.
@node Features, Missing, Extending make, Top
@chapter Features of GNU @code{make} @chapter Features of GNU @code{make}
@cindex features of GNU @code{make} @cindex features of GNU @code{make}
@cindex portability @cindex portability

View File

@ -104,8 +104,10 @@ func_guile (char *o, char **argv, const char *funcname UNUSED)
/* ----- Public interface ----- */ /* ----- Public interface ----- */
/* We could send the flocp to define_new_function(), but since guile is
"kind of" built-in, that didn't seem so useful. */
int int
setup_guile () guile_gmake_setup (const struct floc *flocp UNUSED)
{ {
/* Initialize the Guile interpreter. */ /* Initialize the Guile interpreter. */
scm_with_guile (guile_init, NULL); scm_with_guile (guile_init, NULL);
@ -113,8 +115,5 @@ setup_guile ()
/* Create a make function "guile". */ /* Create a make function "guile". */
define_new_function (NILF, "guile", 0, 1, 1, func_guile); define_new_function (NILF, "guile", 0, 1, 1, func_guile);
/* Add 'guile' to the list of features. */
do_variable_definition (NILF, ".FEATURES", "guile", o_default, f_append, 0);
return 1; return 1;
} }

157
load.c Normal file
View File

@ -0,0 +1,157 @@
/* Loading dynamic objects for GNU Make.
Copyright (C) 2012 Free Software Foundation, Inc.
This file is part of GNU Make.
GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.
GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>. */
#include "make.h"
#if MAKE_LOAD
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <errno.h>
#define SYMBOL_EXTENSION "_gmake_setup"
static void *global_dl = NULL;
#include "debug.h"
#include "filedef.h"
#include "variable.h"
static int
init_symbol (const struct floc *flocp, const char *ldname, load_func_t symp)
{
int r;
const char *p;
int nmlen = strlen (ldname);
char *loaded = allocated_variable_expand("$(.LOADED)");
/* If it's already been loaded don't do it again. */
p = strstr (loaded, ldname);
r = p && (p==loaded || p[-1]==' ') && (p[nmlen]=='\0' || p[nmlen]==' ');
free (loaded);
if (r)
return 1;
/* Now invoke the symbol. */
r = (*symp) (flocp);
/* If it succeeded, add the symbol to the loaded variable. */
if (r > 0)
do_variable_definition (flocp, ".LOADED", ldname, o_default, f_append, 0);
return r;
}
int
load_file (const struct floc *flocp, const char *ldname, int noerror)
{
load_func_t symp;
const char *fp;
char *symname = NULL;
char *new = alloca (strlen (ldname) + CSTRLEN (SYMBOL_EXTENSION) + 1);
if (! global_dl)
{
global_dl = dlopen (NULL, RTLD_NOW|RTLD_GLOBAL);
if (! global_dl)
fatal (flocp, _("Failed to open global symbol table: %s"), dlerror());
}
/* If a symbol name was provided, use it. */
fp = strchr (ldname, '(');
if (fp)
{
const char *ep;
/* If there's an open paren, see if there's a close paren: if so use
that as the symbol name. We can't have whitespace: it would have
been chopped up before this function is called. */
ep = strchr (fp+1, ')');
if (ep && ep[1] == '\0')
{
int l = fp - ldname;;
++fp;
if (fp == ep)
fatal (flocp, _("Empty symbol name for load: %s"), ldname);
/* Make a copy of the ldname part. */
memcpy (new, ldname, l);
new[l] = '\0';
ldname = new;
/* Make a copy of the symbol name part. */
symname = new + l + 1;
memcpy (symname, fp, ep - fp);
symname[ep - fp] = '\0';
}
}
/* If we didn't find a symbol name yet, construct it from the ldname. */
if (! symname)
{
char *p = new;
fp = strrchr (ldname, '/');
if (!fp)
fp = ldname;
else
++fp;
while (isalnum (*fp) || *fp == '_')
*(p++) = *(fp++);
strcpy (p, SYMBOL_EXTENSION);
symname = new;
}
DB (DB_VERBOSE, (_("Loading symbol %s from %s\n"), symname, ldname));
/* See if it's already defined. */
symp = (load_func_t) dlsym (global_dl, symname);
if (! symp) {
void *dlp = dlopen (ldname, RTLD_LAZY|RTLD_GLOBAL);
if (! dlp)
{
if (noerror)
DB (DB_BASIC, ("%s", dlerror()));
else
error (flocp, "%s", dlerror());
return 0;
}
symp = dlsym (dlp, symname);
if (! symp)
fatal (flocp, _("Failed to load symbol %s from %s: %s"),
symname, ldname, dlerror());
}
/* Invoke the symbol to initialize the loaded object. */
return init_symbol(flocp, ldname, symp);
}
#else
int
load_file (const struct floc *flocp, const char *ldname, int noerror)
{
if (! noerror)
fatal (flocp, _("The 'load' operation is not supported on this platform."));
return 0;
}
#endif /* MAKE_LOAD */

13
main.c
View File

@ -1148,6 +1148,12 @@ main (int argc, char **argv, char **envp)
#endif #endif
#ifdef MAKE_SYMLINKS #ifdef MAKE_SYMLINKS
" check-symlink" " check-symlink"
#endif
#ifdef HAVE_GUILE
" guile"
#endif
#ifdef MAKE_LOAD
" load"
#endif #endif
; ;
@ -1156,7 +1162,7 @@ main (int argc, char **argv, char **envp)
#ifdef HAVE_GUILE #ifdef HAVE_GUILE
/* Configure GNU Guile support */ /* Configure GNU Guile support */
setup_guile (); guile_gmake_setup (NILF);
#endif #endif
/* Read in variables from the environment. It is important that this be /* Read in variables from the environment. It is important that this be
@ -1661,8 +1667,7 @@ main (int argc, char **argv, char **envp)
/* Read all the makefiles. */ /* Read all the makefiles. */
read_makefiles read_makefiles = read_all_makefiles (makefiles == 0 ? 0 : makefiles->list);
= read_all_makefiles (makefiles == 0 ? 0 : makefiles->list);
#ifdef WINDOWS32 #ifdef WINDOWS32
/* look one last time after reading all Makefiles */ /* look one last time after reading all Makefiles */
@ -3271,7 +3276,7 @@ die (int status)
if (directory_before_chdir != 0) if (directory_before_chdir != 0)
{ {
/* If it fails we don't care: shut up GCC. */ /* If it fails we don't care: shut up GCC. */
int _x; int _x UNUSED;
_x = chdir (directory_before_chdir); _x = chdir (directory_before_chdir);
} }

7
make.h
View File

@ -472,8 +472,13 @@ const char *strcache_add_len (const char *str, unsigned int len);
int strcache_setbufsize (unsigned int size); int strcache_setbufsize (unsigned int size);
/* Guile support */ /* Guile support */
int setup_guile (void); #ifdef HAVE_GUILE
int guile_gmake_setup (const struct floc *flocp);
#endif
/* Loadable object support */
typedef int (*load_func_t)(const struct floc *flocp);
int load_file (const struct floc *flocp, const char *filename, int noerror);
#ifdef HAVE_VFORK_H #ifdef HAVE_VFORK_H
# include <vfork.h> # include <vfork.h>

73
read.c
View File

@ -595,9 +595,8 @@ eval (struct ebuffer *ebuf, int set_default)
when the start of the next rule (or eof) is encountered. when the start of the next rule (or eof) is encountered.
When you see a "continue" in the loop below, that means we are moving on When you see a "continue" in the loop below, that means we are moving on
to the next line _without_ ending any rule that we happen to be working to the next line. If you see record_waiting_files(), then the statement
with at the moment. If you see a "goto rule_complete", then the we are parsing also finishes the previous rule. */
statement we just parsed also finishes the previous rule. */
commands = xmalloc (200); commands = xmalloc (200);
@ -707,6 +706,9 @@ eval (struct ebuffer *ebuf, int set_default)
struct variable *v; struct variable *v;
enum variable_origin origin = vmod.override_v ? o_override : o_file; enum variable_origin origin = vmod.override_v ? o_override : o_file;
/* Variable assignment ends the previous rule. */
record_waiting_files ();
/* If we're ignoring then we're done now. */ /* If we're ignoring then we're done now. */
if (ignoring) if (ignoring)
{ {
@ -718,9 +720,7 @@ eval (struct ebuffer *ebuf, int set_default)
if (vmod.undefine_v) if (vmod.undefine_v)
{ {
do_undefine (p, origin, ebuf); do_undefine (p, origin, ebuf);
continue;
/* This line has been dealt with. */
goto rule_complete;
} }
else if (vmod.define_v) else if (vmod.define_v)
v = do_define (p, origin, ebuf); v = do_define (p, origin, ebuf);
@ -735,7 +735,7 @@ eval (struct ebuffer *ebuf, int set_default)
v->private_var = 1; v->private_var = 1;
/* This line has been dealt with. */ /* This line has been dealt with. */
goto rule_complete; continue;
} }
/* If this line is completely empty, ignore it. */ /* If this line is completely empty, ignore it. */
@ -779,6 +779,9 @@ eval (struct ebuffer *ebuf, int set_default)
{ {
int exporting = *p == 'u' ? 0 : 1; int exporting = *p == 'u' ? 0 : 1;
/* Export/unexport ends the previous rule. */
record_waiting_files ();
/* (un)export by itself causes everything to be (un)exported. */ /* (un)export by itself causes everything to be (un)exported. */
if (*p2 == '\0') if (*p2 == '\0')
export_all_variables = exporting; export_all_variables = exporting;
@ -803,7 +806,7 @@ eval (struct ebuffer *ebuf, int set_default)
free (ap); free (ap);
} }
goto rule_complete; continue;
} }
/* Handle the special syntax for vpath. */ /* Handle the special syntax for vpath. */
@ -812,6 +815,10 @@ eval (struct ebuffer *ebuf, int set_default)
const char *cp; const char *cp;
char *vpat; char *vpat;
unsigned int l; unsigned int l;
/* vpath ends the previous rule. */
record_waiting_files ();
cp = variable_expand (p2); cp = variable_expand (p2);
p = find_next_token (&cp, &l); p = find_next_token (&cp, &l);
if (p != 0) if (p != 0)
@ -828,7 +835,7 @@ eval (struct ebuffer *ebuf, int set_default)
if (vpat != 0) if (vpat != 0)
free (vpat); free (vpat);
goto rule_complete; continue;
} }
/* Handle include and variants. */ /* Handle include and variants. */
@ -843,6 +850,9 @@ eval (struct ebuffer *ebuf, int set_default)
exist. "sinclude" is an alias for this from SGI. */ exist. "sinclude" is an alias for this from SGI. */
int noerror = (p[0] != 'i'); int noerror = (p[0] != 'i');
/* Include ends the previous rule. */
record_waiting_files ();
p = allocated_variable_expand (p2); p = allocated_variable_expand (p2);
/* If no filenames, it's a no-op. */ /* If no filenames, it's a no-op. */
@ -887,9 +897,51 @@ eval (struct ebuffer *ebuf, int set_default)
/* Restore conditional state. */ /* Restore conditional state. */
restore_conditionals (save); restore_conditionals (save);
goto rule_complete; continue;
} }
/* Handle the load operations. */
if (word1eq ("load") || word1eq ("-load"))
{
/* A 'load' line specifies a dynamic object to load. */
struct nameseq *files;
int noerror = (p[0] == '-');
/* Load ends the previous rule. */
record_waiting_files ();
p = allocated_variable_expand (p2);
/* If no filenames, it's a no-op. */
if (*p == '\0')
{
free (p);
continue;
}
/* Parse the list of file names.
Don't expand archive references or strip "./" */
p2 = p;
files = PARSE_FILE_SEQ (&p2, struct nameseq, '\0', NULL,
PARSEFS_NOAR|PARSEFS_NOSTRIP);
free (p);
/* Load each file. */
while (files != 0)
{
struct nameseq *next = files->next;
const char *name = files->name;
free_ns (files);
files = next;
if (! load_file (&ebuf->floc, name, noerror) && ! noerror)
fatal (&ebuf->floc, _("%s: failed to load"), name);
}
continue;
}
/* This line starts with a tab but was not caught above because there /* This line starts with a tab but was not caught above because there
was no preceding target, and the line might have been usable as a was no preceding target, and the line might have been usable as a
variable definition. But now we know it is definitely lossage. */ variable definition. But now we know it is definitely lossage. */
@ -1293,7 +1345,6 @@ eval (struct ebuffer *ebuf, int set_default)
/* We get here except in the case that we just read a rule line. /* We get here except in the case that we just read a rule line.
Record now the last rule we read, so following spurious Record now the last rule we read, so following spurious
commands are properly diagnosed. */ commands are properly diagnosed. */
rule_complete:
record_waiting_files (); record_waiting_files ();
} }

View File

@ -1,3 +1,7 @@
2012-10-29 Paul Smith <psmith@gnu.org>
* scripts/features/load: New test suite for the "load" directive.
2012-09-09 Paul Smith <psmith@gnu.org> 2012-09-09 Paul Smith <psmith@gnu.org>
* scripts/functions/file: Get errors in the C locale, not the * scripts/functions/file: Get errors in the C locale, not the

View File

@ -97,6 +97,17 @@ sub valid_option
$old_makefile = undef; $old_makefile = undef;
sub subst_make_string
{
local $_ = shift;
$makefile and s/#MAKEFILE#/$makefile/g;
s/#MAKEPATH#/$mkpath/g;
s/#MAKE#/$make_name/g;
s/#PERL#/$perl_name/g;
s/#PWD#/$pwd/g;
return $_;
}
sub run_make_test sub run_make_test
{ {
local ($makestring, $options, $answer, $err_code, $timeout) = @_; local ($makestring, $options, $answer, $err_code, $timeout) = @_;
@ -114,16 +125,9 @@ sub run_make_test
$makefile = &get_tmpfile(); $makefile = &get_tmpfile();
} }
# Make sure it ends in a newline. # Make sure it ends in a newline and substitute any special tokens.
$makestring && $makestring !~ /\n$/s and $makestring .= "\n"; $makestring && $makestring !~ /\n$/s and $makestring .= "\n";
$makestring = subst_make_string($makestring);
# Replace @MAKEFILE@ with the makefile name and @MAKE@ with the path to
# make
$makestring =~ s/#MAKEFILE#/$makefile/g;
$makestring =~ s/#MAKEPATH#/$mkpath/g;
$makestring =~ s/#MAKE#/$make_name/g;
$makestring =~ s/#PERL#/$perl_name/g;
$makestring =~ s/#PWD#/$pwd/g;
# Populate the makefile! # Populate the makefile!
open(MAKEFILE, "> $makefile") || die "Failed to open $makefile: $!\n"; open(MAKEFILE, "> $makefile") || die "Failed to open $makefile: $!\n";
@ -132,13 +136,8 @@ sub run_make_test
} }
# Do the same processing on $answer as we did on $makestring. # Do the same processing on $answer as we did on $makestring.
$answer && $answer !~ /\n$/s and $answer .= "\n"; $answer && $answer !~ /\n$/s and $answer .= "\n";
$answer =~ s/#MAKEFILE#/$makefile/g; $answer = subst_make_string($answer);
$answer =~ s/#MAKEPATH#/$mkpath/g;
$answer =~ s/#MAKE#/$make_name/g;
$answer =~ s/#PERL#/$perl_name/g;
$answer =~ s/#PWD#/$pwd/g;
run_make_with_options($makefile, $options, &get_logfile(0), run_make_with_options($makefile, $options, &get_logfile(0),
$err_code, $timeout); $err_code, $timeout);

View File

@ -0,0 +1,84 @@
# -*-perl-*-
$description = "Test the load operator.";
$details = "Test dynamic loading of modules.";
# Don't do anything if this system doesn't support "load"
exists $FEATURES{load} or return -1;
# First build a shared object
# Provide both a default and non-default load symbol
unlink(qw(testload.c testload.so));
open(my $F, '> testload.c') or die "open: testload.c: $!\n";
print $F <<'EOF' ;
#include <string.h>
#include <stdio.h>
void define_new_function (void *, const char *, int, int, int,
char *(*)(char *, char **, const char *));
char *variable_buffer_output (char *, const char *, unsigned int);
static char *
func_test(char *o, char **argv, const char *funcname)
{
return variable_buffer_output (o, funcname, strlen (funcname));
}
int
testload_gmake_setup ()
{
define_new_function (0, "func-a", 1, 1, 1, func_test);
return 1;
}
int
explicit_setup ()
{
define_new_function (0, "func-b", 1, 1, 1, func_test);
return 1;
}
EOF
close($F) or die "close: testload.c: $!\n";
run_make_test('testload.so: testload.c ; @$(CC) -g -shared -fPIC -o $@ $<',
'', '');
# TEST 1
run_make_test(q!
all: ; @echo $(func-a foo) $(func-b bar)
load ./testload.so
!,
'', "func-a\n");
# TEST 2
# Load a different function
run_make_test(q!
all: ; @echo $(func-a foo) $(func-b bar)
load ./testload.so(explicit_setup)
!,
'', "func-b\n");
# TEST 3
# Verify the .LOADED variable
run_make_test(q!
all: ; @echo $(filter ./testload.so,$(.LOADED)) $(func-a foo) $(func-b bar)
load ./testload.so(explicit_setup)
!,
'', "./testload.so func-b\n");
# TEST 4
# Check multiple loads
run_make_test(q!
all: ; @echo $(filter ./testload.so,$(.LOADED)) $(func-a foo) $(func-b bar)
load ./testload.so
load ./testload.so(explicit_setup)
!,
'', "./testload.so func-a\n");
unlink(qw(testload.c testload.so)) unless $keep;
# This tells the test driver that the perl test script executed properly.
1;

View File

@ -229,7 +229,7 @@ file2: file1 ; @touch $@
!, !,
'--no-print-directory -j2', "touch file3"); '--no-print-directory -j2', "touch file3");
#rmfiles('file1', 'file2', 'file3', 'file4'); rmfiles('file1', 'file2', 'file3', 'file4');
if ($all_tests) { if ($all_tests) {
# Jobserver FD handling is messed up in some way. # Jobserver FD handling is messed up in some way.

View File

@ -5,6 +5,20 @@ $description = 'Test the $(guile ...) function.';
$details = 'This only works on systems that support it.'; $details = 'This only works on systems that support it.';
# If this instance of make doesn't support GNU Guile, skip it # If this instance of make doesn't support GNU Guile, skip it
# This detects if guile is loaded using the "load" directive
# $makefile = get_tmpfile();
# open(MAKEFILE, "> $makefile") || die "Failed to open $makefile: $!\n";
# print MAKEFILE q!
# -load guile
# all: ; @echo $(filter guile,$(.LOADED))
# !;
# close(MAKEFILE) || die "Failed to write $makefile: $!\n";
# $cmd = subst_make_string("#MAKEPATH# -f $makefile");
# $log = get_logfile(0);
# $code = run_command_with_output($log, $cmd);
# read_file_into_string ($log) eq "guile\n" and $FEATURES{guile} = 1;
# If we don't have Guile support, never mind.
exists $FEATURES{guile} or return -1; exists $FEATURES{guile} or return -1;
# Verify simple data type conversions # Verify simple data type conversions