Add memory allocation cleanup to loadable objects.

Add gmk_alloc() and gmk_free() functions so loadable objects can access our
memory model.  Also provide a more extensive example in the manual.
This commit is contained in:
Paul Smith 2013-05-04 17:38:53 -04:00
parent a0c5d0c63f
commit 3484c9675a
7 changed files with 159 additions and 20 deletions

View File

@ -1,5 +1,12 @@
2013-05-04 Paul Smith <psmith@gnu.org> 2013-05-04 Paul Smith <psmith@gnu.org>
* loadapi.c (gmk_alloc): New function.
* gnumake.h: Add gmk_alloc(). Clean GMK_EXPORT a bit to avoid MAIN.
* makeint.h (GMK_EXPORT): New handling, vs. MAIN.
* doc/make.texi (Loaded Object API): Add information on the memory
handling functions.
(Loaded Object Example): Create an example.
* job.c (pump_from_tmp): (Rename) Write to stdout/stderr using * job.c (pump_from_tmp): (Rename) Write to stdout/stderr using
FILE* rather than fd. It's not a good idea to mix and match. FILE* rather than fd. It's not a good idea to mix and match.

View File

@ -354,6 +354,7 @@ Loading Dynamic Objects
* load Directive:: Loading dynamic objects as extensions. * load Directive:: Loading dynamic objects as extensions.
* 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
@end detailmenu @end detailmenu
@end menu @end menu
@ -10892,6 +10893,7 @@ for example, and the ``setup'' function would register them with GNU
* load Directive:: Loading dynamic objects as extensions. * load Directive:: Loading dynamic objects as extensions.
* 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
@end menu @end menu
@node load Directive, Remaking Loaded Objects, Loading Objects, Loading Objects @node load Directive, Remaking Loaded Objects, Loading Objects, Loading Objects
@ -10992,7 +10994,7 @@ support this.@refill
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 loaded object. rebuilding the loaded object.
@node Loaded Object API, , 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
@ -11009,8 +11011,8 @@ implementations in future versions of GNU @code{make}.
To be useful, loaded objects must be able to interact with GNU To be useful, loaded objects must be able to interact with GNU
@code{make}. This interaction includes both interfaces the loaded @code{make}. This interaction includes both interfaces the loaded
object provides to makefiles and also interfaces the loaded object can object provides to makefiles and also interfaces @code{make} provides
use to manipulate @code{make}'s operation. 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
@ -11021,7 +11023,8 @@ Typically, a loaded object will register one or more new GNU
@code{make} functions using the @code{gmk_add_function} routine from @code{make} functions using the @code{gmk_add_function} routine from
within its setup function. The implementations of these @code{make} within its setup function. The implementations of these @code{make}
functions may make use of the @code{gmk_expand} and @code{gmk_eval} functions may make use of the @code{gmk_expand} and @code{gmk_eval}
routines to perform their tasks. routines to perform their tasks, then optionally return a string as
the result of the function expansion.
@subsubheading Data Structures @subsubheading Data Structures
@ -11033,6 +11036,7 @@ where the definition occurred if necessary.
@end table @end table
@subsubheading Registering Functions @subsubheading Registering Functions
@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 loaded object: through the @code{make} function call by the loaded object: through the @code{make} function call
@ -11087,15 +11091,18 @@ works with.
@table @code @table @code
@item gmk_expand @item 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. The result of the expansion is returned in a string expansion rules. The result of the expansion is returned in a
that has been allocated using @code{malloc}. The caller is nil-terminated string buffer. The caller is responsible for calling
responsible for calling @code{free} on the string when done. @code{gmk_free} with a pointer to the returned buffer when done.
@item gmk_eval @item 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. This function can be used to define new variables, new rules, syntax. This function can be used to define new variables, new rules,
etc. It is equivalent to using the @code{eval} @code{make} function. etc. It is equivalent to using the @code{eval} @code{make} function.
@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
@ -11103,8 +11110,108 @@ the latter case the string will be expanded @emph{twice}; once by
@code{gmk_expand} and then again by the @code{eval} function. Using @code{gmk_expand} and then again by the @code{eval} function. Using
@code{gmk_eval} the buffer is only expanded once, at most (as it's @code{gmk_eval} the buffer is only expanded once, at most (as it's
read by the @code{make} parser). read by the @code{make} parser).
@subsubheading Memory Management
Some systems allow for different memory management schemes. Thus you
should never pass memory that you've allocated directly to any
@code{make} function, nor should you attempt to directly free any
memory returned to you by any @code{make} function. Instead, use the
@code{gmk_alloc} and @code{gmk_free} functions.
@table @code
@item gmk_alloc
@findex gmk_alloc
Return a pointer to a newly-allocated buffer. This function will
always return a valid pointer; if not enough memory is available
@code{make} will exit.
@item gmk_free
@findex gmk_free
Free a buffer returned to you by @code{make}. Once the
@code{gmk_free} function returns the string will no longer be valid.
@end table @end table
@node Loaded Object Example, , Loaded Object API, Loading Objects
@subsection Example Loaded Object
@cindex loaded object example
@cindex example of loaded objects
Let's suppose we wanted to write a new GNU @code{make} function that
would create a temporary file and return its name. We would like our
function to take a prefix as an argument. First we can write the
function in a file @file{mk_temp.c}:
@example
@group
#include <stdlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <gnumake.h>
char *
gen_tmpfile(const char *nm, int argc, char **argv)
@{
int fd;
/* Compute the size of the filename and allocate space for it. */
int len = strlen (argv[0]) + 6 + 1;
char *buf = gmk_alloc (len);
strcpy (buf, argv[0]);
strcat (buf, "XXXXXX");
fd = mkstemp(buf);
if (fd >= 0)
@{
/* Don't leak the file descriptor. */
close (fd);
return buf;
@}
/* Failure. */
fprintf (stderr, "mkstemp(%s) failed: %s\n", buf, strerror (errno));
gmk_free (buf);
return NULL;
@}
int
mk_temp_gmk_setup ()
@{
/* Register the function with make name "mk-temp". */
gmk_add_function ("mk-temp", gen_tmpfile, 1, 1, 1);
return 1;
@}
@end group
@end example
Next, we will write a makefile that can build this shared object, load
it, and use it:
@example
@group
all:
@@echo Temporary file: $(mk-temp tmpfile.)
load mk_temp.so
mk_temp.so: mk_temp.c
$(CC) -shared -fPIC -o $@ $<
@end group
@end example
Now when you run @code{make} you'll see something like:
@example
$ make
cc -shared -fPIC -o mk_temp.so mk_temp.c
Temporary filename: tmpfile.A7JEwd
@end example
@node Features, Missing, Extending make, Top @node Features, Missing, Extending make, Top
@chapter Features of GNU @code{make} @chapter Features of GNU @code{make}
@cindex features of GNU @code{make} @cindex features of GNU @code{make}

View File

@ -26,22 +26,26 @@ typedef struct
unsigned long lineno; unsigned long lineno;
} gmk_floc; } gmk_floc;
#ifdef _WIN32 #ifdef _WIN32
# ifdef MAIN # ifndef GMK_EXPORT
# define GMK_EXPORT __declspec(dllexport)
# else
# define GMK_EXPORT __declspec(dllimport) # define GMK_EXPORT __declspec(dllimport)
# endif # endif
#else #else
# define GMK_EXPORT # define GMK_EXPORT
#endif #endif
/* Free memory returned by the gmk_expand() function. */
void GMK_EXPORT gmk_free (char *str);
/* Allocate memory in GNU make's context. */
char * GMK_EXPORT gmk_alloc (unsigned int len);
/* Run $(eval ...) on the provided string BUFFER. */ /* Run $(eval ...) on the provided string BUFFER. */
void GMK_EXPORT gmk_eval (const char *buffer, const gmk_floc *floc); void GMK_EXPORT gmk_eval (const char *buffer, const gmk_floc *floc);
/* Run GNU make expansion on the provided string STR. /* Run GNU make expansion on the provided string STR.
Returns an allocated buffer that the caller must free. */ Returns an allocated buffer that the caller must free with gmk_free(). */
char * GMK_EXPORT gmk_expand (const char *str); char * GMK_EXPORT gmk_expand (const char *str);
/* Register a new GNU make function NAME (maximum of 255 chars long). /* Register a new GNU make function NAME (maximum of 255 chars long).
@ -50,7 +54,7 @@ char * GMK_EXPORT gmk_expand (const char *str);
The return value of FUNC must be either NULL, in which case it expands to The return value of FUNC must be either NULL, in which case it expands to
the empty string, or a pointer to the result of the expansion in a string the empty string, or a pointer to the result of the expansion in a string
created by malloc(). GNU make will free() the memory when it's done. created by gmk_alloc(). GNU make will free the memory when it's done.
MIN_ARGS is the minimum number of arguments the function requires. MIN_ARGS is the minimum number of arguments the function requires.
MAX_ARGS is the maximum number of arguments (or 0 if there's no maximum). MAX_ARGS is the maximum number of arguments (or 0 if there's no maximum).

View File

@ -20,6 +20,20 @@ this program. If not, see <http://www.gnu.org/licenses/>. */
#include "variable.h" #include "variable.h"
#include "dep.h" #include "dep.h"
/* Allocate a buffer in our context, so we can free it. */
char *
gmk_alloc (unsigned int len)
{
return xmalloc (len);
}
/* Free a buffer returned by gmk_expand(). */
void
gmk_free (char *s)
{
free (s);
}
/* Evaluate a buffer as make syntax. /* Evaluate a buffer as make syntax.
Ideally eval_buffer() will take const char *, but not yet. */ Ideally eval_buffer() will take const char *, but not yet. */
void void
@ -31,7 +45,7 @@ gmk_eval (const char *buffer, const gmk_floc *floc)
} }
/* Expand a string and return an allocated buffer. /* Expand a string and return an allocated buffer.
Caller must free() this buffer. */ Caller must call gmk_free() with this buffer. */
char * char *
gmk_expand (const char *ref) gmk_expand (const char *ref)
{ {

View File

@ -49,11 +49,12 @@ char *alloca ();
/* Include the externally-visible content. /* Include the externally-visible content.
Be sure to use the local one, and not one installed on the system. Be sure to use the local one, and not one installed on the system.
Define MAIN for proper selection of dllexport/dllimport declarations Define GMK_EXPORT for proper selection of dllexport/dllimport declarations
for MS-Windows. */ for MS-Windows. */
#define MAIN #ifdef WINDOWS32
# define GMK_EXPORT __declspec(dllexport)
#endif
#include "gnumake.h" #include "gnumake.h"
#undef MAIN
#ifdef CRAY #ifdef CRAY
/* This must happen before #include <signal.h> so /* This must happen before #include <signal.h> so

View File

@ -1,5 +1,7 @@
2013-05-04 Paul Smith <psmith@gnu.org> 2013-05-04 Paul Smith <psmith@gnu.org>
* scripts/features/loadapi: Use the new alloc functions.
* scripts/features/output-sync (output_sync_set): New test for * scripts/features/output-sync (output_sync_set): New test for
ordered recursive output for -Ojob / -Otarget. ordered recursive output for -Ojob / -Otarget.

View File

@ -36,13 +36,17 @@ test_expand (const char *val)
static char * static char *
func_test (const char *funcname, int argc, char **argv) func_test (const char *funcname, int argc, char **argv)
{ {
char *mem;
if (strcmp (funcname, "test-expand") == 0) if (strcmp (funcname, "test-expand") == 0)
return test_expand (argv[0]); return test_expand (argv[0]);
if (strcmp (funcname, "test-eval") == 0) if (strcmp (funcname, "test-eval") == 0)
return test_eval (argv[0]); return test_eval (argv[0]);
return strdup ("unknown"); mem = gmk_alloc (strlen ("unknown") + 1);
strcpy (mem, "unknown");
return mem;
} }
int int