Add memory allocation cleanup to loadable objects.
authorPaul Smith <psmith@gnu.org>
Sat, 4 May 2013 21:38:53 +0000 (17:38 -0400)
committerPaul Smith <psmith@gnu.org>
Sat, 4 May 2013 21:38:53 +0000 (17:38 -0400)
Add gmk_alloc() and gmk_free() functions so loadable objects can access our
memory model.  Also provide a more extensive example in the manual.

ChangeLog
doc/make.texi
gnumake.h
loadapi.c
makeint.h
tests/ChangeLog
tests/scripts/features/loadapi

index ea207538bd1e157c99d39adc17b3d598f21892da..9f30bf78d6ba7811c3e38b164ba1446b7fc26ca0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
 
index 3a11ffd479c0c29457bfea884aeaf75e2be49be9..ea58d6e732783b23ad90c42dc627867bfa352cb8 100644 (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}
index a6308fe09ef8f0be068df71792b298ccffb74e49..168f37082f909ae75c793e63a326e964eb46fa7f 100644 (file)
--- a/gnumake.h
+++ b/gnumake.h
@@ -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_ */
index d79c41c6cd34de3dd85da172a35dccf41ace2a82..e314390b5f80819097cab9a2576ff933820519d0 100644 (file)
--- a/loadapi.c
+++ b/loadapi.c
@@ -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)
 {
index 9f0d1b81bda686f43058a5d27dcbb25fd9b824c4..6cce84848a0968cf150d7b6e9a530a95bcddc27a 100644 (file)
--- a/makeint.h
+++ b/makeint.h
@@ -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
index 991e96f4c0671d355eb9d605ade02a1d0c32d085..ade64f0b67c323a0354fbb2c7ae2fe4585ac9610 100644 (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.
 
index cecb114f383dde31aa85a1095e30a75b6075ef51..94a48a713dc4e0d97cddaaf393c4400e6781f713 100644 (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