summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog12
-rw-r--r--Makefile.am2
-rw-r--r--NEWS7
-rw-r--r--configure.in63
-rw-r--r--doc/make.texi593
-rw-r--r--guile.c7
-rw-r--r--load.c157
-rw-r--r--main.c13
-rw-r--r--make.h7
-rw-r--r--read.c73
-rw-r--r--tests/ChangeLog4
-rwxr-xr-xtests/run_make_tests.pl29
-rw-r--r--tests/scripts/features/load84
-rw-r--r--tests/scripts/features/parallelism2
-rw-r--r--tests/scripts/functions/guile14
15 files changed, 807 insertions, 260 deletions
diff --git a/ChangeLog b/ChangeLog
index 0b7e3d9..fb91f7b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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>
* configure.in: Require a new version of gettext (1.18.1).
diff --git a/Makefile.am b/Makefile.am
index 592de9f..47f2919 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -39,7 +39,7 @@ else
endif
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 \
strcache.c variable.c version.c vpath.c hash.c \
$(remote)
diff --git a/NEWS b/NEWS
index 63b5f01..3e48acc 100644
--- a/NEWS
+++ b/NEWS
@@ -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
reporting bugs.
-Version 3.82.90
+Version 3.99.90
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.
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.
* On failure, the makefile name and linenumber of the recipe that failed are
diff --git a/configure.in b/configure.in
index f8c2482..7c06757 100644
--- a/configure.in
+++ b/configure.in
@@ -252,9 +252,7 @@ AS_IF([test "$PATH_SEPARATOR" = ';'],
[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
-
AC_SUBST([REMOTE]) REMOTE=stub
use_customs=false
AC_ARG_WITH([customs],
@@ -280,7 +278,6 @@ AC_ARG_WITH([customs],
AM_CONDITIONAL([USE_CUSTOMS], [test "$use_customs" = true])
# See if the user asked to handle case insensitive file systems.
-
AH_TEMPLATE([HAVE_CASE_INSENSITIVE_FS], [Use case insensitive file names])
AC_ARG_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])])])
# See if we can handle the job server feature, and if the user wants it.
-
AC_ARG_ENABLE([job-server],
AC_HELP_STRING([--disable-job-server],
[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.])
])
+# 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
# timechecks.
AS_IF([test "$ac_cv_func_lstat" = yes && test "$ac_cv_func_readlink" = yes],
-[ AC_DEFINE([MAKE_SYMLINKS], [1],
- [Define to 1 to enable symbolic link timestamp checking.])
+ [ AC_DEFINE([MAKE_SYMLINKS], [1],
+ [Define to 1 to enable symbolic link timestamp checking.])
])
# 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
])
+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.
AC_CONFIG_FILES([Makefile glob/Makefile po/Makefile.in config/Makefile \
doc/Makefile w32/Makefile])
diff --git a/doc/make.texi b/doc/make.texi
index 67f45c6..3a16dd3 100644
--- a/doc/make.texi
+++ b/doc/make.texi
@@ -100,6 +100,7 @@ Cover art by Etienne Suvasa.
* Implicit Rules:: Use implicit rules to treat many files alike,
based on their file names.
* 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.
* Missing:: What GNU @code{make} lacks from other @code{make}s.
* Makefile Conventions:: Conventions for writing makefiles for
@@ -277,13 +278,7 @@ Functions for Transforming Text
* Flavor Function:: Find out the flavor of a variable.
* Make Control Functions:: Functions that control how make runs.
* Shell Function:: Substitute the output of a shell command.
-* Guile Function:: Call the 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}.
+* Guile Function:: Use GNU Guile embedded scripting language.
How to Run @code{make}
@@ -339,6 +334,21 @@ Implicit Rule for Archive Member Targets
* 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 menu
@@ -6280,7 +6290,11 @@ Supports the @code{undefine} directive. @xref{Undefine Directive}.
@item guile
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
@@ -6422,12 +6436,12 @@ endif
or:
@example
-@var{conditional-directive}
+@var{conditional-directive-one}
@var{text-if-one-is-true}
-else @var{conditional-directive}
-@var{text-if-true}
+else @var{conditional-directive-two}
+@var{text-if-two-is-true}
else
-@var{text-if-false}
+@var{text-if-one-and-two-are-false}
endif
@end example
@@ -6631,7 +6645,7 @@ be substituted.
* Flavor Function:: Find out the flavor of a variable.
* Make Control Functions:: Functions that control how make runs.
* 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
@node Syntax of Functions, Text Functions, Functions, Functions
@@ -7896,208 +7910,17 @@ exists).@refill
@findex guile
@cindex Guile
-GNU make may be built with support for GNU Guile as an embedded
-extension language. You can check the @code{.FEATURES} variable for
-the word @samp{guile} to determine if your version of GNU make
-provides this capability.
-
-GNU 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.
-
-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.
+If GNU @code{make} is built with support for GNU Guile as an embedded
+extension language then the @code{guile} function will be available.
+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.
+See @ref{Guile Integration, ,GNU Guile Integration} for details on
+writing extensions to @code{make} in Guile.
+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
@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
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
@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{%.@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}
@cindex features of GNU @code{make}
@cindex portability
diff --git a/guile.c b/guile.c
index edba6d6..9c9d958 100644
--- a/guile.c
+++ b/guile.c
@@ -104,8 +104,10 @@ func_guile (char *o, char **argv, const char *funcname UNUSED)
/* ----- 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
-setup_guile ()
+guile_gmake_setup (const struct floc *flocp UNUSED)
{
/* Initialize the Guile interpreter. */
scm_with_guile (guile_init, NULL);
@@ -113,8 +115,5 @@ setup_guile ()
/* Create a make function "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;
}
diff --git a/load.c b/load.c
new file mode 100644
index 0000000..076dd30
--- /dev/null
+++ b/load.c
@@ -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 */
diff --git a/main.c b/main.c
index a6d1454..7bcc07a 100644
--- a/main.c
+++ b/main.c
@@ -1149,6 +1149,12 @@ main (int argc, char **argv, char **envp)
#ifdef MAKE_SYMLINKS
" check-symlink"
#endif
+#ifdef HAVE_GUILE
+ " guile"
+#endif
+#ifdef MAKE_LOAD
+ " load"
+#endif
;
define_variable_cname (".FEATURES", features, o_default, 0);
@@ -1156,7 +1162,7 @@ main (int argc, char **argv, char **envp)
#ifdef HAVE_GUILE
/* Configure GNU Guile support */
- setup_guile ();
+ guile_gmake_setup (NILF);
#endif
/* 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_makefiles
- = read_all_makefiles (makefiles == 0 ? 0 : makefiles->list);
+ read_makefiles = read_all_makefiles (makefiles == 0 ? 0 : makefiles->list);
#ifdef WINDOWS32
/* look one last time after reading all Makefiles */
@@ -3271,7 +3276,7 @@ die (int status)
if (directory_before_chdir != 0)
{
/* If it fails we don't care: shut up GCC. */
- int _x;
+ int _x UNUSED;
_x = chdir (directory_before_chdir);
}
diff --git a/make.h b/make.h
index 87d7bdb..a1b34f3 100644
--- a/make.h
+++ b/make.h
@@ -472,8 +472,13 @@ const char *strcache_add_len (const char *str, unsigned int len);
int strcache_setbufsize (unsigned int size);
/* 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
# include <vfork.h>
diff --git a/read.c b/read.c
index 677c233..912ca71 100644
--- a/read.c
+++ b/read.c
@@ -595,9 +595,8 @@ eval (struct ebuffer *ebuf, int set_default)
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
- to the next line _without_ ending any rule that we happen to be working
- with at the moment. If you see a "goto rule_complete", then the
- statement we just parsed also finishes the previous rule. */
+ to the next line. If you see record_waiting_files(), then the statement
+ we are parsing also finishes the previous rule. */
commands = xmalloc (200);
@@ -707,6 +706,9 @@ eval (struct ebuffer *ebuf, int set_default)
struct variable *v;
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 (ignoring)
{
@@ -718,9 +720,7 @@ eval (struct ebuffer *ebuf, int set_default)
if (vmod.undefine_v)
{
do_undefine (p, origin, ebuf);
-
- /* This line has been dealt with. */
- goto rule_complete;
+ continue;
}
else if (vmod.define_v)
v = do_define (p, origin, ebuf);
@@ -735,7 +735,7 @@ eval (struct ebuffer *ebuf, int set_default)
v->private_var = 1;
/* This line has been dealt with. */
- goto rule_complete;
+ continue;
}
/* 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;
+ /* Export/unexport ends the previous rule. */
+ record_waiting_files ();
+
/* (un)export by itself causes everything to be (un)exported. */
if (*p2 == '\0')
export_all_variables = exporting;
@@ -803,7 +806,7 @@ eval (struct ebuffer *ebuf, int set_default)
free (ap);
}
- goto rule_complete;
+ continue;
}
/* Handle the special syntax for vpath. */
@@ -812,6 +815,10 @@ eval (struct ebuffer *ebuf, int set_default)
const char *cp;
char *vpat;
unsigned int l;
+
+ /* vpath ends the previous rule. */
+ record_waiting_files ();
+
cp = variable_expand (p2);
p = find_next_token (&cp, &l);
if (p != 0)
@@ -828,7 +835,7 @@ eval (struct ebuffer *ebuf, int set_default)
if (vpat != 0)
free (vpat);
- goto rule_complete;
+ continue;
}
/* Handle include and variants. */
@@ -843,6 +850,9 @@ eval (struct ebuffer *ebuf, int set_default)
exist. "sinclude" is an alias for this from SGI. */
int noerror = (p[0] != 'i');
+ /* Include ends the previous rule. */
+ record_waiting_files ();
+
p = allocated_variable_expand (p2);
/* If no filenames, it's a no-op. */
@@ -887,9 +897,51 @@ eval (struct ebuffer *ebuf, int set_default)
/* Restore conditional state. */
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
was no preceding target, and the line might have been usable as a
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.
Record now the last rule we read, so following spurious
commands are properly diagnosed. */
- rule_complete:
record_waiting_files ();
}
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 5e35053..7ffd6b0 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -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>
* scripts/functions/file: Get errors in the C locale, not the
diff --git a/tests/run_make_tests.pl b/tests/run_make_tests.pl
index 477b117..4accd4a 100755
--- a/tests/run_make_tests.pl
+++ b/tests/run_make_tests.pl
@@ -97,6 +97,17 @@ sub valid_option
$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
{
local ($makestring, $options, $answer, $err_code, $timeout) = @_;
@@ -114,16 +125,9 @@ sub run_make_test
$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";
-
- # 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;
+ $makestring = subst_make_string($makestring);
# Populate the makefile!
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.
-
$answer && $answer !~ /\n$/s and $answer .= "\n";
- $answer =~ s/#MAKEFILE#/$makefile/g;
- $answer =~ s/#MAKEPATH#/$mkpath/g;
- $answer =~ s/#MAKE#/$make_name/g;
- $answer =~ s/#PERL#/$perl_name/g;
- $answer =~ s/#PWD#/$pwd/g;
+ $answer = subst_make_string($answer);
run_make_with_options($makefile, $options, &get_logfile(0),
$err_code, $timeout);
diff --git a/tests/scripts/features/load b/tests/scripts/features/load
new file mode 100644
index 0000000..8117bbd
--- /dev/null
+++ b/tests/scripts/features/load
@@ -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;
diff --git a/tests/scripts/features/parallelism b/tests/scripts/features/parallelism
index 76d24a7..08c822e 100644
--- a/tests/scripts/features/parallelism
+++ b/tests/scripts/features/parallelism
@@ -229,7 +229,7 @@ file2: file1 ; @touch $@
!,
'--no-print-directory -j2', "touch file3");
-#rmfiles('file1', 'file2', 'file3', 'file4');
+rmfiles('file1', 'file2', 'file3', 'file4');
if ($all_tests) {
# Jobserver FD handling is messed up in some way.
diff --git a/tests/scripts/functions/guile b/tests/scripts/functions/guile
index 82c02bc..93a18ab 100644
--- a/tests/scripts/functions/guile
+++ b/tests/scripts/functions/guile
@@ -5,6 +5,20 @@ $description = 'Test the $(guile ...) function.';
$details = 'This only works on systems that support 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;
# Verify simple data type conversions