Expand the loadable object support.
authorPaul Smith <psmith@gnu.org>
Mon, 25 Feb 2013 06:38:36 +0000 (01:38 -0500)
committerPaul Smith <psmith@gnu.org>
Mon, 25 Feb 2013 06:38:36 +0000 (01:38 -0500)
Provide a simple API for loaded objects to interact with GNU make.  I still
won't guarantee that this API won't change but it's much closer to something
that's supported and provides easy-to-use interfaces with a public header
file.

19 files changed:
.gitignore
ChangeLog
Makefile.am
dep.h
doc/make.texi
function.c
gmk-default.scm
gnumake.h
guile.c
load.c
loadapi.c [new file with mode: 0644]
main.c
read.c
tests/ChangeLog
tests/run_make_tests.pl
tests/scripts/features/load
tests/scripts/features/loadapi [new file with mode: 0644]
tests/scripts/functions/guile
variable.h

index 5d0af62a394453502fcb39650401f74dd4cdc989..0173826b6dec1b7ac3fb4322f71d4644b5aee3a1 100644 (file)
@@ -42,4 +42,5 @@ config.ami
 config.h-vms
 config.h.W32
 configh.dos
-make*.tar.*
+make-[0-9]*/
+make-[0-9]*.tar.*
index a2d2796d832b043496c23336a15eef356c7957be..9918dc6b13c0efff59ba3af98a496c3899d53128 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,30 @@
+2013-02-25  Paul Smith  <psmith@gnu.org>
+
+       Add a proposed supported API for GNU make loaded objects.
+
+       * doc/make.texi (Loaded Object API): Document it.
+       * Makefile.am (make_SOURCES): Add new loadapi.c.
+       * dep.h: Remove eval_buffer(); moved to loadapi.c:gmk_eval().
+       * read.c (eval_buffer): Change eval_buffer() signature.
+       * main.c (main): Change eval_buffer() signature.
+       * variable.h (define_new_function): Change func_ptr signature.
+       * load.c (SYMBOL_EXTENSION): Change the extension.
+       * loadapi.c: Implement the new API.
+       * gnumake.h (gmk_eval): New function prototype.
+       (gmk_expand) Ditto.
+       (gmk_add_function) Ditto.
+       * gmk-default.scm (gmk-eval): Remove: now implemented in guile.c.
+       * guile.c (guile_expand_wrapper): Use gmk_expand()
+       (guile_eval_wrapper): Implement eval here to avoid double-expansion.
+       (guile_define_module): Define gmk-eval.
+       (func_guile): Use new func_ptr calling model.
+       (guile_gmake_setup): Use gmk_add_function() to declare $(guile ...)
+       * function.c (function_table_entry): Provide alternative func_ptr.
+       (func_eval): New signature for eval_buffer();
+       (function_table_init): New initialization for function_table_entry.
+       (expand_builtin_function): Support alternative invocation signature.
+       (define_new_function): Ditto.
+
 2013-01-20  Paul Smith  <psmith@gnu.org>
 
        * gnumake.h: New file to contain externally-visible content.
index 3e3b4800266a6afb2a1f8b761de78c8171bcdd8e..35ff73bef4420a0d962fa654cfac1845c81780a5 100644 (file)
@@ -40,8 +40,8 @@ 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 load.c main.c \
-               misc.c read.c remake.c rule.c signame.c \
+               function.c getopt.c getopt1.c implicit.c job.c load.c \
+               loadapi.c main.c misc.c read.c remake.c rule.c signame.c \
                strcache.c variable.c version.c vpath.c hash.c \
                $(remote)
 
@@ -184,18 +184,18 @@ loadavg_LDADD = @GETLOADAVG_LIBS@
 MAKETESTFLAGS =
 
 check-regression:
-       @if test -f "$(srcdir)/tests/run_make_tests"; then \
+       @if test -f '$(srcdir)/tests/run_make_tests'; then \
          if $(PERL) -v >/dev/null 2>&1; then \
-           case `cd $(srcdir); pwd` in `pwd`) : ;; \
+           case `cd '$(srcdir)'; pwd` in `pwd`) : ;; \
              *) test -d tests || mkdir tests; \
                 rm -f srctests; \
-                if ln -s "$(srcdir)/tests" srctests; then \
+                if ln -s '$(srcdir)/tests' srctests; then \
                   for f in run_make_tests run_make_tests.pl test_driver.pl scripts; do \
                     rm -f tests/$$f; ln -s ../srctests/$$f tests; \
                   done; fi ;; \
            esac; \
-           echo "cd tests && $(PERL) ./run_make_tests.pl -make ../make$(EXEEXT) $(MAKETESTFLAGS)"; \
-           cd tests && $(PERL) ./run_make_tests.pl -make ../make$(EXEEXT) $(MAKETESTFLAGS); \
+           echo "cd tests && $(PERL) ./run_make_tests.pl -srcdir $(abs_srcdir) -make ../make$(EXEEXT) $(MAKETESTFLAGS)"; \
+           cd tests && $(PERL) ./run_make_tests.pl -srcdir '$(abs_srcdir)' -make '../make$(EXEEXT)' $(MAKETESTFLAGS); \
          else \
            echo "Can't find a working Perl ($(PERL)); the test suite requires Perl."; \
          fi; \
diff --git a/dep.h b/dep.h
index 3854f31013ccc7429d2301c6fbddfecdc20a060f..1a50e255db31901ac5ec519c33f56f1c86ff5135 100644 (file)
--- a/dep.h
+++ b/dep.h
@@ -87,5 +87,4 @@ struct dep *copy_dep_chain (const struct dep *d);
 void free_dep_chain (struct dep *d);
 void free_ns_chain (struct nameseq *n);
 struct dep *read_all_makefiles (const char **makefiles);
-void eval_buffer (char *buffer);
 int update_goal_chain (struct dep *goals);
index f5ca1160b4c9b9bc0621c3a154462455957d2b82..2fe7aa6bdd7199106df19a5687703b6c34a25639 100644 (file)
@@ -348,6 +348,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.
 
 @end detailmenu
 @end menu
@@ -10658,23 +10659,24 @@ exports these procedures as public interfaces from that module:
 
 @table @code
 @item gmk-expand
+@findex 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
+@findex 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.
+Note that @code{gmk-eval} is not quite the same as using
+@code{gmk-expand} with the @code{eval} function: in the latter case
+the evaluated string will be expanded @emph{twice}; first by
+@code{gmk-expand}, then again by the @code{eval} function.
 
 @end table
 
@@ -10756,8 +10758,8 @@ 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 loaded objects
+@cindex objects, loaded
 @cindex extensions, loading
 
 @cartouche
@@ -10767,12 +10769,6 @@ The @code{load} directive and extension capability is considered a
 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
 
@@ -10791,6 +10787,7 @@ for example, and the ``setup'' function would register them with GNU
 @menu
 * load Directive::              Loading dynamic objects as extensions.
 * Remaking Loaded Objects::     How loaded objects get remade.
+* Loaded Object API::           Programmatic interface for loaded objects.
 @end menu
 
 @node load Directive, Remaking Loaded Objects, Loading Objects, Loading Objects
@@ -10829,7 +10826,7 @@ If no @var{symbol-name} is provided, the initializing function 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}.
+this prefix will be appended the suffix @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
@@ -10848,7 +10845,7 @@ load ../mk_funcs.so
 
 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}.
+by the shared object) @code{mk_funcs_gmk_setup}.
 
 On the other hand:
 
@@ -10875,11 +10872,11 @@ 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 Remaking Loaded Objects,  , load Directive, Loading Objects
+@node Remaking Loaded Objects, Loaded Object API, load Directive, Loading Objects
 @subsection How Loaded Objects Are Remade
-@cindex updating load objects
-@cindex remaking load objects
-@cindex load objects, remaking of
+@cindex updating loaded objects
+@cindex remaking loaded objects
+@cindex loaded objects, remaking of
 
 Loaded objects undergo the same re-make procedure as makefiles
 (@pxref{Remaking Makefiles, ,How Makefiles Are Remade}).  If any
@@ -10891,6 +10888,119 @@ 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
+@subsection Loaded Object Interface
+@cindex loaded object API
+@cindex interface for loaded objects
+
+@cartouche
+@quotation Warning
+For this feature to be useful your extensions will need to invoke
+various functions internal to GNU @code{make}.  The programming
+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 the loaded object can
+use 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
+should include this header file.  Any loaded object not written in C
+will need to implement the interface defined in this header file.
+
+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.
+
+@subsubheading Data Structures
+
+@table @code
+@item gmk_floc
+This structure represents a filename/location pair.  It is provided
+when defining items, so GNU @code{make} can inform the user later
+where the definition occurred if necessary.
+@end table
+
+@subsubheading Registering Functions
+
+There is currently one way for makefiles to invoke operations provided
+by the loaded object: through the @code{make} function call
+interface.  A loaded object can register one or more new functions
+which may then be invoked from within the makefile in the same way as
+any other function.
+
+Use @code{gmk_add_function} to create a new @code{make} function.  Its
+arguments are as follows:
+
+@table @code
+@item name
+The function name.  This is what the makefile should use to invoke the
+function.  The name must be between 1 and 255 characters long.
+
+@item func_ptr
+A pointer to a function that @code{make} will invoke when it expands
+the function in a makefile.  This function must be defined by the
+loaded object.  GNU @code{make} will call it with three arguments:
+@code{name} (the same name as above), @code{argc} (the number of
+arguments to the function), and @code{argv} (the list of arguments to
+the function).  The last argument (that is, @code{argv[argc]} will be
+null (@code{0}).
+
+@item min_args
+The minimum number of arguments the function will accept.  Must be
+between 0 and 255.  GNU @code{make} will check this and fail before
+invoking @code{func_ptr} if the function was invoked with too few
+arguments.
+
+@item max_args
+The maximum number of arguments the function will accept.  Must be
+between 0 and 255.  GNU @code{make} will check this and fail before
+invoking @code{func_ptr} if the function was invoked with too few
+arguments.  If the value is 0, then any number of arguments is
+accepted.  If the value is greater than 0, then it must be greater
+than or equal to @code{min_args}.
+
+@item expand_args
+If this value is 0, then @code{make} will not expand the arguments to
+the function before passing them to @code{func_ptr}.  If the value is
+non-0, then the arguments will be expanded first.
+@end table
+
+@subsubheading GNU @code{make} Facilities
+
+There are some facilities exported by GNU @code{make} for use by
+loaded objects.  Typically these would be run from within the
+setup function and/or the functions registered via
+@code{gmk_add_function}, to retrieve or modify the data @code{make}
+works with.
+
+@table @code
+@item 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.
+
+@item 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.
+
+Note that there is a difference between @code{gmk_eval} and calling
+@code{gmk_expand} with a string using the @code{eval} function: in
+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).
+@end table
+
 @node Features, Missing, Extending make, Top
 @chapter Features of GNU @code{make}
 @cindex features of GNU @code{make}
index 15b7aea6d55cc876cda760114fdbd4a187efe8de..b6060d6f54ad38ab845274d6b1fc8634f5888f8a 100644 (file)
@@ -29,12 +29,16 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 struct function_table_entry
   {
+    union {
+      char *(*func_ptr) (char *output, char **argv, const char *fname);
+      char *(*alloc_func_ptr) (const char *fname, int argc, char **argv);
+    } fptr;
     const char *name;
     unsigned char len;
     unsigned char minimum_args;
     unsigned char maximum_args;
-    char expand_args;
-    char *(*func_ptr) (char *output, char **argv, const char *fname);
+    unsigned char expand_args:1;
+    unsigned char alloc_fn:1;
   };
 
 static unsigned long
@@ -1361,7 +1365,7 @@ func_eval (char *o, char **argv, const char *funcname UNUSED)
 
   install_variable_buffer (&buf, &len);
 
-  eval_buffer (argv[0]);
+  eval_buffer (argv[0], NULL);
 
   restore_variable_buffer (buf, len);
 
@@ -2186,49 +2190,51 @@ func_abspath (char *o, char **argv, const char *funcname UNUSED)
 
 static char *func_call (char *o, char **argv, const char *funcname);
 
+#define FT_ENTRY(_name, _min, _max, _exp, _func) \
+  { (_func), STRING_SIZE_TUPLE(_name), (_min), (_max), (_exp), 0 }
 
 static struct function_table_entry function_table_init[] =
 {
- /* Name/size */                    /* MIN MAX EXP? Function */
-  { STRING_SIZE_TUPLE("abspath"),       0,  1,  1,  func_abspath},
-  { STRING_SIZE_TUPLE("addprefix"),     2,  2,  1,  func_addsuffix_addprefix},
-  { STRING_SIZE_TUPLE("addsuffix"),     2,  2,  1,  func_addsuffix_addprefix},
-  { STRING_SIZE_TUPLE("basename"),      0,  1,  1,  func_basename_dir},
-  { STRING_SIZE_TUPLE("dir"),           0,  1,  1,  func_basename_dir},
-  { STRING_SIZE_TUPLE("notdir"),        0,  1,  1,  func_notdir_suffix},
-  { STRING_SIZE_TUPLE("subst"),         3,  3,  1,  func_subst},
-  { STRING_SIZE_TUPLE("suffix"),        0,  1,  1,  func_notdir_suffix},
-  { STRING_SIZE_TUPLE("filter"),        2,  2,  1,  func_filter_filterout},
-  { STRING_SIZE_TUPLE("filter-out"),    2,  2,  1,  func_filter_filterout},
-  { STRING_SIZE_TUPLE("findstring"),    2,  2,  1,  func_findstring},
-  { STRING_SIZE_TUPLE("firstword"),     0,  1,  1,  func_firstword},
-  { STRING_SIZE_TUPLE("flavor"),        0,  1,  1,  func_flavor},
-  { STRING_SIZE_TUPLE("join"),          2,  2,  1,  func_join},
-  { STRING_SIZE_TUPLE("lastword"),      0,  1,  1,  func_lastword},
-  { STRING_SIZE_TUPLE("patsubst"),      3,  3,  1,  func_patsubst},
-  { STRING_SIZE_TUPLE("realpath"),      0,  1,  1,  func_realpath},
-  { STRING_SIZE_TUPLE("shell"),         0,  1,  1,  func_shell},
-  { STRING_SIZE_TUPLE("sort"),          0,  1,  1,  func_sort},
-  { STRING_SIZE_TUPLE("strip"),         0,  1,  1,  func_strip},
-  { STRING_SIZE_TUPLE("wildcard"),      0,  1,  1,  func_wildcard},
-  { STRING_SIZE_TUPLE("word"),          2,  2,  1,  func_word},
-  { STRING_SIZE_TUPLE("wordlist"),      3,  3,  1,  func_wordlist},
-  { STRING_SIZE_TUPLE("words"),         0,  1,  1,  func_words},
-  { STRING_SIZE_TUPLE("origin"),        0,  1,  1,  func_origin},
-  { STRING_SIZE_TUPLE("foreach"),       3,  3,  0,  func_foreach},
-  { STRING_SIZE_TUPLE("call"),          1,  0,  1,  func_call},
-  { STRING_SIZE_TUPLE("info"),          0,  1,  1,  func_error},
-  { STRING_SIZE_TUPLE("error"),         0,  1,  1,  func_error},
-  { STRING_SIZE_TUPLE("warning"),       0,  1,  1,  func_error},
-  { STRING_SIZE_TUPLE("if"),            2,  3,  0,  func_if},
-  { STRING_SIZE_TUPLE("or"),            1,  0,  0,  func_or},
-  { STRING_SIZE_TUPLE("and"),           1,  0,  0,  func_and},
-  { STRING_SIZE_TUPLE("value"),         0,  1,  1,  func_value},
-  { STRING_SIZE_TUPLE("eval"),          0,  1,  1,  func_eval},
-  { STRING_SIZE_TUPLE("file"),          1,  2,  1,  func_file},
+ /*         Name            MIN MAX EXP? Function */
+  FT_ENTRY ("abspath",       0,  1,  1,  func_abspath),
+  FT_ENTRY ("addprefix",     2,  2,  1,  func_addsuffix_addprefix),
+  FT_ENTRY ("addsuffix",     2,  2,  1,  func_addsuffix_addprefix),
+  FT_ENTRY ("basename",      0,  1,  1,  func_basename_dir),
+  FT_ENTRY ("dir",           0,  1,  1,  func_basename_dir),
+  FT_ENTRY ("notdir",        0,  1,  1,  func_notdir_suffix),
+  FT_ENTRY ("subst",         3,  3,  1,  func_subst),
+  FT_ENTRY ("suffix",        0,  1,  1,  func_notdir_suffix),
+  FT_ENTRY ("filter",        2,  2,  1,  func_filter_filterout),
+  FT_ENTRY ("filter-out",    2,  2,  1,  func_filter_filterout),
+  FT_ENTRY ("findstring",    2,  2,  1,  func_findstring),
+  FT_ENTRY ("firstword",     0,  1,  1,  func_firstword),
+  FT_ENTRY ("flavor",        0,  1,  1,  func_flavor),
+  FT_ENTRY ("join",          2,  2,  1,  func_join),
+  FT_ENTRY ("lastword",      0,  1,  1,  func_lastword),
+  FT_ENTRY ("patsubst",      3,  3,  1,  func_patsubst),
+  FT_ENTRY ("realpath",      0,  1,  1,  func_realpath),
+  FT_ENTRY ("shell",         0,  1,  1,  func_shell),
+  FT_ENTRY ("sort",          0,  1,  1,  func_sort),
+  FT_ENTRY ("strip",         0,  1,  1,  func_strip),
+  FT_ENTRY ("wildcard",      0,  1,  1,  func_wildcard),
+  FT_ENTRY ("word",          2,  2,  1,  func_word),
+  FT_ENTRY ("wordlist",      3,  3,  1,  func_wordlist),
+  FT_ENTRY ("words",         0,  1,  1,  func_words),
+  FT_ENTRY ("origin",        0,  1,  1,  func_origin),
+  FT_ENTRY ("foreach",       3,  3,  0,  func_foreach),
+  FT_ENTRY ("call",          1,  0,  1,  func_call),
+  FT_ENTRY ("info",          0,  1,  1,  func_error),
+  FT_ENTRY ("error",         0,  1,  1,  func_error),
+  FT_ENTRY ("warning",       0,  1,  1,  func_error),
+  FT_ENTRY ("if",            2,  3,  0,  func_if),
+  FT_ENTRY ("or",            1,  0,  0,  func_or),
+  FT_ENTRY ("and",           1,  0,  0,  func_and),
+  FT_ENTRY ("value",         0,  1,  1,  func_value),
+  FT_ENTRY ("eval",          0,  1,  1,  func_eval),
+  FT_ENTRY ("file",          1,  2,  1,  func_file),
 #ifdef EXPERIMENTAL
-  { STRING_SIZE_TUPLE("eq"),            2,  2,  1,  func_eq},
-  { STRING_SIZE_TUPLE("not"),           0,  1,  1,  func_not},
+  FT_ENTRY ("eq",            2,  2,  1,  func_eq),
+  FT_ENTRY ("not",           0,  1,  1,  func_not),
 #endif
 };
 
@@ -2241,23 +2247,38 @@ static char *
 expand_builtin_function (char *o, int argc, char **argv,
                          const struct function_table_entry *entry_p)
 {
+  char *p;
+
   if (argc < (int)entry_p->minimum_args)
     fatal (*expanding_var,
            _("insufficient number of arguments (%d) to function '%s'"),
            argc, entry_p->name);
 
-  /* I suppose technically some function could do something with no
-     arguments, but so far none do, so just test it for all functions here
+  /* I suppose technically some function could do something with no arguments,
+     but so far no internal ones do, so just test it for all functions here
      rather than in each one.  We can change it later if necessary.  */
 
-  if (!argc)
+  if (!argc && !entry_p->alloc_fn)
     return o;
 
-  if (!entry_p->func_ptr)
+  if (!entry_p->fptr.func_ptr)
     fatal (*expanding_var,
            _("unimplemented on this platform: function '%s'"), entry_p->name);
 
-  return entry_p->func_ptr (o, argv, entry_p->name);
+  if (!entry_p->alloc_fn)
+    return entry_p->fptr.func_ptr (o, argv, entry_p->name);
+
+  /* This function allocates memory and returns it to us.
+     Write it to the variable buffer, then free it.  */
+
+  p = entry_p->fptr.alloc_func_ptr (entry_p->name, argc, argv);
+  if (p)
+    {
+      o = variable_buffer_output (o, p, strlen (p));
+      free (p);
+    }
+
+  return o;
 }
 
 /* Check for a function invocation in *STRINGP.  *STRINGP points at the
@@ -2486,26 +2507,28 @@ func_call (char *o, char **argv, const char *funcname UNUSED)
 void
 define_new_function(const gmk_floc *flocp,
                     const char *name, int min, int max, int expand,
-                    char *(*func)(char *, char **, const char *))
+                    char *(*func)(const char *, int, char **))
 {
+  struct function_table_entry *ent;
   size_t len = strlen (name);
-  struct function_table_entry *ent = xmalloc (sizeof (struct function_table_entry));
 
   if (len > 255)
     fatal (flocp, _("Function name too long: %s\n"), name);
   if (min < 0 || min > 255)
     fatal (flocp, _("Invalid minimum argument count (%d) for function %s\n"),
            min, name);
-  if (max < 0 || max > 255 || max < min)
+  if (max < 0 || max > 255 || (max && max < min))
     fatal (flocp, _("Invalid maximum argument count (%d) for function %s\n"),
            max, name);
 
+  ent = xmalloc (sizeof (struct function_table_entry));
   ent->name = name;
   ent->len = len;
   ent->minimum_args = min;
   ent->maximum_args = max;
   ent->expand_args = expand ? 1 : 0;
-  ent->func_ptr = func;
+  ent->alloc_fn = 1;
+  ent->fptr.alloc_func_ptr = func;
 
   hash_insert (&function_table, ent);
 }
index 84a21140fafa6ee787ab595c0cf13844b4ce438e..d33dfb2ef7cbedf2c1dc303c84617c725409fc4e 100644 (file)
     (walk x)
     (string-join (reverse! acc))))
 
-;; eval (GNU make eval) the input string S
-(define (gmk-eval s)
-  (gmk-expand (format #f "$(eval ~a)" (obj-to-str s))))
-
 ;; Return the value of the GNU make variable V
 (define (gmk-var v)
   (gmk-expand (format #f "$(~a)" (obj-to-str v))))
index 1648b3355f26153491555f4d518a00ed549c6550..c6f7bd82a58bd17919d6b3b92f47d514db307918 100644 (file)
--- a/gnumake.h
+++ b/gnumake.h
@@ -1,4 +1,6 @@
 /* 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 Free Software Foundation, Inc.
 This file is part of GNU Make.
 
@@ -24,4 +26,31 @@ typedef struct
     unsigned long lineno;
   } gmk_floc;
 
+
+/* Run $(eval ...) on the provided string BUFFER.  */
+void 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.  */
+char *gmk_expand (const char *str);
+
+/* Register a new GNU make function NAME (maximum of 255 chars long).
+   When the function is expanded in the makefile, FUNC will be invoked with
+   the appropriate arguments.
+
+   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.
+
+   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).
+   MIN_ARGS and MAX_ARGS must be >= 0 and <= 255.
+
+   If EXPAND_ARGS is 0, the arguments to the function will not be expanded
+   before FUNC is called.  If EXPAND_ARGS is non-0, they will be expanded.
+*/
+void gmk_add_function (const char *name,
+                       char *(*func)(const char *nm, int argc, char **argv),
+                       int min_args, int max_args, int expand_args);
+
 #endif  /* _GNUMAKE_H_ */
diff --git a/guile.c b/guile.c
index 47a21f90c459790fc355c396e225159b2743e736..28fcf39b34fc3f09eb0e6e46310ba39814ec6251 100644 (file)
--- a/guile.c
+++ b/guile.c
@@ -14,6 +14,8 @@ 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 "gnumake.h"
+
 #include "makeint.h"
 #include "debug.h"
 #include "dep.h"
@@ -40,7 +42,7 @@ guile_expand_wrapper (SCM obj)
   char *res;
 
   DB (DB_BASIC, (_("guile: Expanding '%s'\n"), str));
-  res = allocated_variable_expand (str);
+  res = gmk_expand (str);
   ret = scm_from_locale_string (res);
 
   free (str);
@@ -49,6 +51,18 @@ guile_expand_wrapper (SCM obj)
   return ret;
 }
 
+/* Perform the GNU make eval function.  */
+static SCM
+guile_eval_wrapper (SCM obj)
+{
+  char *str = cvt_scm_to_str (obj);
+
+  DB (DB_BASIC, (_("guile: Evaluating '%s'\n"), str));
+  gmk_eval (str, 0);
+
+  return SCM_BOOL_F;
+}
+
 /* Invoked by scm_c_define_module(), in the context of the GNU make module.  */
 static void
 guile_define_module (void *data UNUSED)
@@ -59,6 +73,9 @@ guile_define_module (void *data UNUSED)
   /* Register a subr for GNU make's eval capability.  */
   scm_c_define_gsubr ("gmk-expand", 1, 0, 0, guile_expand_wrapper);
 
+  /* Register a subr for GNU make's eval capability.  */
+  scm_c_define_gsubr ("gmk-eval", 1, 0, 0, guile_eval_wrapper);
+
   /* Define the rest of the module.  */
   scm_c_eval_string (GUILE_module_defn);
 }
@@ -87,19 +104,12 @@ internal_guile_eval (void *arg)
 
 /* This is the function registered with make  */
 static char *
-func_guile (char *o, char **argv, const char *funcname UNUSED)
+func_guile (const char *funcname UNUSED, int argc UNUSED, char **argv)
 {
   if (argv[0] && argv[0][0] != '\0')
-    {
-      char *str = scm_with_guile (internal_guile_eval, argv[0]);
-      if (str)
-        {
-          o = variable_buffer_output (o, str, strlen (str));
-          free (str);
-        }
-    }
-
-  return o;
+    return scm_with_guile (internal_guile_eval, argv[0]);
+
+  return NULL;
 }
 
 /* ----- Public interface ----- */
@@ -113,7 +123,7 @@ guile_gmake_setup (const gmk_floc *flocp UNUSED)
   scm_with_guile (guile_init, NULL);
 
   /* Create a make function "guile".  */
-  define_new_function (NILF, "guile", 0, 1, 1, func_guile);
+  gmk_add_function ("guile", func_guile, 0, 1, 1);
 
   return 1;
 }
diff --git a/load.c b/load.c
index 0ed05f9ad7764b65c9c8b69a8706c1c65bc9b0bc..081d66f009e3cfd696743ab41e82435bc208b6a8 100644 (file)
--- a/load.c
+++ b/load.c
@@ -24,7 +24,7 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <dlfcn.h>
 #include <errno.h>
 
-#define SYMBOL_EXTENSION        "_gmake_setup"
+#define SYMBOL_EXTENSION        "_gmk_setup"
 
 static void *global_dl = NULL;
 
diff --git a/loadapi.c b/loadapi.c
new file mode 100644 (file)
index 0000000..3170dd1
--- /dev/null
+++ b/loadapi.c
@@ -0,0 +1,49 @@
+/* API for GNU Make dynamic objects.
+Copyright (C) 2013 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 "gnumake.h"
+
+#include "makeint.h"
+
+#include "filedef.h"
+#include "variable.h"
+
+/* Evaluate a buffer as make syntax.
+   Ideally eval_buffer() will take const char *, but not yet.  */
+void
+gmk_eval (const char *buffer, const gmk_floc *floc)
+{
+  char *s = xstrdup (buffer);
+  eval_buffer (s, floc);
+  free (s);
+}
+
+/* Expand a string and return an allocated buffer.
+   Caller must free() this buffer.  */
+char *
+gmk_expand (const char *ref)
+{
+  return allocated_variable_expand (ref);
+}
+
+/* Register a function to be called from makefiles.  */
+void
+gmk_add_function (const char *name,
+                  char *(*func)(const char *nm, int argc, char **argv),
+                  int min, int max, int expand)
+{
+  define_new_function (reading_file, name, min, max, expand, func);
+}
diff --git a/main.c b/main.c
index d06825e476a83aa82340b00ad49250ba095db0d2..0a2774cd1849141da523f3898595568a436ec820 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1648,7 +1648,7 @@ main (int argc, char **argv, char **envp)
         {
           p = xstrdup (eval_strings->list[i]);
           len += 2 * strlen (p);
-          eval_buffer (p);
+          eval_buffer (p, NULL);
           free (p);
         }
 
diff --git a/read.c b/read.c
index 68287b7347c40cc46791393928048908bb19a8c8..af5cf01d290c7f0ec0ef90ac5601d4cc8293c582 100644 (file)
--- a/read.c
+++ b/read.c
@@ -435,7 +435,7 @@ eval_makefile (const char *filename, int flags)
 }
 
 void
-eval_buffer (char *buffer)
+eval_buffer (char *buffer, const gmk_floc *floc)
 {
   struct ebuffer ebuf;
   struct conditionals *saved;
@@ -448,7 +448,9 @@ eval_buffer (char *buffer)
   ebuf.buffer = ebuf.bufnext = ebuf.bufstart = buffer;
   ebuf.fp = NULL;
 
-  if (reading_file)
+  if (floc)
+    ebuf.floc = *floc;
+  else if (reading_file)
     ebuf.floc = *reading_file;
   else
     ebuf.floc.filenm = NULL;
index 2c1cb1b4b75750c275f1865514c6a12d8c4705db..afa1cbe27d6c7ccf0378b30db4e6c9f75361b20d 100644 (file)
@@ -1,3 +1,12 @@
+2013-02-25  Paul Smith  <psmith@gnu.org>
+
+       * run_make_tests.pl (valid_option): Support the -srcdir flag.
+       (set_more_defaults): Set up $srcdir if it's not set yet.
+
+       * scripts/functions/guile: Verify gmk-eval doesn't expand twice.
+       * scripts/features/load: Rework to test just the load capability.
+       * scripts/features/loadapi: New set of tests for the load API.
+
 2013-01-19  Paul Smith  <psmith@gnu.org>
 
        * scripts/features/load: Test loaded files with and without "./"
index 4accd4aadb7b88e5548af4f58ba6aa3eeb53673f..a596328c6217960d77da34243c7739339efeca45 100755 (executable)
@@ -35,6 +35,9 @@ $memcheck_args = '--num-callers=15 --tool=memcheck --leak-check=full --suppressi
 $massif_args = '--num-callers=15 --tool=massif --alloc-fn=xmalloc --alloc-fn=xcalloc --alloc-fn=xrealloc --alloc-fn=xstrdup --alloc-fn=xstrndup';
 $pure_log = undef;
 
+# The location of the GNU make source directory
+$srcdir = '';
+
 $command_string = '';
 
 $all_tests = 0;
@@ -59,6 +62,15 @@ sub valid_option
        return 1;
    }
 
+   if ($option =~ /^-srcdir$/i) {
+       $srcdir = shift @argv;
+       if (! -f "$srcdir/gnumake.h") {
+           print "$option $srcdir: Not a valid GNU make source directory.\n";
+           exit 0;
+       }
+       return 1;
+   }
+
    if ($option =~ /^-all([-_]?tests)?$/i) {
        $all_tests = 1;
        return 1;
@@ -226,14 +238,16 @@ sub run_make_with_options {
 sub print_usage
 {
    &print_standard_usage ("run_make_tests",
-                          "[-make_path make_pathname] [-memcheck] [-massif]",);
+                          "[-make MAKE_PATHNAME] [-srcdir SRCDIR] [-memcheck] [-massif]",);
 }
 
 sub print_help
 {
    &print_standard_help (
-        "-make_path",
+        "-make",
         "\tYou may specify the pathname of the copy of make to run.",
+        "-srcdir",
+        "\tSpecify the make source directory.",
         "-valgrind",
         "-memcheck",
         "\tRun the test suite under valgrind's memcheck tool.",
@@ -327,12 +341,8 @@ sub set_more_defaults
      $make_name = $1;
    }
    else {
-     if ($make_path =~ /$pathsep([^\n$pathsep]*)$/) {
-       $make_name = $1;
-     }
-     else {
-       $make_name = $make_path;
-     }
+     $make_path =~ /^(?:.*$pathsep)?(.+)$/;
+     $make_name = $1;
    }
 
    # prepend pwd if this is a relative path (ie, does not
@@ -348,6 +358,15 @@ sub set_more_defaults
       $mkpath = $make_path;
    }
 
+   # If srcdir wasn't provided on the command line, see if the
+   # location of the make program gives us a clue.  Don't fail if not;
+   # we'll assume it's been installed into /usr/include or wherever.
+   if (! $srcdir) {
+       $make_path =~ /^(.*$pathsep)?/;
+       my $d = $1 || '../';
+       -f "${d}gnumake.h" and $srcdir = $d;
+   }
+
    # Get Purify log info--if any.
 
    if (exists $ENV{PURIFYOPTIONS}
index dd3daf8096c82c4b60ac5de7491cfeb71abe258b..47267feea254d1178dd9802210f2dd95c8b34da4 100644 (file)
@@ -6,6 +6,8 @@ $details = "Test dynamic loading of modules.";
 # Don't do anything if this system doesn't support "load"
 exists $FEATURES{load} or return -1;
 
+my $sobuild = '$(CC) '.($srcdir? "-I$srcdir":'').' -g -shared -fPIC -o $@ $<';
+
 # First build a shared object
 # Provide both a default and non-default load symbol
 
@@ -16,67 +18,56 @@ 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));
-}
+#include "gnumake.h"
 
 int
-testload_gmake_setup ()
+testload_gmk_setup ()
 {
-    define_new_function (0, "func-a", 1, 1, 1, func_test);
+    gmk_eval ("TESTLOAD = implicit", 0);
     return 1;
 }
 
 int
 explicit_setup ()
 {
-    define_new_function (0, "func-b", 1, 1, 1, func_test);
+    gmk_eval ("TESTLOAD = explicit", 0);
     return 1;
 }
 EOF
 close($F) or die "close: testload.c: $!\n";
 
-run_make_test('testload.so: testload.c ; @$(CC) -g -shared -fPIC -o $@ $<',
-              '', '');
+# Make sure we can compile
+run_make_test('testload.so: testload.c ; @'.$sobuild, '', '');
 
 # TEST 1
 run_make_test(q!
-all: ; @echo $(func-a foo) $(func-b bar)
+PRE := $(.LOADED)
 load testload.so
+POST := $(.LOADED)
+all: ; @echo pre=$(PRE) post=$(POST) $(TESTLOAD)
 !,
-              '', "func-a\n");
+              '', "pre= post=testload.so implicit\n");
 
 # TEST 2
-# Load a different function
+# Load using an explicit function
 run_make_test(q!
-all: ; @echo $(func-a foo) $(func-b bar)
+PRE := $(.LOADED)
 load ./testload.so(explicit_setup)
+POST := $(.LOADED)
+all: ; @echo pre=$(PRE) post=$(POST) $(TESTLOAD)
 !,
-              '', "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");
+              '', "pre= post=testload.so explicit\n");
 
 # TEST 4
 # Check multiple loads
 run_make_test(q!
-all: ; @echo $(filter testload.so,$(.LOADED)) $(func-a foo) $(func-b bar)
+PRE := $(.LOADED)
 load ./testload.so
 load testload.so(explicit_setup)
+POST := $(.LOADED)
+all: ; @echo pre=$(PRE) post=$(POST) $(TESTLOAD)
 !,
-              '', "testload.so func-a\n");
+              '', "pre= post=testload.so implicit\n");
 
 # TEST 5
 # Check auto-rebuild of loaded file that's out of date
@@ -84,22 +75,24 @@ utouch(-10, 'testload.so');
 touch('testload.c');
 
 run_make_test(q!
-all: ; @echo $(func-a foo) $(func-b bar)
+PRE := $(.LOADED)
 load ./testload.so
-testload.so: testload.c ; @echo "rebuilding $@"; $(CC) -g -shared -fPIC -o $@ $<
-!,
-              '', "rebuilding testload.so\nfunc-a\n");
+POST := $(.LOADED)
+all: ; @echo pre=$(PRE) post=$(POST) $(TESTLOAD)
+testload.so: testload.c ; @echo "rebuilding $@"; !.$sobuild,
+              '', "rebuilding testload.so\npre= post=testload.so implicit\n");
 
 # TEST 5
 # Check auto-rebuild of loaded file when it doesn't exist
 unlink('testload.so');
 
 run_make_test(q!
-all: ; @echo $(func-a foo) $(func-b bar)
+PRE := $(.LOADED)
 -load ./testload.so(explicit_setup)
-%.so: %.c ; @echo "rebuilding $@"; $(CC) -g -shared -fPIC -o $@ $<
-!,
-              '', "rebuilding testload.so\nfunc-b\n");
+POST := $(.LOADED)
+all: ; @echo pre=$(PRE) post=$(POST) $(TESTLOAD)
+%.so: %.c ; @echo "rebuilding $@"; !.$sobuild,
+              '', "rebuilding testload.so\npre= post=testload.so explicit\n");
 
 unlink(qw(testload.c testload.so)) unless $keep;
 
diff --git a/tests/scripts/features/loadapi b/tests/scripts/features/loadapi
new file mode 100644 (file)
index 0000000..cecb114
--- /dev/null
@@ -0,0 +1,84 @@
+#                                                                    -*-perl-*-
+$description = "Test the shared object load API.";
+
+$details = "Verify the different aspects of the shared object API.";
+
+# Don't do anything if this system doesn't support "load"
+exists $FEATURES{load} or return -1;
+
+my $sobuild = '$(CC) '.($srcdir? "-I$srcdir":'').' -g -shared -fPIC -o $@ $<';
+
+# First build a shared object
+# Provide both a default and non-default load symbol
+
+unlink(qw(testapi.c testapi.so));
+
+open(my $F, '> testapi.c') or die "open: testapi.c: $!\n";
+print $F <<'EOF' ;
+#include <string.h>
+#include <stdio.h>
+
+#include "gnumake.h"
+
+static char *
+test_eval (const char *buf)
+{
+    gmk_eval (buf, 0);
+    return NULL;
+}
+
+static char *
+test_expand (const char *val)
+{
+    return gmk_expand (val);
+}
+
+static char *
+func_test (const char *funcname, int argc, char **argv)
+{
+    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");
+}
+
+int
+testapi_gmk_setup ()
+{
+    gmk_add_function ("test-expand", func_test, 1, 1, 1);
+    gmk_add_function ("test-eval", func_test, 1, 1, 1);
+    return 1;
+}
+EOF
+close($F) or die "close: testapi.c: $!\n";
+
+run_make_test('testapi.so: testapi.c ; @'.$sobuild, '', '');
+
+# TEST 1
+# Check the gmk_expand() function
+run_make_test(q!
+EXPAND = expansion
+all: ; @echo $(test-expand $$(EXPAND))
+load testapi.so
+!,
+              '', "expansion\n");
+
+# TEST 2
+# Check the eval operation.  Prove that the argument is expanded only once
+run_make_test(q!
+load testapi.so
+TEST = bye
+ASSIGN = VAR = $(TEST) $(shell echo there)
+$(test-eval $(value ASSIGN))
+TEST = hi
+all:;@echo '$(VAR)'
+!,
+              '', "hi there\n");
+
+unlink(qw(testapi.c testapi.so)) unless $keep;
+
+# This tells the test driver that the perl test script executed properly.
+1;
index 93a18ab636d85ed1de73317d43141d794dd35775..c63bec9ba1d4e8dd4a94dc7db594a7ae343036cc 100644 (file)
@@ -44,8 +44,12 @@ x:;@echo '$(VAR)'
               '', "hi");
 
 # Verify the gmk-eval function
+# Prove that the string is expanded only once (by eval)
 run_make_test(q!
-$(guile (gmk-eval "VAR = hi $(shell echo there)"))
+TEST = bye
+EVAL = VAR = $(TEST) $(shell echo there)
+$(guile (gmk-eval "$(value EVAL)"))
+TEST = hi
 x:;@echo '$(VAR)'
 !,
               '', "hi there");
@@ -80,7 +84,7 @@ define fib
 ;; A procedure for counting the n:th Fibonacci number
 ;; See SICP, p. 37
 (define (fib n)
-  (cond ((= n 0) 0)  
+  (cond ((= n 0) 0)
         ((= n 1) 1)
         (else (+ (fib (- n 1))
                  (fib (- n 2))))))
index ac096d459307eddeeaa17e9576d4706c59e93811..9b5d61306d35164a7edc47eafbc7d9d324a58f89 100644 (file)
@@ -167,7 +167,7 @@ void init_hash_global_variable_set (void);
 void hash_init_function_table (void);
 void define_new_function(const gmk_floc *flocp,
                          const char *name, int min, int max, int expand,
-                         char *(*func)(char *, char **, const char *));
+                         char *(*func)(const char *, int, char **));
 struct variable *lookup_variable (const char *name, unsigned int length);
 struct variable *lookup_variable_in_set (const char *name, unsigned int length,
                                          const struct variable_set *set);