This also defines SOEXT as the extension of shared objects (ie. "so" or "dll"), and uses it everywhere. I assumed this must already be defined by either autoconf or mingw (like OBJEXT) but I cannot find anything except in glib (G_MODULE_SUFFIX). Thanks: Zebediah Figura for helping out with exec vs spawn on Windows. --- configure.ac | 8 +++- common/utils/windows-compat.h | 3 ++ server/fuzzer.c | 2 +- server/main.c | 6 +-- wrapper.c | 72 ++++++++++++++++++++++++++++++++--- README | 18 ++++----- 6 files changed, 87 insertions(+), 22 deletions(-) diff --git a/configure.ac b/configure.ac index 0b17ef95..61ee3a02 100644 --- a/configure.ac +++ b/configure.ac @@ -489,12 +489,18 @@ AS_CASE([$host_os], LIBS="$LIBS -lmsvcrt -lkernel32 -luser32" NO_UNDEFINED_ON_WINDOWS="-no-undefined" IMPORT_LIBRARY_ON_WINDOWS='-Wl,-L$(top_builddir)/server -Wl,-lnbdkit' + SOEXT="dll" ], - [is_windows=no] + [is_windows=no], [ + SOEXT="so" + ] ) AC_MSG_RESULT([$is_windows]) AC_SUBST([NO_UNDEFINED_ON_WINDOWS]) AC_SUBST([IMPORT_LIBRARY_ON_WINDOWS]) +AC_SUBST([SOEXT]) +AC_DEFINE_UNQUOTED([SOEXT],["$SOEXT"],[Extension used for shared objects/DLLs.]) +AC_DEFINE_UNQUOTED([EXEEXT],["$EXEEXT"],[Extension used for executables.]) AM_CONDITIONAL([IS_WINDOWS],[test "x$is_windows" = "xyes"]) AS_IF([test "x$is_windows" = "xyes"],[ diff --git a/common/utils/windows-compat.h b/common/utils/windows-compat.h index 74241a19..0fcb20d2 100644 --- a/common/utils/windows-compat.h +++ b/common/utils/windows-compat.h @@ -103,6 +103,9 @@ extern int win_send (int fd, const void *buf, size_t len, int flags); #define dup _dup #define dup2 _dup2 +/* setenv replacement. */ +#define setenv(k, v, replace) _putenv_s ((k), (v)); + /* Unfortunately quite commonly used at the moment. Make it a common * macro so we can easily find places which need porting. * diff --git a/server/fuzzer.c b/server/fuzzer.c index c28a1798..4bbb0061 100644 --- a/server/fuzzer.c +++ b/server/fuzzer.c @@ -119,7 +119,7 @@ server (int sock) "nbdkit", "-s", /* take input from stdin/stdout */ "--log=null", /* discard error messages */ - "plugins/memory/.libs/nbdkit-memory-plugin.so", "1M", + "plugins/memory/.libs/nbdkit-memory-plugin." SOEXT, "1M", NULL }; const int argc = sizeof argv / sizeof argv[0] - 1; diff --git a/server/main.c b/server/main.c index fa5073d6..4b6f72e1 100644 --- a/server/main.c +++ b/server/main.c @@ -497,7 +497,7 @@ main (int argc, char *argv[]) /* Incorrect use of --dump-plugin. */ fprintf (stderr, "%s: use 'nbdkit plugin --dump-plugin' or\n" - "'nbdkit /path/to/plugin.so --dump-plugin'\n", + "'nbdkit /path/to/plugin." SOEXT " --dump-plugin'\n", program_name); exit (EXIT_FAILURE); } @@ -819,7 +819,7 @@ open_plugin_so (size_t i, const char *name, int short_name) if (short_name) { /* Short names are rewritten relative to the plugindir. */ if (asprintf (&filename, - "%s/nbdkit-%s-plugin.so", plugindir, name) == -1) { + "%s/nbdkit-%s-plugin." SOEXT, plugindir, name) == -1) { perror ("asprintf"); exit (EXIT_FAILURE); } @@ -872,7 +872,7 @@ open_filter_so (struct backend *next, size_t i, if (short_name) { /* Short names are rewritten relative to the filterdir. */ if (asprintf (&filename, - "%s/nbdkit-%s-filter.so", filterdir, name) == -1) { + "%s/nbdkit-%s-filter." SOEXT, filterdir, name) == -1) { perror ("asprintf"); exit (EXIT_FAILURE); } diff --git a/wrapper.c b/wrapper.c index c27afae0..fe750936 100644 --- a/wrapper.c +++ b/wrapper.c @@ -72,6 +72,7 @@ #include <time.h> #include "options.h" +#include "windows-compat.h" #include "utils.h" /* Construct an array of parameters passed through to real nbdkit. */ @@ -131,6 +132,45 @@ print_command (void) fputc ('\n', stderr); } +#ifdef WIN32 +/* Windows behaviour of _spawnvp is completely retarded: + * https://stackoverflow.com/questions/4146980/how-to-avoid-space-splitting-and... + */ +static const char * +quote_string_for_spawn (const char *str) +{ + size_t i, len; + char *p, *ret = (char *) str; + + if (strchr (str, ' ') || strchr (str, '"')) { + len = strlen (str); + + p = ret = malloc (2 + len*2 + 1); + if (ret == NULL) { + perror ("malloc"); + exit (EXIT_FAILURE); + } + + *p++ = '"'; + for (i = 0; i < len; ++i) { + switch (str[i]) { + case '"': + *p++ = '\\'; + *p++ = '"'; + break; + default: + *p++ = str[i]; + } + } + *p++ = '"'; + *p++ = '\0'; + } + + /* We never free these strings. */ + return ret; +} +#endif /* WIN32 */ + int main (int argc, char *argv[]) { @@ -141,6 +181,7 @@ main (int argc, char *argv[]) char ts[32]; int r; +#ifndef WIN32 /* If NBDKIT_VALGRIND=1 is set in the environment, then we run the * program under valgrind. This is used by the tests. Similarly if * NBDKIT_GDB=1 is set, we run the program under GDB, useful during @@ -167,9 +208,15 @@ main (int argc, char *argv[]) passthru ("--args"); } } +#endif /* Needed for plugins written in OCaml. */ - s = getenv ("LD_LIBRARY_PATH"); +#ifndef WIN32 +#define LD_LIBRARY_PATH "LD_LIBRARY_PATH" +#else +#define LD_LIBRARY_PATH "PATH" +#endif + s = getenv (LD_LIBRARY_PATH); if (s) r = asprintf (&s, "%s/plugins/ocaml/.libs:%s", builddir, s); else @@ -178,7 +225,7 @@ main (int argc, char *argv[]) perror ("asprintf"); exit (EXIT_FAILURE); } - setenv ("LD_LIBRARY_PATH", s, 1); + setenv (LD_LIBRARY_PATH, s, 1); free (s); s = getenv ("LIBRARY_PATH"); if (s) @@ -193,7 +240,7 @@ main (int argc, char *argv[]) free (s); /* Absolute path of the real nbdkit command. */ - passthru_format ("%s/server/nbdkit", builddir); + passthru_format ("%s/server/nbdkit" EXEEXT, builddir); /* Option parsing. We don't really parse options here. We are only * interested in which options have arguments and which need @@ -225,7 +272,8 @@ main (int argc, char *argv[]) /* Filters can be rewritten if they are a short name. */ else if (c == FILTER_OPTION) { if (is_short_name (optarg)) - passthru_format ("--filter=%s/filters/%s/.libs/nbdkit-%s-filter.so", + passthru_format ("--filter=" + "%s/filters/%s/.libs/nbdkit-%s-filter." SOEXT, builddir, optarg, optarg); else passthru_format ("--filter=%s", optarg); @@ -258,13 +306,13 @@ main (int argc, char *argv[]) if (is_short_name (argv[optind])) { /* Special plugins written in Perl. */ if (is_perl_plugin (argv[optind])) { - passthru_format ("%s/plugins/perl/.libs/nbdkit-perl-plugin.so", + passthru_format ("%s/plugins/perl/.libs/nbdkit-perl-plugin." SOEXT, builddir); passthru_format ("%s/plugins/%s/nbdkit-%s-plugin", builddir, argv[optind], argv[optind]); } else { - passthru_format ("%s/plugins/%s/.libs/nbdkit-%s-plugin.so", + passthru_format ("%s/plugins/%s/.libs/nbdkit-%s-plugin." SOEXT, builddir, argv[optind], argv[optind]); } ++optind; @@ -295,7 +343,19 @@ main (int argc, char *argv[]) setenv ("MALLOC_PERTURB_", ts, 0); /* Run the final command. */ +#ifndef WIN32 execvp (cmd[0], (char **) cmd); perror (cmd[0]); exit (EXIT_FAILURE); +#else /* WIN32 */ + size_t i; + for (i = 1; cmd[i] != NULL; ++i) + cmd[i] = quote_string_for_spawn (cmd[i]); + r = _spawnvp (_P_WAIT, cmd[0], cmd); + if (r == -1) { + perror (cmd[0]); + exit (EXIT_FAILURE); + } + exit (r == 0 ? EXIT_SUCCESS : EXIT_FAILURE); +#endif /* WIN32 */ } diff --git a/README b/README index 2bf8ce5f..56b0c984 100644 --- a/README +++ b/README @@ -331,21 +331,17 @@ To cross compile do: mingw64-configure --disable-ocaml --disable-perl --disable-vddk mingw64-make -It is expected to fail, but check that it gets as far as building -server/nbdkit.exe. You can test if the server is working by doing: +You can test if the server is working by doing: - wine server/nbdkit.exe --dump-config + ./nbdkit.exe --dump-config -Now try to build plugins and filters (many will not compile): +(This usually runs wine automatically. If not, you may need to prefix +the command "wine ./nbdkit.exe ...") - mingw64-make -k +To see which plugins and filters were compiled: -To see which ones were compiled: - - find -name '*.dll' + find plugins filters -name '*.dll' You can run them under Wine without installing using eg: - wine server/nbdkit.exe -f -v \ - plugins/memory/.libs/nbdkit-memory-plugin.dll \ - size=1G + ./nbdkit.exe -f -v memory 1G -- 2.27.0