Implement new "load" directive.
authorPaul Smith <psmith@gnu.org>
Mon, 29 Oct 2012 07:05:21 +0000 (07:05 +0000)
committerPaul Smith <psmith@gnu.org>
Mon, 29 Oct 2012 07:05:21 +0000 (07:05 +0000)
Provides support for dynamically loadable objects in GNU make, as a
"technology preview".

15 files changed:
ChangeLog
Makefile.am
NEWS
configure.in
doc/make.texi
guile.c
load.c [new file with mode: 0644]
main.c
make.h
read.c
tests/ChangeLog
tests/run_make_tests.pl
tests/scripts/features/load [new file with mode: 0644]
tests/scripts/features/parallelism
tests/scripts/functions/guile

index 0b7e3d91edfaae29aabf4dbe77f3613702f93c95..fb91f7b953a78105a67fb6ec1606f1fe5ab90c3d 100644 (file)
--- 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).
index 592de9f8e4b3d96fbe668e0cd4c1f190dcd53173..47f29194d16f749c7efa5c8da4332e85698f133c 100644 (file)
@@ -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 63b5f01d298ab065357a86e950d24d7e48c137d6..3e48acc4793babfeed7e8cf0ce9201c94e91dc87 100644 (file)
--- 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.
 \f
-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
index f8c2482e611e61035e82655a1c4de4d0835223ad..7c06757a233a46b1b27515a85c55b9ff4e0bdea3 100644 (file)
@@ -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])
index 67f45c654ec99de2fe36a69a0ddd9059baab8f33..3a16dd36432604c7acd835183ab7c24fa242e21b 100644 (file)
@@ -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 edba6d69daab1e57ed97bb754d51f42281681794..9c9d958f0464f626155d9f80295e412e49dbcecf 100644 (file)
--- 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 (file)
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 a6d1454541c4c9939aebb2c3ce0eaf0b7d038122..7bcc07a1f4ad8d6607e00d5e13ad37c7c3c7f52d 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1148,6 +1148,12 @@ main (int argc, char **argv, char **envp)
 #endif
 #ifdef MAKE_SYMLINKS
                            " check-symlink"
+#endif
+#ifdef HAVE_GUILE
+                           " guile"
+#endif
+#ifdef MAKE_LOAD
+                           " load"
 #endif
                            ;
 
@@ -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 87d7bdbb7a2c37c84367d46469730aeeac2a8bda..a1b34f33d52eac37bd566445dd190617465ef587 100644 (file)
--- 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 677c2333da40161af463f9267bd5022349603327..912ca71cf8f4d7a215eaaf199081928ea56208a6 100644 (file)
--- 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 ();
     }
 
index 5e35053e83bb1c151e1e87d7500b4e2328f83fe8..7ffd6b04399fb0db94f28243dc48b673b772892c 100644 (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>
 
        * scripts/functions/file: Get errors in the C locale, not the
index 477b1176721156996f631b666ae6689d9af2d728..4accd4aadb7b88e5548af4f58ba6aa3eeb53673f 100755 (executable)
@@ -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 (file)
index 0000000..8117bbd
--- /dev/null
@@ -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;
index 76d24a745aeaed92aecfb3fa86d9e36c3742ef1c..08c822e37664c1c5106cde4dfeb731696a27c487 100644 (file)
@@ -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.
index 82c02bcc976cc7f7402e199bb57128dc573e7f1e..93a18ab636d85ed1de73317d43141d794dd35775 100644 (file)
@@ -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