mirror of
https://git.savannah.gnu.org/git/make.git
synced 2024-12-26 14:00:55 +00:00
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:
parent
4baf9ab456
commit
5058a94ee7
19 changed files with 503 additions and 148 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -42,4 +42,5 @@ config.ami
|
|||
config.h-vms
|
||||
config.h.W32
|
||||
configh.dos
|
||||
make*.tar.*
|
||||
make-[0-9]*/
|
||||
make-[0-9]*.tar.*
|
||||
|
|
27
ChangeLog
27
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.
|
||||
|
|
14
Makefile.am
14
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; \
|
||||
|
|
1
dep.h
1
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);
|
||||
|
|
148
doc/make.texi
148
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}
|
||||
|
|
125
function.c
125
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);
|
||||
}
|
||||
|
|
|
@ -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))))
|
||||
|
|
29
gnumake.h
29
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_ */
|
||||
|
|
34
guile.c
34
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 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
2
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;
|
||||
|
||||
|
|
49
loadapi.c
Normal file
49
loadapi.c
Normal 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
2
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);
|
||||
}
|
||||
|
||||
|
|
6
read.c
6
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;
|
||||
|
|
|
@ -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 "./"
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
84
tests/scripts/features/loadapi
Normal file
84
tests/scripts/features/loadapi
Normal 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;
|
|
@ -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))))))
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue