summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--ChangeLog27
-rw-r--r--Makefile.am14
-rw-r--r--dep.h1
-rw-r--r--doc/make.texi148
-rw-r--r--function.c125
-rw-r--r--gmk-default.scm4
-rw-r--r--gnumake.h29
-rw-r--r--guile.c36
-rw-r--r--load.c2
-rw-r--r--loadapi.c49
-rw-r--r--main.c2
-rw-r--r--read.c6
-rw-r--r--tests/ChangeLog9
-rwxr-xr-xtests/run_make_tests.pl35
-rw-r--r--tests/scripts/features/load69
-rw-r--r--tests/scripts/features/loadapi84
-rw-r--r--tests/scripts/functions/guile8
-rw-r--r--variable.h2
19 files changed, 504 insertions, 149 deletions
diff --git a/.gitignore b/.gitignore
index 5d0af62..0173826 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,4 +42,5 @@ config.ami
config.h-vms
config.h.W32
configh.dos
-make*.tar.*
+make-[0-9]*/
+make-[0-9]*.tar.*
diff --git a/ChangeLog b/ChangeLog
index a2d2796..9918dc6 100644
--- 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.
diff --git a/Makefile.am b/Makefile.am
index 3e3b480..35ff73b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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 3854f31..1a50e25 100644
--- 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);
diff --git a/doc/make.texi b/doc/make.texi
index f5ca116..2fe7aa6 100644
--- a/doc/make.texi
+++ b/doc/make.texi
@@ -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}
diff --git a/function.c b/function.c
index 15b7aea..b6060d6 100644
--- a/function.c
+++ b/function.c
@@ -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);
}
diff --git a/gmk-default.scm b/gmk-default.scm
index 84a2114..d33dfb2 100644
--- a/gmk-default.scm
+++ b/gmk-default.scm
@@ -45,10 +45,6 @@
(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))))
diff --git a/gnumake.h b/gnumake.h
index 1648b33..c6f7bd8 100644
--- 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 47a21f9..28fcf39 100644
--- 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 0ed05f9..081d66f 100644
--- 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
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 d06825e..0a2774c 100644
--- 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 68287b7..af5cf01 100644
--- 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;
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 2c1cb1b..afa1cbe 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -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 "./"
diff --git a/tests/run_make_tests.pl b/tests/run_make_tests.pl
index 4accd4a..a596328 100755
--- a/tests/run_make_tests.pl
+++ b/tests/run_make_tests.pl
@@ -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}
diff --git a/tests/scripts/features/load b/tests/scripts/features/load
index dd3daf8..47267fe 100644
--- a/tests/scripts/features/load
+++ b/tests/scripts/features/load
@@ -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
index 0000000..cecb114
--- /dev/null
+++ b/tests/scripts/features/loadapi
@@ -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;
diff --git a/tests/scripts/functions/guile b/tests/scripts/functions/guile
index 93a18ab..c63bec9 100644
--- a/tests/scripts/functions/guile
+++ b/tests/scripts/functions/guile
@@ -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))))))
diff --git a/variable.h b/variable.h
index ac096d4..9b5d613 100644
--- a/variable.h
+++ b/variable.h
@@ -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);