Implement new "load" directive.

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

View file

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

View 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)

7
NEWS
View file

@ -9,7 +9,7 @@ manual, which is contained in this distribution as the file doc/make.texi.
See the README file and the GNU make manual for instructions for
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

View 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])

View 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

View file

@ -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;
}

157
load.c Normal file
View file

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

13
main.c
View file

@ -1148,6 +1148,12 @@ main (int argc, char **argv, char **envp)
#endif
#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);
}

7
make.h
View file

@ -472,8 +472,13 @@ const char *strcache_add_len (const char *str, unsigned int len);
int strcache_setbufsize (unsigned int size);
/* 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>

73
read.c
View file

@ -595,9 +595,8 @@ eval (struct ebuffer *ebuf, int set_default)
when the start of the next rule (or eof) is encountered.
When 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 ();
}

View file

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

View file

@ -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);

View file

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

View file

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

View file

@ -5,6 +5,20 @@ $description = 'Test the $(guile ...) function.';
$details = 'This only works on systems that support it.';
# 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