mirror of
https://github.com/mirror/make.git
synced 2025-03-09 09:10:36 +08:00
Allow dynamically loaded objects to be rebuilt by make.
This commit is contained in:
parent
8e0a5645b8
commit
b70aa3709e
19
ChangeLog
19
ChangeLog
@ -1,5 +1,24 @@
|
|||||||
2013-01-19 Paul Smith <psmith@gnu.org>
|
2013-01-19 Paul Smith <psmith@gnu.org>
|
||||||
|
|
||||||
|
* doc/make.texi (load Directive): Update to discuss location of
|
||||||
|
loaded object file.
|
||||||
|
(Remaking Loaded Objects): Document remaking of loaded objects.
|
||||||
|
|
||||||
|
* main.c (main): Rename READ_MAKEFILES to READ_FILES.
|
||||||
|
* read.c: Change READ_MAKEFILES to READ_FILES since it now
|
||||||
|
contains loaded object files as well.
|
||||||
|
(read_all_makefiles): Ditto.
|
||||||
|
(eval_makefile): Ditto.
|
||||||
|
(eval): Add any loaded file to the READ_FILES list, so that it
|
||||||
|
will be considered for re-build.
|
||||||
|
|
||||||
|
* load.c (load_file): Return the simple filename (no symbol) in
|
||||||
|
the LDNAME argument (now a const char **).
|
||||||
|
This filename should no longer have "./" prepended: modify the
|
||||||
|
function to always check the current directory if the name has no
|
||||||
|
"/", before using the normal methods.
|
||||||
|
* make.h: Change the load_file() prototype.
|
||||||
|
|
||||||
* README.git: Add a bit more documentation on Git workflow & rules.
|
* README.git: Add a bit more documentation on Git workflow & rules.
|
||||||
|
|
||||||
2013-01-13 Paul Smith <psmith@gnu.org>
|
2013-01-13 Paul Smith <psmith@gnu.org>
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
@include version.texi
|
@include version.texi
|
||||||
@set EDITION 0.72
|
@set EDITION 0.72
|
||||||
@set RCSID $Id$
|
|
||||||
|
|
||||||
@settitle GNU @code{make}
|
@settitle GNU @code{make}
|
||||||
@setchapternewpage odd
|
@setchapternewpage odd
|
||||||
@ -27,7 +26,7 @@ of @cite{The GNU Make Manual}, for GNU @code{make} version @value{VERSION}.
|
|||||||
|
|
||||||
Copyright @copyright{} 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
|
Copyright @copyright{} 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
|
||||||
1996, 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007,
|
1996, 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007,
|
||||||
2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
|
2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
|
||||||
|
|
||||||
@quotation
|
@quotation
|
||||||
Permission is granted to copy, distribute and/or modify this document
|
Permission is granted to copy, distribute and/or modify this document
|
||||||
@ -348,6 +347,7 @@ GNU Guile Integration
|
|||||||
Loading Dynamic Objects
|
Loading Dynamic Objects
|
||||||
|
|
||||||
* load Directive:: Loading dynamic objects as extensions.
|
* load Directive:: Loading dynamic objects as extensions.
|
||||||
|
* Remaking Loaded Objects:: How loaded objects get remade.
|
||||||
|
|
||||||
@end detailmenu
|
@end detailmenu
|
||||||
@end menu
|
@end menu
|
||||||
@ -1287,7 +1287,6 @@ in the makefiles. @xref{Include, , Including Other Makefiles}.
|
|||||||
|
|
||||||
@node Remaking Makefiles, Overriding Makefiles, MAKEFILES Variable, Makefiles
|
@node Remaking Makefiles, Overriding Makefiles, MAKEFILES Variable, Makefiles
|
||||||
@section How Makefiles Are Remade
|
@section How Makefiles Are Remade
|
||||||
|
|
||||||
@cindex updating makefiles
|
@cindex updating makefiles
|
||||||
@cindex remaking makefiles
|
@cindex remaking makefiles
|
||||||
@cindex makefile, remaking of
|
@cindex makefile, remaking of
|
||||||
@ -6295,7 +6294,6 @@ Has GNU Guile available as an embedded extension language.
|
|||||||
@item load
|
@item load
|
||||||
Supports dynamically loadable objects for creating custom extensions.
|
Supports dynamically loadable objects for creating custom extensions.
|
||||||
@xref{Loading Objects, ,Loading Dynamic Objects}.
|
@xref{Loading Objects, ,Loading Dynamic Objects}.
|
||||||
|
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
@vindex .INCLUDE_DIRS @r{(list of include directories)}
|
@vindex .INCLUDE_DIRS @r{(list of include directories)}
|
||||||
@ -10539,11 +10537,11 @@ program, although this can be inefficient.
|
|||||||
In cases where the built-in capabilities of GNU @code{make} are
|
In cases where the built-in capabilities of GNU @code{make} are
|
||||||
insufficient to your requirements there are two options for extending
|
insufficient to your requirements there are two options for extending
|
||||||
@code{make}. On systems where it's provided, you can utilize GNU
|
@code{make}. On systems where it's provided, you can utilize GNU
|
||||||
Guile as an embedded scripting language (@pxref{Guile Integration,
|
Guile as an embedded scripting language (@pxref{Guile Integration,,GNU
|
||||||
,GNU Guile Integration}). On systems which support dynamically
|
Guile Integration}). On systems which support dynamically loadable
|
||||||
loadable objects, you can write your own extension in any language
|
objects, you can write your own extension in any language (which can
|
||||||
(which can be compiled into such an object) and load it to provide
|
be compiled into such an object) and load it to provide extended
|
||||||
extended capabilities (@pxref{load Directive, ,The @code{load} Directive}).
|
capabilities (@pxref{load Directive, ,The @code{load} Directive}).
|
||||||
|
|
||||||
@menu
|
@menu
|
||||||
* Guile Integration:: Using Guile as an embedded scripting language.
|
* Guile Integration:: Using Guile as an embedded scripting language.
|
||||||
@ -10786,15 +10784,16 @@ providing new capabilities which may then be invoked by your makefile.
|
|||||||
The @code{load} directive is used to load a dynamic object. Once the
|
The @code{load} directive is used to load a dynamic object. Once the
|
||||||
object is loaded, a ``setup'' function will be invoked to allow the
|
object is loaded, a ``setup'' function will be invoked to allow the
|
||||||
object to initialize itself and register new facilities with GNU
|
object to initialize itself and register new facilities with GNU
|
||||||
@code{make}. Typically a dynamic object would create new functions,
|
@code{make}. A dynamic object might include new @code{make} functions,
|
||||||
for example, and the ``setup'' function would register them with GNU
|
for example, and the ``setup'' function would register them with GNU
|
||||||
@code{make}'s function handling system.
|
@code{make}'s function handling system.
|
||||||
|
|
||||||
@menu
|
@menu
|
||||||
* load Directive:: Loading dynamic objects as extensions.
|
* load Directive:: Loading dynamic objects as extensions.
|
||||||
|
* Remaking Loaded Objects:: How loaded objects get remade.
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
@node load Directive, , Loading Objects, Loading Objects
|
@node load Directive, Remaking Loaded Objects, Loading Objects, Loading Objects
|
||||||
@subsection The @code{load} Directive
|
@subsection The @code{load} Directive
|
||||||
@cindex load directive
|
@cindex load directive
|
||||||
@cindex extensions, load directive
|
@cindex extensions, load directive
|
||||||
@ -10814,21 +10813,33 @@ or:
|
|||||||
load @var{object-file}(@var{symbol-name}) @dots{}
|
load @var{object-file}(@var{symbol-name}) @dots{}
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
In the first form, the file @var{object-file} is dynamically loaded by
|
The file @var{object-file} is dynamically loaded by GNU @code{make}.
|
||||||
GNU @code{make}. On failure, @code{make} will print a message and
|
If @var{object-file} does not include a directory path then it is
|
||||||
exit. If the load succeeds @code{make} will invoke an initializing
|
first looked for in the current directory. If it is not found there,
|
||||||
function whose name is created by taking the base file name of
|
or a directory path is included, then system-specific paths will be
|
||||||
@var{object-file}, up to the first character which is not a valid
|
searched. If the load fails for any reason, @code{make} will print a
|
||||||
symbol name character (alphanumerics and underscores are valid symbol
|
message and exit.
|
||||||
name characters). To this prefix will be appended the suffix
|
|
||||||
@code{_gmake_setup}, then this symbol will be invoked.
|
|
||||||
|
|
||||||
In the second form, the function @var{symbol-name} will be invoked.
|
If the load succeeds @code{make} will invoke an initializing function.
|
||||||
|
|
||||||
|
If @var{symbol-name} is provided, it will be used as the name of the
|
||||||
|
initializing function.
|
||||||
|
|
||||||
|
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}.
|
||||||
|
|
||||||
More than one object file may be loaded with a single @code{load}
|
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
|
directive, and both forms of @code{load} arguments may be used in the
|
||||||
same directive.
|
same directive.
|
||||||
|
|
||||||
|
The initializing function will be provided the file name and line
|
||||||
|
number of the invocation of the @code{load} operation. It should
|
||||||
|
return a value of type @code{int}, which must be @code{0} on failure
|
||||||
|
and non-@code{0} on success.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
@example
|
@example
|
||||||
@ -10864,6 +10875,22 @@ generated if an object fails to load. The failed object is not added
|
|||||||
to the @code{.LOADED} variable, which can then be consulted to
|
to the @code{.LOADED} variable, which can then be consulted to
|
||||||
determine if the load was successful.
|
determine if the load was successful.
|
||||||
|
|
||||||
|
@node Remaking Loaded Objects, , load Directive, Loading Objects
|
||||||
|
@subsection How Loaded Objects Are Remade
|
||||||
|
@cindex updating load objects
|
||||||
|
@cindex remaking load objects
|
||||||
|
@cindex load objects, remaking of
|
||||||
|
|
||||||
|
Loaded objects undergo the same re-make procedure as makefiles
|
||||||
|
(@pxref{Remaking Makefiles, ,How Makefiles Are Remade}). If any
|
||||||
|
loaded object is recreated, then @code{make} will start from scratch
|
||||||
|
and re-read all the makefiles, and reload the object files again. It
|
||||||
|
is not necessary for the loaded object to do anything special to
|
||||||
|
support this.@refill
|
||||||
|
|
||||||
|
It's up to the makefile author to provide the rules needed for
|
||||||
|
rebuilding the loaded object.
|
||||||
|
|
||||||
@node Features, Missing, Extending make, Top
|
@node Features, Missing, Extending make, Top
|
||||||
@chapter Features of GNU @code{make}
|
@chapter Features of GNU @code{make}
|
||||||
@cindex features of GNU @code{make}
|
@cindex features of GNU @code{make}
|
||||||
@ -11007,6 +11034,12 @@ nonexistent file comes from SunOS 4 @code{make}. (But note that SunOS 4
|
|||||||
The @code{!=} shell assignment operator exists in many BSD of
|
The @code{!=} shell assignment operator exists in many BSD of
|
||||||
@code{make} and is purposefully implemented here to behave identically
|
@code{make} and is purposefully implemented here to behave identically
|
||||||
to those implementations.
|
to those implementations.
|
||||||
|
|
||||||
|
@item
|
||||||
|
Various build management tools are implemented using scripting
|
||||||
|
languages such as Perl or Python and thus provide a natural embedded
|
||||||
|
scripting language, similar to GNU @code{make}'s integration of GNU
|
||||||
|
Guile.
|
||||||
@end itemize
|
@end itemize
|
||||||
|
|
||||||
The remaining features are inventions new in GNU @code{make}:
|
The remaining features are inventions new in GNU @code{make}:
|
||||||
@ -11116,6 +11149,10 @@ Various new built-in implicit rules.
|
|||||||
The built-in variable @samp{MAKE_VERSION} gives the version number of
|
The built-in variable @samp{MAKE_VERSION} gives the version number of
|
||||||
@code{make}.
|
@code{make}.
|
||||||
@vindex MAKE_VERSION
|
@vindex MAKE_VERSION
|
||||||
|
|
||||||
|
@item
|
||||||
|
Load dynamic objects which can modify the behavior of @code{make}.
|
||||||
|
@xref{Loading Objects, ,Loading Dynamic Objects}.
|
||||||
@end itemize
|
@end itemize
|
||||||
|
|
||||||
@node Missing, Makefile Conventions, Features, Top
|
@node Missing, Makefile Conventions, Features, Top
|
||||||
|
43
load.c
43
load.c
@ -58,12 +58,12 @@ init_symbol (const struct floc *flocp, const char *ldname, load_func_t symp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
load_file (const struct floc *flocp, const char *ldname, int noerror)
|
load_file (const struct floc *flocp, const char **ldname, int noerror)
|
||||||
{
|
{
|
||||||
load_func_t symp;
|
load_func_t symp;
|
||||||
const char *fp;
|
const char *fp;
|
||||||
char *symname = NULL;
|
char *symname = NULL;
|
||||||
char *new = alloca (strlen (ldname) + CSTRLEN (SYMBOL_EXTENSION) + 1);
|
char *new = alloca (strlen (*ldname) + CSTRLEN (SYMBOL_EXTENSION) + 1);
|
||||||
|
|
||||||
if (! global_dl)
|
if (! global_dl)
|
||||||
{
|
{
|
||||||
@ -73,27 +73,27 @@ load_file (const struct floc *flocp, const char *ldname, int noerror)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If a symbol name was provided, use it. */
|
/* If a symbol name was provided, use it. */
|
||||||
fp = strchr (ldname, '(');
|
fp = strchr (*ldname, '(');
|
||||||
if (fp)
|
if (fp)
|
||||||
{
|
{
|
||||||
const char *ep;
|
const char *ep;
|
||||||
|
|
||||||
/* If there's an open paren, see if there's a close paren: if so use
|
/* There's an open paren, so see if there's a close paren: if so use
|
||||||
that as the symbol name. We can't have whitespace: it would have
|
that as the symbol name. We can't have whitespace: it would have
|
||||||
been chopped up before this function is called. */
|
been chopped up before this function is called. */
|
||||||
ep = strchr (fp+1, ')');
|
ep = strchr (fp+1, ')');
|
||||||
if (ep && ep[1] == '\0')
|
if (ep && ep[1] == '\0')
|
||||||
{
|
{
|
||||||
int l = fp - ldname;;
|
int l = fp - *ldname;;
|
||||||
|
|
||||||
++fp;
|
++fp;
|
||||||
if (fp == ep)
|
if (fp == ep)
|
||||||
fatal (flocp, _("Empty symbol name for load: %s"), ldname);
|
fatal (flocp, _("Empty symbol name for load: %s"), *ldname);
|
||||||
|
|
||||||
/* Make a copy of the ldname part. */
|
/* Make a copy of the ldname part. */
|
||||||
memcpy (new, ldname, l);
|
memcpy (new, *ldname, l);
|
||||||
new[l] = '\0';
|
new[l] = '\0';
|
||||||
ldname = new;
|
*ldname = new;
|
||||||
|
|
||||||
/* Make a copy of the symbol name part. */
|
/* Make a copy of the symbol name part. */
|
||||||
symname = new + l + 1;
|
symname = new + l + 1;
|
||||||
@ -102,14 +102,17 @@ load_file (const struct floc *flocp, const char *ldname, int noerror)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add this name to the string cache so it can be reused later. */
|
||||||
|
*ldname = strcache_add (*ldname);
|
||||||
|
|
||||||
/* If we didn't find a symbol name yet, construct it from the ldname. */
|
/* If we didn't find a symbol name yet, construct it from the ldname. */
|
||||||
if (! symname)
|
if (! symname)
|
||||||
{
|
{
|
||||||
char *p = new;
|
char *p = new;
|
||||||
|
|
||||||
fp = strrchr (ldname, '/');
|
fp = strrchr (*ldname, '/');
|
||||||
if (!fp)
|
if (!fp)
|
||||||
fp = ldname;
|
fp = *ldname;
|
||||||
else
|
else
|
||||||
++fp;
|
++fp;
|
||||||
while (isalnum (*fp) || *fp == '_')
|
while (isalnum (*fp) || *fp == '_')
|
||||||
@ -118,12 +121,22 @@ load_file (const struct floc *flocp, const char *ldname, int noerror)
|
|||||||
symname = new;
|
symname = new;
|
||||||
}
|
}
|
||||||
|
|
||||||
DB (DB_VERBOSE, (_("Loading symbol %s from %s\n"), symname, ldname));
|
DB (DB_VERBOSE, (_("Loading symbol %s from %s\n"), symname, *ldname));
|
||||||
|
|
||||||
/* See if it's already defined. */
|
/* See if it's already defined. */
|
||||||
symp = (load_func_t) dlsym (global_dl, symname);
|
symp = (load_func_t) dlsym (global_dl, symname);
|
||||||
if (! symp) {
|
if (! symp) {
|
||||||
void *dlp = dlopen (ldname, RTLD_LAZY|RTLD_GLOBAL);
|
void *dlp = NULL;
|
||||||
|
|
||||||
|
/* If the path has no "/", try the current directory first. */
|
||||||
|
if (! strchr (*ldname, '/'))
|
||||||
|
dlp = dlopen (concat (2, "./", *ldname), RTLD_LAZY|RTLD_GLOBAL);
|
||||||
|
|
||||||
|
/* If we haven't opened it yet, try the default search path. */
|
||||||
|
if (! dlp)
|
||||||
|
dlp = dlopen (*ldname, RTLD_LAZY|RTLD_GLOBAL);
|
||||||
|
|
||||||
|
/* Still no? Then fail. */
|
||||||
if (! dlp)
|
if (! dlp)
|
||||||
{
|
{
|
||||||
if (noerror)
|
if (noerror)
|
||||||
@ -136,17 +149,17 @@ load_file (const struct floc *flocp, const char *ldname, int noerror)
|
|||||||
symp = dlsym (dlp, symname);
|
symp = dlsym (dlp, symname);
|
||||||
if (! symp)
|
if (! symp)
|
||||||
fatal (flocp, _("Failed to load symbol %s from %s: %s"),
|
fatal (flocp, _("Failed to load symbol %s from %s: %s"),
|
||||||
symname, ldname, dlerror());
|
symname, *ldname, dlerror());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Invoke the symbol to initialize the loaded object. */
|
/* Invoke the symbol to initialize the loaded object. */
|
||||||
return init_symbol(flocp, ldname, symp);
|
return init_symbol(flocp, *ldname, symp);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
int
|
int
|
||||||
load_file (const struct floc *flocp, const char *ldname, int noerror)
|
load_file (const struct floc *flocp, const char **ldname, int noerror)
|
||||||
{
|
{
|
||||||
if (! noerror)
|
if (! noerror)
|
||||||
fatal (flocp, _("The 'load' operation is not supported on this platform."));
|
fatal (flocp, _("The 'load' operation is not supported on this platform."));
|
||||||
|
4
make.h
4
make.h
@ -476,9 +476,9 @@ int strcache_setbufsize (unsigned int size);
|
|||||||
int guile_gmake_setup (const struct floc *flocp);
|
int guile_gmake_setup (const struct floc *flocp);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Loadable object support */
|
/* Loadable object support. Sets to the strcached name of the loaded file. */
|
||||||
typedef int (*load_func_t)(const struct floc *flocp);
|
typedef int (*load_func_t)(const struct floc *flocp);
|
||||||
int load_file (const struct floc *flocp, const char *filename, int noerror);
|
int load_file (const struct floc *flocp, const char **filename, int noerror);
|
||||||
|
|
||||||
#ifdef HAVE_VFORK_H
|
#ifdef HAVE_VFORK_H
|
||||||
# include <vfork.h>
|
# include <vfork.h>
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
2013-01-19 Paul Smith <psmith@gnu.org>
|
||||||
|
|
||||||
|
* scripts/features/load: Test loaded files with and without "./"
|
||||||
|
prefix. Add tests for automatically rebuilding loaded files if
|
||||||
|
they are out of date or non-existent.
|
||||||
|
|
||||||
2013-01-13 Paul Smith <psmith@gnu.org>
|
2013-01-13 Paul Smith <psmith@gnu.org>
|
||||||
|
|
||||||
* scripts/features/archives: Add a check targets that have parens,
|
* scripts/features/archives: Add a check targets that have parens,
|
||||||
|
@ -49,7 +49,7 @@ run_make_test('testload.so: testload.c ; @$(CC) -g -shared -fPIC -o $@ $<',
|
|||||||
# TEST 1
|
# TEST 1
|
||||||
run_make_test(q!
|
run_make_test(q!
|
||||||
all: ; @echo $(func-a foo) $(func-b bar)
|
all: ; @echo $(func-a foo) $(func-b bar)
|
||||||
load ./testload.so
|
load testload.so
|
||||||
!,
|
!,
|
||||||
'', "func-a\n");
|
'', "func-a\n");
|
||||||
|
|
||||||
@ -64,19 +64,42 @@ load ./testload.so(explicit_setup)
|
|||||||
# TEST 3
|
# TEST 3
|
||||||
# Verify the .LOADED variable
|
# Verify the .LOADED variable
|
||||||
run_make_test(q!
|
run_make_test(q!
|
||||||
all: ; @echo $(filter ./testload.so,$(.LOADED)) $(func-a foo) $(func-b bar)
|
all: ; @echo $(filter testload.so,$(.LOADED)) $(func-a foo) $(func-b bar)
|
||||||
load ./testload.so(explicit_setup)
|
load testload.so(explicit_setup)
|
||||||
!,
|
!,
|
||||||
'', "./testload.so func-b\n");
|
'', "testload.so func-b\n");
|
||||||
|
|
||||||
# TEST 4
|
# TEST 4
|
||||||
# Check multiple loads
|
# Check multiple loads
|
||||||
run_make_test(q!
|
run_make_test(q!
|
||||||
all: ; @echo $(filter ./testload.so,$(.LOADED)) $(func-a foo) $(func-b bar)
|
all: ; @echo $(filter testload.so,$(.LOADED)) $(func-a foo) $(func-b bar)
|
||||||
load ./testload.so
|
load ./testload.so
|
||||||
load ./testload.so(explicit_setup)
|
load testload.so(explicit_setup)
|
||||||
!,
|
!,
|
||||||
'', "./testload.so func-a\n");
|
'', "testload.so func-a\n");
|
||||||
|
|
||||||
|
# TEST 5
|
||||||
|
# Check auto-rebuild of loaded file that's out of date
|
||||||
|
utouch(-10, 'testload.so');
|
||||||
|
touch('testload.c');
|
||||||
|
|
||||||
|
run_make_test(q!
|
||||||
|
all: ; @echo $(func-a foo) $(func-b bar)
|
||||||
|
load ./testload.so
|
||||||
|
testload.so: testload.c ; @echo "rebuilding $@"; $(CC) -g -shared -fPIC -o $@ $<
|
||||||
|
!,
|
||||||
|
'', "rebuilding testload.so\nfunc-a\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)
|
||||||
|
-load ./testload.so(explicit_setup)
|
||||||
|
%.so: %.c ; @echo "rebuilding $@"; $(CC) -g -shared -fPIC -o $@ $<
|
||||||
|
!,
|
||||||
|
'', "rebuilding testload.so\nfunc-b\n");
|
||||||
|
|
||||||
unlink(qw(testload.c testload.so)) unless $keep;
|
unlink(qw(testload.c testload.so)) unless $keep;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user