Expand the loadable object support.

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.
This commit is contained in:
Paul Smith 2013-02-25 01:38:36 -05:00
parent 4baf9ab456
commit 5058a94ee7
19 changed files with 503 additions and 148 deletions

3
.gitignore vendored
View file

@ -42,4 +42,5 @@ config.ami
config.h-vms
config.h.W32
configh.dos
make*.tar.*
make-[0-9]*/
make-[0-9]*.tar.*

View file

@ -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.

View 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; \

1
dep.h
View file

@ -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);

View 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}

View 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);
}

View file

@ -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))))

View file

@ -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_ */

34
guile.c
View file

@ -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 scm_with_guile (internal_guile_eval, argv[0]);
return o;
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;
}

2
load.c
View file

@ -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;

49
loadapi.c Normal file
View file

@ -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);
}

2
main.c
View file

@ -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);
}

6
read.c
View file

@ -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;

View 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 "./"

View file

@ -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}

View 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;

View file

@ -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;

View 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))))))

View 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);