Fix interfacing with and remaking dynamic objects on MS-Windows.

load.c (load_object, load_file): Accept an additional argument
 DLP and return in it a pointer that can be used to unload the
 dynamic object.
 read.c (eval): Call load_file with an additional argument, and
 record the pointer returned there in the 'struct file' object of
 dynamic objects in that object's 'struct file'.
 commands.c (execute_file_commands): Unload dynamic objects
 before remaking them, to avoid failure to remake if the OS doesn't
 allow overwriting objects that are in use.
 filedef.h (struct file): New member dlopen_ptr.
 gnumake.h (GMK_EXPORT): Define to dllexport/dllimport
 decorations for Windows and to nothing on other platforms.
 (gmk_eval, gmk_expand, gmk_add_function): Add GMK_EXPORT qualifier
 to prototypes.
 makeint.h (MAIN): Define before including gnumake.h, to give
 correct dllexport decorations to exported functions.
 (load_file): Adjust prototype.
 loadapi.c: Don't include gnumake.h, since makeint.h already
 includes it, and takes care of defining MAIN before doing so.
 build_w32.bat (LinkGCC): Produce an import library for functions
 exported by Make for loadable dynamic objects.

 w32/compat/posixfcn.c (dlclose): New function.
 w32/include/dlfcn.h (dlclose): Add prototype.

 scripts/features/load: Fix signatures of testload_gmk_setup and
 explicit_setup, to bring them in line with the documentation.
This commit is contained in:
Eli Zaretskii 2013-05-03 16:09:12 +03:00
parent b5ea49bae7
commit a66469e003
13 changed files with 105 additions and 22 deletions

View File

@ -1,3 +1,38 @@
2013-05-03 Eli Zaretskii <eliz@gnu.org>
* load.c (load_object, load_file): Accept an additional argument
DLP and return in it a pointer that can be used to unload the
dynamic object.
* read.c (eval): Call load_file with an additional argument, and
record the pointer returned there in the 'struct file' object of
dynamic objects in that object's 'struct file'.
* commands.c (execute_file_commands): Unload dynamic objects
before remaking them, to avoid failure to remake if the OS doesn't
allow overwriting objects that are in use.
* filedef.h (struct file): New member dlopen_ptr.
* gnumake.h (GMK_EXPORT): Define to dllexport/dllimport
decorations for Windows and to nothing on other platforms.
(gmk_eval, gmk_expand, gmk_add_function): Add GMK_EXPORT qualifier
to prototypes.
* makeint.h (MAIN): Define before including gnumake.h, to give
correct dllexport decorations to exported functions.
(load_file): Adjust prototype.
* loadapi.c: Don't include gnumake.h, since makeint.h already
includes it, and takes care of defining MAIN before doing so.
* build_w32.bat (LinkGCC): Produce an import library for functions
exported by Make for loadable dynamic objects.
* w32/compat/posixfcn.c (dlclose): New function.
* w32/include/dlfcn.h (dlclose): Add prototype.
2013-05-01 Eli Zaretskii <eliz@gnu.org> 2013-05-01 Eli Zaretskii <eliz@gnu.org>
* job.c (start_job_command) [WINDOWS32]: Make the same fix for * job.c (start_job_command) [WINDOWS32]: Make the same fix for

View File

@ -290,8 +290,10 @@ set GUILEOBJ=guile.o
echo on echo on
gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% %GUILECFLAGS% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c guile.c gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% %GUILECFLAGS% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c guile.c
:LinkGCC :LinkGCC
Rem The version NN of libgnumake-NN.dll.a should be bumped whenever
Rem the API changes in binary-incompatible manner.
@echo on @echo on
gcc -mthreads -gdwarf-2 -g3 -o gnumake.exe variable.o rule.o remote-stub.o commands.o file.o getloadavg.o default.o signame.o expand.o dir.o main.o getopt1.o %GUILEOBJ% job.o read.o version.o getopt.o arscan.o remake.o misc.o hash.o strcache.o ar.o function.o vpath.o implicit.o loadapi.o load.o glob.o fnmatch.o pathstuff.o posixfcn.o w32_misc.o sub_proc.o w32err.o %GUILELIBS% -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32 gcc -mthreads -gdwarf-2 -g3 -o gnumake.exe variable.o rule.o remote-stub.o commands.o file.o getloadavg.o default.o signame.o expand.o dir.o main.o getopt1.o %GUILEOBJ% job.o read.o version.o getopt.o arscan.o remake.o misc.o hash.o strcache.o ar.o function.o vpath.o implicit.o loadapi.o load.o glob.o fnmatch.o pathstuff.o posixfcn.o w32_misc.o sub_proc.o w32err.o %GUILELIBS% -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32 -Wl,--out-implib=libgnumake-1.dll.a
@GoTo BuildEnd @GoTo BuildEnd
:Usage :Usage
echo Usage: %0 [options] [gcc] echo Usage: %0 [options] [gcc]

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 You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>. */ this program. If not, see <http://www.gnu.org/licenses/>. */
#include <dlfcn.h>
#include "makeint.h" #include "makeint.h"
#include "dep.h" #include "dep.h"
#include "filedef.h" #include "filedef.h"
@ -468,6 +470,12 @@ execute_file_commands (struct file *file)
set_file_variables (file); set_file_variables (file);
/* If this is a loaded dynamic object, unload it before remaking.
Some systems don't allow to overwrite a loaded shared
library. */
if (file->dlopen_ptr)
dlclose (file->dlopen_ptr);
/* Start the commands running. */ /* Start the commands running. */
new_job (file); new_job (file);
} }

View File

@ -61,6 +61,8 @@ struct file
int command_flags; /* Flags OR'd in for cmds; see commands.h. */ int command_flags; /* Flags OR'd in for cmds; see commands.h. */
char update_status; /* Status of the last attempt to update, char update_status; /* Status of the last attempt to update,
or -1 if none has been made. */ or -1 if none has been made. */
void *dlopen_ptr; /* For dynamic loaded objects: pointer to
pass to dlclose to unload the object. */
enum cmd_state /* State of the commands. */ enum cmd_state /* State of the commands. */
{ /* Note: It is important that cs_not_started be zero. */ { /* Note: It is important that cs_not_started be zero. */
cs_not_started, /* Not yet started. */ cs_not_started, /* Not yet started. */

View File

@ -26,13 +26,23 @@ typedef struct
unsigned long lineno; unsigned long lineno;
} gmk_floc; } gmk_floc;
#ifdef _WIN32
# ifdef MAIN
# define GMK_EXPORT __declspec(dllexport)
# else
# define GMK_EXPORT __declspec(dllimport)
# endif
#else
# define GMK_EXPORT
#endif
/* Run $(eval ...) on the provided string BUFFER. */ /* Run $(eval ...) on the provided string BUFFER. */
void gmk_eval (const char *buffer, const gmk_floc *floc); void GMK_EXPORT gmk_eval (const char *buffer, const gmk_floc *floc);
/* Run GNU make expansion on the provided string STR. /* Run GNU make expansion on the provided string STR.
Returns an allocated buffer that the caller must free. */ Returns an allocated buffer that the caller must free. */
char *gmk_expand (const char *str); char * GMK_EXPORT gmk_expand (const char *str);
/* Register a new GNU make function NAME (maximum of 255 chars long). /* 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 When the function is expanded in the makefile, FUNC will be invoked with
@ -49,8 +59,9 @@ char *gmk_expand (const char *str);
If EXPAND_ARGS is 0, the arguments to the function will not be expanded 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. before FUNC is called. If EXPAND_ARGS is non-0, they will be expanded.
*/ */
void gmk_add_function (const char *name, void GMK_EXPORT gmk_add_function (const char *name,
char *(*func)(const char *nm, int argc, char **argv), char *(*func)(const char *nm,
int min_args, int max_args, int expand_args); int argc, char **argv),
int min_args, int max_args, int expand_args);
#endif /* _GNUMAKE_H_ */ #endif /* _GNUMAKE_H_ */

21
load.c
View File

@ -32,11 +32,13 @@ this program. If not, see <http://www.gnu.org/licenses/>. */
static load_func_t static load_func_t
load_object (const gmk_floc *flocp, int noerror, load_object (const gmk_floc *flocp, int noerror,
const char *ldname, const char *symname) const char *ldname, const char *symname, void **dlp)
{ {
static void *global_dl = NULL; static void *global_dl = NULL;
load_func_t symp; load_func_t symp;
*dlp = NULL;
if (! global_dl) if (! global_dl)
{ {
global_dl = dlopen (NULL, RTLD_NOW|RTLD_GLOBAL); global_dl = dlopen (NULL, RTLD_NOW|RTLD_GLOBAL);
@ -46,7 +48,6 @@ load_object (const gmk_floc *flocp, int noerror,
symp = (load_func_t) dlsym (global_dl, symname); symp = (load_func_t) dlsym (global_dl, symname);
if (! symp) { if (! symp) {
void *dlp = NULL;
/* If the path has no "/", try the current directory first. */ /* If the path has no "/", try the current directory first. */
if (! strchr (ldname, '/') if (! strchr (ldname, '/')
@ -54,14 +55,14 @@ load_object (const gmk_floc *flocp, int noerror,
&& ! strchr (ldname, '\\') && ! strchr (ldname, '\\')
#endif #endif
) )
dlp = dlopen (concat (2, "./", ldname), RTLD_LAZY|RTLD_GLOBAL); *dlp = dlopen (concat (2, "./", ldname), RTLD_LAZY|RTLD_GLOBAL);
/* If we haven't opened it yet, try the default search path. */ /* If we haven't opened it yet, try the default search path. */
if (! dlp) if (! *dlp)
dlp = dlopen (ldname, RTLD_LAZY|RTLD_GLOBAL); *dlp = dlopen (ldname, RTLD_LAZY|RTLD_GLOBAL);
/* Still no? Then fail. */ /* Still no? Then fail. */
if (! dlp) if (! *dlp)
{ {
if (noerror) if (noerror)
DB (DB_BASIC, ("%s", dlerror())); DB (DB_BASIC, ("%s", dlerror()));
@ -70,7 +71,7 @@ load_object (const gmk_floc *flocp, int noerror,
return NULL; return NULL;
} }
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());
@ -80,7 +81,7 @@ load_object (const gmk_floc *flocp, int noerror,
} }
int int
load_file (const gmk_floc *flocp, const char **ldname, int noerror) load_file (const gmk_floc *flocp, const char **ldname, int noerror, void **dlp)
{ {
int nmlen = strlen (*ldname); int nmlen = strlen (*ldname);
char *new = alloca (nmlen + CSTRLEN (SYMBOL_EXTENSION) + 1); char *new = alloca (nmlen + CSTRLEN (SYMBOL_EXTENSION) + 1);
@ -90,6 +91,8 @@ load_file (const gmk_floc *flocp, const char **ldname, int noerror)
int r; int r;
load_func_t symp; load_func_t symp;
*dlp = NULL;
/* Break the input into an object file name and a symbol name. If no symbol /* Break the input into an object file name and a symbol name. If no symbol
name was provided, compute one from the object file name. */ name was provided, compute one from the object file name. */
fp = strchr (*ldname, '('); fp = strchr (*ldname, '(');
@ -165,7 +168,7 @@ load_file (const gmk_floc *flocp, const char **ldname, int noerror)
DB (DB_VERBOSE, (_("Loading symbol %s from %s\n"), symname, *ldname)); DB (DB_VERBOSE, (_("Loading symbol %s from %s\n"), symname, *ldname));
/* Load it! */ /* Load it! */
symp = load_object(flocp, noerror, *ldname, symname); symp = load_object(flocp, noerror, *ldname, symname, dlp);
if (! symp) if (! symp)
return 0; return 0;

View File

@ -14,8 +14,6 @@ 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 You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>. */ this program. If not, see <http://www.gnu.org/licenses/>. */
#include "gnumake.h"
#include "makeint.h" #include "makeint.h"
#include "filedef.h" #include "filedef.h"

View File

@ -48,8 +48,12 @@ char *alloca ();
#endif #endif
/* Include the externally-visible content. /* Include the externally-visible content.
Be sure to use the local one, and not one installed on the system. */ Be sure to use the local one, and not one installed on the system.
Define MAIN for proper selection of dllexport/dllimport declarations
for MS-Windows. */
#define MAIN
#include "gnumake.h" #include "gnumake.h"
#undef MAIN
#ifdef CRAY #ifdef CRAY
/* This must happen before #include <signal.h> so /* This must happen before #include <signal.h> so
@ -476,7 +480,8 @@ int guile_gmake_setup (const gmk_floc *flocp);
/* Loadable object support. Sets to the strcached name of the loaded file. */ /* Loadable object support. Sets to the strcached name of the loaded file. */
typedef int (*load_func_t)(const gmk_floc *flocp); typedef int (*load_func_t)(const gmk_floc *flocp);
int load_file (const gmk_floc *flocp, const char **filename, int noerror); int load_file (const gmk_floc *flocp, const char **filename, int noerror,
void **dlp);
#ifdef HAVE_VFORK_H #ifdef HAVE_VFORK_H
# include <vfork.h> # include <vfork.h>

4
read.c
View File

@ -937,11 +937,12 @@ eval (struct ebuffer *ebuf, int set_default)
struct nameseq *next = files->next; struct nameseq *next = files->next;
const char *name = files->name; const char *name = files->name;
struct dep *deps; struct dep *deps;
void *dlp;
free_ns (files); free_ns (files);
files = next; files = next;
if (! load_file (&ebuf->floc, &name, noerror) && ! noerror) if (! load_file (&ebuf->floc, &name, noerror, &dlp) && ! noerror)
fatal (&ebuf->floc, _("%s: failed to load"), name); fatal (&ebuf->floc, _("%s: failed to load"), name);
deps = alloc_dep (); deps = alloc_dep ();
@ -950,6 +951,7 @@ eval (struct ebuffer *ebuf, int set_default)
deps->file = lookup_file (name); deps->file = lookup_file (name);
if (deps->file == 0) if (deps->file == 0)
deps->file = enter_file (name); deps->file = enter_file (name);
deps->file->dlopen_ptr = dlp;
} }
continue; continue;

View File

@ -1,3 +1,8 @@
2013-05-03 Eli Zaretskii <eliz@gnu.org>
* scripts/features/load: Fix signatures of testload_gmk_setup and
explicit_setup, to bring them in line with the documentation.
2013-04-28 Paul Smith <psmith@gnu.org> 2013-04-28 Paul Smith <psmith@gnu.org>
* scripts/features/output-sync (output_sync_set): Add tests for * scripts/features/output-sync (output_sync_set): Add tests for

View File

@ -21,14 +21,14 @@ print $F <<'EOF' ;
#include "gnumake.h" #include "gnumake.h"
int int
testload_gmk_setup () testload_gmk_setup (gmk_floc *pos)
{ {
gmk_eval ("TESTLOAD = implicit", 0); gmk_eval ("TESTLOAD = implicit", 0);
return 1; return 1;
} }
int int
explicit_setup () explicit_setup (gmk_floc *pos)
{ {
gmk_eval ("TESTLOAD = explicit", 0); gmk_eval ("TESTLOAD = explicit", 0);
return 1; return 1;

View File

@ -338,6 +338,17 @@ dlsym (void *handle, const char *name)
return (void *)addr; return (void *)addr;
} }
int
dlclose (void *handle)
{
if (!handle || handle == INVALID_HANDLE_VALUE)
return -1;
if (!FreeLibrary (handle))
return -1;
return 0;
}
#endif /* MAKE_LOAD */ #endif /* MAKE_LOAD */

View File

@ -9,5 +9,6 @@
extern void *dlopen (const char *, int); extern void *dlopen (const char *, int);
extern void *dlsym (void *, const char *); extern void *dlsym (void *, const char *);
extern char *dlerror (void); extern char *dlerror (void);
extern int dlclose (void *);
#endif /* DLFCN_H */ #endif /* DLFCN_H */