Remove the "preview" status from the loaded object feature

Add an ABI version both to the header file and passed to the setup
function.  Unfortunately this itself is an ABI break and I couldn't
find a good way to avoid it.

* NEWS: Announce the ABI is not a preview and the incompatibility.
* doc/make.texi: Remove the preview warnings for object loading.
Document the new ABI version argument.
* src/gnumake.h (GMK_ABI_VERSION): Set the ABI version to 1.
Add comments documenting the format of the setup function.
* src/load.c (setup_func_t): Rename from load_func_t.
(load_file): Pass the ABI version to the setup function.
* tests/scripts/features/load: Rework the setup function.
* tests/scripts/features/loadapi: Ditto.
This commit is contained in:
Paul Smith 2023-05-06 16:21:39 -04:00
parent 3f28ec2f58
commit 8e0e6c678f
7 changed files with 234 additions and 203 deletions

9
NEWS
View File

@ -20,6 +20,15 @@ https://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=111&se
This version of GNU Make no longer supports AmigaOS. If you need support This version of GNU Make no longer supports AmigaOS. If you need support
for AmigaOS please use one of the older versions of GNU Make. for AmigaOS please use one of the older versions of GNU Make.
* WARNING: Loaded Object ABI incompatibility!
This release changes the loaded object feature from "technology preview" to
fully-supported feature. However, it introduces an ABI incompatibility with
previous releases: the setup function now takes an ABI version as its first
argument. When compiling your loaded object you can test the GMK_ABI_VERSION
constant at compile time to detect which ABI should be used. At runtime
your initialization function can check the provided ABI version to verify
it's being loaded correctly.
* New feature: Makefile warning reporting control * New feature: Makefile warning reporting control
A new option "--warn" controls reporting of warnings for makefiles. Actions A new option "--warn" controls reporting of warnings for makefiles. Actions
can be set to "ignore", "warn", or "error". Two new warnings are reported: can be set to "ignore", "warn", or "error". Two new warnings are reported:

View File

@ -366,6 +366,7 @@ GNU Guile Integration
Loading Dynamic Objects Loading Dynamic Objects
* load Directive:: Loading dynamic objects as extensions. * load Directive:: Loading dynamic objects as extensions.
* Initializing Functions:: How initializing functions are called.
* Remaking Loaded Objects:: How loaded objects get remade. * Remaking Loaded Objects:: How loaded objects get remade.
* Loaded Object API:: Programmatic interface for loaded objects. * Loaded Object API:: Programmatic interface for loaded objects.
* Loaded Object Example:: Example of a loaded object * Loaded Object Example:: Example of a loaded object
@ -11936,37 +11937,27 @@ symbol to be stored in a @code{make} variable.
@cindex objects, loaded @cindex objects, loaded
@cindex extensions, loading @cindex extensions, loading
@cartouche Many operating systems provide a facility for dynamically loading compiled
@quotation Warning objects. If your system provides this facility, GNU @code{make} can make use
The @code{load} directive and extension capability is considered a of it to load dynamic objects at runtime, providing new capabilities which may
``technology preview'' in this release of GNU Make. We encourage you then be invoked by your makefile.
to experiment with this feature and we appreciate any feedback on it.
However we cannot guarantee to maintain backward-compatibility in the
next release. Consider using GNU Guile instead for extending GNU Make
(@pxref{Guile Function, ,The @code{guile} Function}).
@end quotation
@end cartouche
Many operating systems provide a facility for dynamically loading The @code{load} makefile directive is used to load a dynamic object. Once the
compiled objects. If your system provides this facility, GNU object is loaded, an initializing function will be invoked to allow the object
@code{make} can make use of it to load dynamic objects at runtime, to initialize itself and register new facilities with GNU @code{make}. A
providing new capabilities which may then be invoked by your makefile. dynamic object might include new @code{make} functions, for example, and the
initializing function would register them with GNU @code{make}'s function
The @code{load} directive is used to load a dynamic object. Once the handling system.
object is loaded, a ``setup'' function will be invoked to allow the
object to initialize itself and register new facilities with GNU
@code{make}. A dynamic object might include new @code{make} functions,
for example, and the ``setup'' function would register them with GNU
@code{make}'s function handling system.
@menu @menu
* load Directive:: Loading dynamic objects as extensions. * load Directive:: Loading dynamic objects as extensions.
* Initializing Functions:: How initializing functions are called.
* Remaking Loaded Objects:: How loaded objects get remade. * Remaking Loaded Objects:: How loaded objects get remade.
* Loaded Object API:: Programmatic interface for loaded objects. * Loaded Object API:: Programmatic interface for loaded objects.
* Loaded Object Example:: Example of a loaded object * Loaded Object Example:: Example of a loaded object
@end menu @end menu
@node load Directive, Remaking Loaded Objects, Loading Objects, Loading Objects @node load Directive, Initializing Functions, Loading Objects, Loading Objects
@subsection The @code{load} Directive @subsection The @code{load} Directive
@cindex load directive @cindex load directive
@cindex extensions, load directive @cindex extensions, load directive
@ -11986,34 +11977,24 @@ or:
load @var{object-file}(@var{symbol-name}) @dots{} load @var{object-file}(@var{symbol-name}) @dots{}
@end example @end example
The file @var{object-file} is dynamically loaded by GNU @code{make}. More than one object file may be loaded with a single @code{load} directive,
If @var{object-file} does not include a directory path then it is and both forms of @code{load} arguments may be used in the same directive.
first looked for in the current directory. If it is not found there,
or a directory path is included, then system-specific paths will be
searched. If the load fails for any reason, @code{make} will print a
message and exit.
If the load succeeds @code{make} will invoke an initializing function. The file @var{object-file} is dynamically loaded by GNU @code{make}. If
@var{object-file} does not include a directory path then it is first looked
for in the current directory. If it is not found there, or a directory path
is included, then system-specific paths will be searched. If the load fails
for any reason, @code{make} will print a message and exit.
If @var{symbol-name} is provided, it will be used as the name of the If the load succeeds @code{make} will invoke an initializing function. If
initializing function. @var{symbol-name} is provided, it will be used as the name of the initializing
function.
If no @var{symbol-name} is provided, the initializing function name is If no @var{symbol-name} is provided, the initializing function name is created
created by taking the base file name of @var{object-file}, up to the by taking the base file name of @var{object-file}, up to the first character
first character which is not a valid symbol name character which is not a valid symbol name character (alphanumerics and underscores are
(alphanumerics and underscores are valid symbol name characters). To valid symbol name characters). To this prefix will be appended the suffix
this prefix will be appended the suffix @code{_gmk_setup}. @code{_gmk_setup}.
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.
The initializing function will be provided the file name and line
number of the invocation of the @code{load} operation. It should
return a value of type @code{int}, which must be @code{0} on failure
and non-@code{0} on success. If the return value is @code{-1}, then
GNU Make will @emph{not} attempt to rebuild the object file
(@pxref{Remaking Loaded Objects, ,How Loaded Objects Are Remade}).
For example: For example:
@ -12021,9 +12002,9 @@ For example:
load ../mk_funcs.so load ../mk_funcs.so
@end example @end example
will load the dynamic object @file{../mk_funcs.so}. After the object will load the dynamic object @file{../mk_funcs.so}. After the object is
is loaded, @code{make} will invoke the function (assumed to be defined loaded, @code{make} will invoke the initializing function (assumed to be
by the shared object) @code{mk_funcs_gmk_setup}. defined by the shared object) @code{mk_funcs_gmk_setup}.
On the other hand: On the other hand:
@ -12031,86 +12012,96 @@ On the other hand:
load ../mk_funcs.so(init_mk_func) load ../mk_funcs.so(init_mk_func)
@end example @end example
will load the dynamic object @file{../mk_funcs.so}. After the object will load the dynamic object @file{../mk_funcs.so}. After the object is
is loaded, @code{make} will invoke the function @code{init_mk_func}. loaded, @code{make} will invoke the initializing function @code{init_mk_func}.
Regardless of how many times an object file appears in a @code{load} Regardless of how many times an object file appears in a @code{load}
directive, it will only be loaded (and its setup function will only directive, it will only be loaded (and its setup function will only be
be invoked) once. invoked) once.
@vindex .LOADED @vindex .LOADED
After an object has been successfully loaded, its file name is After an object has been successfully loaded, its file name is appended to the
appended to the @code{.LOADED} variable. @code{.LOADED} variable.
@findex -load @findex -load
If you would prefer that failure to load a dynamic object not be If you would prefer that failure to load a dynamic object not be reported as
reported as an error, you can use the @code{-load} directive instead an error, you can use the @code{-load} directive instead of @code{load}. GNU
of @code{load}. GNU @code{make} will not fail and no message will be @code{make} will not fail and no message will be generated if an object fails
generated if an object fails to load. The failed object is not added to load. The failed object is not added to the @code{.LOADED} variable, which
to the @code{.LOADED} variable, which can then be consulted to can then be consulted to determine if the load was successful.
determine if the load was successful.
@node Remaking Loaded Objects, Loaded Object API, load Directive, Loading Objects @node Initializing Functions, Remaking Loaded Objects, load Directive, Loading Objects
@subsection Initializing Functions
@cindex loaded object initializing function
@cindex initializing function, for loaded objects
The initializing function defined by the loaded object must have this
signature:
@example
int <name> (unsigned int abi_version, const gmk_floc *floc);
@end example
Where @emph{<name>} is described in the previous section.
The @code{abi_version} value will be the value of the @code{GMK_ABI_VERSION}
constant (see the @file{gnumake.h} file) for this GNU Make release. The
@code{floc} pointer provides the file name and line number of the invocation
of the @code{load} operation.
The initializing function should return an @code{int}, which must be @code{0}
on failure and non-@code{0} on success. If the return value is @code{-1},
then GNU Make will @emph{not} attempt to rebuild the object file
(@pxref{Remaking Loaded Objects, ,How Loaded Objects Are Remade}).
@node Remaking Loaded Objects, Loaded Object API, Initializing Functions, Loading Objects
@subsection How Loaded Objects Are Remade @subsection How Loaded Objects Are Remade
@cindex updating loaded objects @cindex updating loaded objects
@cindex remaking loaded objects @cindex remaking loaded objects
@cindex loaded objects, remaking of @cindex loaded objects, remaking of
Loaded objects undergo the same re-make procedure as makefiles Loaded objects undergo the same re-make procedure as makefiles
(@pxref{Remaking Makefiles, ,How Makefiles Are Remade}). If any (@pxref{Remaking Makefiles, ,How Makefiles Are Remade}). If any loaded object
loaded object is recreated, then @code{make} will start from scratch is recreated, then @code{make} will start from scratch and re-read all the
and re-read all the makefiles, and reload the object files again. It makefiles, and reload the object files again. It is not necessary for the
is not necessary for the loaded object to do anything special to loaded object to do anything special to support this.
support this.
It's up to the makefile author to provide the rules needed for It's up to the makefile author to provide the rules needed for rebuilding the
rebuilding the loaded object. loaded object.
@node Loaded Object API, Loaded Object Example, Remaking Loaded Objects, Loading Objects @node Loaded Object API, Loaded Object Example, Remaking Loaded Objects, Loading Objects
@subsection Loaded Object Interface @subsection Loaded Object Interface
@cindex loaded object API @cindex loaded object API
@cindex interface for loaded objects @cindex interface for loaded objects
@cartouche To be useful, loaded objects must be able to interact with GNU @code{make}.
@quotation Warning This interaction includes both interfaces the loaded object provides to
For this feature to be useful your extensions will need to invoke makefiles and also interfaces @code{make} provides to the loaded object to
various functions internal to GNU @code{make}. The programming manipulate @code{make}'s operation.
interfaces provided in this release should not be considered stable:
functions may be added, removed, or change calling signatures or
implementations in future versions of GNU @code{make}.
@end quotation
@end cartouche
To be useful, loaded objects must be able to interact with GNU
@code{make}. This interaction includes both interfaces the loaded
object provides to makefiles and also interfaces @code{make} provides
to the loaded object to manipulate @code{make}'s operation.
The interface between loaded objects and @code{make} is defined by the The interface between loaded objects and @code{make} is defined by the
@file{gnumake.h} C header file. All loaded objects written in C @file{gnumake.h} C header file. All loaded objects written in C should
should include this header file. Any loaded object not written in C include this header file. Any loaded object not written in C will need to
will need to implement the interface defined in this header file. implement the interface defined in this header file.
Typically, a loaded object will register one or more new GNU Typically, a loaded object will register one or more new GNU @code{make}
@code{make} functions using the @code{gmk_add_function} routine from functions using the @code{gmk_add_function} routine from within its setup
within its setup function. The implementations of these @code{make} function. The implementations of these @code{make} functions may make use of
functions may make use of the @code{gmk_expand} and @code{gmk_eval} the @code{gmk_expand} and @code{gmk_eval} routines to perform their tasks,
routines to perform their tasks, then optionally return a string as then optionally return a string as the result of the function expansion.
the result of the function expansion.
@subsubheading Loaded Object Licensing @subsubheading Loaded Object Licensing
@cindex loaded object licensing @cindex loaded object licensing
@cindex plugin_is_GPL_compatible @cindex plugin_is_GPL_compatible
Every dynamic extension should define the global symbol Every dynamic extension should define the global symbol
@code{plugin_is_GPL_compatible} to assert that it has been licensed @code{plugin_is_GPL_compatible} to assert that it has been licensed under a
under a GPL-compatible license. If this symbol does not exist, GPL-compatible license. If this symbol does not exist, @code{make} emits a
@code{make} emits a fatal error and exits when it tries to load your fatal error and exits when it tries to load your extension.
extension.
The declared type of the symbol should be @code{int}. It does not need The declared type of the symbol should be @code{int}. It does not need to be
to be in any allocated section, though. The code merely asserts that in any allocated section, though. The code merely asserts that the symbol
the symbol exists in the global scope. Something like this is enough: exists in the global scope. Something like this is enough:
@example @example
int plugin_is_GPL_compatible; int plugin_is_GPL_compatible;
@ -12120,19 +12111,44 @@ int plugin_is_GPL_compatible;
@table @code @table @code
@item gmk_floc @item gmk_floc
This structure represents a filename/location pair. It is provided This structure represents a filename/location pair. It is provided when
when defining items, so GNU @code{make} can inform the user later defining items, so GNU @code{make} can inform the user where the definition
where the definition occurred if necessary. occurred if necessary.
@end table @end table
@subsubheading Checking Versions
@findex gmk_get_version
The @code{gmk_get_version} allows loaded objects to check which loaded object
API version is supported by GNU Make. The API version is specified as two
values: the @emph{major} version and the @emph{minor} version. Note, these
two values are not the same as the version of GNU Make!
The @emph{major} version is incremented when there is a change to the loaded
object ABI, which might cause .
It is called as:
@example
void gmk_get_version (unsigned int *major, unsigned int *minor);
@end example
@table @code
@item major
If not NULL, the major version number is placed here.
@item minor
If not NULL, the minor version number is placed here.
@end table
@subsubheading Registering Functions @subsubheading Registering Functions
@findex gmk_add_function @findex gmk_add_function
There is currently one way for makefiles to invoke operations provided There is currently one way for makefiles to invoke operations provided by the
by the loaded object: through the @code{make} function call loaded object: through the @code{make} function call interface. A loaded
interface. A loaded object can register one or more new functions object can register one or more new functions which may then be invoked from
which may then be invoked from within the makefile in the same way as within the makefile in the same way as any other function.
any other function.
Use @code{gmk_add_function} to create a new @code{make} function. Its Use @code{gmk_add_function} to create a new @code{make} function. Its
arguments are as follows: arguments are as follows:
@ -12140,109 +12156,101 @@ arguments are as follows:
@table @code @table @code
@item name @item name
The function name. This is what the makefile should use to invoke the The function name. This is what the makefile should use to invoke the
function. The name must be between 1 and 255 characters long and it function. The name must be between 1 and 255 characters long and it may only
may only contain alphanumeric, period (@samp{.}), dash (@samp{-}), and contain alphanumeric, period (@samp{.}), dash (@samp{-}), and underscore
underscore (@samp{_}) characters. It may not begin with a period. (@samp{_}) characters. It may not begin with a period.
@item func_ptr @item func_ptr
A pointer to a function that @code{make} will invoke when it expands A pointer to a function that @code{make} will invoke when it expands the
the function in a makefile. This function must be defined by the function in a makefile. This function must be defined by the loaded object.
loaded object.
@item min_args @item min_args
The minimum number of arguments the function will accept. Must be The minimum number of arguments the function will accept. Must be between 0
between 0 and 255. GNU @code{make} will check this and fail before and 255. GNU @code{make} will check this and fail before invoking
invoking @code{func_ptr} if the function was invoked with too few @code{func_ptr} if the function was invoked with too few arguments.
arguments.
@item max_args @item max_args
The maximum number of arguments the function will accept. Must be The maximum number of arguments the function will accept. Must be between 0
between 0 and 255. GNU @code{make} will check this and fail before and 255. GNU @code{make} will check this and fail before invoking
invoking @code{func_ptr} if the function was invoked with too many @code{func_ptr} if the function was invoked with too many arguments. If the
arguments. If the value is 0, then any number of arguments is value is 0, then any number of arguments is accepted. If the value is greater
accepted. If the value is greater than 0, then it must be greater than 0, then it must be greater than or equal to @code{min_args}.
than or equal to @code{min_args}.
@item flags @item flags
Flags that specify how this function will operate; the desired flags Flags that specify how this function will operate; the desired flags should be
should be OR'd together. If the @code{GMK_FUNC_NOEXPAND} flag is OR'd together. If the @code{GMK_FUNC_NOEXPAND} flag is given then the
given then the function arguments will not be expanded before the function arguments will not be expanded before the function is called;
function is called; otherwise they will be expanded first. otherwise they will be expanded first.
@end table @end table
@subsubheading Registered Function Interface @subsubheading Registered Function Interface
@findex gmk_func_ptr @findex gmk_func_ptr
A function registered with @code{make} must match the A function registered with @code{make} must match the @code{gmk_func_ptr}
@code{gmk_func_ptr} type. It will be invoked with three parameters: type. It will be invoked with three parameters: @code{name} (the name of the
@code{name} (the name of the function), @code{argc} (the number of function), @code{argc} (the number of arguments to the function), and
arguments to the function), and @code{argv} (an array of pointers to @code{argv} (an array of pointers to arguments to the function). The last
arguments to the function). The last pointer (that is, pointer (that is, @code{argv[argc]}) will be null (@code{0}).
@code{argv[argc]}) will be null (@code{0}).
The return value of the function is the result of expanding the The return value of the function is the result of expanding the function. If
function. If the function expands to nothing the return value may be the function expands to nothing the return value may be null. Otherwise, it
null. Otherwise, it must be a pointer to a string created with must be a pointer to a string created with @code{gmk_alloc}. Once the
@code{gmk_alloc}. Once the function returns, @code{make} owns this function returns, @code{make} owns this string and will free it when
string and will free it when appropriate; it cannot be accessed by the appropriate; it cannot be accessed by the loaded object.
loaded object.
@subsubheading GNU @code{make} Facilities @subsubheading GNU @code{make} Facilities
There are some facilities exported by GNU @code{make} for use by There are some facilities exported by GNU @code{make} for use by loaded
loaded objects. Typically these would be run from within the objects. Typically these would be run from within the setup function and/or
setup function and/or the functions registered via the functions registered via @code{gmk_add_function}, to retrieve or modify
@code{gmk_add_function}, to retrieve or modify the data @code{make} the data @code{make} works with.
works with.
@table @code @table @code
@item gmk_expand @item gmk_expand
@findex gmk_expand @findex gmk_expand
This function takes a string and expands it using @code{make} This function takes a string and expands it using @code{make} expansion rules.
expansion rules. The result of the expansion is returned in a The result of the expansion is returned in a nil-terminated string buffer.
nil-terminated string buffer. The caller is responsible for calling The caller is responsible for calling @code{gmk_free} with a pointer to the
@code{gmk_free} with a pointer to the returned buffer when done. returned buffer when done.
@item gmk_eval @item gmk_eval
@findex gmk_eval @findex gmk_eval
This function takes a buffer and evaluates it as a segment of makefile This function takes a buffer and evaluates it as a segment of makefile syntax.
syntax. This function can be used to define new variables, new rules, This function can be used to define new variables, new rules, etc. It is
etc. It is equivalent to using the @code{eval} @code{make} function. equivalent to using the @code{eval} @code{make} function.
@end table @end table
Note that there is a difference between @code{gmk_eval} and calling Note that there is a difference between @code{gmk_eval} and calling
@code{gmk_expand} with a string using the @code{eval} function: in @code{gmk_expand} with a string using the @code{eval} function: in the latter
the latter case the string will be expanded @emph{twice}; once by case the string will be expanded @emph{twice}; once by @code{gmk_expand} and
@code{gmk_expand} and then again by the @code{eval} function. Using then again by the @code{eval} function. Using @code{gmk_eval} the buffer is
@code{gmk_eval} the buffer is only expanded once, at most (as it's only expanded once, at most (as it's read by the @code{make} parser).
read by the @code{make} parser).
@subsubheading Memory Management @subsubheading Memory Management
Some systems allow for different memory management schemes. Thus you Some systems allow for different memory management schemes. Thus you should
should never pass memory that you've allocated directly to any never pass memory that you've allocated directly to any @code{make} function,
@code{make} function, nor should you attempt to directly free any nor should you attempt to directly free any memory returned to you by any
memory returned to you by any @code{make} function. Instead, use the @code{make} function. Instead, use the @code{gmk_alloc} and @code{gmk_free}
@code{gmk_alloc} and @code{gmk_free} functions. functions.
In particular, the string returned to @code{make} by a function In particular, the string returned to @code{make} by a function registered
registered using @code{gmk_add_function} @emph{must} be allocated using @code{gmk_add_function} @emph{must} be allocated using @code{gmk_alloc},
using @code{gmk_alloc}, and the string returned from the @code{make} and the string returned from the @code{make} @code{gmk_expand} function
@code{gmk_expand} function @emph{must} be freed (when no longer @emph{must} be freed (when no longer needed) using @code{gmk_free}.
needed) using @code{gmk_free}.
@table @code @table @code
@item gmk_alloc @item gmk_alloc
@findex gmk_alloc @findex gmk_alloc
Return a pointer to a newly-allocated buffer. This function will Return a pointer to a newly-allocated buffer. This function will always
always return a valid pointer; if not enough memory is available return a valid pointer; if not enough memory is available @code{make} will
@code{make} will exit. @code{gmk_alloc} does not initialize allocated memory. exit. @code{gmk_alloc} does not initialize allocated memory.
@item gmk_free @item gmk_free
@findex gmk_free @findex gmk_free
Free a buffer returned to you by @code{make}. Once the Free a buffer returned to you by @code{make}. Once the @code{gmk_free}
@code{gmk_free} function returns the string will no longer be valid. function returns the string will no longer be valid. If NULL is passed to
If NULL is passed to @code{gmk_free}, no operation is performed. @code{gmk_free}, no operation is performed.
@end table @end table
@node Loaded Object Example, , Loaded Object API, Loading Objects @node Loaded Object Example, , Loaded Object API, Loading Objects
@ -12250,10 +12258,10 @@ If NULL is passed to @code{gmk_free}, no operation is performed.
@cindex loaded object example @cindex loaded object example
@cindex example of loaded objects @cindex example of loaded objects
Let's suppose we wanted to write a new GNU @code{make} function that Let's suppose we wanted to write a new GNU @code{make} function that would
would create a temporary file and return its name. We would like our create a temporary file and return its name. We would like our function to
function to take a prefix as an argument. First we can write the take a prefix as an argument. First we can write the function in a file
function in a file @file{mk_temp.c}: @file{mk_temp.c}:
@example @example
@group @group
@ -12294,9 +12302,10 @@ gen_tmpfile(const char *nm, int argc, char **argv)
@} @}
int int
mk_temp_gmk_setup (const gmk_floc *floc) mk_temp_gmk_setup (unsigned int abi, const gmk_floc *floc)
@{ @{
printf ("mk_temp plugin loaded from %s:%lu\n", floc->filenm, floc->lineno); printf ("mk_temp abi %u plugin loaded from %s:%lu\n",
abi, floc->filenm, floc->lineno);
/* Register the function with make name "mk-temp". */ /* Register the function with make name "mk-temp". */
gmk_add_function ("mk-temp", gen_tmpfile, 1, 1, 1); gmk_add_function ("mk-temp", gen_tmpfile, 1, 1, 1);
return 1; return 1;
@ -12319,12 +12328,12 @@ mk_temp.so: mk_temp.c
@end group @end group
@end example @end example
On MS-Windows, due to peculiarities of how shared objects are On MS-Windows, due to peculiarities of how shared objects are produced, the
produced, the compiler needs to scan the @dfn{import library} produced compiler needs to scan the @dfn{import library} produced when building
when building @code{make}, typically called @code{make}, typically called @file{libgnumake-@var{version}.dll.a}, where
@file{libgnumake-@var{version}.dll.a}, where @var{version} is the @var{version} is the version of the load object API. So the recipe to produce
version of the load object API. So the recipe to produce a shared a shared object will look on Windows like this (assuming the API version is
object will look on Windows like this (assuming the API version is 1): 1):
@example @example
@group @group
@ -12337,7 +12346,7 @@ Now when you run @code{make} you'll see something like:
@example @example
$ make $ make
mk_temp plugin loaded from Makefile:4 mk_temp abi 1 plugin loaded from Makefile:4
cc -shared -fPIC -o mk_temp.so mk_temp.c cc -shared -fPIC -o mk_temp.so mk_temp.c
Temporary filename: tmpfile.A7JEwd Temporary filename: tmpfile.A7JEwd
@end example @end example

View File

@ -1,5 +1,4 @@
/* External interfaces usable by dynamic objects loaded into GNU Make. /* External interfaces usable by dynamic objects loaded into GNU Make.
--THIS API IS A "TECHNOLOGY PREVIEW" ONLY. IT IS NOT A STABLE INTERFACE--
Copyright (C) 2013-2023 Free Software Foundation, Inc. Copyright (C) 2013-2023 Free Software Foundation, Inc.
This file is part of GNU Make. This file is part of GNU Make.
@ -19,6 +18,8 @@ this program. If not, see <https://www.gnu.org/licenses/>. */
#ifndef _GNUMAKE_H_ #ifndef _GNUMAKE_H_
#define _GNUMAKE_H_ #define _GNUMAKE_H_
#define GMK_ABI_VERSION 1
/* Specify the location of elements read from makefiles. */ /* Specify the location of elements read from makefiles. */
typedef struct typedef struct
{ {
@ -28,6 +29,14 @@ typedef struct
typedef char *(*gmk_func_ptr)(const char *nm, unsigned int argc, char **argv); typedef char *(*gmk_func_ptr)(const char *nm, unsigned int argc, char **argv);
/* When an object is loaded by GNU Make, a setup method will be invoked.
The name of the method is either derived from the filename of the object,
or specified explicitly in the makefile. It has the signature:
int <setup_fn> (unsigned int abi_version, const gmk_floc *flocp);
The abi_version will be set to GMK_ABI_VERSION. */
#ifdef _WIN32 #ifdef _WIN32
# ifdef GMK_BUILDING_MAKE # ifdef GMK_BUILDING_MAKE
# define GMK_EXPORT __declspec(dllexport) # define GMK_EXPORT __declspec(dllexport)
@ -38,7 +47,7 @@ typedef char *(*gmk_func_ptr)(const char *nm, unsigned int argc, char **argv);
# define GMK_EXPORT # define GMK_EXPORT
#endif #endif
/* Free memory returned by the gmk_expand() function. */ /* Free memory returned by the gmk_expand() and gmk_free() functions. */
GMK_EXPORT void gmk_free (char *str); GMK_EXPORT void gmk_free (char *str);
/* Allocate memory in GNU Make's context. */ /* Allocate memory in GNU Make's context. */

View File

@ -44,12 +44,14 @@ struct load_list
static struct load_list *loaded_syms = NULL; static struct load_list *loaded_syms = NULL;
static load_func_t typedef int (*setup_func_t)(unsigned int abi, const floc *flocp);
static setup_func_t
load_object (const floc *flocp, int noerror, const char *ldname, load_object (const floc *flocp, int noerror, const char *ldname,
const char *symname) const char *symname)
{ {
static void *global_dl = NULL; static void *global_dl = NULL;
load_func_t symp; setup_func_t symp;
if (! global_dl) if (! global_dl)
{ {
@ -61,7 +63,7 @@ load_object (const floc *flocp, int noerror, const char *ldname,
} }
} }
symp = (load_func_t) dlsym (global_dl, symname); symp = (setup_func_t) dlsym (global_dl, symname);
if (! symp) if (! symp)
{ {
struct load_list *new; struct load_list *new;
@ -93,13 +95,13 @@ load_object (const floc *flocp, int noerror, const char *ldname,
DB (DB_VERBOSE, (_("Loaded shared object %s\n"), ldname)); DB (DB_VERBOSE, (_("Loaded shared object %s\n"), ldname));
/* Assert that the GPL license symbol is defined. */ /* Assert that the GPL license symbol is defined. */
symp = (load_func_t) dlsym (dlp, "plugin_is_GPL_compatible"); symp = (setup_func_t) dlsym (dlp, "plugin_is_GPL_compatible");
if (! symp) if (! symp)
OS (fatal, flocp, OS (fatal, flocp,
_("loaded object %s is not declared to be GPL compatible"), _("loaded object %s is not declared to be GPL compatible"),
ldname); ldname);
symp = (load_func_t) dlsym (dlp, symname); symp = (setup_func_t) dlsym (dlp, symname);
if (! symp) if (! symp)
{ {
const char *err = dlerror (); const char *err = dlerror ();
@ -129,7 +131,7 @@ load_file (const floc *flocp, struct file *file, int noerror)
char *symname = NULL; char *symname = NULL;
const char *fp; const char *fp;
int r; int r;
load_func_t symp; setup_func_t symp;
/* Break the input into an object file name and a symbol name. If no symbol /* Break the input into an object file name and a symbol name. If no symbol
name was provided, compute one from the object file name. */ name was provided, compute one from the object file name. */
@ -210,8 +212,11 @@ load_file (const floc *flocp, struct file *file, int noerror)
if (! symp) if (! symp)
return 0; return 0;
/* Invoke the symbol. */ /* Invoke the setup function. */
r = (*symp) (flocp); {
unsigned int abi = GMK_ABI_VERSION;
r = (*symp) (abi, flocp);
}
/* If the load didn't fail, add the file to the .LOADED variable. */ /* If the load didn't fail, add the file to the .LOADED variable. */
if (r) if (r)

View File

@ -672,7 +672,6 @@ const char *strcache_add_len (const char *str, size_t len);
int guile_gmake_setup (const floc *flocp); int guile_gmake_setup (const floc *flocp);
/* Loadable object support. Sets to the strcached name of the loaded file. */ /* Loadable object support. Sets to the strcached name of the loaded file. */
typedef int (*load_func_t)(const floc *flocp);
int load_file (const floc *flocp, struct file *file, int noerror); int load_file (const floc *flocp, struct file *file, int noerror);
int unload_file (const char *name); int unload_file (const char *name);

View File

@ -25,11 +25,11 @@ char* getenv (const char*);
int plugin_is_GPL_compatible; int plugin_is_GPL_compatible;
int testload_gmk_setup (gmk_floc *); int testload_gmk_setup (unsigned int, gmk_floc *);
int explicit_setup (gmk_floc *); int explicit_setup (unsigned int, gmk_floc *);
int int
testload_gmk_setup (gmk_floc *pos) testload_gmk_setup (unsigned int abi, gmk_floc *pos)
{ {
(void)pos; (void)pos;
gmk_eval ("TESTLOAD = implicit", 0); gmk_eval ("TESTLOAD = implicit", 0);
@ -39,7 +39,7 @@ testload_gmk_setup (gmk_floc *pos)
} }
int int
explicit_setup (gmk_floc *pos) explicit_setup (unsigned int abi, gmk_floc *pos)
{ {
(void)pos; (void)pos;
gmk_eval ("TESTLOAD = explicit", 0); gmk_eval ("TESTLOAD = explicit", 0);

View File

@ -28,7 +28,7 @@ char *getenv (const char*);
int plugin_is_GPL_compatible; int plugin_is_GPL_compatible;
int testapi_gmk_setup (); int testapi_gmk_setup (unsigned int abi, const gmk_floc *floc);
static char * static char *
test_eval (const char *buf) test_eval (const char *buf)
@ -71,7 +71,7 @@ func_test (const char *funcname, unsigned int argc, char **argv)
} }
int int
testapi_gmk_setup (const gmk_floc *floc) testapi_gmk_setup (unsigned int abi, const gmk_floc *floc)
{ {
const char *verbose = getenv ("TESTAPI_VERBOSE"); const char *verbose = getenv ("TESTAPI_VERBOSE");