source: trunk/src/kmk/kmkbuiltin/kSubmit.c@ 3173

Last change on this file since 3173 was 3173, checked in by bird, 7 years ago

kmkbultin: environment fixes and stats.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.1 KB
Line 
1/* $Id: kSubmit.c 3173 2018-03-21 21:37:41Z bird $ */
2/** @file
3 * kMk Builtin command - submit job to a kWorker.
4 */
5
6/*
7 * Copyright (c) 2007-2016 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
8 *
9 * This file is part of kBuild.
10 *
11 * kBuild is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * kBuild is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
23 *
24 */
25
26/*******************************************************************************
27* Header Files *
28*******************************************************************************/
29#ifdef __APPLE__
30# define _POSIX_C_SOURCE 1 /* 10.4 sdk and unsetenv */
31#endif
32#include "makeint.h"
33#include "job.h"
34#include "variable.h"
35#include "pathstuff.h"
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <errno.h>
40#include <assert.h>
41#ifdef HAVE_ALLOCA_H
42# include <alloca.h>
43#endif
44#if defined(_MSC_VER)
45# include <ctype.h>
46# include <io.h>
47# include <direct.h>
48# include <process.h>
49#else
50# include <unistd.h>
51#endif
52#ifdef KBUILD_OS_WINDOWS
53# ifndef CONFIG_NEW_WIN_CHILDREN
54# include "sub_proc.h"
55# else
56# include "../w32/winchildren.h"
57# endif
58#endif
59
60#include "kbuild.h"
61#include "kmkbuiltin.h"
62#include "err.h"
63
64
65/*********************************************************************************************************************************
66* Defined Constants And Macros *
67*********************************************************************************************************************************/
68/** Hashes a pid. */
69#define KWORKER_PID_HASH(a_pid) ((size_t)(a_pid) % 61)
70
71
72/*********************************************************************************************************************************
73* Structures and Typedefs *
74*********************************************************************************************************************************/
75typedef struct WORKERINSTANCE *PWORKERINSTANCE;
76typedef struct WORKERINSTANCE
77{
78 /** Pointer to the next worker instance. */
79 PWORKERINSTANCE pNext;
80 /** Pointer to the previous worker instance. */
81 PWORKERINSTANCE pPrev;
82 /** Pointer to the next worker with the same pid hash slot. */
83 PWORKERINSTANCE pNextPidHash;
84 /** 32 or 64. */
85 unsigned cBits;
86 /** The process ID of the kWorker process. */
87 pid_t pid;
88 union
89 {
90 struct
91 {
92 /** The exit code. */
93 int32_t rcExit;
94 /** Set to 1 if the worker is exiting. */
95 uint8_t bWorkerExiting;
96 uint8_t abUnused[3];
97 } s;
98 uint8_t ab[8];
99 } Result;
100 /** Number of result bytes read alread. */
101 size_t cbResultRead;
102
103#ifdef KBUILD_OS_WINDOWS
104 /** The process handle. */
105 HANDLE hProcess;
106 /** The bi-directional pipe we use to talk to the kWorker process. */
107 HANDLE hPipe;
108 /** For overlapped read (have valid event semaphore). */
109 OVERLAPPED OverlappedRead;
110#else
111 /** The socket descriptor we use to talk to the kWorker process. */
112 int fdSocket;
113#endif
114
115 /** What it's busy with. NULL if idle. */
116 struct child *pBusyWith;
117} WORKERINSTANCE;
118
119
120typedef struct WORKERLIST
121{
122 /** The head of the list. NULL if empty. */
123 PWORKERINSTANCE pHead;
124 /** The tail of the list. NULL if empty. */
125 PWORKERINSTANCE pTail;
126 /** Number of list entries. */
127 size_t cEntries;
128} WORKERLIST;
129typedef WORKERLIST *PWORKERLIST;
130
131
132/*********************************************************************************************************************************
133* Global Variables *
134*********************************************************************************************************************************/
135/** List of idle worker.*/
136static WORKERLIST g_IdleList;
137/** List of busy workers. */
138static WORKERLIST g_BusyList;
139/** PID hash table for the workers.
140 * @sa KWORKER_PID_HASH() */
141static PWORKERINSTANCE g_apPidHash[61];
142
143#ifdef KBUILD_OS_WINDOWS
144/** For naming the pipes.
145 * Also indicates how many worker instances we've spawned. */
146static unsigned g_uWorkerSeqNo = 0;
147#endif
148/** Set if we've registred the atexit handler already. */
149static int g_fAtExitRegistered = 0;
150
151/** @var g_cArchBits
152 * The bit count of the architecture this binary is compiled for. */
153/** @var g_szArch
154 * The name of the architecture this binary is compiled for. */
155/** @var g_cArchBits
156 * The bit count of the alternative architecture. */
157/** @var g_szAltArch
158 * The name of the alternative architecture. */
159#if defined(KBUILD_ARCH_AMD64)
160static unsigned g_cArchBits = 64;
161static char const g_szArch[] = "amd64";
162static unsigned g_cAltArchBits = 32;
163static char const g_szAltArch[] = "x86";
164#elif defined(KBUILD_ARCH_X86)
165static unsigned g_cArchBits = 32;
166static char const g_szArch[] = "x86";
167static unsigned g_cAltArchBits = 64;
168static char const g_szAltArch[] = "amd64";
169#else
170# error "Port me!"
171#endif
172
173
174
175/**
176 * Unlinks a worker instance from a list.
177 *
178 * @param pList The list.
179 * @param pWorker The worker.
180 */
181static void kSubmitListUnlink(PWORKERLIST pList, PWORKERINSTANCE pWorker)
182{
183 PWORKERINSTANCE pNext = pWorker->pNext;
184 PWORKERINSTANCE pPrev = pWorker->pPrev;
185
186 if (pNext)
187 {
188 assert(pNext->pPrev == pWorker);
189 pNext->pPrev = pPrev;
190 }
191 else
192 {
193 assert(pList->pTail == pWorker);
194 pList->pTail = pPrev;
195 }
196
197 if (pPrev)
198 {
199 assert(pPrev->pNext == pWorker);
200 pPrev->pNext = pNext;
201 }
202 else
203 {
204 assert(pList->pHead == pWorker);
205 pList->pHead = pNext;
206 }
207
208 assert(!pList->pHead || pList->pHead->pPrev == NULL);
209 assert(!pList->pTail || pList->pTail->pNext == NULL);
210
211 assert(pList->cEntries > 0);
212 pList->cEntries--;
213
214 pWorker->pNext = NULL;
215 pWorker->pPrev = NULL;
216}
217
218
219/**
220 * Appends a worker instance to the tail of a list.
221 *
222 * @param pList The list.
223 * @param pWorker The worker.
224 */
225static void kSubmitListAppend(PWORKERLIST pList, PWORKERINSTANCE pWorker)
226{
227 PWORKERINSTANCE pTail = pList->pTail;
228
229 assert(pTail != pWorker);
230 assert(pList->pHead != pWorker);
231
232 pWorker->pNext = NULL;
233 pWorker->pPrev = pTail;
234 if (pTail != NULL)
235 {
236 assert(pTail->pNext == NULL);
237 pTail->pNext = pWorker;
238 }
239 else
240 {
241 assert(pList->pHead == NULL);
242 pList->pHead = pWorker;
243 }
244 pList->pTail = pWorker;
245
246 assert(pList->pHead->pPrev == NULL);
247 assert(pList->pTail->pNext == NULL);
248
249 pList->cEntries++;
250}
251
252
253/**
254 * Remove worker from the process ID hash table.
255 *
256 * @param pWorker The worker.
257 */
258static void kSubmitPidHashRemove(PWORKERINSTANCE pWorker)
259{
260 size_t idxHash = KWORKER_PID_HASH(pWorker->pid);
261 if (g_apPidHash[idxHash] == pWorker)
262 g_apPidHash[idxHash] = pWorker->pNext;
263 else
264 {
265 PWORKERINSTANCE pPrev = g_apPidHash[idxHash];
266 while (pPrev && pPrev->pNext != pWorker)
267 pPrev = pPrev->pNext;
268 assert(pPrev != NULL);
269 if (pPrev)
270 pPrev->pNext = pWorker->pNext;
271 }
272 pWorker->pid = -1;
273}
274
275
276/**
277 * Looks up a worker by its process ID.
278 *
279 * @returns Pointer to the worker instance if found. NULL if not.
280 * @param pid The process ID of the worker.
281 */
282static PWORKERINSTANCE kSubmitFindWorkerByPid(pid_t pid)
283{
284 PWORKERINSTANCE pWorker = g_apPidHash[KWORKER_PID_HASH(pid)];
285 while (pWorker && pWorker->pid != pid)
286 pWorker = pWorker->pNextPidHash;
287 return pWorker;
288}
289
290
291/**
292 * Creates a new worker process.
293 *
294 * @returns 0 on success, non-zero value on failure.
295 * @param pWorker The worker structure. Caller does the linking
296 * (as we might be reusing an existing worker
297 * instance because a worker shut itself down due
298 * to high resource leak level).
299 * @param cVerbosity The verbosity level.
300 */
301static int kSubmitSpawnWorker(PWORKERINSTANCE pWorker, int cVerbosity)
302{
303#if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
304 static const char s_szWorkerName[] = "kWorker.exe";
305#else
306 static const char s_szWorkerName[] = "kWorker";
307#endif
308 const char *pszBinPath = get_kbuild_bin_path();
309 size_t const cchBinPath = strlen(pszBinPath);
310 size_t cchExectuable;
311 size_t const cbExecutableBuf = GET_PATH_MAX;
312 PATH_VAR(szExecutable);
313#define TUPLE(a_sz) a_sz, sizeof(a_sz) - 1
314 struct variable *pVarVolatile = lookup_variable(TUPLE("PATH_OUT"));
315 if (pVarVolatile)
316 { /* likely */ }
317 else
318 {
319 pVarVolatile = lookup_variable(TUPLE("PATH_OUT_BASE"));
320 if (!pVarVolatile)
321 warn("Neither PATH_OUT_BASE nor PATH_OUT was found.");
322 }
323
324 /*
325 * Construct the executable path.
326 */
327 if ( pWorker->cBits == g_cArchBits
328 ? cchBinPath + 1 + sizeof(s_szWorkerName) <= cbExecutableBuf
329 : cchBinPath + 1 - sizeof(g_szArch) + sizeof(g_szAltArch) + sizeof(s_szWorkerName) <= cbExecutableBuf )
330 {
331#ifdef KBUILD_OS_WINDOWS
332 static DWORD s_fDenyRemoteClients = ~(DWORD)0;
333 wchar_t wszPipeName[64];
334 HANDLE hWorkerPipe;
335 SECURITY_ATTRIBUTES SecAttrs = { /*nLength:*/ sizeof(SecAttrs), /*pAttrs:*/ NULL, /*bInheritHandle:*/ TRUE };
336#else
337 int aiPair[2] = { -1, -1 };
338#endif
339
340 memcpy(szExecutable, pszBinPath, cchBinPath);
341 cchExectuable = cchBinPath;
342
343 /* Replace the arch bin directory extension with the alternative one if requested. */
344 if (pWorker->cBits != g_cArchBits)
345 {
346 if ( cchBinPath < sizeof(g_szArch)
347 || memcmp(&szExecutable[cchBinPath - sizeof(g_szArch) + 1], g_szArch, sizeof(g_szArch) - 1) != 0)
348 return errx(1, "KBUILD_BIN_PATH does not end with main architecture (%s) as expected: %s", pszBinPath, g_szArch);
349 cchExectuable -= sizeof(g_szArch) - 1;
350 memcpy(&szExecutable[cchExectuable], g_szAltArch, sizeof(g_szAltArch) - 1);
351 cchExectuable += sizeof(g_szAltArch) - 1;
352 }
353
354 /* Append a slash and the worker name. */
355 szExecutable[cchExectuable++] = '/';
356 memcpy(&szExecutable[cchExectuable], s_szWorkerName, sizeof(s_szWorkerName));
357
358#ifdef KBUILD_OS_WINDOWS
359 /*
360 * Create the bi-directional pipe. Worker end is marked inheritable, our end is not.
361 */
362 if (s_fDenyRemoteClients == ~(DWORD)0)
363 s_fDenyRemoteClients = GetVersion() >= 0x60000 ? PIPE_REJECT_REMOTE_CLIENTS : 0;
364 _snwprintf(wszPipeName, sizeof(wszPipeName), L"\\\\.\\pipe\\kmk-%u-kWorker-%u", getpid(), g_uWorkerSeqNo++);
365 hWorkerPipe = CreateNamedPipeW(wszPipeName,
366 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE /* win2k sp2+ */,
367 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | s_fDenyRemoteClients,
368 1 /* cMaxInstances */,
369 64 /*cbOutBuffer*/,
370 65536 /*cbInBuffer*/,
371 0 /*cMsDefaultTimeout -> 50ms*/,
372 &SecAttrs /* inherit */);
373 if (hWorkerPipe != INVALID_HANDLE_VALUE)
374 {
375 pWorker->hPipe = CreateFileW(wszPipeName,
376 GENERIC_READ | GENERIC_WRITE,
377 0 /* dwShareMode - no sharing */,
378 NULL /*pSecAttr - no inherit */,
379 OPEN_EXISTING,
380 FILE_FLAG_OVERLAPPED,
381 NULL /*hTemplate*/);
382 if (pWorker->hPipe != INVALID_HANDLE_VALUE)
383 {
384 pWorker->OverlappedRead.hEvent = CreateEventW(NULL /*pSecAttrs - no inherit*/, TRUE /*bManualReset*/,
385 TRUE /*bInitialState*/, NULL /*pwszName*/);
386 if (pWorker->OverlappedRead.hEvent != NULL)
387 {
388 char szHandleArg[32];
389 extern int process_priority; /* main.c */
390 char szPriorityArg[32];
391 const char *apszArgs[10];
392 int cArgs = 0;
393 apszArgs[cArgs++] = szExecutable;
394 apszArgs[cArgs++] = "--pipe";
395 _snprintf(szHandleArg, sizeof(szHandleArg), "%p", hWorkerPipe);
396 apszArgs[cArgs++] = szHandleArg;
397 if (pVarVolatile)
398 {
399 apszArgs[cArgs++] = "--volatile";
400 apszArgs[cArgs++] = pVarVolatile->value;
401 }
402 if (process_priority != 0)
403 {
404 apszArgs[cArgs++] = "--priority";
405 _snprintf(szPriorityArg, sizeof(szPriorityArg), "%u", process_priority);
406 apszArgs[cArgs++] = szPriorityArg;
407 }
408 apszArgs[cArgs] = NULL;
409
410 /*
411 * Create the worker process.
412 */
413 pWorker->hProcess = (HANDLE) _spawnve(_P_NOWAIT, szExecutable, apszArgs, environ);
414 if ((intptr_t)pWorker->hProcess != -1)
415 {
416 CloseHandle(hWorkerPipe);
417 pWorker->pid = GetProcessId(pWorker->hProcess);
418 if (cVerbosity > 0)
419 fprintf(stderr, "kSubmit: created %d bit worker %d\n", pWorker->cBits, pWorker->pid);
420 return 0;
421 }
422 err(1, "_spawnve(,%s,,)", szExecutable);
423 CloseHandle(pWorker->OverlappedRead.hEvent);
424 pWorker->OverlappedRead.hEvent = INVALID_HANDLE_VALUE;
425 }
426 else
427 errx(1, "CreateEventW failed: %u", GetLastError());
428 CloseHandle(pWorker->hPipe);
429 pWorker->hPipe = INVALID_HANDLE_VALUE;
430 }
431 else
432 errx(1, "Opening named pipe failed: %u", GetLastError());
433 CloseHandle(hWorkerPipe);
434 }
435 else
436 errx(1, "CreateNamedPipeW failed: %u", GetLastError());
437
438#else
439 /*
440 * Create a socket pair.
441 */
442 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, aiPair) == 0)
443 {
444 pWorker->fdSocket = aiPair[1];
445 }
446 else
447 err(1, "socketpair");
448#endif
449 }
450 else
451 errx(1, "KBUILD_BIN_PATH is too long");
452 return -1;
453}
454
455
456/**
457 * Selects an idle worker or spawns a new one.
458 *
459 * @returns Pointer to the selected worker instance. NULL on error.
460 * @param pWorker The idle worker instance to respawn.
461 * On failure this will be freed!
462 * @param cBitsWorker The worker bitness - 64 or 32.
463 */
464static int kSubmitRespawnWorker(PWORKERINSTANCE pWorker, int cVerbosity)
465{
466 /*
467 * Clean up after the old worker.
468 */
469#ifdef KBUILD_OS_WINDOWS
470 DWORD rcWait;
471
472 /* Close the pipe handle first, breaking the pipe in case it's not already
473 busted up. Close the event semaphore too before waiting for the process. */
474 if (pWorker->hPipe != INVALID_HANDLE_VALUE)
475 {
476 if (!CloseHandle(pWorker->hPipe))
477 warnx("CloseHandle(pWorker->hPipe): %u", GetLastError());
478 pWorker->hPipe = INVALID_HANDLE_VALUE;
479 }
480
481 if (!CloseHandle(pWorker->OverlappedRead.hEvent))
482 warnx("CloseHandle(pWorker->OverlappedRead.hEvent): %u", GetLastError());
483 pWorker->OverlappedRead.hEvent = INVALID_HANDLE_VALUE;
484
485 /* It's probably shutdown already, if not give it 10 milliseconds before
486 we terminate it forcefully. */
487 rcWait = WaitForSingleObject(pWorker->hProcess, 10);
488 if (rcWait != WAIT_OBJECT_0)
489 {
490 BOOL fRc = TerminateProcess(pWorker->hProcess, 127);
491 rcWait = WaitForSingleObject(pWorker->hProcess, 100);
492 if (rcWait != WAIT_OBJECT_0)
493 warnx("WaitForSingleObject returns %u (and TerminateProcess %d)", rcWait, fRc);
494 }
495
496 if (!CloseHandle(pWorker->hProcess))
497 warnx("CloseHandle(pWorker->hProcess): %u", GetLastError());
498 pWorker->hProcess = INVALID_HANDLE_VALUE;
499
500#else
501 pid_t pidWait;
502 int rc;
503
504 if (pWorker->fdSocket != -1)
505 {
506 if (close(pWorker->fdSocket) != 0)
507 warn("close(pWorker->fdSocket)");
508 pWorker->fdSocket = -1;
509 }
510
511 kill(pWorker->pid, SIGTERM);
512 pidWait = waitpid(pWorker->pid, &rc, 0);
513 if (pidWait != pWorker->pid)
514 warn("waitpid(pWorker->pid,,0)");
515#endif
516
517 /*
518 * Unlink it from the hash table.
519 */
520 kSubmitPidHashRemove(pWorker);
521
522 /*
523 * Respawn it.
524 */
525 if (kSubmitSpawnWorker(pWorker, cVerbosity) == 0)
526 {
527 /*
528 * Insert it into the process ID hash table and idle list.
529 */
530 size_t idxHash = KWORKER_PID_HASH(pWorker->pid);
531 pWorker->pNextPidHash = g_apPidHash[idxHash];
532 g_apPidHash[idxHash] = pWorker;
533 return 0;
534 }
535
536 kSubmitListUnlink(&g_IdleList, pWorker);
537 free(pWorker);
538 return -1;
539}
540
541
542/**
543 * Selects an idle worker or spawns a new one.
544 *
545 * @returns Pointer to the selected worker instance. NULL on error.
546 * @param cBitsWorker The worker bitness - 64 or 32.
547 */
548static PWORKERINSTANCE kSubmitSelectWorkSpawnNewIfNecessary(unsigned cBitsWorker, int cVerbosity)
549{
550 /*
551 * Lookup up an idle worker.
552 */
553 PWORKERINSTANCE pWorker = g_IdleList.pHead;
554 while (pWorker)
555 {
556 if (pWorker->cBits == cBitsWorker)
557 return pWorker;
558 pWorker = pWorker->pNext;
559 }
560
561 /*
562 * Create a new worker instance.
563 */
564 pWorker = (PWORKERINSTANCE)xcalloc(sizeof(*pWorker));
565 pWorker->cBits = cBitsWorker;
566 if (kSubmitSpawnWorker(pWorker, cVerbosity) == 0)
567 {
568 /*
569 * Insert it into the process ID hash table and idle list.
570 */
571 size_t idxHash = KWORKER_PID_HASH(pWorker->pid);
572 pWorker->pNextPidHash = g_apPidHash[idxHash];
573 g_apPidHash[idxHash] = pWorker;
574
575 kSubmitListAppend(&g_IdleList, pWorker);
576 return pWorker;
577 }
578
579 free(pWorker);
580 return NULL;
581}
582
583
584/**
585 * Composes a JOB mesage for a worker.
586 *
587 * @returns Pointer to the message.
588 * @param pszExecutable The executable to run.
589 * @param papszArgs The argument vector.
590 * @param papszEnvVars The environment vector.
591 * @param pszCwd The current directory.
592 * @param fWatcomBrainDamage The wcc/wcc386 workaround.
593 * @param fNoPchCaching Whether to disable precompiled header caching.
594 * @param papszPostCmdArgs The post command and it's arguments.
595 * @param cPostCmdArgs Number of post command argument, including the
596 * command. Zero if no post command scheduled.
597 * @param pcbMsg Where to return the message length.
598 */
599static void *kSubmitComposeJobMessage(const char *pszExecutable, char **papszArgs, char **papszEnvVars,
600 const char *pszCwd, int fWatcomBrainDamage, int fNoPchCaching,
601 char **papszPostCmdArgs, uint32_t cPostCmdArgs, uint32_t *pcbMsg)
602{
603 size_t cbTmp;
604 uint32_t i;
605 uint32_t cbMsg;
606 uint32_t cArgs;
607 uint32_t cEnvVars;
608 uint8_t *pbMsg;
609 uint8_t *pbCursor;
610
611 /*
612 * Adjust input.
613 */
614 if (!pszExecutable)
615 pszExecutable = papszArgs[0];
616
617 /*
618 * Calculate the message length first.
619 */
620 cbMsg = sizeof(cbMsg);
621 cbMsg += sizeof("JOB");
622 cbMsg += strlen(pszExecutable) + 1;
623 cbMsg += strlen(pszCwd) + 1;
624
625 cbMsg += sizeof(cArgs);
626 for (i = 0; papszArgs[i] != NULL; i++)
627 cbMsg += 1 + strlen(papszArgs[i]) + 1;
628 cArgs = i;
629
630 cbMsg += sizeof(cArgs);
631 for (i = 0; papszEnvVars[i] != NULL; i++)
632 cbMsg += strlen(papszEnvVars[i]) + 1;
633 cEnvVars = i;
634
635 cbMsg += 1; /* fWatcomBrainDamage */
636 cbMsg += 1; /* fNoPchCaching */
637
638 cbMsg += sizeof(cPostCmdArgs);
639 for (i = 0; i < cPostCmdArgs; i++)
640 cbMsg += strlen(papszPostCmdArgs[i]) + 1;
641
642 /*
643 * Compose the message.
644 */
645 pbMsg = pbCursor = xmalloc(cbMsg);
646
647 /* header */
648 memcpy(pbCursor, &cbMsg, sizeof(cbMsg));
649 pbCursor += sizeof(cbMsg);
650 memcpy(pbCursor, "JOB", sizeof("JOB"));
651 pbCursor += sizeof("JOB");
652
653 /* executable. */
654 cbTmp = strlen(pszExecutable) + 1;
655 memcpy(pbCursor, pszExecutable, cbTmp);
656 pbCursor += cbTmp;
657
658 /* cwd */
659 cbTmp = strlen(pszCwd) + 1;
660 memcpy(pbCursor, pszCwd, cbTmp);
661 pbCursor += cbTmp;
662
663 /* argument */
664 memcpy(pbCursor, &cArgs, sizeof(cArgs));
665 pbCursor += sizeof(cArgs);
666 for (i = 0; papszArgs[i] != NULL; i++)
667 {
668 *pbCursor++ = 0; /* Argument expansion flags (MSC, EMX). */
669 cbTmp = strlen(papszArgs[i]) + 1;
670 memcpy(pbCursor, papszArgs[i], cbTmp);
671 pbCursor += cbTmp;
672 }
673 assert(i == cArgs);
674
675 /* environment */
676 memcpy(pbCursor, &cEnvVars, sizeof(cEnvVars));
677 pbCursor += sizeof(cEnvVars);
678 for (i = 0; papszEnvVars[i] != NULL; i++)
679 {
680 cbTmp = strlen(papszEnvVars[i]) + 1;
681 memcpy(pbCursor, papszEnvVars[i], cbTmp);
682 pbCursor += cbTmp;
683 }
684 assert(i == cEnvVars);
685
686 /* flags */
687 *pbCursor++ = fWatcomBrainDamage != 0;
688 *pbCursor++ = fNoPchCaching != 0;
689
690 /* post command */
691 memcpy(pbCursor, &cPostCmdArgs, sizeof(cPostCmdArgs));
692 pbCursor += sizeof(cPostCmdArgs);
693 for (i = 0; i < cPostCmdArgs; i++)
694 {
695 cbTmp = strlen(papszPostCmdArgs[i]) + 1;
696 memcpy(pbCursor, papszPostCmdArgs[i], cbTmp);
697 pbCursor += cbTmp;
698 }
699 assert(i == cPostCmdArgs);
700
701 assert(pbCursor - pbMsg == (size_t)cbMsg);
702
703 /*
704 * Done.
705 */
706 *pcbMsg = cbMsg;
707 return pbMsg;
708}
709
710
711/**
712 * Sends the job message to the given worker, respawning the worker if
713 * necessary.
714 *
715 * @returns 0 on success, non-zero on failure.
716 *
717 * @param pWorker The work to send the request to. The worker is
718 * on the idle list.
719 * @param pvMsg The message to send.
720 * @param cbMsg The size of the message.
721 * @param fNoRespawning Set if
722 * @param cVerbosity The verbosity level.
723 */
724static int kSubmitSendJobMessage(PWORKERINSTANCE pWorker, void const *pvMsg, uint32_t cbMsg, int fNoRespawning, int cVerbosity)
725{
726 int cRetries;
727
728 /*
729 * Respawn the worker if it stopped by itself and we closed the pipe already.
730 */
731#ifdef KBUILD_OS_WINDOWS
732 if (pWorker->hPipe == INVALID_HANDLE_VALUE)
733#else
734 if (pWorker->fdSocket == -1)
735#endif
736 {
737 if (!fNoRespawning)
738 {
739 if (cVerbosity > 0)
740 fprintf(stderr, "kSubmit: Respawning worker (#1)...\n");
741 if (kSubmitRespawnWorker(pWorker, cVerbosity) != 0)
742 return 2;
743 }
744
745 }
746
747 /*
748 * Restart-on-broken-pipe loop. Necessary?
749 */
750 for (cRetries = !fNoRespawning ? 1 : 0; ; cRetries--)
751 {
752 /*
753 * Try write the message.
754 */
755 uint32_t cbLeft = cbMsg;
756 uint8_t const *pbLeft = (uint8_t const *)pvMsg;
757#ifdef KBUILD_OS_WINDOWS
758 DWORD dwErr;
759 DWORD cbWritten;
760 while (WriteFile(pWorker->hPipe, pbLeft, cbLeft, &cbWritten, NULL /*pOverlapped*/))
761 {
762 assert(cbWritten <= cbLeft);
763 cbLeft -= cbWritten;
764 if (!cbLeft)
765 return 0;
766
767 /* This scenario shouldn't really ever happen. But just in case... */
768 pbLeft += cbWritten;
769 }
770 dwErr = GetLastError();
771 if ( ( dwErr != ERROR_BROKEN_PIPE
772 && dwErr != ERROR_NO_DATA)
773 || cRetries <= 0)
774 return errx(1, "Error writing to worker: %u", dwErr);
775#else
776 ssize_t cbWritten
777 while ((cbWritten = write(pWorker->fdSocket, pbLeft, cbLeft)) >= 0)
778 {
779 assert(cbWritten <= cbLeft);
780 cbLeft -= cbWritten;
781 if (!cbLeft)
782 return 0;
783
784 pbLeft += cbWritten;
785 }
786 if ( ( errno != EPIPE
787 && errno != ENOTCONN
788 && errno != ECONNRESET))
789 || cRetries <= 0)
790 return err(1, "Error writing to worker");
791# error "later"
792#endif
793
794 /*
795 * Broken connection. Try respawn the worker.
796 */
797 if (cVerbosity > 0)
798 fprintf(stderr, "kSubmit: Respawning worker (#2)...\n");
799 if (kSubmitRespawnWorker(pWorker, cVerbosity) != 0)
800 return 2;
801 }
802}
803
804
805/**
806 * Closes the connection on a worker that said it is going to exit now.
807 *
808 * This is a way of dealing with imperfect resource management in the worker, it
809 * will monitor it a little and trigger a respawn when it looks bad.
810 *
811 * This function just closes the pipe / socket connection to the worker. The
812 * kSubmitSendJobMessage function will see this a trigger a respawn the next
813 * time the worker is engaged. This will usually mean there's a little delay in
814 * which the process can terminate without us having to actively wait for it.
815 *
816 * @param pWorker The worker instance.
817 */
818static void kSubmitCloseConnectOnExitingWorker(PWORKERINSTANCE pWorker)
819{
820#ifdef KBUILD_OS_WINDOWS
821 if (!CloseHandle(pWorker->hPipe))
822 warnx("CloseHandle(pWorker->hPipe): %u", GetLastError());
823 pWorker->hPipe = INVALID_HANDLE_VALUE;
824#else
825 if (close(pWorker->fdSocket) != 0)
826 warn("close(pWorker->fdSocket)");
827 pWorker->fdSocket = -1;
828#endif
829}
830
831
832#ifdef KBUILD_OS_WINDOWS
833
834/**
835 * Handles read failure.
836 *
837 * @returns Exit code.
838 * @param pWorker The worker instance.
839 * @param dwErr The error code.
840 * @param pszWhere Where it failed.
841 */
842static int kSubmitWinReadFailed(PWORKERINSTANCE pWorker, DWORD dwErr, const char *pszWhere)
843{
844 DWORD dwExitCode;
845
846 if (pWorker->cbResultRead == 0)
847 errx(1, "%s/ReadFile failed: %u", pszWhere, dwErr);
848 else
849 errx(1, "%s/ReadFile failed: %u (read %u bytes)", pszWhere, dwErr, pWorker->cbResultRead);
850 assert(dwErr != 0);
851
852 /* Complete the result. */
853 pWorker->Result.s.rcExit = 127;
854 pWorker->Result.s.bWorkerExiting = 1;
855 pWorker->cbResultRead = sizeof(pWorker->Result);
856
857 if (GetExitCodeProcess(pWorker->hProcess, &dwExitCode))
858 {
859 if (dwExitCode != 0)
860 pWorker->Result.s.rcExit = dwExitCode;
861 }
862
863 return dwErr != 0 ? (int)(dwErr & 0x7fffffff) : 0x7fffffff;
864
865}
866
867
868/**
869 * Used by
870 * @returns 0 if we got the whole result, -1 if I/O is pending, and windows last
871 * error on ReadFile failure.
872 * @param pWorker The worker instance.
873 */
874static int kSubmitReadMoreResultWin(PWORKERINSTANCE pWorker, const char *pszWhere)
875{
876 /*
877 * Set up the result read, telling the sub_proc.c unit about it.
878 */
879 while (pWorker->cbResultRead < sizeof(pWorker->Result))
880 {
881 DWORD cbRead = 0;
882
883 BOOL fRc = ResetEvent(pWorker->OverlappedRead.hEvent);
884 assert(fRc); (void)fRc;
885
886 pWorker->OverlappedRead.Offset = 0;
887 pWorker->OverlappedRead.OffsetHigh = 0;
888
889 if (!ReadFile(pWorker->hPipe, &pWorker->Result.ab[pWorker->cbResultRead],
890 sizeof(pWorker->Result) - pWorker->cbResultRead,
891 &cbRead,
892 &pWorker->OverlappedRead))
893 {
894 DWORD dwErr = GetLastError();
895 if (dwErr == ERROR_IO_PENDING)
896 return -1;
897 return kSubmitWinReadFailed(pWorker, dwErr, pszWhere);
898 }
899
900 pWorker->cbResultRead += cbRead;
901 assert(pWorker->cbResultRead <= sizeof(pWorker->Result));
902 }
903 return 0;
904}
905
906#endif /* KBUILD_OS_WINDOWS */
907
908/**
909 * Marks the worker active.
910 *
911 * On windows this involves setting up the async result read and telling
912 * sub_proc.c about the process.
913 *
914 * @returns Exit code.
915 * @param pWorker The worker instance to mark as active.
916 * @param cVerbosity The verbosity level.
917 * @param pChild The kmk child to associate the job with.
918 * @param pPidSpawned If @a *pPidSpawned is non-zero if the child is
919 * running, otherwise the worker is already done
920 * and we've returned the exit code of the job.
921 */
922static int kSubmitMarkActive(PWORKERINSTANCE pWorker, int cVerbosity, struct child *pChild, pid_t *pPidSpawned)
923{
924#ifdef KBUILD_OS_WINDOWS
925 int rc;
926#endif
927
928 pWorker->cbResultRead = 0;
929
930#ifdef KBUILD_OS_WINDOWS
931 /*
932 * Setup the async result read on windows. If we're slow and the worker
933 * very fast, this may actually get the result immediately.
934 */
935l_again:
936 rc = kSubmitReadMoreResultWin(pWorker, "kSubmitMarkActive");
937 if (rc == -1)
938 {
939# ifndef CONFIG_NEW_WIN_CHILDREN
940 if (process_kmk_register_submit(pWorker->OverlappedRead.hEvent, (intptr_t)pWorker, pPidSpawned) == 0)
941 { /* likely */ }
942 else
943 {
944 /* We need to do the waiting here because sub_proc.c has too much to do. */
945 warnx("Too many processes for sub_proc.c to handle!");
946 WaitForSingleObject(pWorker->OverlappedRead.hEvent, INFINITE);
947 goto l_again;
948 }
949# else
950 if (MkWinChildCreateSubmit((intptr_t)pWorker->OverlappedRead.hEvent, pWorker, pPidSpawned) == 0)
951 { /* likely */ }
952 else
953 {
954 /* We need to do the waiting here because sub_proc.c has too much to do. */
955 warnx("MkWinChildCreateSubmit failed!");
956 WaitForSingleObject(pWorker->OverlappedRead.hEvent, INFINITE);
957 goto l_again;
958 }
959# endif
960 }
961 else
962 {
963 assert(rc == 0 || pWorker->Result.s.rcExit != 0);
964 if (pWorker->Result.s.bWorkerExiting)
965 kSubmitCloseConnectOnExitingWorker(pWorker);
966 *pPidSpawned = 0;
967 return pWorker->Result.s.rcExit;
968 }
969#endif
970
971 /*
972 * Mark it busy and move it to the active instance.
973 */
974 pWorker->pBusyWith = pChild;
975#ifndef KBUILD_OS_WINDOWS
976 *pPidSpawned = pWorker->pid;
977#endif
978
979 kSubmitListUnlink(&g_IdleList, pWorker);
980 kSubmitListAppend(&g_BusyList, pWorker);
981 return 0;
982}
983
984
985#ifdef KBUILD_OS_WINDOWS
986
987/**
988 * Retrieve the worker child result.
989 *
990 * If incomplete, we restart the ReadFile operation like kSubmitMarkActive does.
991 *
992 * @returns 0 on success, -1 if ReadFile was restarted.
993 * @param pvUser The worker instance.
994 * @param prcExit Where to return the exit code.
995 * @param piSigNo Where to return the signal number.
996 */
997int kSubmitSubProcGetResult(intptr_t pvUser, int *prcExit, int *piSigNo)
998{
999 PWORKERINSTANCE pWorker = (PWORKERINSTANCE)pvUser;
1000
1001 /*
1002 * Get the overlapped result. There should be one since we're here
1003 * because of a satisfied WaitForMultipleObject.
1004 */
1005 DWORD cbRead = 0;
1006 if (GetOverlappedResult(pWorker->hPipe, &pWorker->OverlappedRead, &cbRead, TRUE))
1007 {
1008 pWorker->cbResultRead += cbRead;
1009 assert(pWorker->cbResultRead <= sizeof(pWorker->Result));
1010
1011 /* More to be read? */
1012 while (pWorker->cbResultRead < sizeof(pWorker->Result))
1013 {
1014 int rc = kSubmitReadMoreResultWin(pWorker, "kSubmitSubProcGetResult/more");
1015 if (rc == -1)
1016 return -1;
1017 assert(rc == 0 || pWorker->Result.s.rcExit != 0);
1018 }
1019 assert(pWorker->cbResultRead == sizeof(pWorker->Result));
1020 }
1021 else
1022 {
1023 DWORD dwErr = GetLastError();
1024 kSubmitWinReadFailed(pWorker, dwErr, "kSubmitSubProcGetResult/result");
1025 }
1026
1027 /*
1028 * Okay, we've got a result.
1029 */
1030 *prcExit = pWorker->Result.s.rcExit;
1031 switch (pWorker->Result.s.rcExit)
1032 {
1033 default: *piSigNo = 0; break;
1034 case CONTROL_C_EXIT: *piSigNo = SIGINT; break;
1035 case STATUS_INTEGER_DIVIDE_BY_ZERO: *piSigNo = SIGFPE; break;
1036 case STATUS_ACCESS_VIOLATION: *piSigNo = SIGSEGV; break;
1037 case STATUS_PRIVILEGED_INSTRUCTION:
1038 case STATUS_ILLEGAL_INSTRUCTION: *piSigNo = SIGILL; break;
1039 }
1040 if (pWorker->Result.s.bWorkerExiting)
1041 kSubmitCloseConnectOnExitingWorker(pWorker);
1042
1043 return 0;
1044}
1045
1046
1047int kSubmitSubProcKill(intptr_t pvUser, int iSignal)
1048{
1049 return -1;
1050}
1051
1052
1053/**
1054 * Called by process_cleanup when it's done with the worker.
1055 *
1056 * @param pvUser The worker instance.
1057 */
1058void kSubmitSubProcCleanup(intptr_t pvUser)
1059{
1060 PWORKERINSTANCE pWorker = (PWORKERINSTANCE)pvUser;
1061 kSubmitListUnlink(&g_BusyList, pWorker);
1062 kSubmitListAppend(&g_IdleList, pWorker);
1063}
1064
1065#endif /* KBUILD_OS_WINDOWS */
1066
1067
1068/**
1069 * atexit callback that trigger worker termination.
1070 */
1071static void kSubmitAtExitCallback(void)
1072{
1073 PWORKERINSTANCE pWorker;
1074 DWORD msStartTick;
1075 DWORD cKillRaids = 0;
1076
1077 /*
1078 * Tell all the workers to exit by breaking the connection.
1079 */
1080 for (pWorker = g_IdleList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
1081 kSubmitCloseConnectOnExitingWorker(pWorker);
1082 for (pWorker = g_BusyList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
1083 kSubmitCloseConnectOnExitingWorker(pWorker);
1084
1085 /*
1086 * Wait a little while for them to stop.
1087 */
1088 Sleep(0);
1089 msStartTick = GetTickCount();
1090 for (;;)
1091 {
1092 /*
1093 * Collect handles of running processes.
1094 */
1095 PWORKERINSTANCE apWorkers[MAXIMUM_WAIT_OBJECTS];
1096 HANDLE ahHandles[MAXIMUM_WAIT_OBJECTS];
1097 DWORD cHandles = 0;
1098
1099 for (pWorker = g_IdleList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
1100 if (pWorker->hProcess != INVALID_HANDLE_VALUE)
1101 {
1102 if (cHandles < MAXIMUM_WAIT_OBJECTS)
1103 {
1104 apWorkers[cHandles] = pWorker;
1105 ahHandles[cHandles] = pWorker->hProcess;
1106 }
1107 cHandles++;
1108 }
1109 for (pWorker = g_BusyList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
1110 if (pWorker->hProcess != INVALID_HANDLE_VALUE)
1111 {
1112 if (cHandles < MAXIMUM_WAIT_OBJECTS)
1113 {
1114 apWorkers[cHandles] = pWorker;
1115 ahHandles[cHandles] = pWorker->hProcess;
1116 }
1117 cHandles++;
1118 }
1119 if (cHandles == 0)
1120 return;
1121
1122 /*
1123 * Wait for the processes.
1124 */
1125 for (;;)
1126 {
1127 DWORD cMsElapsed = GetTickCount() - msStartTick;
1128 DWORD dwWait = WaitForMultipleObjects(cHandles <= MAXIMUM_WAIT_OBJECTS ? cHandles : MAXIMUM_WAIT_OBJECTS,
1129 ahHandles, FALSE /*bWaitAll*/,
1130 cMsElapsed < 5000 ? 5000 - cMsElapsed + 16 : 16);
1131 if ( dwWait >= WAIT_OBJECT_0
1132 && dwWait <= WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS)
1133 {
1134 size_t idx = dwWait - WAIT_OBJECT_0;
1135 CloseHandle(apWorkers[idx]->hProcess);
1136 apWorkers[idx]->hProcess = INVALID_HANDLE_VALUE;
1137
1138 if (cHandles <= MAXIMUM_WAIT_OBJECTS)
1139 {
1140 /* Restart the wait with the worker removed, or quit if it was the last worker. */
1141 cHandles--;
1142 if (!cHandles)
1143 return;
1144 if (idx != cHandles)
1145 {
1146 apWorkers[idx] = apWorkers[cHandles];
1147 ahHandles[idx] = ahHandles[cHandles];
1148 }
1149 continue;
1150 }
1151 /* else: Reconstruct the wait array so we get maximum coverage. */
1152 }
1153 else if (dwWait == WAIT_TIMEOUT)
1154 {
1155 /* Terminate the whole bunch. */
1156 cKillRaids++;
1157 if (cKillRaids == 1 && getenv("KMK_KSUBMIT_NO_KILL") == NULL)
1158 {
1159 fprintf(stderr, "kmk/kSubmit: Killing %u lingering worker processe(s)!\n", cHandles);
1160 for (pWorker = g_IdleList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
1161 if (pWorker->hProcess != INVALID_HANDLE_VALUE)
1162 TerminateProcess(pWorker->hProcess, WAIT_TIMEOUT);
1163 for (pWorker = g_BusyList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
1164 if (pWorker->hProcess != INVALID_HANDLE_VALUE)
1165 TerminateProcess(pWorker->hProcess, WAIT_TIMEOUT);
1166 }
1167 else
1168 {
1169 fprintf(stderr, "kmk/kSubmit: Giving up on the last %u worker processe(s). :-(\n", cHandles);
1170 return;
1171 }
1172 }
1173 else
1174 {
1175 /* Some kind of wait error. Could be a bad handle, check each and remove
1176 bad ones as well as completed ones. */
1177 size_t idx;
1178 fprintf(stderr, "kmk/kSubmit: WaitForMultipleObjects unexpectedly returned %#u (err=%u)\n",
1179 dwWait, GetLastError());
1180 for (idx = 0; idx < cHandles; idx++)
1181 {
1182 dwWait = WaitForSingleObject(ahHandles[idx], 0 /*ms*/);
1183 if (dwWait != WAIT_TIMEOUT)
1184 {
1185 CloseHandle(apWorkers[idx]->hProcess);
1186 apWorkers[idx]->hProcess = INVALID_HANDLE_VALUE;
1187 }
1188 }
1189 }
1190 break;
1191 } /* wait loop */
1192 } /* outer wait loop */
1193}
1194
1195
1196static int usage(FILE *pOut, const char *argv0)
1197{
1198 fprintf(pOut,
1199 "usage: %s [-Z|--zap-env] [-E|--set <var=val>] [-U|--unset <var=val>]\n"
1200 " [-A|--append <var=val>] [-D|--prepend <var=val>]\n"
1201 " [-C|--chdir <dir>] [--wcc-brain-damage] [--no-pch-caching]\n"
1202 " [-3|--32-bit] [-6|--64-bit] [-v]\n"
1203 " [-P|--post-cmd <cmd> [args]] -- <program> [args]\n"
1204 " or: %s --help\n"
1205 " or: %s --version\n"
1206 "\n"
1207 "Options:\n"
1208 " -Z, --zap-env, -i, --ignore-environment\n"
1209 " Zaps the environment. Position dependent.\n"
1210 " -E, --set <var>=[value]\n"
1211 " Sets an enviornment variable putenv fashion. Position dependent.\n"
1212 " -U, --unset <var>\n"
1213 " Removes an environment variable. Position dependent.\n"
1214 " -A, --append <var>=<value>\n"
1215 " Appends the given value to the environment variable.\n"
1216 " -D,--prepend <var>=<value>\n"
1217 " Prepends the given value to the environment variable.\n"
1218 " -C, --chdir <dir>\n"
1219 " Specifies the current directory for the program. Relative paths\n"
1220 " are relative to the previous -C option. Default is getcwd value.\n"
1221 " -3, --32-bit\n"
1222 " Selects a 32-bit kWorker process. Default: kmk bit count\n"
1223 " -6, --64-bit\n"
1224 " Selects a 64-bit kWorker process. Default: kmk bit count\n"
1225 " --wcc-brain-damage\n"
1226 " Works around wcc and wcc386 (Open Watcom) not following normal\n"
1227 " quoting conventions on Windows, OS/2, and DOS.\n"
1228 " --no-pch-caching\n"
1229 " Do not cache precompiled header files because they're being created.\n"
1230 " -v,--verbose\n"
1231 " More verbose execution.\n"
1232 " -P|--post-cmd <cmd> ...\n"
1233 " For running a built-in command on the output, specifying the command\n"
1234 " and all it's parameters. Currently supported commands:\n"
1235 " kDepObj\n"
1236 " -V,--version\n"
1237 " Show the version number.\n"
1238 " -h,--help\n"
1239 " Show this usage information.\n"
1240 "\n"
1241 ,
1242 argv0, argv0, argv0);
1243 return 2;
1244}
1245
1246
1247int kmk_builtin_kSubmit(int argc, char **argv, char **envp, struct child *pChild, pid_t *pPidSpawned)
1248{
1249 int rcExit = 0;
1250 int iArg;
1251 unsigned cAllocatedEnvVars;
1252 unsigned cEnvVars;
1253 char **papszEnvVars;
1254 const char *pszExecutable = NULL;
1255 int iPostCmd = argc;
1256 int cPostCmdArgs = 0;
1257 unsigned cBitsWorker = g_cArchBits;
1258 int fWatcomBrainDamage = 0;
1259 int fNoPchCaching = 0;
1260 int cVerbosity = 0;
1261 size_t const cbCwdBuf = GET_PATH_MAX;
1262 PATH_VAR(szCwd);
1263
1264 g_progname = argv[0];
1265
1266 /*
1267 * Create default program environment.
1268 *
1269 * Note! We only clean up the environment on successful return, assuming
1270 * make will stop after that.
1271 */
1272 if (getcwd_fs(szCwd, cbCwdBuf) != NULL)
1273 { /* likely */ }
1274 else
1275 return err(1, "getcwd_fs failed\n");
1276
1277 /* The environment starts out in read-only mode and will be duplicated if modified. */
1278 cAllocatedEnvVars = 0;
1279 papszEnvVars = envp;
1280 cEnvVars = 0;
1281 while (papszEnvVars[cEnvVars] != NULL)
1282 cEnvVars++;
1283
1284 /*
1285 * Parse the command line.
1286 */
1287 for (iArg = 1; iArg < argc; iArg++)
1288 {
1289 const char *pszArg = argv[iArg];
1290 if (*pszArg == '-')
1291 {
1292 char chOpt = *++pszArg;
1293 pszArg++;
1294 if (chOpt != '-')
1295 {
1296 if (chOpt != '\0')
1297 { /* likely */ }
1298 else
1299 {
1300 errx(1, "Incomplete option: '-'");
1301 return usage(stderr, argv[0]);
1302 }
1303 }
1304 else
1305 {
1306 /* '--' indicates where the bits to execute start. */
1307 if (*pszArg == '\0')
1308 {
1309 iArg++;
1310 break;
1311 }
1312
1313 if ( strcmp(pszArg, "wcc-brain-damage") == 0
1314 || strcmp(pszArg, "watcom-brain-damage") == 0)
1315 {
1316 fWatcomBrainDamage = 1;
1317 continue;
1318 }
1319
1320 if (strcmp(pszArg, "no-pch-caching") == 0)
1321 {
1322 fNoPchCaching = 1;
1323 continue;
1324 }
1325
1326 /* convert to short. */
1327 if (strcmp(pszArg, "help") == 0)
1328 chOpt = 'h';
1329 else if (strcmp(pszArg, "version") == 0)
1330 chOpt = 'V';
1331 else if (strcmp(pszArg, "set") == 0)
1332 chOpt = 'E';
1333 else if (strcmp(pszArg, "append") == 0)
1334 chOpt = 'A';
1335 else if (strcmp(pszArg, "prepend") == 0)
1336 chOpt = 'D';
1337 else if (strcmp(pszArg, "unset") == 0)
1338 chOpt = 'U';
1339 else if ( strcmp(pszArg, "zap-env") == 0
1340 || strcmp(pszArg, "ignore-environment") == 0 /* GNU env compatibility. */ )
1341 chOpt = 'Z';
1342 else if (strcmp(pszArg, "chdir") == 0)
1343 chOpt = 'C';
1344 else if (strcmp(pszArg, "post-cmd") == 0)
1345 chOpt = 'P';
1346 else if (strcmp(pszArg, "32-bit") == 0)
1347 chOpt = '3';
1348 else if (strcmp(pszArg, "64-bit") == 0)
1349 chOpt = '6';
1350 else if (strcmp(pszArg, "verbose") == 0)
1351 chOpt = 'v';
1352 else if (strcmp(pszArg, "executable") == 0)
1353 chOpt = 'e';
1354 else
1355 {
1356 errx(2, "Unknown option: '%s'", pszArg - 2);
1357 return usage(stderr, argv[0]);
1358 }
1359 pszArg = "";
1360 }
1361
1362 do
1363 {
1364 /* Get option value first, if the option takes one. */
1365 const char *pszValue = NULL;
1366 switch (chOpt)
1367 {
1368 case 'A':
1369 case 'C':
1370 case 'E':
1371 case 'U':
1372 case 'D':
1373 case 'e':
1374 if (*pszArg != '\0')
1375 pszValue = pszArg + (*pszArg == ':' || *pszArg == '=');
1376 else if (++iArg < argc)
1377 pszValue = argv[iArg];
1378 else
1379 {
1380 errx(1, "Option -%c requires a value!", chOpt);
1381 return usage(stderr, argv[0]);
1382 }
1383 break;
1384 }
1385
1386 switch (chOpt)
1387 {
1388 case 'Z':
1389 case 'i': /* GNU env compatibility. */
1390 rcExit = kBuiltinOptEnvZap(&papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity);
1391 if (rcExit == 0)
1392 break;
1393 return rcExit;
1394
1395 case 'E':
1396 rcExit = kBuiltinOptEnvSet(&papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1397 if (rcExit == 0)
1398 break;
1399 return rcExit;
1400
1401 case 'A':
1402 rcExit = kBuiltinOptEnvAppend(&papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1403 if (rcExit == 0)
1404 break;
1405 return rcExit;
1406
1407 case 'D':
1408 rcExit = kBuiltinOptEnvPrepend(&papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1409 if (rcExit == 0)
1410 break;
1411 return rcExit;
1412
1413 case 'U':
1414 rcExit = kBuiltinOptEnvUnset(&papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1415 if (rcExit == 0)
1416 break;
1417 return rcExit;
1418
1419 case 'C':
1420 rcExit = kBuiltinOptChDir(szCwd, cbCwdBuf, pszValue);
1421 if (rcExit == 0)
1422 break;
1423 return rcExit;
1424
1425 case 'P':
1426 if (cPostCmdArgs > 0)
1427 return errx(1, "The -P option can only be used once!");
1428 if (*pszArg != '\0')
1429 return errx(1, "The cmd part of the -P needs to be a separate argument!");
1430 iPostCmd = ++iArg;
1431 if (iArg >= argc)
1432 return errx(1, "The -P option requires a command following it!");
1433 while (iArg < argc && strcmp(argv[iArg], "--") != 0)
1434 iArg++;
1435 cPostCmdArgs = iArg - iPostCmd;
1436 iArg--;
1437 break;
1438
1439 case '3':
1440 cBitsWorker = 32;
1441 break;
1442
1443 case '6':
1444 cBitsWorker = 64;
1445 break;
1446
1447 case 'e':
1448 pszExecutable = pszValue;
1449 break;
1450
1451 case 'v':
1452 cVerbosity++;
1453 break;
1454
1455 case 'h':
1456 usage(stdout, argv[0]);
1457 kBuiltinOptEnvCleanup(&papszEnvVars, cEnvVars, &cAllocatedEnvVars);
1458 return 0;
1459
1460 case 'V':
1461 kBuiltinOptEnvCleanup(&papszEnvVars, cEnvVars, &cAllocatedEnvVars);
1462 return kbuild_version(argv[0]);
1463 }
1464 } while ((chOpt = *pszArg++) != '\0');
1465 }
1466 else
1467 {
1468 errx(1, "Unknown argument: '%s'", pszArg);
1469 return usage(stderr, argv[0]);
1470 }
1471 }
1472
1473 /*
1474 * Check that we've got something to execute.
1475 */
1476 if (iArg < argc)
1477 {
1478 uint32_t cbMsg;
1479 void *pvMsg = kSubmitComposeJobMessage(pszExecutable, &argv[iArg], papszEnvVars, szCwd,
1480 fWatcomBrainDamage, fNoPchCaching,
1481 &argv[iPostCmd], cPostCmdArgs, &cbMsg);
1482 PWORKERINSTANCE pWorker = kSubmitSelectWorkSpawnNewIfNecessary(cBitsWorker, cVerbosity);
1483 if (pWorker)
1484 {
1485 if (!pszExecutable)
1486 pszExecutable = argv[iArg];
1487
1488 rcExit = kSubmitSendJobMessage(pWorker, pvMsg, cbMsg, 0 /*fNoRespawning*/, cVerbosity);
1489 if (rcExit == 0)
1490 rcExit = kSubmitMarkActive(pWorker, cVerbosity, pChild, pPidSpawned);
1491
1492 if (!g_fAtExitRegistered)
1493 if (atexit(kSubmitAtExitCallback) == 0)
1494 g_fAtExitRegistered = 1;
1495 }
1496 else
1497 rcExit = 1;
1498 free(pvMsg);
1499 }
1500 else
1501 {
1502 errx(1, "Nothing to executed!");
1503 rcExit = usage(stderr, argv[0]);
1504 }
1505
1506 kBuiltinOptEnvCleanup(&papszEnvVars, cEnvVars, &cAllocatedEnvVars);
1507 return rcExit;
1508}
1509
1510
1511
Note: See TracBrowser for help on using the repository browser.