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>
* 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
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.
* Remaking Loaded Objects:: How loaded objects get remade.
* Loaded Object API:: Programmatic interface for loaded objects.
* Loaded Object Example:: Example of a loaded object
@end detailmenu
@end menu
@ -10892,6 +10893,7 @@ for example, and the ``setup'' function would register them with GNU
* load Directive:: Loading dynamic objects as extensions.
* Remaking Loaded Objects:: How loaded objects get remade.
* Loaded Object API:: Programmatic interface for loaded objects.
* Loaded Object Example:: Example of a loaded object
@end menu
@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
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
@cindex loaded object API
@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
@code{make}. This interaction includes both interfaces the loaded
object provides to makefiles and also interfaces the loaded object can
use to manipulate @code{make}'s operation.
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
@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
within its setup function. The implementations of these @code{make}
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
@ -11033,6 +11036,7 @@ where the definition occurred if necessary.
@end table
@subsubheading Registering Functions
@findex gmk_add_function
There is currently one way for makefiles to invoke operations provided
by the loaded object: through the @code{make} function call
@ -11087,15 +11091,18 @@ works with.
@table @code
@item gmk_expand
@findex gmk_expand
This function takes a string and expands it using @code{make}
expansion rules. The result of the expansion is returned in a string
that has been allocated using @code{malloc}. The caller is
responsible for calling @code{free} on the string when done.
expansion rules. The result of the expansion is returned in a
nil-terminated string buffer. The caller is responsible for calling
@code{gmk_free} with a pointer to the returned buffer when done.
@item gmk_eval
@findex gmk_eval
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,
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
@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_eval} the buffer is only expanded once, at most (as it's
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
@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
@chapter Features of GNU @code{make}
@cindex features of GNU @code{make}

View file

@ -26,22 +26,26 @@ typedef struct
unsigned long lineno;
} gmk_floc;
#ifdef _WIN32
# ifdef MAIN
# define GMK_EXPORT __declspec(dllexport)
# else
# ifndef GMK_EXPORT
# define GMK_EXPORT __declspec(dllimport)
# endif
#else
# define GMK_EXPORT
#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. */
void GMK_EXPORT gmk_eval (const char *buffer, const gmk_floc *floc);
/* 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);
/* 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 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.
MAX_ARGS is the maximum number of arguments (or 0 if there's no maximum).
@ -60,8 +64,8 @@ char * GMK_EXPORT gmk_expand (const char *str);
before FUNC is called. If EXPAND_ARGS is non-0, they will be expanded.
*/
void GMK_EXPORT gmk_add_function (const char *name,
char *(*func)(const char *nm,
int argc, char **argv),
int min_args, int max_args, int expand_args);
char *(*func)(const char *nm,
int argc, char **argv),
int min_args, int max_args, int expand_args);
#endif /* _GNUMAKE_H_ */

View file

@ -20,6 +20,20 @@ this program. If not, see <http://www.gnu.org/licenses/>. */
#include "variable.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.
Ideally eval_buffer() will take const char *, but not yet. */
void
@ -31,7 +45,7 @@ gmk_eval (const char *buffer, const gmk_floc *floc)
}
/* Expand a string and return an allocated buffer.
Caller must free() this buffer. */
Caller must call gmk_free() with this buffer. */
char *
gmk_expand (const char *ref)
{

View file

@ -49,11 +49,12 @@ char *alloca ();
/* Include the externally-visible content.
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. */
#define MAIN
#ifdef WINDOWS32
# define GMK_EXPORT __declspec(dllexport)
#endif
#include "gnumake.h"
#undef MAIN
#ifdef CRAY
/* This must happen before #include <signal.h> so

View file

@ -1,5 +1,7 @@
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
ordered recursive output for -Ojob / -Otarget.

View file

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