Support --output-sync on MS-Windows.
authorEli Zaretskii <eliz@gnu.org>
Sat, 27 Apr 2013 11:20:49 +0000 (14:20 +0300)
committerEli Zaretskii <eliz@gnu.org>
Sat, 27 Apr 2013 11:20:49 +0000 (14:20 +0300)
 w32/compat/posixfcn.c: New file, with emulations of Posix
 functions and Posix functionality for MS-Windows.
 w32/subproc/sub_proc.c: Include io.h.
 (process_noinherit): New function, forces a file descriptor to not
 be inherited by child processes.
 (process_easy): Accept two additional arguments, and use them to
 set up the standard output and standard error handles of the child
 process.
 w32/include/sub_proc.h (process_easy): Adjust prototype.
 (process_noinherit): Add prototype.

 read.c [WINDOWS32]: Include windows.h and sub_proc.h.
 makeint.h (LOCALEDIR) [WINDOWS32}: Define to NULL if not
 defined.  This is needed because the MS-Windows build doesn't have
 a canonical place for LOCALEDIR.
 (WIN32_LEAN_AND_MEAN) [WINDOWS32]: Define, to avoid getting from
 windows.h header too much stuff that could conflict with the code.
 main.c <sync_mutex>: New static variable.
 <switches>: Add support for "--sync-mutex" switch.
 (decode_output_sync_flags): Decode the --sync-mutex= switch.
 (prepare_mutex_handle_string) [WINDOWS32]: New function.
 (main): Add "output-sync" to .FEATURES.
 job.h (CLOSE_ON_EXEC) [WINDOWS32]: Define to call
 process_noinherit.
 (F_GETFD, F_SETLKW, F_WRLCK, F_UNLCK, struct flock) [WINDOWS32]:
 New macros.
 (RECORD_SYNC_MUTEX): New macro, a no-op for Posix platforms.
 (sync_handle_t): New typedef.
 job.c <sync_handle>: Change type to sync_handle_t.
 (FD_NOT_EMPTY): Seek to the file's end.  Suggested by Frank
 Heckenbach <f.heckenbach@fh-soft.de>.
 (pump_from_tmp_fd) [WINDOWS32]: Switch to_fd to binary mode for
 the duration of this function, and then change back before
 returning.
 (start_job_command) [WINDOWS32]: Support output_sync mode on
 MS-Windows.  Use a system-wide mutex instead of locking
 stdout/stderr.  Call process_easy with two additional arguments:
 child->outfd and child->errfd.
 (exec_command) [WINDOWS32]: Pass two additional arguments, both
 -1, to process_easy, to adjust for the changed function signature.
 function.c (windows32_openpipe) [WINDOWS32]: This function now
 returns an int, which is -1 if it fails and zero otherwise.  It
 also calls 'error' instead of 'fatal', to avoid exiting
 prematurely.
 (func_shell_base) [WINDOWS32]: Call perror_with_name if
 windows32_openpipe fails, now that it always returns.  This avoids
 a compiler warning that error_prefix is not used in the MS-Windows
 build.
 config.h.W32.template (OUTPUT_SYNC): Define.
 build_w32.bat: Add w32/compat/posixfcn.c to compilation and
 linking commands.

 From Frank Heckenbach <f.heckenbach@fh-soft.de>:
 job.c (sync_output): Don't discard the output if
 acquire_semaphore fails; instead, dump the output unsynchronized.

12 files changed:
ChangeLog
build_w32.bat
config.h.W32.template
function.c
job.c
job.h
main.c
makeint.h
read.c
w32/compat/posixfcn.c [new file with mode: 0644]
w32/include/sub_proc.h
w32/subproc/sub_proc.c

index 58214198928dea4d7b8c65e714173b1eb84de652..dc1d2ff2dc894faebb08d9bee94d4ca63d9d7bba 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,20 +1,73 @@
-2013-04-25  Eli Zaretskii  <eliz@gnu.org>
+2013-04-27  Frank Heckenbach <f.heckenbach@fh-soft.de>  (tiny change)
 
-       * build_w32.bat: Improve.  Remove 'setlocal', as it isn't
-       supported on Windows 9X.  Add --help and usage instructions.
-       Support both debug and optimized builds with GCC under --debug.
-       If building out of Git repo, always produce config.h, and edit
-       gmk-default.scm into gmk-default.h.
+       * job.c (sync_output): Don't discard the output if
+       acquire_semaphore fails; instead, dump the output unsynchronized.
 
-       * w32/subproc/build.bat: Support debug and optimized builds with
-       GCC.
+2013-04-27  Eli Zaretskii  <eliz@gnu.org>
 
-2013-04-16  Paul Smith  <psmith@gnu.org>
+       Support --output-sync on MS-Windows.
+       * w32/compat/posixfcn.c: New file, with emulations of Posix
+       functions and Posix functionality for MS-Windows.
+
+       * w32/subproc/sub_proc.c: Include io.h.
+       (process_noinherit): New function, forces a file descriptor to not
+       be inherited by child processes.
+       (process_easy): Accept two additional arguments, and use them to
+       set up the standard output and standard error handles of the child
+       process.
 
-       * build_w32.bat: Add load.c to the Windows build.
-       * main.c: Parse the output-sync options, even if they're not
-       supported on the platform.  They'll just be ignored.
-       Patches from Ray Donnelly <mingw.android@gmail.com>
+       * w32/include/sub_proc.h (process_easy): Adjust prototype.
+       (process_noinherit): Add prototype.
+
+       * read.c [WINDOWS32]: Include windows.h and sub_proc.h.
+
+       * makeint.h (LOCALEDIR) [WINDOWS32}: Define to NULL if not
+       defined.  This is needed because the MS-Windows build doesn't have
+       a canonical place for LOCALEDIR.
+       (WIN32_LEAN_AND_MEAN) [WINDOWS32]: Define, to avoid getting from
+       windows.h header too much stuff that could conflict with the code.
+
+       * main.c <sync_mutex>: New static variable.
+       <switches>: Add support for "--sync-mutex" switch.
+       (decode_output_sync_flags): Decode the --sync-mutex= switch.
+       (prepare_mutex_handle_string) [WINDOWS32]: New function.
+       (main): Add "output-sync" to .FEATURES.
+
+       * job.h (CLOSE_ON_EXEC) [WINDOWS32]: Define to call
+       process_noinherit.
+       (F_GETFD, F_SETLKW, F_WRLCK, F_UNLCK, struct flock) [WINDOWS32]:
+       New macros.
+       (RECORD_SYNC_MUTEX): New macro, a no-op for Posix platforms.
+       (sync_handle_t): New typedef.
+
+       * job.c <sync_handle>: Change type to sync_handle_t.
+       (FD_NOT_EMPTY): Seek to the file's end.  Suggested by Frank
+       Heckenbach <f.heckenbach@fh-soft.de>.
+       (pump_from_tmp_fd) [WINDOWS32]: Switch to_fd to binary mode for
+       the duration of this function, and then change back before
+       returning.
+       (start_job_command) [WINDOWS32]: Support output_sync mode on
+       MS-Windows.  Use a system-wide mutex instead of locking
+       stdout/stderr.  Call process_easy with two additional arguments:
+       child->outfd and child->errfd.
+       (exec_command) [WINDOWS32]: Pass two additional arguments, both
+       -1, to process_easy, to adjust for the changed function signature.
+
+       * function.c (windows32_openpipe) [WINDOWS32]: This function now
+       returns an int, which is -1 if it fails and zero otherwise.  It
+       also calls 'error' instead of 'fatal', to avoid exiting
+       prematurely.
+       (func_shell_base) [WINDOWS32]: Call perror_with_name if
+       windows32_openpipe fails, now that it always returns.  This avoids
+       a compiler warning that error_prefix is not used in the MS-Windows
+       build.
+
+       * config.h.W32.template (OUTPUT_SYNC): Define.
+
+       * build_w32.bat: Add w32/compat/posixfcn.c to compilation and
+       linking commands.
+
+2013-04-16  Paul Smith  <psmith@gnu.org>
 
        * misc.c (open_tmpfd): Add a new function that returns a temporary
        file by file descriptor.
index e4d28deb4e62f3aa91b11b0759e1907be7600e3b..27ac3dba9b31a2baf1b6c8e66f8c4b790072fa56 100644 (file)
@@ -152,6 +152,8 @@ cl.exe /nologo /MT /W4 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D
 echo WinDebug\load.obj >>link.dbg\r
 cl.exe /nologo /MT /W4 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WINDOWS32 /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /FR.\WinDebug/ /Fp.\WinDebug/%make%.pch /Fo.\WinDebug/ /Fd.\WinDebug/%make%.pdb /c  .\w32\compat\dirent.c\r
 echo WinDebug\dirent.obj >>link.dbg\r
+cl.exe /nologo /MT /W4 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WINDOWS32 /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /FR.\WinDebug/ /Fp.\WinDebug/%make%.pch /Fo.\WinDebug/ /Fd.\WinDebug/%make%.pdb /c  .\w32\compat\posixfcn.c\r
+echo WinDebug\posixfcn.obj >>link.dbg\r
 cl.exe /nologo /MT /W4 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WINDOWS32 /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /FR.\WinDebug/ /Fp.\WinDebug/%make%.pch /Fo.\WinDebug/ /Fd.\WinDebug/%make%.pdb /c  .\glob\glob.c\r
 echo WinDebug\glob.obj >>link.dbg\r
 cl.exe /nologo /MT /W4 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WINDOWS32 /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /FR.\WinDebug/ /Fp.\WinDebug/%make%.pch /Fo.\WinDebug/ /Fd.\WinDebug/%make%.pdb /c  .\glob\fnmatch.c\r
@@ -227,6 +229,8 @@ cl.exe /nologo /MT /W4 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIND
 echo WinRel\load.obj >>link.rel\r
 cl.exe /nologo /MT /W4 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WINDOWS32 /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /FR.\WinRel/ /Fp.\WinRel/%make%.pch /Fo.\WinRel/ /c  .\w32\compat\dirent.c\r
 echo WinRel\dirent.obj >>link.rel\r
+cl.exe /nologo /MT /W4 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WINDOWS32 /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /FR.\WinRel/ /Fp.\WinRel/%make%.pch /Fo.\WinRel/ /c  .\w32\compat\posixfcn.c\r
+echo WinRel\posixfcn.obj >>link.rel\r
 cl.exe /nologo /MT /W4 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WINDOWS32 /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /FR.\WinRel/ /Fp.\WinRel/%make%.pch /Fo.\WinRel/ /c  .\glob\glob.c\r
 echo WinRel\glob.obj >>link.rel\r
 cl.exe /nologo /MT /W4 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WINDOWS32 /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /FR.\WinRel/ /Fp.\WinRel/%make%.pch /Fo.\WinRel/ /c  .\glob\fnmatch.c\r
@@ -278,6 +282,7 @@ gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32
 gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c ./glob/glob.c -o glob.o\r
 gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c ./glob/fnmatch.c -o fnmatch.o\r
 gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c ./w32/pathstuff.c -o pathstuff.o\r
+gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c ./w32/compat/posixfcn.c -o posixfcn.o\r
 @echo off\r
 set GUILEOBJ=\r
 if "%GUILESRC%" == "" GoTo LinkGCC\r
@@ -286,7 +291,7 @@ echo on
 gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% %GUILECFLAGS% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c guile.c\r
 :LinkGCC\r
 @echo on\r
-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 w32_misc.o sub_proc.o w32err.o %GUILELIBS% -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32\r
+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\r
 @GoTo BuildEnd\r
 :Usage\r
 echo Usage: %0 [options] [gcc]\r
index 03efa99962de6ca198ecefa61a72ac19c72b6807..a00e0958ca703e5ec97930d7deef6db5b961dec5 100644 (file)
@@ -554,3 +554,5 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  */
 #ifdef HAVE_CYGWIN_SHELL
 #undef BATCH_MODE_ONLY_SHELL
 #endif
+
+#define OUTPUT_SYNC
index 1242d9abf2e2fbed36acb0a606f8145af8af0756..321c0f87bc4bb7b3e184279100ffafc7336c0e52 100644 (file)
@@ -1431,7 +1431,7 @@ int shell_function_pid = 0, shell_function_completed;
 #include "sub_proc.h"
 
 
-void
+int
 windows32_openpipe (int *pipedes, pid_t *pid_p, char **command_argv, char **envp)
 {
   SECURITY_ATTRIBUTES saAttr;
@@ -1442,6 +1442,10 @@ windows32_openpipe (int *pipedes, pid_t *pid_p, char **command_argv, char **envp
   HANDLE hProcess, tmpIn, tmpErr;
   DWORD e;
 
+  /* Set status for return.  */
+  pipedes[0] = pipedes[1] = -1;
+  *pid_p = (pid_t)-1;
+
   saAttr.nLength = sizeof (SECURITY_ATTRIBUTES);
   saAttr.bInheritHandle = TRUE;
   saAttr.lpSecurityDescriptor = NULL;
@@ -1472,8 +1476,10 @@ windows32_openpipe (int *pipedes, pid_t *pid_p, char **command_argv, char **envp
                             DUPLICATE_SAME_ACCESS) == FALSE)
        CloseHandle(tmpIn);
     }
-    if (hIn == INVALID_HANDLE_VALUE)
-      fatal (NILF, _("windows32_openpipe: DuplicateHandle(In) failed (e=%ld)\n"), e);
+    if (hIn == INVALID_HANDLE_VALUE) {
+      error (NILF, _("windows32_openpipe: DuplicateHandle(In) failed (e=%ld)\n"), e);
+      return -1;
+    }
   }
   tmpErr = GetStdHandle(STD_ERROR_HANDLE);
   if (DuplicateHandle(GetCurrentProcess(),
@@ -1497,17 +1503,23 @@ windows32_openpipe (int *pipedes, pid_t *pid_p, char **command_argv, char **envp
                             DUPLICATE_SAME_ACCESS) == FALSE)
        CloseHandle(tmpErr);
     }
-    if (hErr == INVALID_HANDLE_VALUE)
-      fatal (NILF, _("windows32_openpipe: DuplicateHandle(Err) failed (e=%ld)\n"), e);
+    if (hErr == INVALID_HANDLE_VALUE) {
+      error (NILF, _("windows32_openpipe: DuplicateHandle(Err) failed (e=%ld)\n"), e);
+      return -1;
+    }
   }
 
-  if (!CreatePipe(&hChildOutRd, &hChildOutWr, &saAttr, 0))
-    fatal (NILF, _("CreatePipe() failed (e=%ld)\n"), GetLastError());
+  if (!CreatePipe(&hChildOutRd, &hChildOutWr, &saAttr, 0)) {
+    error (NILF, _("CreatePipe() failed (e=%ld)\n"), GetLastError());
+    return -1;
+  }
 
   hProcess = process_init_fd(hIn, hChildOutWr, hErr);
 
-  if (!hProcess)
-    fatal (NILF, _("windows32_openpipe(): process_init_fd() failed\n"));
+  if (!hProcess) {
+    error (NILF, _("windows32_openpipe(): process_init_fd() failed\n"));
+    return -1;
+  }
 
   /* make sure that CreateProcess() has Path it needs */
   sync_Path_environment();
@@ -1527,6 +1539,7 @@ windows32_openpipe (int *pipedes, pid_t *pid_p, char **command_argv, char **envp
 
     /* this will be closed almost right away */
        pipedes[1] = _open_osfhandle((intptr_t) hChildOutWr, O_APPEND);
+       return 0;
   } else {
     /* reap/cleanup the failed process */
        process_cleanup(hProcess);
@@ -1541,9 +1554,7 @@ windows32_openpipe (int *pipedes, pid_t *pid_p, char **command_argv, char **envp
        CloseHandle(hChildOutRd);
        CloseHandle(hChildOutWr);
 
-    /* set status for return */
-    pipedes[0] = pipedes[1] = -1;
-    *pid_p = (pid_t)-1;
+    return -1;
   }
 }
 #endif
@@ -1698,6 +1709,7 @@ func_shell_base (char *o, char **argv, int trim_newlines)
     {
       /* Open of the pipe failed, mark as failed execution.  */
       shell_function_completed = -1;
+      perror_with_name (error_prefix, "pipe");
       return o;
     }
   else
diff --git a/job.c b/job.c
index d88fc57565b48bacad84bda49461caabbcc6df68..18494f6dbfaa93846558d75788a9b2e8df2220ee 100644 (file)
--- a/job.c
+++ b/job.c
@@ -246,11 +246,11 @@ unsigned int jobserver_tokens = 0;
 #ifdef OUTPUT_SYNC
 /* Semaphore for use in -j mode with output_sync. */
 
-int sync_handle = -1;
+sync_handle_t sync_handle = -1;
 
 #define STREAM_OK(_s)       ((fcntl (fileno (_s), F_GETFD) != -1) || (errno != EBADF))
 
-#define FD_NOT_EMPTY(_f)    ((_f) >= 0 && lseek ((_f), 0, SEEK_CUR) > 0)
+#define FD_NOT_EMPTY(_f)    ((_f) >= 0 && lseek ((_f), 0, SEEK_END) > 0)
 #endif /* OUTPUT_SYNC */
 \f
 #ifdef WINDOWS32
@@ -588,6 +588,14 @@ pump_from_tmp_fd (int from_fd, int to_fd)
   ssize_t nleft, nwrite;
   char buffer[8192];
 
+#ifdef WINDOWS32
+  int prev_mode;
+
+  /* from_fd is opened by open_tmpfd, which does it in binary mode, so
+     we need the mode of to_fd to match that.  */
+  prev_mode = _setmode (to_fd, _O_BINARY);
+#endif
+
   if (lseek (from_fd, 0, SEEK_SET) == -1)
     perror ("lseek()");
 
@@ -605,13 +613,20 @@ pump_from_tmp_fd (int from_fd, int to_fd)
         if (nwrite < 0)
           {
             perror ("write()");
-            return;
+            goto finished;
           }
 
         write_buf += nwrite;
         nleft -= nwrite;
       }
     }
+finished:
+
+#ifdef WINDOWS32
+  /* Switch to_fd back to its original mode, so that log messages by
+     Make have the same EOL format as without --output-sync.  */
+  _setmode (to_fd, prev_mode);
+#endif
 }
 
 /* Support routine for sync_output() */
@@ -622,7 +637,7 @@ acquire_semaphore (void)
 
   fl.l_type = F_WRLCK;
   fl.l_whence = SEEK_SET;
-  fl.l_start = 0; /* lock just one byte according to pid */
+  fl.l_start = 0; /* lock just one byte */
   fl.l_len = 1;
   if (fcntl (sync_handle, F_SETLKW, &fl) != -1)
     return &fl;
@@ -648,13 +663,15 @@ release_semaphore (void *sem)
 static void
 sync_output (struct child *c)
 {
-  void *sem;
-
   int outfd_not_empty = FD_NOT_EMPTY (c->outfd);
   int errfd_not_empty = FD_NOT_EMPTY (c->errfd);
 
-  if ((outfd_not_empty || errfd_not_empty) && (sem = acquire_semaphore ()))
+  if (outfd_not_empty || errfd_not_empty)
     {
+      /* Try to acquire the semaphore.  If it fails, dump the output
+         unsynchronized; still better than silently discarding it.  */
+      void *sem = acquire_semaphore ();
+
       /* We've entered the "critical section" during which a lock is held.
          We want to keep it as short as possible.  */
       if (outfd_not_empty)
@@ -667,7 +684,8 @@ sync_output (struct child *c)
         pump_from_tmp_fd (c->errfd, fileno (stderr));
 
       /* Exit the critical section.  */
-      release_semaphore (sem);
+      if (sem)
+       release_semaphore (sem);
     }
 
   if (c->outfd >= 0)
@@ -1723,6 +1741,42 @@ start_job_command (struct child *child)
       HANDLE hPID;
       char* arg0;
 
+#ifdef OUTPUT_SYNC
+      if (output_sync)
+        {
+          static int combined_output;
+          /* If output_sync is turned on, create a mutex to
+              synchronize on.  This is done only once.  */
+          if (sync_handle == -1)
+           {
+             if ((!STREAM_OK (stdout) && !STREAM_OK (stderr))
+                 || (sync_handle = create_mutex ()) == -1)
+                {
+                  perror_with_name ("output-sync suppressed: ", "stderr");
+                  output_sync = 0;
+                }
+             else
+               {
+                 combined_output = same_stream (stdout, stderr);
+                 prepare_mutex_handle_string (sync_handle);
+               }
+           }
+          /* If we can synchronize, create a temporary file to hold
+              child's stdout, and another one for its stderr, if they
+              are separate. */
+          if (output_sync == OUTPUT_SYNC_MAKE
+              || (output_sync == OUTPUT_SYNC_TARGET
+                 && !(flags & COMMANDS_RECURSE)))
+            {
+              if (!assign_child_tempfiles (child, combined_output))
+               {
+                  perror_with_name ("output-sync suppressed: ", "stderr");
+                 output_sync = 0;
+               }
+            }
+        }
+#endif /* OUTPUT_SYNC */
+
       /* make UNC paths safe for CreateProcess -- backslash format */
       arg0 = argv[0];
       if (arg0 && arg0[0] == '/' && arg0[1] == '/')
@@ -1733,7 +1787,14 @@ start_job_command (struct child *child)
       /* make sure CreateProcess() has Path it needs */
       sync_Path_environment();
 
-      hPID = process_easy(argv, child->environment);
+#ifdef OUTPUT_SYNC
+          /* Divert child output into tempfile(s) if output_sync in use. */
+          if (output_sync)
+           hPID = process_easy(argv, child->environment,
+                               child->outfd, child->errfd);
+         else
+#endif
+           hPID = process_easy(argv, child->environment, -1, -1);
 
       if (hPID != INVALID_HANDLE_VALUE)
         child->pid = (pid_t) hPID;
@@ -2417,7 +2478,7 @@ exec_command (char **argv, char **envp)
   sync_Path_environment();
 
   /* launch command */
-  hPID = process_easy(argv, envp);
+  hPID = process_easy(argv, envp, -1, -1);
 
   /* make sure launch ok */
   if (hPID == INVALID_HANDLE_VALUE)
diff --git a/job.h b/job.h
index 0e4e11832cc9c9fb73d5a4728d3710f5e303cb94..df742703781d0d25b1ba75f186c4ea2371effb78 100644 (file)
--- a/job.h
+++ b/job.h
@@ -26,7 +26,11 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  */
 /* How to set close-on-exec for a file descriptor.  */
 
 #if !defined F_SETFD
-# define CLOSE_ON_EXEC(_d)
+# ifdef WINDOWS32
+#  define CLOSE_ON_EXEC(_d)  process_noinherit(_d)
+# else
+#  define CLOSE_ON_EXEC(_d)
+# endif
 #else
 # ifndef FD_CLOEXEC
 #  define FD_CLOEXEC 1
@@ -34,6 +38,47 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  */
 # define CLOSE_ON_EXEC(_d) (void) fcntl ((_d), F_SETFD, FD_CLOEXEC)
 #endif
 
+#ifdef OUTPUT_SYNC
+# ifdef WINDOWS32
+/* For emulations in w32/compat/posixfcn.c.  */
+#  define F_GETFD 1
+#  define F_SETLKW 2
+/* Implementation note: None of the values of l_type below can be zero
+   -- they are compared with a static instance of the struct, so zero
+   means unknown/invalid, see w32/compat/posixfcn.c. */
+#  define F_WRLCK 1
+#  define F_UNLCK 2
+
+struct flock {
+  short l_type;
+  short l_whence;
+  off_t l_start;
+  off_t l_len;
+  pid_t l_pid;
+};
+
+/* This type is actually a HANDLE, but we want to avoid including
+   windows.h as much as possible.  */
+typedef intptr_t sync_handle_t;
+
+/* Public functions emulated/provided in posixfcn.c.  */
+int fcntl (intptr_t fd, int cmd, ...);
+intptr_t create_mutex (void);
+int same_stream (FILE *f1, FILE *f2);
+
+#  define RECORD_SYNC_MUTEX(m) record_sync_mutex(m)
+void record_sync_mutex (const char *str);
+void prepare_mutex_handle_string (intptr_t hdl);
+
+# else /* !WINDOWS32 */
+
+typedef int sync_handle_t;     /* file descriptor */
+
+#  define RECORD_SYNC_MUTEX(m) (void)(m)
+
+# endif
+#endif /* OUTPUT_SYNC */
+
 /* Structure describing a running or dead child process.  */
 
 struct child
diff --git a/main.c b/main.c
index 8a6968396bec99ee6ce74c0080bf326dd9cf7087..00375878c3dc1ae0f0018a857c2ac27a8d8793b2 100644 (file)
--- a/main.c
+++ b/main.c
@@ -239,6 +239,11 @@ static struct stringlist *jobserver_fds = 0;
 int job_fds[2] = { -1, -1 };
 int job_rfd = -1;
 
+/* Handle for the mutex used on Windows to synchronize output of our
+   children under -O.  */
+
+static struct stringlist *sync_mutex = 0;
+
 /* Maximum load average at which multiple jobs will be run.
    Negative values mean unlimited, while zero means limit to
    zero load (which could be useful to start infinite jobs remotely
@@ -415,6 +420,7 @@ static const struct command_switch switches[] =
     { 'n', flag, &just_print_flag, 1, 1, 1, 0, 0, "just-print" },
     { 'o', filename, &old_files, 0, 0, 0, 0, 0, "old-file" },
     { 'O', string, &output_sync_option, 1, 1, 0, "target", 0, "output-sync" },
+    { CHAR_MAX+7, string, &sync_mutex, 1, 1, 0, 0, 0, "sync-mutex" },
     { 'p', flag, &print_data_base_flag, 1, 1, 0, 0, 0, "print-data-base" },
     { 'q', flag, &question_flag, 1, 1, 1, 0, 0, "question" },
     { 'r', flag, &no_builtin_rules_flag, 1, 1, 0, 0, 0, "no-builtin-rules" },
@@ -696,9 +702,47 @@ decode_output_sync_flags (void)
       else
         fatal (NILF, _("unknown output-sync type '%s'"), p);
     }
+
+  if (sync_mutex)
+    {
+      const char *mp;
+      unsigned int idx;
+
+      for (idx = 1; idx < sync_mutex->idx; idx++)
+        if (!streq (sync_mutex->list[0], sync_mutex->list[idx]))
+          fatal (NILF, _("internal error: multiple --sync-mutex options"));
+
+      /* Now parse the mutex handle string.  */
+      mp = sync_mutex->list[0];
+      RECORD_SYNC_MUTEX (mp);
+    }
 }
 
 #ifdef WINDOWS32
+
+/* This is called from start_job_command when it detects that
+   output_sync option is in effect.  The handle to the synchronization
+   mutex is passed, as a string, to sub-makes via the --sync-mutex
+   command-line argument.  */
+void
+prepare_mutex_handle_string (sync_handle_t handle)
+{
+  if (!sync_mutex)
+    {
+      /* 2 hex digits per byte + 2 characters for "0x" + null.  */
+      char hdl_string[2 * sizeof (sync_handle_t) + 2 + 1];
+
+      /* Prepare the mutex handle string for our children.  */
+      sprintf (hdl_string, "0x%x", handle);
+      sync_mutex = xmalloc (sizeof (struct stringlist));
+      sync_mutex->list = xmalloc (sizeof (char *));
+      sync_mutex->list[0] = xstrdup (hdl_string);
+      sync_mutex->idx = 1;
+      sync_mutex->max = 1;
+      define_makeflags (1, 0);
+    }
+}
+
 /*
  * HANDLE runtime exceptions by avoiding a requestor on the GUI. Capture
  * exception and print it to stderr instead.
@@ -1137,6 +1181,9 @@ main (int argc, char **argv, char **envp)
 #ifdef MAKE_JOBSERVER
                            " jobserver"
 #endif
+#ifdef OUTPUT_SYNC
+                           " output-sync"
+#endif
 #ifdef MAKE_SYMLINKS
                            " check-symlink"
 #endif
index 976b0404c6e743001a74327178aa79230837a0a7..d1b00c671f202c3cc9c727c31e948ecaaa485234 100644 (file)
--- a/makeint.h
+++ b/makeint.h
@@ -355,6 +355,14 @@ extern int no_default_sh_exe;
 
 /* is default_shell unixy? */
 extern int unixy_shell;
+
+/* We don't have a preferred fixed value for LOCALEDIR.  */
+# ifndef LOCALEDIR
+#  define LOCALEDIR NULL
+# endif
+
+/* Include only the minimal stuff from windows.h.   */
+#define WIN32_LEAN_AND_MEAN
 #endif  /* WINDOWS32 */
 
 #if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT)
diff --git a/read.c b/read.c
index af5cf01d290c7f0ec0ef90ac5601d4cc8293c582..86b9fc4ed015889210115e9377d0959a35d14787 100644 (file)
--- a/read.c
+++ b/read.c
@@ -30,7 +30,10 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "hash.h"
 
 
-#ifndef WINDOWS32
+#ifdef WINDOWS32
+#include <windows.h>
+#include "sub_proc.h"
+#else  /* !WINDOWS32 */
 #ifndef _AMIGA
 #ifndef VMS
 #include <pwd.h>
diff --git a/w32/compat/posixfcn.c b/w32/compat/posixfcn.c
new file mode 100644 (file)
index 0000000..90534d0
--- /dev/null
@@ -0,0 +1,258 @@
+/* Replacements for Posix functions and Posix functionality for MS-Windows.
+
+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 <string.h>
+#include <io.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <windows.h>
+
+#include "makeint.h"
+#include "job.h"
+
+#ifdef OUTPUT_SYNC
+/* Support for OUTPUT_SYNC and related functionality.  */
+
+/* Emulation of fcntl that supports only F_GETFD and F_SETLKW.  */
+int
+fcntl (intptr_t fd, int cmd, ...)
+{
+  va_list ap;
+
+  va_start (ap, cmd);
+
+  switch (cmd)
+    {
+      case F_GETFD:
+       va_end (ap);
+       /* Could have used GetHandleInformation, but that isn't
+          supported on Windows 9X.  */
+       if (_get_osfhandle (fd) == -1)
+         return -1;
+       return 0;
+      case F_SETLKW:
+       {
+         void *buf = va_arg (ap, void *);
+         struct flock *fl = (struct flock *)buf;
+         HANDLE hmutex = (HANDLE)fd;
+         static struct flock last_fl;
+         short last_type = last_fl.l_type;
+
+         va_end (ap);
+
+         if (hmutex == INVALID_HANDLE_VALUE || !hmutex)
+           return -1;
+
+         last_fl = *fl;
+
+         switch (fl->l_type)
+           {
+
+             case F_WRLCK:
+               {
+                 DWORD result;
+
+                 if (last_type == F_WRLCK)
+                   {
+                     /* Don't call WaitForSingleObject if we already
+                        own the mutex, because doing so will require
+                        us to call ReleaseMutex an equal number of
+                        times, before the mutex is actually
+                        released.  */
+                     return 0;
+                   }
+
+                 result = WaitForSingleObject (hmutex, INFINITE);
+                 switch (result)
+                   {
+                     case WAIT_OBJECT_0:
+                       /* We don't care if the mutex owner crashed or
+                          exited.  */
+                     case WAIT_ABANDONED:
+                       return 0;
+                     case WAIT_FAILED:
+                     case WAIT_TIMEOUT: /* cannot happen, really */
+                       {
+                         DWORD err = GetLastError ();
+
+                         /* Invalidate the last command.  */
+                         memset (&last_fl, 0, sizeof (last_fl));
+
+                         switch (err)
+                           {
+                             case ERROR_INVALID_HANDLE:
+                             case ERROR_INVALID_FUNCTION:
+                               errno = EINVAL;
+                               return -1;
+                             default:
+                               errno = EDEADLOCK;
+                               return -1;
+                           }
+                       }
+                   }
+               }
+             case F_UNLCK:
+               {
+                 /* FIXME: Perhaps we should call ReleaseMutex
+                    repatedly until it errors out, to make sure the
+                    mutext is released even if we somehow managed to
+                    to take ownership multiple times?  */
+                 BOOL status = ReleaseMutex (hmutex);
+
+                 if (status)
+                   return 0;
+                 else
+                   {
+                     DWORD err = GetLastError ();
+
+                     if (err == ERROR_NOT_OWNER)
+                       errno = EPERM;
+                     else
+                       {
+                         memset (&last_fl, 0, sizeof (last_fl));
+                         errno = EINVAL;
+                       }
+                     return -1;
+                   }
+               }
+             default:
+               errno = ENOSYS;
+               return -1;
+           }
+       }
+      default:
+       errno = ENOSYS;
+       va_end (ap);
+       return -1;
+    }
+}
+
+static intptr_t mutex_handle = -1;
+
+/* Record in a static variable the mutex handle we were requested to
+   use.  That nameless mutex was created by the top-level Make, and
+   its handle was passed to us via inheritance.  The value of that
+   handle is passed via the command-line arguments, so that we know
+   which handle to use.  */
+void
+record_sync_mutex (const char *str)
+{
+  char *endp;
+  intptr_t hmutex = strtol (str, &endp, 16);
+
+  if (*endp == '\0')
+    mutex_handle = hmutex;
+  else
+    {
+      mutex_handle = -1;
+      errno = EINVAL;
+    }
+}
+
+/* Create a new mutex or reuse one created by our parent.  */
+intptr_t
+create_mutex (void)
+{
+  SECURITY_ATTRIBUTES secattr;
+  intptr_t hmutex = -1;
+
+  /* If we have a mutex handle passed from the parent Make, just use
+     that.  */
+  if (mutex_handle > 0)
+    return mutex_handle;
+
+  /* We are the top-level Make, and we want the handle to be inherited
+     by our child processes.  */
+  secattr.nLength = sizeof (secattr);
+  secattr.lpSecurityDescriptor = NULL; /* use default security descriptor */
+  secattr.bInheritHandle = TRUE;
+
+  hmutex = (intptr_t)CreateMutex (&secattr, FALSE, NULL);
+  if (!hmutex)
+    {
+      DWORD err = GetLastError ();
+
+      fprintf (stderr, "CreateMutex: error %lu\n", err);
+      errno = ENOLCK;
+      hmutex = -1;
+    }
+
+  mutex_handle = hmutex;
+  return hmutex;
+}
+
+/* Return non-zero if F1 and F2 are 2 streams representing the same
+   file or pipe or device.  */
+int
+same_stream (FILE *f1, FILE *f2)
+{
+  HANDLE fh1 = (HANDLE)_get_osfhandle (fileno (f1));
+  HANDLE fh2 = (HANDLE)_get_osfhandle (fileno (f2));
+
+  /* Invalid file descriptors get treated as different streams.  */
+  if (fh1 && fh1 != INVALID_HANDLE_VALUE
+      && fh2 && fh2 != INVALID_HANDLE_VALUE)
+    {
+      if (fh1 == fh2)
+       return 1;
+      else
+       {
+         DWORD ftyp1 = GetFileType (fh1), ftyp2 = GetFileType (fh2);
+
+         if (ftyp1 != ftyp2
+             || ftyp1 == FILE_TYPE_UNKNOWN || ftyp2 == FILE_TYPE_UNKNOWN)
+           return 0;
+         else if (ftyp1 == FILE_TYPE_CHAR)
+           {
+             /* For character devices, check if they both refer to a
+                console.  This loses if both handles refer to the
+                null device (FIXME!), but in that case we don't care
+                in the context of Make.  */
+             DWORD conmode1, conmode2;
+
+             /* Each process on Windows can have at most 1 console,
+                so if both handles are for the console device, they
+                are the same.  We also compare the console mode to
+                distinguish between tsdin and stdout/stderr.  */
+             if (GetConsoleMode (fh1, &conmode1)
+                 && GetConsoleMode (fh2, &conmode2)
+                 && conmode1 == conmode2)
+               return 1;
+           }
+         else
+           {
+             /* For disk files and pipes, compare their unique
+                attributes.  */
+             BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2;
+
+             /* Pipes get zero in the volume serial number, but do
+                appear to have meaningful information in file index
+                attributes.  We test file attributes as well, for a
+                good measure.  */
+             if (GetFileInformationByHandle (fh1, &bhfi1)
+                 && GetFileInformationByHandle (fh2, &bhfi2))
+               return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber
+                       && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow
+                       && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh
+                       && bhfi1.dwFileAttributes == bhfi2.dwFileAttributes);
+           }
+       }
+    }
+  return 0;
+}
+
+#endif /* OUTPUT_SYNC */
index 0aeaf003ffc81d4ae4d0e6fb21c65f2ac32561ec..d209aff0b1cdb2175b9277ec7a1d4e4db4e7edf7 100644 (file)
@@ -41,7 +41,8 @@ EXTERN_DECL(long process_file_io, (HANDLE proc));
 EXTERN_DECL(void process_cleanup, (HANDLE proc));
 EXTERN_DECL(HANDLE process_wait_for_any, (int block, DWORD* pdwWaitStatus));
 EXTERN_DECL(void process_register, (HANDLE proc));
-EXTERN_DECL(HANDLE process_easy, (char** argv, char** env));
+EXTERN_DECL(HANDLE process_easy, (char** argv, char** env,
+                                 int outfd, int errfd));
 EXTERN_DECL(BOOL process_kill, (HANDLE proc, int signal));
 EXTERN_DECL(int process_used_slots, (VOID_DECL));
 
@@ -55,6 +56,7 @@ EXTERN_DECL(char * process_errbuf, (HANDLE proc));
 EXTERN_DECL(int process_outcnt, (HANDLE proc));
 EXTERN_DECL(int process_errcnt, (HANDLE proc));
 EXTERN_DECL(void process_pipes, (HANDLE proc, int pipes[3]));
+EXTERN_DECL(void process_noinherit, (int fildes));
 
 /* jobserver routines */
 EXTERN_DECL(int open_jobserver_semaphore, (const char* name));
index 6cc3081ed0a18a1b46ecd170e27a1abea5e180eb..2c3677731732960d33fba7285c2562ccd2bfd1d5 100644 (file)
@@ -17,6 +17,7 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <config.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <io.h>                /* for _get_osfhandle */
 #ifdef _MSC_VER
 # include <stddef.h>    /* for intptr_t */
 #else
@@ -341,6 +342,15 @@ process_exit_code(HANDLE proc)
        return (((sub_process *)proc)->exit_code);
 }
 
+void
+process_noinherit(int fd)
+{
+  HANDLE fh = (HANDLE)_get_osfhandle(fd);
+
+  if (fh && fh != INVALID_HANDLE_VALUE)
+       SetHandleInformation(fh, HANDLE_FLAG_INHERIT, 0);
+}
+
 /*
 2006-02:
 All the following functions are currently unused.
@@ -1340,7 +1350,9 @@ make_command_line( char *shell_name, char *full_exec_path, char **argv)
 HANDLE
 process_easy(
        char **argv,
-       char **envp)
+       char **envp,
+       int outfd,
+       int errfd)
 {
   HANDLE hIn = INVALID_HANDLE_VALUE;
   HANDLE hOut = INVALID_HANDLE_VALUE;
@@ -1383,7 +1395,10 @@ process_easy(
       return INVALID_HANDLE_VALUE;
     }
   }
-  tmpOut = GetStdHandle (STD_OUTPUT_HANDLE);
+  if (outfd >= 0)
+    tmpOut = (HANDLE)_get_osfhandle (outfd);
+  else
+    tmpOut = GetStdHandle (STD_OUTPUT_HANDLE);
   if (DuplicateHandle(GetCurrentProcess(),
                      tmpOut,
                      GetCurrentProcess(),
@@ -1410,7 +1425,10 @@ process_easy(
       return INVALID_HANDLE_VALUE;
     }
   }
-  tmpErr = GetStdHandle(STD_ERROR_HANDLE);
+  if (errfd >= 0)
+    tmpErr = (HANDLE)_get_osfhandle (errfd);
+  else
+    tmpErr = GetStdHandle(STD_ERROR_HANDLE);
   if (DuplicateHandle(GetCurrentProcess(),
                      tmpErr,
                      GetCurrentProcess(),