1 /* Windows32-based operating system interface for GNU Make.
2 Copyright (C) 2016-2022 Free Software Foundation, Inc.
3 This file is part of GNU Make.
5 GNU Make is free software; you can redistribute it and/or modify it under the
6 terms of the GNU General Public License as published by the Free Software
7 Foundation; either version 3 of the License, or (at your option) any later
10 GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
11 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License along with
15 this program. If not, see <https://www.gnu.org/licenses/>. */
25 #if _WIN32_WINNT > 0x0601
28 #include "pathstuff.h"
37 static unsigned int state = IO_UNKNOWN;
39 /* We only need to compute this once per process. */
40 if (state != IO_UNKNOWN)
43 /* Could have used GetHandleInformation, but that isn't supported
45 HANDLE outfd = (HANDLE)_get_osfhandle (fileno (stdout));
46 HANDLE errfd = (HANDLE)_get_osfhandle (fileno (stderr));
48 if ((HANDLE)_get_osfhandle (fileno (stdin)) != INVALID_HANDLE_VALUE)
50 if (outfd != INVALID_HANDLE_VALUE)
51 state |= IO_STDOUT_OK;
52 if (errfd != INVALID_HANDLE_VALUE)
53 state |= IO_STDERR_OK;
55 if (ALL_SET (state, IO_STDOUT_OK|IO_STDERR_OK))
57 unsigned int combined = 0;
60 combined = IO_COMBINED_OUTERR;
63 DWORD outtype = GetFileType (outfd), errtype = GetFileType (errfd);
65 if (outtype == errtype
66 && outtype != FILE_TYPE_UNKNOWN && errtype != FILE_TYPE_UNKNOWN)
68 if (outtype == FILE_TYPE_CHAR)
70 /* For character devices, check if they both refer to a
71 console. This loses if both handles refer to the
72 null device (FIXME!), but in that case we don't care
73 in the context of Make. */
74 DWORD outmode, errmode;
76 /* Each process on Windows can have at most 1 console,
77 so if both handles are for the console device, they
78 are the same. We also compare the console mode to
79 distinguish between stdin and stdout/stderr. */
80 if (GetConsoleMode (outfd, &outmode)
81 && GetConsoleMode (errfd, &errmode)
82 && outmode == errmode)
83 combined = IO_COMBINED_OUTERR;
87 /* For disk files and pipes, compare their unique
89 BY_HANDLE_FILE_INFORMATION outfi, errfi;
91 /* Pipes get zero in the volume serial number, but do
92 appear to have meaningful information in file index
93 attributes. We test file attributes as well, for a
95 if (GetFileInformationByHandle (outfd, &outfi)
96 && GetFileInformationByHandle (errfd, &errfi)
97 && outfi.dwVolumeSerialNumber == errfi.dwVolumeSerialNumber
98 && outfi.nFileIndexLow == errfi.nFileIndexLow
99 && outfi.nFileIndexHigh == errfi.nFileIndexHigh
100 && outfi.dwFileAttributes == errfi.dwFileAttributes)
101 combined = IO_COMBINED_OUTERR;
111 /* A replacement for tmpfile, since the MSVCRT implementation creates
112 the file in the root directory of the current drive, which might
113 not be writable by our user, and also it returns a FILE* and we want a file
114 descriptor. Mostly borrowed from create_batch_file, see job.c. */
118 char temp_path[MAX_PATH+1];
119 unsigned path_size = GetTempPath (sizeof (temp_path), temp_path);
122 /* These variables are static so we won't try to reuse a name that was
123 generated a little while ago, because that file might not be on disk yet,
124 since we use FILE_ATTRIBUTE_TEMPORARY below, which tells the OS it
125 doesn't need to flush the cache to disk. If the file is not yet on disk,
126 we might think the name is available, while it really isn't. This
127 happens in parallel builds. */
128 static unsigned uniq = 0;
129 static int second_loop = 0;
131 const char base[] = "gmake_tmpf";
132 const unsigned sizemax = sizeof (base) - 1 + 4 + 10 + 10;
133 unsigned pid = GetCurrentProcessId ();
137 path_size = GetCurrentDirectory (sizeof (temp_path), temp_path);
142 if (uniq >= 0x10000 && !second_loop)
144 /* If we already had 64K batch files in this
145 process, make a second loop through the numbers,
146 looking for free slots, i.e. files that were
147 deleted in the meantime. */
152 while (path_size > 0 && path_size + sizemax < sizeof (temp_path)
153 && (uniq < 0x10000 || !second_loop))
157 sprintf (temp_path + path_size,
159 temp_path[path_size - 1] == '\\' ? "" : "\\",
161 h = CreateFile (temp_path, /* file name */
162 GENERIC_READ | GENERIC_WRITE | DELETE, /* desired access */
163 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
164 NULL, /* default security attributes */
165 CREATE_NEW, /* creation disposition */
166 FILE_ATTRIBUTE_NORMAL | /* flags and attributes */
167 FILE_ATTRIBUTE_TEMPORARY |
168 FILE_FLAG_DELETE_ON_CLOSE,
169 NULL); /* no template file */
171 if (h != INVALID_HANDLE_VALUE)
172 return _open_osfhandle ((intptr_t)h, 0);
175 const DWORD er = GetLastError ();
177 if (er == ERROR_FILE_EXISTS || er == ERROR_ALREADY_EXISTS)
180 if (uniq == 0x10000 && !second_loop)
186 /* The temporary path is not guaranteed to exist, or might not be
187 writable by user. Use the current directory as fallback. */
190 path_size = GetCurrentDirectory (sizeof (temp_path), temp_path);
206 #if defined(MAKE_JOBSERVER)
208 /* This section provides OS-specific functions to support the jobserver. */
210 static char jobserver_semaphore_name[MAX_PATH + 1];
211 static HANDLE jobserver_semaphore = NULL;
214 jobserver_setup (int slots, const char *style)
216 /* sub_proc.c is limited in the number of objects it can wait for. */
218 if (style && strcmp (style, "sem") != 0)
219 OS (fatal, NILF, _("Unknown jobserver auth style '%s'"), style);
221 if (slots > process_table_usable_size())
223 slots = process_table_usable_size();
224 DB (DB_JOBS, (_("Jobserver slots limited to %d\n"), slots));
227 sprintf (jobserver_semaphore_name, "gmake_semaphore_%d", _getpid ());
229 jobserver_semaphore = CreateSemaphore (
230 NULL, /* Use default security descriptor */
231 slots, /* Initial count */
232 slots, /* Maximum count */
233 jobserver_semaphore_name); /* Semaphore name */
235 if (jobserver_semaphore == NULL)
237 DWORD err = GetLastError ();
238 const char *estr = map_windows32_error_to_string (err);
240 _("creating jobserver semaphore: (Error %ld: %s)"), err, estr);
247 jobserver_parse_auth (const char *auth)
249 jobserver_semaphore = OpenSemaphore (
250 SEMAPHORE_ALL_ACCESS, /* Semaphore access setting */
251 FALSE, /* Child processes DON'T inherit */
252 auth); /* Semaphore name */
254 if (jobserver_semaphore == NULL)
256 DWORD err = GetLastError ();
257 const char *estr = map_windows32_error_to_string (err);
258 fatal (NILF, strlen (auth) + INTSTR_LENGTH + strlen (estr),
259 _("internal error: unable to open jobserver semaphore '%s': (Error %ld: %s)"),
262 DB (DB_JOBS, (_("Jobserver client (semaphore %s)\n"), auth));
268 jobserver_get_auth ()
270 return xstrdup (jobserver_semaphore_name);
274 jobserver_get_invalid_auth ()
276 /* Because we're using a semaphore we don't need to invalidate. */
283 return jobserver_semaphore != NULL;
286 /* Close jobserver semaphore */
290 if (jobserver_semaphore != NULL)
292 CloseHandle (jobserver_semaphore);
293 jobserver_semaphore = NULL;
298 jobserver_release (int is_fatal)
300 if (! ReleaseSemaphore (
301 jobserver_semaphore, /* handle to semaphore */
302 1, /* increase count by one */
303 NULL)) /* not interested in previous count */
307 DWORD err = GetLastError ();
308 const char *estr = map_windows32_error_to_string (err);
310 _("release jobserver semaphore: (Error %ld: %s)"), err, estr);
312 perror_with_name ("release_jobserver_semaphore", "");
317 jobserver_acquire_all ()
319 unsigned int tokens = 0;
322 DWORD dwEvent = WaitForSingleObject (
323 jobserver_semaphore, /* Handle to semaphore */
324 0); /* DON'T wait on semaphore */
326 if (dwEvent != WAIT_OBJECT_0)
338 void jobserver_pre_child (int recursive)
342 void jobserver_post_child (int recursive)
347 jobserver_pre_acquire ()
351 /* Returns 1 if we got a token, or 0 if a child has completed.
352 The Windows implementation doesn't support load detection. */
354 jobserver_acquire (int timeout)
360 handles = xmalloc(process_table_actual_size() * sizeof(HANDLE));
362 /* Add jobserver semaphore to first slot. */
363 handles[0] = jobserver_semaphore;
365 /* Build array of handles to wait for. */
366 dwHandleCount = 1 + process_set_handles (&handles[1]);
368 dwEvent = process_wait_for_multiple_objects (
369 dwHandleCount, /* number of objects in array */
370 handles, /* array of objects */
371 FALSE, /* wait for any object */
372 INFINITE); /* wait until object is signalled */
376 if (dwEvent == WAIT_FAILED)
378 DWORD err = GetLastError ();
379 const char *estr = map_windows32_error_to_string (err);
381 _("semaphore or child process wait: (Error %ld: %s)"),
385 /* WAIT_OBJECT_0 indicates that the semaphore was signalled. */
386 return dwEvent == WAIT_OBJECT_0;
389 #endif /* MAKE_JOBSERVER */
391 #if !defined(NO_OUTPUT_SYNC)
393 #define MUTEX_PREFIX "fnm:"
395 /* Since we're using this with CreateMutex, NULL is invalid. */
396 static HANDLE osync_handle = NULL;
401 return osync_handle != NULL;
407 SECURITY_ATTRIBUTES secattr;
409 /* We are the top-level make, and we want the handle to be inherited
410 by our child processes. */
411 secattr.nLength = sizeof (secattr);
412 secattr.lpSecurityDescriptor = NULL; /* use default security descriptor */
413 secattr.bInheritHandle = TRUE;
415 osync_handle = CreateMutex (&secattr, FALSE, NULL);
418 DWORD err = GetLastError ();
419 fprintf (stderr, "CreateMutex: error %lu\n", err);
429 if (osync_enabled ())
431 /* Prepare the mutex handle string for our children.
432 2 hex digits per byte + 2 characters for "0x" + null. */
433 mutex = xmalloc ((2 * sizeof (osync_handle)) + 2 + 1);
434 sprintf (mutex, "0x%Ix", (unsigned long long)(DWORD_PTR)osync_handle);
441 osync_parse_mutex (const char *mutex)
444 unsigned long long i;
447 i = strtoull (mutex, &endp, 16);
449 OSS (fatal, NILF, _("cannot parse output sync mutex %s: %s"),
450 mutex, strerror (errno));
452 OS (fatal, NILF, _("invalid output sync mutex: %s"), mutex);
454 osync_handle = (HANDLE) (DWORD_PTR) i;
464 CloseHandle (osync_handle);
474 DWORD result = WaitForSingleObject (osync_handle, INFINITE);
475 if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
486 /* FIXME: Perhaps we should call ReleaseMutex repatedly until it errors
487 out, to make sure the mutext is released even if we somehow managed to
488 to take ownership multiple times? */
489 ReleaseMutex (osync_handle);
492 #endif /* NO_OUTPUT_SYNC */
497 HANDLE fh = (HANDLE)_get_osfhandle(fd);
499 if (fh && fh != INVALID_HANDLE_VALUE)
500 SetHandleInformation(fh, HANDLE_FLAG_INHERIT, 1);
506 HANDLE fh = (HANDLE)_get_osfhandle(fd);
508 if (fh && fh != INVALID_HANDLE_VALUE)
509 SetHandleInformation(fh, HANDLE_FLAG_INHERIT, 0);
513 fd_set_append (int fd)