VirtualBox

source: kBuild/trunk/src/kObjCache/kObjCache.c@ 2617

Last change on this file since 2617 was 2617, checked in by bird, 12 years ago

use _O_TEXT when possible.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 156.3 KB
Line 
1/* $Id: kObjCache.c 2617 2012-08-02 02:03:50Z bird $ */
2/** @file
3 * kObjCache - Object Cache.
4 */
5
6/*
7 * Copyright (c) 2007-2012 knut st. osmundsen <[email protected]>
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#if 0
30# define ELECTRIC_HEAP
31# include "../kmk/electric.h"
32#endif
33#include <string.h>
34#include <stdlib.h>
35#include <stdarg.h>
36#include <stdio.h>
37#include <errno.h>
38#include <assert.h>
39#include <sys/stat.h>
40#include <fcntl.h>
41#include <limits.h>
42#include <ctype.h>
43#ifndef PATH_MAX
44# define PATH_MAX _MAX_PATH /* windows */
45#endif
46#if defined(__OS2__) || defined(__WIN__)
47# include <process.h>
48# include <io.h>
49# ifdef __OS2__
50# include <unistd.h>
51# include <sys/wait.h>
52# include <sys/time.h>
53# endif
54# if defined(_MSC_VER)
55# include <direct.h>
56 typedef intptr_t pid_t;
57# endif
58# ifndef _P_WAIT
59# define _P_WAIT P_WAIT
60# endif
61# ifndef _P_NOWAIT
62# define _P_NOWAIT P_NOWAIT
63# endif
64#else
65# include <unistd.h>
66# include <sys/wait.h>
67# include <sys/time.h>
68# ifndef O_BINARY
69# define O_BINARY 0
70# endif
71#endif
72#if defined(__WIN__)
73# include <Windows.h>
74# include "quoted_spawn.h"
75#endif
76#if defined(__HAIKU__)
77# include <posix/sys/file.h>
78#endif
79
80#include "crc32.h"
81#include "md5.h"
82#include "kDep.h"
83
84
85/*******************************************************************************
86* Defined Constants And Macros *
87*******************************************************************************/
88/** The max line length in a cache file. */
89#define KOBJCACHE_MAX_LINE_LEN 16384
90#if defined(__WIN__)
91# define PATH_SLASH '\\'
92#else
93# define PATH_SLASH '/'
94#endif
95#if defined(__OS2__) || defined(__WIN__)
96# define IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
97# define IS_SLASH_DRV(ch) ((ch) == '/' || (ch) == '\\' || (ch) == ':')
98#else
99# define IS_SLASH(ch) ((ch) == '/')
100# define IS_SLASH_DRV(ch) ((ch) == '/')
101#endif
102
103#ifndef STDIN_FILENO
104# define STDIN_FILENO 0
105#endif
106#ifndef STDOUT_FILENO
107# define STDOUT_FILENO 1
108#endif
109#ifndef STDERR_FILENO
110# define STDERR_FILENO 2
111#endif
112
113#define MY_IS_BLANK(a_ch) ((a_ch) == ' ' || (a_ch) == '\t')
114
115#define KOC_BUF_MIN KOC_BUF_ALIGNMENT
116#define KOC_BUF_INCR KOC_BUF_ALIGNMENT
117#define KOC_BUF_ALIGNMENT (4U*1024U*1024U)
118
119
120/*******************************************************************************
121* Global Variables *
122*******************************************************************************/
123/** Whether verbose output is enabled. */
124static unsigned g_cVerbosityLevel = 0;
125/** What to prefix the errors with. */
126static char g_szErrorPrefix[128];
127
128/** Read buffer shared by the cache components. */
129static char g_szLine[KOBJCACHE_MAX_LINE_LEN + 16];
130
131/** How many times we've moved memory around. */
132static size_t g_cMemMoves = 0;
133/** How much memory we've moved. */
134static size_t g_cbMemMoved = 0;
135
136
137/*******************************************************************************
138* Internal Functions *
139*******************************************************************************/
140static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir);
141static char *CalcRelativeName(const char *pszPath, const char *pszDir);
142static FILE *FOpenFileInDir(const char *pszName, const char *pszDir, const char *pszMode);
143static int UnlinkFileInDir(const char *pszName, const char *pszDir);
144static int RenameFileInDir(const char *pszOldName, const char *pszNewName, const char *pszDir);
145static int DoesFileInDirExist(const char *pszName, const char *pszDir);
146static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile);
147
148
149void FatalMsg(const char *pszFormat, ...)
150{
151 va_list va;
152
153 if (g_szErrorPrefix[0])
154 fprintf(stderr, "%s - fatal error: ", g_szErrorPrefix);
155 else
156 fprintf(stderr, "fatal error: ");
157
158 va_start(va, pszFormat);
159 vfprintf(stderr, pszFormat, va);
160 va_end(va);
161}
162
163
164void FatalDie(const char *pszFormat, ...)
165{
166 va_list va;
167
168 if (g_szErrorPrefix[0])
169 fprintf(stderr, "%s - fatal error: ", g_szErrorPrefix);
170 else
171 fprintf(stderr, "fatal error: ");
172
173 va_start(va, pszFormat);
174 vfprintf(stderr, pszFormat, va);
175 va_end(va);
176
177 exit(1);
178}
179
180
181#if 0 /* unused */
182static void ErrorMsg(const char *pszFormat, ...)
183{
184 va_list va;
185
186 if (g_szErrorPrefix[0])
187 fprintf(stderr, "%s - error: ", g_szErrorPrefix);
188 else
189 fprintf(stderr, "error: ");
190
191 va_start(va, pszFormat);
192 vfprintf(stderr, pszFormat, va);
193 va_end(va);
194}
195#endif /* unused */
196
197
198static void InfoMsg(unsigned uLevel, const char *pszFormat, ...)
199{
200 if (uLevel <= g_cVerbosityLevel)
201 {
202 va_list va;
203
204 if (g_szErrorPrefix[0])
205 fprintf(stderr, "%s - info: ", g_szErrorPrefix);
206 else
207 fprintf(stderr, "info: ");
208
209 va_start(va, pszFormat);
210 vfprintf(stderr, pszFormat, va);
211 va_end(va);
212 }
213}
214
215
216static void SetErrorPrefix(const char *pszPrefix, ...)
217{
218 int cch;
219 va_list va;
220
221 va_start(va, pszPrefix);
222#if defined(_MSC_VER) || defined(__sun__)
223 cch = vsprintf(g_szErrorPrefix, pszPrefix, va);
224 if (cch >= sizeof(g_szErrorPrefix))
225 FatalDie("Buffer overflow setting error prefix!\n");
226#else
227 vsnprintf(g_szErrorPrefix, sizeof(g_szErrorPrefix), pszPrefix, va);
228#endif
229 va_end(va);
230 (void)cch;
231}
232
233#ifndef ELECTRIC_HEAP
234void *xmalloc(size_t cb)
235{
236 void *pv = malloc(cb);
237 if (!pv)
238 FatalDie("out of memory (%d)\n", (int)cb);
239 return pv;
240}
241
242
243void *xrealloc(void *pvOld, size_t cb)
244{
245 void *pv = realloc(pvOld, cb);
246 if (!pv)
247 FatalDie("out of memory (%d)\n", (int)cb);
248 return pv;
249}
250
251
252char *xstrdup(const char *pszIn)
253{
254 char *psz;
255 if (pszIn)
256 {
257 psz = strdup(pszIn);
258 if (!psz)
259 FatalDie("out of memory (%d)\n", (int)strlen(pszIn));
260 }
261 else
262 psz = NULL;
263 return psz;
264}
265#endif
266
267
268void *xmallocz(size_t cb)
269{
270 void *pv = xmalloc(cb);
271 memset(pv, 0, cb);
272 return pv;
273}
274
275
276
277
278
279/**
280 * Returns a millisecond timestamp.
281 *
282 * @returns Millisecond timestamp.
283 */
284static uint32_t NowMs(void)
285{
286#if defined(__WIN__)
287 return GetTickCount();
288#else
289 int iSavedErrno = errno;
290 struct timeval tv = {0, 0};
291
292 gettimeofday(&tv, NULL);
293 errno = iSavedErrno;
294
295 return tv.tv_sec * 1000 + tv.tv_usec / 1000;
296#endif
297}
298
299
300/**
301 * Gets the absolute path
302 *
303 * @returns A new heap buffer containing the absolute path.
304 * @param pszPath The path to make absolute. (Readonly)
305 */
306static char *AbsPath(const char *pszPath)
307{
308/** @todo this isn't really working as it should... */
309 char szTmp[PATH_MAX];
310#if defined(__OS2__)
311 if ( _fullpath(szTmp, *pszPath ? pszPath : ".", sizeof(szTmp))
312 && !realpath(pszPath, szTmp))
313 return xstrdup(pszPath);
314#elif defined(__WIN__)
315 if (!_fullpath(szTmp, *pszPath ? pszPath : ".", sizeof(szTmp)))
316 return xstrdup(pszPath);
317#else
318 if (!realpath(pszPath, szTmp))
319 return xstrdup(pszPath);
320#endif
321 return xstrdup(szTmp);
322}
323
324
325/**
326 * Utility function that finds the filename part in a path.
327 *
328 * @returns Pointer to the file name part (this may be "").
329 * @param pszPath The path to parse.
330 */
331static const char *FindFilenameInPath(const char *pszPath)
332{
333 const char *pszFilename = strchr(pszPath, '\0') - 1;
334 if (pszFilename < pszPath)
335 return pszPath;
336 while ( pszFilename > pszPath
337 && !IS_SLASH_DRV(pszFilename[-1]))
338 pszFilename--;
339 return pszFilename;
340}
341
342
343/**
344 * Utility function that combines a filename and a directory into a path.
345 *
346 * @returns malloced buffer containing the result.
347 * @param pszName The file name.
348 * @param pszDir The directory path.
349 */
350static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir)
351{
352 size_t cchName = strlen(pszName);
353 size_t cchDir = strlen(pszDir);
354 char *pszBuf = xmalloc(cchName + cchDir + 2);
355 memcpy(pszBuf, pszDir, cchDir);
356 if (cchDir > 0 && !IS_SLASH_DRV(pszDir[cchDir - 1]))
357 pszBuf[cchDir++] = PATH_SLASH;
358 memcpy(pszBuf + cchDir, pszName, cchName + 1);
359 return pszBuf;
360}
361
362
363/**
364 * Compares two path strings to see if they are identical.
365 *
366 * This doesn't do anything fancy, just the case ignoring and
367 * slash unification.
368 *
369 * @returns 1 if equal, 0 otherwise.
370 * @param pszPath1 The first path.
371 * @param pszPath2 The second path.
372 */
373static int ArePathsIdentical(const char *pszPath1, const char *pszPath2)
374{
375#if defined(__OS2__) || defined(__WIN__)
376 if (stricmp(pszPath1, pszPath2))
377 {
378 /* Slashes may differ, compare char by char. */
379 const char *psz1 = pszPath1;
380 const char *psz2 = pszPath2;
381 for (;;)
382 {
383 if (*psz1 != *psz2)
384 {
385 if ( tolower(*psz1) != tolower(*psz2)
386 && toupper(*psz1) != toupper(*psz2)
387 && *psz1 != '/'
388 && *psz1 != '\\'
389 && *psz2 != '/'
390 && *psz2 != '\\')
391 return 0;
392 }
393 if (!*psz1)
394 break;
395 psz1++;
396 psz2++;
397 }
398 }
399 return 1;
400#else
401 return !strcmp(pszPath1, pszPath2);
402#endif
403}
404
405/**
406 * Compares two path strings to see if they are identical.
407 *
408 * This doesn't do anything fancy, just the case ignoring and
409 * slash unification.
410 *
411 * @returns 1 if equal, 0 otherwise.
412 * @param pszPath1 The first path.
413 * @param pszPath2 The second path.
414 * @param cch The number of characters to compare.
415 */
416static int ArePathsIdenticalN(const char *pszPath1, const char *pszPath2, size_t cch)
417{
418#if defined(__OS2__) || defined(__WIN__)
419 if (strnicmp(pszPath1, pszPath2, cch))
420 {
421 /* Slashes may differ, compare char by char. */
422 const char *psz1 = pszPath1;
423 const char *psz2 = pszPath2;
424 for ( ; cch; psz1++, psz2++, cch--)
425 {
426 if (*psz1 != *psz2)
427 {
428 if ( tolower(*psz1) != tolower(*psz2)
429 && toupper(*psz1) != toupper(*psz2)
430 && *psz1 != '/'
431 && *psz1 != '\\'
432 && *psz2 != '/'
433 && *psz2 != '\\')
434 return 0;
435 }
436 }
437 }
438 return 1;
439#else
440 return !strncmp(pszPath1, pszPath2, cch);
441#endif
442}
443
444
445/**
446 * Calculate how to get to pszPath from pszDir.
447 *
448 * @returns The relative path from pszDir to path pszPath.
449 * @param pszPath The path to the object.
450 * @param pszDir The directory it shall be relative to.
451 */
452static char *CalcRelativeName(const char *pszPath, const char *pszDir)
453{
454 char *pszRet = NULL;
455 char *pszAbsPath = NULL;
456 size_t cchDir = strlen(pszDir);
457
458 /*
459 * This is indeed a bit tricky, so we'll try the easy way first...
460 */
461 if (ArePathsIdenticalN(pszPath, pszDir, cchDir))
462 {
463 if (pszPath[cchDir])
464 pszRet = (char *)pszPath + cchDir;
465 else
466 pszRet = "./";
467 }
468 else
469 {
470 pszAbsPath = AbsPath(pszPath);
471 if (ArePathsIdenticalN(pszAbsPath, pszDir, cchDir))
472 {
473 if (pszPath[cchDir])
474 pszRet = pszAbsPath + cchDir;
475 else
476 pszRet = "./";
477 }
478 }
479 if (pszRet)
480 {
481 while (IS_SLASH_DRV(*pszRet))
482 pszRet++;
483 pszRet = xstrdup(pszRet);
484 free(pszAbsPath);
485 return pszRet;
486 }
487
488 /*
489 * Damn, it's gonna be complicated. Deal with that later.
490 */
491 FatalDie("complicated relative path stuff isn't implemented yet. sorry.\n");
492 return NULL;
493}
494
495
496/**
497 * Utility function that combines a filename and directory and passes it onto fopen.
498 *
499 * @returns fopen return value.
500 * @param pszName The file name.
501 * @param pszDir The directory path.
502 * @param pszMode The fopen mode string.
503 */
504static FILE *FOpenFileInDir(const char *pszName, const char *pszDir, const char *pszMode)
505{
506 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
507 FILE *pFile = fopen(pszPath, pszMode);
508 free(pszPath);
509 return pFile;
510}
511
512
513/**
514 * Utility function that combines a filename and directory and passes it onto open.
515 *
516 * @returns open return value.
517 * @param pszName The file name.
518 * @param pszDir The directory path.
519 * @param fFlags The open flags.
520 * @param fCreateMode The file creation mode.
521 */
522static int OpenFileInDir(const char *pszName, const char *pszDir, int fFlags, int fCreateMode)
523{
524 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
525 int fd = open(pszPath, fFlags, fCreateMode);
526 free(pszPath);
527 return fd;
528}
529
530
531
532/**
533 * Deletes a file in a directory.
534 *
535 * @returns whatever unlink returns.
536 * @param pszName The file name.
537 * @param pszDir The directory path.
538 */
539static int UnlinkFileInDir(const char *pszName, const char *pszDir)
540{
541 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
542 int rc = unlink(pszPath);
543 free(pszPath);
544 return rc;
545}
546
547
548/**
549 * Renames a file in a directory.
550 *
551 * @returns whatever rename returns.
552 * @param pszOldName The new file name.
553 * @param pszNewName The old file name.
554 * @param pszDir The directory path.
555 */
556static int RenameFileInDir(const char *pszOldName, const char *pszNewName, const char *pszDir)
557{
558 char *pszOldPath = MakePathFromDirAndFile(pszOldName, pszDir);
559 char *pszNewPath = MakePathFromDirAndFile(pszNewName, pszDir);
560 int rc = rename(pszOldPath, pszNewPath);
561 free(pszOldPath);
562 free(pszNewPath);
563 return rc;
564}
565
566
567/**
568 * Check if a (regular) file exists in a directory.
569 *
570 * @returns 1 if it exists and is a regular file, 0 if not.
571 * @param pszName The file name.
572 * @param pszDir The directory path.
573 */
574static int DoesFileInDirExist(const char *pszName, const char *pszDir)
575{
576 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
577 struct stat st;
578 int rc = stat(pszPath, &st);
579 free(pszPath);
580#ifdef S_ISREG
581 return !rc && S_ISREG(st.st_mode);
582#elif defined(_MSC_VER)
583 return !rc && (st.st_mode & _S_IFMT) == _S_IFREG;
584#else
585#error "Port me"
586#endif
587}
588
589
590/**
591 * Reads into memory an entire file.
592 *
593 * @returns Pointer to the heap allocation containing the file.
594 * On failure NULL and errno is returned.
595 * @param pszName The file.
596 * @param pszDir The directory the file resides in.
597 * @param pcbFile Where to store the file size.
598 */
599static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile)
600{
601 int SavedErrno;
602 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
603 int fd = open(pszPath, O_RDONLY | O_BINARY);
604 if (fd >= 0)
605 {
606 off_t cbFile = lseek(fd, 0, SEEK_END);
607 if ( cbFile >= 0
608 && lseek(fd, 0, SEEK_SET) == 0)
609 {
610 char *pb = malloc(cbFile + 1);
611 if (pb)
612 {
613 if (read(fd, pb, cbFile) == cbFile)
614 {
615 close(fd);
616 pb[cbFile] = '\0';
617 *pcbFile = (size_t)cbFile;
618 return pb;
619 }
620 SavedErrno = errno;
621 free(pb);
622 }
623 else
624 SavedErrno = ENOMEM;
625 }
626 else
627 SavedErrno = errno;
628 close(fd);
629 }
630 else
631 SavedErrno = errno;
632 free(pszPath);
633 errno = SavedErrno;
634 return NULL;
635}
636
637
638/**
639 * Creates a directory including all necessary parent directories.
640 *
641 * @returns 0 on success, -1 + errno on failure.
642 * @param pszDir The directory.
643 */
644static int MakePath(const char *pszPath)
645{
646 int iErr = 0;
647 char *pszAbsPath = AbsPath(pszPath);
648 char *psz = pszAbsPath;
649
650 /* Skip to the root slash (PC). */
651 while (!IS_SLASH(*psz) && *psz)
652 psz++;
653/** @todo UNC */
654 for (;;)
655 {
656 char chSaved;
657
658 /* skip slashes */
659 while (IS_SLASH(*psz))
660 psz++;
661 if (!*psz)
662 break;
663
664 /* find the next slash or end and terminate the string. */
665 while (!IS_SLASH(*psz) && *psz)
666 psz++;
667 chSaved = *psz;
668 *psz = '\0';
669
670 /* try create the directory, ignore failure because the directory already exists. */
671 errno = 0;
672#ifdef _MSC_VER
673 if ( _mkdir(pszAbsPath)
674 && errno != EEXIST)
675#else
676 if ( mkdir(pszAbsPath, 0777)
677 && errno != EEXIST
678 && errno != ENOSYS /* Solaris nonsensical mkdir crap. */
679 && errno != EACCES /* Solaris nonsensical mkdir crap. */
680 )
681#endif
682 {
683 iErr = errno;
684 break;
685 }
686
687 /* restore the slash/terminator */
688 *psz = chSaved;
689 }
690
691 free(pszAbsPath);
692 return iErr ? -1 : 0;
693}
694
695
696/**
697 * Adds the arguments found in the pszCmdLine string to argument vector.
698 *
699 * The parsing of the pszCmdLine string isn't very sophisticated, no
700 * escaping or quotes.
701 *
702 * @param pcArgs Pointer to the argument counter.
703 * @param ppapszArgs Pointer to the argument vector pointer.
704 * @param pszCmdLine The command line to parse and append.
705 * @param pszWedgeArg Argument to put infront of anything found in pszCmdLine.
706 */
707static void AppendArgs(int *pcArgs, char ***ppapszArgs, const char *pszCmdLine, const char *pszWedgeArg)
708{
709 int i;
710 int cExtraArgs;
711 const char *psz;
712 char **papszArgs;
713
714 /*
715 * Count the new arguments.
716 */
717 cExtraArgs = 0;
718 psz = pszCmdLine;
719 while (*psz)
720 {
721 while (isspace(*psz))
722 psz++;
723 if (!psz)
724 break;
725 cExtraArgs++;
726 while (!isspace(*psz) && *psz)
727 psz++;
728 }
729 if (!cExtraArgs)
730 return;
731
732 /*
733 * Allocate a new vector that can hold the arguments.
734 * (Reallocating might not work since the argv might not be allocated
735 * from the heap but off the stack or somewhere... )
736 */
737 i = *pcArgs;
738 *pcArgs = i + cExtraArgs + !!pszWedgeArg;
739 papszArgs = xmalloc((*pcArgs + 1) * sizeof(char *));
740 *ppapszArgs = memcpy(papszArgs, *ppapszArgs, i * sizeof(char *));
741
742 if (pszWedgeArg)
743 papszArgs[i++] = xstrdup(pszWedgeArg);
744
745 psz = pszCmdLine;
746 while (*psz)
747 {
748 size_t cch;
749 const char *pszEnd;
750 while (isspace(*psz))
751 psz++;
752 if (!psz)
753 break;
754 pszEnd = psz;
755 while (!isspace(*pszEnd) && *pszEnd)
756 pszEnd++;
757
758 cch = pszEnd - psz;
759 papszArgs[i] = xmalloc(cch + 1);
760 memcpy(papszArgs[i], psz, cch);
761 papszArgs[i][cch] = '\0';
762
763 i++;
764 psz = pszEnd;
765 }
766
767 papszArgs[i] = NULL;
768}
769
770
771/**
772 * Dependency collector state.
773 */
774typedef struct KOCDEP
775{
776 /** The statemachine for processing the preprocessed code stream. */
777 enum KOCDEPSTATE
778 {
779 kOCDepState_Invalid = 0,
780 kOCDepState_NeedNewLine,
781 kOCDepState_NeedHash,
782 kOCDepState_NeedLine_l,
783 kOCDepState_NeedLine_l_HaveSpace,
784 kOCDepState_NeedLine_i,
785 kOCDepState_NeedLine_n,
786 kOCDepState_NeedLine_e,
787 kOCDepState_NeedSpaceBeforeDigit,
788 kOCDepState_NeedFirstDigit,
789 kOCDepState_NeedMoreDigits,
790 kOCDepState_NeedQuote,
791 kOCDepState_NeedEndQuote
792 } enmState;
793 /** Current offset into the filename buffer. */
794 uint32_t offFilename;
795 /** The amount of space currently allocated for the filename buffer. */
796 uint32_t cbFilenameAlloced;
797 /** Pointer to the filename buffer. */
798 char *pszFilename;
799 /** The current dependency file. */
800 PDEP pCurDep;
801} KOCDEP;
802/** Pointer to a KOCDEP. */
803typedef KOCDEP *PKOCDEP;
804
805
806/**
807 * Initializes the dependency collector state.
808 *
809 * @param pDepState The dependency collector state.
810 */
811static void kOCDepInit(PKOCDEP pDepState)
812{
813 pDepState->enmState = kOCDepState_NeedHash;
814 pDepState->offFilename = 0;
815 pDepState->cbFilenameAlloced = 0;
816 pDepState->pszFilename = NULL;
817 pDepState->pCurDep = NULL;
818}
819
820
821/**
822 * Deletes the dependency collector state, releasing all resources.
823 *
824 * @param pDepState The dependency collector state.
825 */
826static void kOCDepDelete(PKOCDEP pDepState)
827{
828 pDepState->enmState = kOCDepState_Invalid;
829 free(pDepState->pszFilename);
830 pDepState->pszFilename = NULL;
831 depCleanup();
832}
833
834
835/**
836 * Unescapes a string in place.
837 *
838 * @returns The new string length.
839 * @param psz The string to unescape (input and output).
840 */
841static size_t kOCDepUnescape(char *psz)
842{
843 char *pszSrc = psz;
844 char *pszDst = psz;
845 char ch;
846
847 while ((ch = *pszSrc++) != '\0')
848 {
849 if (ch == '\\')
850 {
851 char ch2 = *pszSrc;
852 if (ch2)
853 {
854 pszSrc++;
855 ch = ch2;
856 }
857 /* else: cannot happen / just ignore */
858 }
859 *pszDst++ = ch;
860 }
861
862 *pszDst = '\0';
863 return pszDst - psz;
864}
865
866
867/**
868 * Checks if the character at @a offChar is escaped or not.
869 *
870 * @returns 1 if escaped, 0 if not.
871 * @param pach The string (not terminated).
872 * @param offChar The offset of the character in question.
873 */
874static int kOCDepIsEscaped(char *pach, size_t offChar)
875{
876 while (offChar > 0 && pach[offChar - 1] == '\\')
877 {
878 if ( offChar == 1
879 || pach[offChar - 2] != '\\')
880 return 1;
881 offChar -= 2;
882 }
883 return 0;
884}
885
886
887/**
888 * This consumes the preprocessor output and generate dependencies from it.
889 *
890 * The trick is to look at the line directives and which files get listed there.
891 *
892 * @returns The new state. This is a convenience for saving code space and it
893 * isn't really meant to be of any use to the caller.
894 * @param pDepState The dependency collector state.
895 * @param pszInput The input.
896 * @param cchInput The input length.
897 */
898static enum KOCDEPSTATE
899kOCDepConsumer(PKOCDEP pDepState, const char *pszInput, size_t cchInput)
900{
901 enum KOCDEPSTATE enmState = pDepState->enmState;
902 const char *psz;
903
904 while (cchInput > 0)
905 {
906 switch (enmState)
907 {
908 case kOCDepState_NeedNewLine:
909 psz = (const char *)memchr(pszInput, '\n', cchInput);
910 if (!psz)
911 return enmState;
912 psz++;
913 cchInput -= psz - pszInput;
914 pszInput = psz;
915
916 case kOCDepState_NeedHash:
917 while (cchInput > 0 && MY_IS_BLANK(*pszInput))
918 cchInput--, pszInput++;
919 if (!cchInput)
920 return pDepState->enmState = kOCDepState_NeedHash;
921
922 if (*pszInput != '#')
923 break;
924 pszInput++;
925 cchInput--;
926 enmState = kOCDepState_NeedLine_l;
927
928 case kOCDepState_NeedLine_l:
929 case kOCDepState_NeedLine_l_HaveSpace:
930 while (cchInput > 0 && MY_IS_BLANK(*pszInput))
931 {
932 enmState = kOCDepState_NeedLine_l_HaveSpace;
933 cchInput--, pszInput++;
934 }
935 if (!cchInput)
936 return pDepState->enmState = enmState;
937
938 if (*pszInput != 'l')
939 {
940 /* # <digit> "<file>" */
941 if (enmState != kOCDepState_NeedLine_l_HaveSpace || !isdigit(*pszInput))
942 break;
943 pszInput++;
944 cchInput--;
945 enmState = kOCDepState_NeedMoreDigits;
946 continue;
947 }
948 pszInput++;
949 if (!--cchInput)
950 return pDepState->enmState = kOCDepState_NeedLine_i;
951
952 case kOCDepState_NeedLine_i:
953 if (*pszInput != 'i')
954 break;
955 pszInput++;
956 if (!--cchInput)
957 return pDepState->enmState = kOCDepState_NeedLine_n;
958
959 case kOCDepState_NeedLine_n:
960 if (*pszInput != 'n')
961 break;
962 pszInput++;
963 if (!--cchInput)
964 return pDepState->enmState = kOCDepState_NeedLine_e;
965
966 case kOCDepState_NeedLine_e:
967 if (*pszInput != 'e')
968 break;
969 pszInput++;
970 if (!--cchInput)
971 return pDepState->enmState = kOCDepState_NeedSpaceBeforeDigit;
972
973 case kOCDepState_NeedSpaceBeforeDigit:
974 if (!MY_IS_BLANK(*pszInput))
975 break;
976 pszInput++;
977 cchInput--;
978
979 case kOCDepState_NeedFirstDigit:
980 while (cchInput > 0 && MY_IS_BLANK(*pszInput))
981 cchInput--, pszInput++;
982 if (!cchInput)
983 return pDepState->enmState = kOCDepState_NeedFirstDigit;
984
985 if (!isdigit(*pszInput))
986 break;
987 pszInput++;
988 cchInput--;
989
990 case kOCDepState_NeedMoreDigits:
991 while (cchInput > 0 && isdigit(*pszInput))
992 cchInput--, pszInput++;
993 if (!cchInput)
994 return pDepState->enmState = kOCDepState_NeedMoreDigits;
995
996 case kOCDepState_NeedQuote:
997 while (cchInput > 0 && MY_IS_BLANK(*pszInput))
998 cchInput--, pszInput++;
999 if (!cchInput)
1000 return pDepState->enmState = kOCDepState_NeedQuote;
1001
1002 if (*pszInput != '"')
1003 break;
1004 pszInput++;
1005 cchInput--;
1006
1007 case kOCDepState_NeedEndQuote:
1008 {
1009 uint32_t off = pDepState->offFilename;
1010 for (;;)
1011 {
1012 char ch;
1013
1014 if (!cchInput)
1015 {
1016 pDepState->offFilename = off;
1017 return pDepState->enmState = kOCDepState_NeedEndQuote;
1018 }
1019
1020 if (off + 1 >= pDepState->cbFilenameAlloced)
1021 {
1022 if (!pDepState->cbFilenameAlloced)
1023 pDepState->cbFilenameAlloced = 32;
1024 else
1025 pDepState->cbFilenameAlloced *= 2;
1026 pDepState->pszFilename = (char *)xrealloc(pDepState->pszFilename, pDepState->cbFilenameAlloced);
1027 }
1028 pDepState->pszFilename[off] = ch = *pszInput++;
1029 cchInput--;
1030
1031 if ( ch == '"'
1032 && ( off == 0
1033 || pDepState->pszFilename[off - 1] != '\\'
1034 || !kOCDepIsEscaped(pDepState->pszFilename, off)))
1035 {
1036 /* Done, unescape and add the file. */
1037 size_t cchFilename;
1038
1039 pDepState->pszFilename[off] = '\0';
1040 cchFilename = kOCDepUnescape(pDepState->pszFilename);
1041
1042 if ( !pDepState->pCurDep
1043 || cchFilename != pDepState->pCurDep->cchFilename
1044 || strcmp(pDepState->pszFilename, pDepState->pCurDep->szFilename))
1045 pDepState->pCurDep = depAdd(pDepState->pszFilename, cchFilename);
1046 pDepState->offFilename = 0;
1047 break;
1048 }
1049
1050 off++;
1051 }
1052 }
1053 }
1054
1055 /* next newline */
1056 enmState = kOCDepState_NeedNewLine;
1057 }
1058
1059 return pDepState->enmState = enmState;
1060}
1061
1062
1063/**
1064 * Writes the dependencies to the specified file.
1065 *
1066 * @param pDepState The dependency collector state.
1067 * @param pszFilename The name of the dependency file.
1068 * @param pszObjFile The object file name, relative to @a pszObjDir.
1069 * @param pszObjDir The object file directory.
1070 * @param fFixCase Whether to fix the case of dependency files.
1071 * @param fQuiet Whether to be quiet about the dependencies.
1072 * @param fGenStubs Whether to generate stubs.
1073 */
1074static void kOCDepWriteToFile(PKOCDEP pDepState, const char *pszFilename, const char *pszObjFile, const char *pszObjDir,
1075 int fFixCase, int fQuiet, int fGenStubs)
1076{
1077 char *pszObjFileAbs;
1078 char *psz;
1079 FILE *pFile = fopen(pszFilename, "w");
1080 if (!pFile)
1081 FatalMsg("Failed to open dependency file '%s': %s\n", pszFilename, strerror(errno));
1082
1083 depOptimize(fFixCase, fQuiet);
1084
1085 /* Make object file name with unix slashes. */
1086 pszObjFileAbs = MakePathFromDirAndFile(pszObjFile, pszObjDir);
1087 psz = pszObjFileAbs;
1088 while ((psz = strchr(psz, '\\')) != NULL)
1089 *psz++ = '/';
1090
1091 fprintf(pFile, "%s:", pszObjFileAbs);
1092 free(pszObjFileAbs);
1093 depPrint(pFile);
1094 if (fGenStubs)
1095 depPrintStubs(pFile);
1096
1097 if (fclose(pFile) != 0)
1098 FatalMsg("Failed to write dependency file '%s': %s\n", pszFilename, strerror(errno));
1099}
1100
1101
1102/**
1103 * Preprocessor output reader state.
1104 */
1105typedef struct KOCCPPRD
1106{
1107 /** Pointer to the preprocessor output. */
1108 char *pszBuf;
1109 /** Allocated buffer size. */
1110 size_t cbBufAlloc;
1111 /** Amount preprocessor output that we've completed optimizations for. */
1112 size_t cchOptimized;
1113 /** The offset of the next bits to process. */
1114 size_t offCur;
1115 /** The offset where to put more raw preprocessor output. */
1116 size_t offRead;
1117 /** The line number corresponding to offOptimized. */
1118 uint32_t uOptLineNo;
1119 /** The current line number. */
1120 uint32_t uCurLineNo;
1121 /** The number of lines found to be empty starting at cchOptimized. */
1122 uint32_t cEmptyLines;
1123 /** Set if we're done, clear if we're expecting more preprocessor output. */
1124 int fDone;
1125 /** The saved character at cchOptimized. */
1126 char chSaved;
1127 /** Whether the optimizations are enabled. */
1128 int fOptimize;
1129
1130 /** Buffer holding the current file name (unescaped). */
1131 char *pszFileNmBuf;
1132 /** The size of the file name buffer. */
1133 size_t cbFileNmBuf;
1134 /** The length of the current file string. */
1135 size_t cchCurFileNm;
1136
1137 /** Line directive / new line sequence buffer. */
1138 char *pszLineBuf;
1139 /** The size of the buffer pointed to by pszLineBuf. */
1140 size_t cbLineBuf;
1141} KOCCPPRD;
1142/** Pointer to a preprocessor reader state. */
1143typedef KOCCPPRD *PKOCCPPRD;
1144
1145
1146/**
1147 * Allocate the initial C preprocessor output buffer.
1148 *
1149 * @param pCppRd The C preprocessor reader instance.
1150 * @param cbOldCpp The size of the output the last time. This is 0 if
1151 * there was not previous run.
1152 * @param fOptimize Whether optimizations are enabled.
1153 */
1154static void kOCCppRdInit(PKOCCPPRD pCppRd, size_t cbOldCpp, int fOptimize)
1155{
1156 pCppRd->cbBufAlloc = cbOldCpp ? (cbOldCpp + KOC_BUF_INCR) & ~(KOC_BUF_ALIGNMENT - 1) : KOC_BUF_MIN;
1157 pCppRd->pszBuf = xmalloc(pCppRd->cbBufAlloc);
1158 pCppRd->cchCurFileNm = 0;
1159 pCppRd->cchOptimized = 0;
1160 pCppRd->offCur = 0;
1161 pCppRd->offRead = 0;
1162 pCppRd->uOptLineNo = 1;
1163 pCppRd->uCurLineNo = 1;
1164 pCppRd->cEmptyLines = 0;
1165 pCppRd->fDone = 0;
1166 pCppRd->chSaved = 0;
1167 pCppRd->fOptimize = fOptimize;
1168
1169 pCppRd->pszFileNmBuf = NULL;
1170 pCppRd->cbFileNmBuf = 0;
1171 pCppRd->cchCurFileNm = 0;
1172
1173 pCppRd->pszLineBuf = NULL;
1174 pCppRd->cbLineBuf = 0;
1175}
1176
1177
1178static void kOCCppRdDelete(PKOCCPPRD pCppRd)
1179{
1180 free(pCppRd->pszBuf);
1181 pCppRd->pszBuf = NULL;
1182
1183 free(pCppRd->pszFileNmBuf);
1184 pCppRd->pszFileNmBuf = NULL;
1185
1186 free(pCppRd->pszLineBuf);
1187 pCppRd->pszLineBuf = NULL;
1188}
1189
1190
1191/**
1192 * Allocate more buffer space for the C preprocessor output.
1193 *
1194 * @param pCppRd The C preprocessor reader instance.
1195 */
1196static size_t kOCCppRdGrowBuffer(PKOCCPPRD pCppRd)
1197{
1198 pCppRd->cbBufAlloc += KOC_BUF_INCR;
1199 pCppRd->pszBuf = xrealloc(pCppRd->pszBuf, pCppRd->cbBufAlloc);
1200
1201 return pCppRd->cbBufAlloc - pCppRd->offRead;
1202}
1203
1204
1205static char *kOCCppRdOptRemove(PKOCCPPRD pCppRd, char *pszStart, size_t cbRemove)
1206{
1207 size_t offStart = pszStart - pCppRd->pszBuf;
1208 size_t cbToMove = pCppRd->offRead - offStart;
1209
1210 assert(pCppRd->offRead >= offStart);
1211
1212 g_cMemMoves++;
1213 g_cbMemMoved += cbToMove + 1;
1214 memmove(pszStart, pszStart + cbRemove, cbToMove + 1);
1215
1216 pCppRd->offRead -= cbRemove;
1217 assert(offStart >= pCppRd->offCur);
1218 assert(pCppRd->offRead < 1 || pCppRd->pszBuf[pCppRd->offRead - 1] != '\0');
1219 return pszStart;
1220}
1221
1222
1223static size_t kOCCppRdOptReplace(PKOCCPPRD pCppRd, size_t offStart, size_t cchReplaced,
1224 const char *pchReplacement, size_t cchReplacement)
1225{
1226 size_t offDelta = cchReplacement - cchReplaced;
1227
1228 if (offDelta)
1229 {
1230 size_t cbToMove = pCppRd->offRead - offStart - cchReplaced;
1231 assert(cbToMove <= pCppRd->offRead);
1232
1233 if (cchReplacement > cchReplaced)
1234 while (pCppRd->offRead + offDelta >= pCppRd->cbBufAlloc)
1235 kOCCppRdGrowBuffer(pCppRd);
1236
1237 g_cMemMoves++;
1238 g_cbMemMoved += cbToMove + 1;
1239 memmove(pCppRd->pszBuf + offStart + cchReplacement,
1240 pCppRd->pszBuf + offStart + cchReplaced,
1241 cbToMove + 1);
1242
1243 pCppRd->offRead += offDelta;
1244 if (pCppRd->offCur > offStart)
1245 {
1246 assert(pCppRd->offCur >= offStart + cchReplaced);
1247 pCppRd->offCur += offDelta;
1248 }
1249 assert(pCppRd->offRead < 1 || pCppRd->pszBuf[pCppRd->offRead - 1] != '\0');
1250 }
1251
1252 memcpy(pCppRd->pszBuf + offStart, pchReplacement, cchReplacement);
1253
1254 return offDelta;
1255}
1256
1257
1258static void kOCCppRdOptCommitEx(PKOCCPPRD pCppRd, size_t offCur)
1259{
1260 pCppRd->cchOptimized = offCur;
1261 pCppRd->uOptLineNo = pCppRd->uCurLineNo;
1262}
1263
1264
1265static void kOCCppRdOptCommit(PKOCCPPRD pCppRd)
1266{
1267 pCppRd->cchOptimized = pCppRd->offCur;
1268 pCppRd->uOptLineNo = pCppRd->uCurLineNo;
1269}
1270
1271
1272
1273static char *kOCCppRdOptGetEol(PKOCCPPRD pCppRd, char *pszCur, size_t cbLeft)
1274{
1275 char *pszEol = memchr(pszCur, '\n', cbLeft);
1276 if (pszEol)
1277 {
1278 if (pszCur != pszEol && pszEol[-1] == '\r')
1279 pszEol--;
1280 }
1281 else if (pCppRd->fDone && cbLeft)
1282 pszEol = pszCur + cbLeft;
1283 return pszEol;
1284}
1285
1286static void kOCCppRdOptSetFile(PKOCCPPRD pCppRd, const char *pchFile, size_t cchFile)
1287{
1288 if (cchFile >= pCppRd->cbFileNmBuf)
1289 {
1290 pCppRd->cbFileNmBuf = (cchFile + 15) & ~(size_t)15;
1291 pCppRd->pszFileNmBuf = xrealloc(pCppRd->pszFileNmBuf, pCppRd->cbFileNmBuf);
1292 }
1293 memcpy(pCppRd->pszFileNmBuf, pchFile, cchFile);
1294 pCppRd->pszFileNmBuf[cchFile] = '\0';
1295 pCppRd->cchCurFileNm = cchFile;
1296}
1297
1298
1299static size_t kOCCppRdOptFmtLine(PKOCCPPRD pCppRd, uint32_t uLine, const char *pchFile, size_t cchFile)
1300{
1301 size_t cchUsed;
1302 size_t cbNeeded;
1303
1304 /* Make sure we've got enough buffer space. */
1305 cbNeeded = sizeof("#line 4888222111 \"\"\n") + cchFile;
1306 if (cbNeeded > pCppRd->cbLineBuf)
1307 {
1308 pCppRd->cbLineBuf = (cbNeeded + 32 + 15) & ~(size_t)15;
1309 pCppRd->pszLineBuf = xrealloc(pCppRd->pszLineBuf, pCppRd->cbLineBuf);
1310 }
1311
1312 /* Do the formatting. */
1313 cchUsed = sprintf(pCppRd->pszLineBuf, "#line %lu", (unsigned long)uLine);
1314 if (cchFile)
1315 {
1316 pCppRd->pszLineBuf[cchUsed++] = ' ';
1317 pCppRd->pszLineBuf[cchUsed++] = '"';
1318 memcpy(&pCppRd->pszLineBuf[cchUsed], pchFile, cchFile);
1319 cchUsed += cchFile;
1320 pCppRd->pszLineBuf[cchUsed++] = '"';
1321 }
1322 pCppRd->pszLineBuf[cchUsed++] = '\n';
1323 pCppRd->pszLineBuf[cchUsed] = '\0';
1324
1325 return cchUsed;
1326}
1327
1328
1329static size_t kOCCppRdOptFmtNewLines(PKOCCPPRD pCppRd, uint32_t cNewLines)
1330{
1331 if (cNewLines + 1 > pCppRd->cbLineBuf)
1332 {
1333 pCppRd->cbLineBuf = (cNewLines + 1 + 32 + 15) & ~(size_t)15;
1334 pCppRd->pszLineBuf = xrealloc(pCppRd->pszLineBuf, pCppRd->cbLineBuf);
1335 }
1336
1337 memset(pCppRd->pszLineBuf, '\n', cNewLines);
1338 pCppRd->pszLineBuf[cNewLines] = '\0';
1339 return cNewLines;
1340}
1341
1342
1343
1344static char *kOCCppRdOptFlush(PKOCCPPRD pCppRd, char *pszCur, int fLineDirNext)
1345{
1346 size_t const cchOptimized = pCppRd->cchOptimized;
1347 size_t const offTmp = pszCur - pCppRd->pszBuf;
1348 assert(offTmp >= cchOptimized);
1349 if (pCppRd->offCur > cchOptimized)
1350 {
1351 /*
1352 * We've got unflushed whitelines.
1353 */
1354 size_t const cchInQuestion = pCppRd->offCur - cchOptimized;
1355 uint32_t const cLinesInQuestion = pCppRd->uCurLineNo - pCppRd->uOptLineNo;
1356 size_t cchLineDir;
1357 size_t offDelta;
1358
1359 if ( cLinesInQuestion <= 7
1360 || (cchLineDir = kOCCppRdOptFmtLine(pCppRd, pCppRd->uCurLineNo, NULL, 0)) >= cLinesInQuestion)
1361 cchLineDir = kOCCppRdOptFmtNewLines(pCppRd, cLinesInQuestion);
1362
1363 offDelta = kOCCppRdOptReplace(pCppRd, cchOptimized, cchInQuestion, pCppRd->pszLineBuf, cchLineDir);
1364 pszCur += offDelta;
1365
1366 kOCCppRdOptCommitEx(pCppRd, cchOptimized + cchLineDir);
1367 /* pCppRd->offCur is adjusted by the replace call */
1368 }
1369
1370 (void)fLineDirNext; /* Use later if required. */
1371 return pszCur;
1372}
1373
1374
1375static int kOCCppRdOptParseLine(PKOCCPPRD pCppRd, char *pszCur, char *pszEol,
1376 uint32_t *puNewLineNo, char **ppszNewFile, size_t *pcchNewFile)
1377{
1378 char *psz = pszCur;
1379 uint32_t uNewLineNo;
1380 int fIsShort;
1381
1382 /*
1383 * Check if it's a #line directive of some kind and parse it.
1384 */
1385 if (*psz != '#')
1386 return 0;
1387 psz++;
1388
1389 fIsShort = MY_IS_BLANK(*psz);
1390 while (MY_IS_BLANK(*psz))
1391 psz++;
1392
1393 if ( psz[0] == 'l'
1394 && psz[1] == 'i'
1395 && psz[2] == 'n'
1396 && psz[3] == 'e'
1397 && MY_IS_BLANK(psz[4]) )
1398 {
1399 fIsShort = 0;
1400 psz += 5;
1401 while (MY_IS_BLANK(*psz))
1402 psz++;
1403 }
1404 else if (fIsShort && isdigit(*psz))
1405 fIsShort = 1;
1406 else
1407 return 0;
1408
1409 /* Parse the line number. */
1410 if (!isdigit(*psz))
1411 return 0;
1412
1413 uNewLineNo = *psz++ - '0';
1414 while (isdigit(*psz))
1415 {
1416 uNewLineNo *= 10;
1417 uNewLineNo += *psz++ - '0';
1418 }
1419 if ( psz != pszEol
1420 && !MY_IS_BLANK(*psz))
1421 return 0;
1422
1423 /*
1424 * The file name part is optional.
1425 */
1426 while (MY_IS_BLANK(*psz))
1427 psz++;
1428
1429 if ( psz != pszEol
1430 && *psz == '"')
1431 {
1432 *ppszNewFile = ++psz;
1433 while ( psz != pszEol
1434 && ( *psz != '"'
1435 || ( psz[-1] == '\\'
1436 && kOCDepIsEscaped(psz, psz - *ppszNewFile)) )
1437 )
1438 psz++;
1439 if (psz == pszEol)
1440 {
1441 /** @todo complain? */
1442 return 0;
1443 }
1444 *pcchNewFile = psz - *ppszNewFile;
1445
1446 do
1447 psz++;
1448 while (psz != pszEol && MY_IS_BLANK(*psz));
1449 }
1450 else
1451 {
1452 /* No file given => Same as the current. */
1453 *ppszNewFile = pCppRd->cchCurFileNm ? pCppRd->pszFileNmBuf : NULL;
1454 *pcchNewFile = pCppRd->cchCurFileNm;
1455 }
1456 if (psz != pszEol)
1457 {
1458 /** @todo complain? */
1459 return 0;
1460 }
1461
1462 *puNewLineNo = uNewLineNo;
1463 return 1;
1464}
1465
1466
1467static char *kOCCppRdOptHandleLine(PKOCCPPRD pCppRd, char *pszCur, size_t *pcbLeft, int *pfEmptyLine, char *pszEol)
1468{
1469 size_t const cchEol = pszEol - pszCur;
1470 char *pszNewFile;
1471 size_t cchNewFile;
1472 uint32_t uNewLineNo;
1473 assert(*pszEol == '\r' || *pszEol == '\n' || *pszEol == '\0');
1474
1475 if (!kOCCppRdOptParseLine(pCppRd, pszCur, pszEol, &uNewLineNo, &pszNewFile, &cchNewFile))
1476 {
1477 /*
1478 * No line directive. Flush pending optimizations and indicate that
1479 * the line isn't empty and needs to be commited at EOL.
1480 */
1481 pszEol = kOCCppRdOptFlush(pCppRd, pszEol, 0);
1482 *pfEmptyLine = 0;
1483 }
1484 else
1485 {
1486 char *pszCurFile = pCppRd->cchCurFileNm ? pCppRd->pszFileNmBuf : NULL;
1487 if ( pszNewFile == pszCurFile
1488 || ( cchNewFile == pCppRd->cchCurFileNm
1489 && !memcmp(pszNewFile, pszCurFile, cchNewFile)) )
1490 {
1491 /*
1492 * A #line directive specifying the same file.
1493 */
1494 if (uNewLineNo >= pCppRd->uCurLineNo)
1495 *pfEmptyLine = 1;
1496 else
1497 {
1498 /*
1499 * It went backwards, so we need to flush the old section of
1500 * the file and emit another directive for starting the new one.
1501 */
1502 size_t const cchOrgLine = pszEol - pCppRd->pszBuf - pCppRd->offCur;
1503 size_t cchLineDir;
1504 pszEol = kOCCppRdOptFlush(pCppRd, pszEol, 1);
1505
1506 cchLineDir = kOCCppRdOptFmtLine(pCppRd, uNewLineNo, NULL, 0) - 1; /* sans \n */
1507
1508 pszEol += kOCCppRdOptReplace(pCppRd, pCppRd->offCur, cchOrgLine,
1509 pCppRd->pszLineBuf, cchLineDir);
1510
1511 *pfEmptyLine = 0;
1512 }
1513 }
1514 else
1515 {
1516 /*
1517 * The #line directive changed the file.
1518 */
1519 size_t const cchOrgLine = pszEol - pCppRd->pszBuf - pCppRd->offCur;
1520 size_t cchLineDir;
1521 size_t offDelta;
1522 assert(*pszEol == '\r' || *pszEol == '\n' || *pszEol == '\0');
1523
1524 offDelta = kOCCppRdOptFlush(pCppRd, pszEol, 1) - pszEol;
1525 pszEol += offDelta;
1526 pszNewFile += offDelta;
1527 assert(*pszEol == '\r' || *pszEol == '\n' || *pszEol == '\0');
1528
1529 kOCCppRdOptSetFile(pCppRd, pszNewFile, cchNewFile);
1530 cchLineDir = kOCCppRdOptFmtLine(pCppRd, uNewLineNo, pszNewFile, cchNewFile) - 1; /* sans \n */
1531 assert(*pszEol == '\r' || *pszEol == '\n' || *pszEol == '\0');
1532
1533 pszEol += kOCCppRdOptReplace(pCppRd, pCppRd->offCur, cchOrgLine,
1534 pCppRd->pszLineBuf, cchLineDir);
1535 assert(*pszEol == '\r' || *pszEol == '\n' || *pszEol == '\0');
1536
1537 *pfEmptyLine = 0;
1538 }
1539
1540 pCppRd->uCurLineNo = uNewLineNo - 1;
1541 assert(*pszEol == '\r' || *pszEol == '\n' || *pszEol == '\0');
1542 }
1543
1544 pCppRd->offCur = pszEol - pCppRd->pszBuf;
1545 *pcbLeft -= cchEol;
1546 return pszEol;
1547}
1548
1549
1550static void kOCCppRdOpt(PKOCCPPRD pCppRd)
1551{
1552 size_t cch;
1553 char *pszEol;
1554 char *pszCur = pCppRd->pszBuf + pCppRd->offCur;
1555 size_t cbTodo = pCppRd->offRead - pCppRd->offCur;
1556 int fEmptyLine = 1;
1557
1558 while (cbTodo > 0)
1559 {
1560 switch (*pszCur)
1561 {
1562 case ' ':
1563 case '\t':
1564 break;
1565
1566 case '\n':
1567 pCppRd->offCur = pszCur - pCppRd->pszBuf + 1;
1568 pCppRd->uCurLineNo++;
1569 if (!fEmptyLine)
1570 kOCCppRdOptCommit(pCppRd);
1571 else
1572 pCppRd->cEmptyLines++;
1573 fEmptyLine = 1;
1574 break;
1575
1576 case '\r': /* "\r\n" -> "\n" */
1577 if (cbTodo == 1 && !pCppRd->fDone)
1578 return;
1579 if (pszCur[1] == '\n')
1580 {
1581 pszCur = kOCCppRdOptRemove(pCppRd, pszCur, 1);
1582 cbTodo -= 1;
1583 continue;
1584 }
1585 break;
1586
1587 case '#':
1588 pszEol = kOCCppRdOptGetEol(pCppRd, pszCur + 1, cbTodo - 1);
1589 if (!pszEol)
1590 return;
1591 pszCur = kOCCppRdOptHandleLine(pCppRd, pszCur, &cbTodo, &fEmptyLine, pszEol);
1592 continue;
1593
1594 default:
1595 /*
1596 * Some non-white stuff encountered, flush pending white
1597 * line optimizations and skip to the end of the line.
1598 */
1599 fEmptyLine = 0;
1600 pszEol = kOCCppRdOptGetEol(pCppRd, pszCur + 1, cbTodo - 1);
1601 if (!pszEol)
1602 return;
1603 cch = pszEol - pszCur;
1604
1605 pszCur = kOCCppRdOptFlush(pCppRd, pszCur, 0);
1606
1607 cbTodo -= cch;
1608 pszCur += cch;
1609 continue;
1610 }
1611
1612 cbTodo--;
1613 pszCur++;
1614 }
1615}
1616
1617
1618static void kOCCppRdOptFinalize(PKOCCPPRD pCppRd)
1619{
1620 pCppRd->fDone = 1;
1621 assert(pCppRd->offRead < 1 || pCppRd->pszBuf[pCppRd->offRead - 1] != '\0');
1622 pCppRd->pszBuf[pCppRd->offRead] = '\0';
1623 kOCCppRdOpt(pCppRd);
1624 kOCCppRdOptFlush(pCppRd, pCppRd->pszBuf + pCppRd->offCur, 0);
1625}
1626
1627
1628
1629/**
1630 * Read C preprocessor output from the given file descriptor, optionally
1631 * optimzing it.
1632 *
1633 * @returns Number of bytes read. 0 indicates end of file.
1634 *
1635 * @param pCppRd The C preprocessor reader instance.
1636 * @param fdIn The file descriptor to read the raw preprocessor output
1637 * from.
1638 * @param ppszRet Where to return the pointer to the output.
1639 *
1640 * @remarks Won't return on error, calls FatalDie on those occasions.
1641 */
1642static long kOCCppRdRead(PKOCCPPRD pCppRd, int fdIn, const char **ppszRet)
1643{
1644 size_t cbLeft;
1645 long cbRead;
1646
1647 if (pCppRd->fOptimize)
1648 {
1649 /*
1650 * Optimize the C preprocessor output on the way thru.
1651 */
1652 size_t const cchOldOptimized = pCppRd->cchOptimized;
1653 if (pCppRd->chSaved)
1654 pCppRd->pszBuf[pCppRd->cchOptimized] = pCppRd->chSaved;
1655
1656 do
1657 {
1658 /* Read more raw C preprocessor output. */
1659 cbLeft = pCppRd->cbBufAlloc - pCppRd->offRead;
1660 if (cbLeft <= 1)
1661 cbLeft = kOCCppRdGrowBuffer(pCppRd);
1662 if (cbLeft > 256*1024)
1663 cbLeft = 256*1024; /* memmove() get's expensive if there is to much to work on - bad algorithm! */
1664
1665 do
1666 cbRead = read(fdIn, pCppRd->pszBuf + pCppRd->offRead, (long)(cbLeft - 1));
1667 while (cbRead < 0 && errno == EINTR);
1668 if (cbRead < 0)
1669 FatalDie("kOCCppRdRead - read(%d,,%ld) failed: %s\n",
1670 fdIn, (long)(cbLeft - 1), strerror(errno));
1671 pCppRd->offRead += cbRead;
1672
1673 /* Optimize it. */
1674 if (!cbRead)
1675 {
1676 kOCCppRdOptFinalize(pCppRd);
1677 break;
1678 }
1679 kOCCppRdOpt(pCppRd);
1680 } while (pCppRd->cchOptimized == cchOldOptimized);
1681
1682 *ppszRet = &pCppRd->pszBuf[cchOldOptimized];
1683 pCppRd->chSaved = pCppRd->pszBuf[pCppRd->cchOptimized];
1684 pCppRd->pszBuf[pCppRd->cchOptimized] = '\0';
1685 cbRead = (long)(pCppRd->cchOptimized - cchOldOptimized);
1686 }
1687 else
1688 {
1689 /*
1690 * Pass thru.
1691 */
1692 char *pszBuf;
1693 cbLeft = pCppRd->cbBufAlloc - pCppRd->offRead;
1694 if (cbLeft <= 1)
1695 cbLeft = kOCCppRdGrowBuffer(pCppRd);
1696 pszBuf = pCppRd->pszBuf + pCppRd->offRead;
1697
1698 do
1699 cbRead = read(fdIn, pszBuf, (long)(cbLeft - 1));
1700 while (cbRead < 0 && errno == EINTR);
1701 if (cbRead < 0)
1702 FatalDie("kOCCppRdRead - read(%d,,%ld) failed: %s\n",
1703 fdIn, (long)(cbLeft - 1), strerror(errno));
1704
1705 *ppszRet = pszBuf;
1706 pCppRd->offRead += cbRead;
1707 pszBuf[cbRead] = '\0';
1708 }
1709
1710 return cbRead;
1711}
1712
1713
1714/**
1715 * Grabs the output buffer from the C preprocessor reader.
1716 *
1717 * @param pCppRd The C preprocessor reader instance.
1718 * @param ppszRet Where to return the pointer to the output.
1719 * @param pcbRet Where to return the size of the output.
1720 */
1721static void kOCCppRdGrabOutput(PKOCCPPRD pCppRd, char **ppszRet, size_t *pcbRet)
1722{
1723 assert(pCppRd->offRead < 1 || pCppRd->pszBuf[pCppRd->offRead - 1] != '\0');
1724 *ppszRet = pCppRd->pszBuf;
1725 *pcbRet = pCppRd->offRead;
1726 pCppRd->pszBuf = NULL;
1727 pCppRd->offRead = 0;
1728}
1729
1730
1731
1732
1733
1734
1735/** A checksum list entry.
1736 * We keep a list checksums (of preprocessor output) that matches.
1737 *
1738 * The matching algorithm doesn't require the preprocessor output to be
1739 * indentical, only to produce the same object files.
1740 */
1741typedef struct KOCSUM
1742{
1743 /** The next checksum. */
1744 struct KOCSUM *pNext;
1745 /** The crc32 checksum. */
1746 uint32_t crc32;
1747 /** The MD5 digest. */
1748 unsigned char md5[16];
1749 /** Valid or not. */
1750 unsigned fUsed;
1751} KOCSUM;
1752/** Pointer to a KOCSUM. */
1753typedef KOCSUM *PKOCSUM;
1754/** Pointer to a const KOCSUM. */
1755typedef const KOCSUM *PCKOCSUM;
1756
1757
1758/**
1759 * Temporary context record used when calculating the checksum of some data.
1760 */
1761typedef struct KOCSUMCTX
1762{
1763 /** The MD5 context. */
1764 struct MD5Context MD5Ctx;
1765} KOCSUMCTX;
1766/** Pointer to a check context record. */
1767typedef KOCSUMCTX *PKOCSUMCTX;
1768
1769
1770
1771/**
1772 * Initializes a checksum object with an associated context.
1773 *
1774 * @param pSum The checksum object.
1775 * @param pCtx The checksum context.
1776 */
1777static void kOCSumInitWithCtx(PKOCSUM pSum, PKOCSUMCTX pCtx)
1778{
1779 memset(pSum, 0, sizeof(*pSum));
1780 MD5Init(&pCtx->MD5Ctx);
1781}
1782
1783
1784/**
1785 * Updates the checksum calculation.
1786 *
1787 * @param pSum The checksum.
1788 * @param pCtx The checksum calcuation context.
1789 * @param pvBuf The input data to checksum.
1790 * @param cbBuf The size of the input data.
1791 */
1792static void kOCSumUpdate(PKOCSUM pSum, PKOCSUMCTX pCtx, const void *pvBuf, size_t cbBuf)
1793{
1794 /*
1795 * Take in relativly small chunks to try keep it in the cache.
1796 */
1797 const unsigned char *pb = (const unsigned char *)pvBuf;
1798 while (cbBuf > 0)
1799 {
1800 size_t cb = cbBuf >= 128*1024 ? 128*1024 : cbBuf;
1801 pSum->crc32 = crc32(pSum->crc32, pb, cb);
1802 MD5Update(&pCtx->MD5Ctx, pb, (unsigned)cb);
1803 cbBuf -= cb;
1804 }
1805}
1806
1807
1808/**
1809 * Finalizes a checksum calculation.
1810 *
1811 * @param pSum The checksum.
1812 * @param pCtx The checksum calcuation context.
1813 */
1814static void kOCSumFinalize(PKOCSUM pSum, PKOCSUMCTX pCtx)
1815{
1816 MD5Final(&pSum->md5[0], &pCtx->MD5Ctx);
1817 pSum->fUsed = 1;
1818}
1819
1820
1821/**
1822 * Init a check sum chain head.
1823 *
1824 * @param pSumHead The checksum head to init.
1825 */
1826static void kOCSumInit(PKOCSUM pSumHead)
1827{
1828 memset(pSumHead, 0, sizeof(*pSumHead));
1829}
1830
1831
1832/**
1833 * Parses the given string into a checksum head object.
1834 *
1835 * @returns 0 on success, -1 on format error.
1836 * @param pSumHead The checksum head to init.
1837 * @param pszVal The string to initialized it from.
1838 */
1839static int kOCSumInitFromString(PKOCSUM pSumHead, const char *pszVal)
1840{
1841 unsigned i;
1842 char *pszNext;
1843 char *pszMD5;
1844
1845 memset(pSumHead, 0, sizeof(*pSumHead));
1846
1847 pszMD5 = strchr(pszVal, ':');
1848 if (pszMD5 == NULL)
1849 return -1;
1850 *pszMD5++ = '\0';
1851
1852 /* crc32 */
1853 pSumHead->crc32 = (uint32_t)strtoul(pszVal, &pszNext, 16);
1854 if (pszNext && *pszNext)
1855 return -1;
1856
1857 /* md5 */
1858 for (i = 0; i < sizeof(pSumHead->md5) * 2; i++)
1859 {
1860 unsigned char ch = pszMD5[i];
1861 int x;
1862 if ((unsigned char)(ch - '0') <= 9)
1863 x = ch - '0';
1864 else if ((unsigned char)(ch - 'a') <= 5)
1865 x = ch - 'a' + 10;
1866 else if ((unsigned char)(ch - 'A') <= 5)
1867 x = ch - 'A' + 10;
1868 else
1869 return -1;
1870 if (!(i & 1))
1871 pSumHead->md5[i >> 1] = x << 4;
1872 else
1873 pSumHead->md5[i >> 1] |= x;
1874 }
1875
1876 pSumHead->fUsed = 1;
1877 return 0;
1878}
1879
1880
1881/**
1882 * Delete a check sum chain.
1883 *
1884 * @param pSumHead The head of the checksum chain.
1885 */
1886static void kOCSumDeleteChain(PKOCSUM pSumHead)
1887{
1888 PKOCSUM pSum = pSumHead->pNext;
1889 while (pSum)
1890 {
1891 void *pvFree = pSum;
1892 pSum = pSum->pNext;
1893 free(pvFree);
1894 }
1895 memset(pSumHead, 0, sizeof(*pSumHead));
1896}
1897
1898
1899/**
1900 * Insert a check sum into the chain.
1901 *
1902 * @param pSumHead The head of the checksum list.
1903 * @param pSumAdd The checksum to add (duplicate).
1904 */
1905static void kOCSumAdd(PKOCSUM pSumHead, PCKOCSUM pSumAdd)
1906{
1907 if (pSumHead->fUsed)
1908 {
1909 PKOCSUM pNew = xmalloc(sizeof(*pNew));
1910 *pNew = *pSumAdd;
1911 pNew->pNext = pSumHead->pNext;
1912 pNew->fUsed = 1;
1913 pSumHead->pNext = pNew;
1914 }
1915 else
1916 {
1917 *pSumHead = *pSumAdd;
1918 pSumHead->pNext = NULL;
1919 pSumHead->fUsed = 1;
1920 }
1921}
1922
1923
1924/**
1925 * Inserts an entrie chain into the given check sum chain.
1926 *
1927 * @param pSumHead The head of the checksum list.
1928 * @param pSumHeadAdd The head of the checksum list to be added.
1929 */
1930static void kOCSumAddChain(PKOCSUM pSumHead, PCKOCSUM pSumHeadAdd)
1931{
1932 while (pSumHeadAdd)
1933 {
1934 kOCSumAdd(pSumHead, pSumHeadAdd);
1935 pSumHeadAdd = pSumHeadAdd->pNext;
1936 }
1937}
1938
1939
1940
1941/**
1942 * Prints the checksum to the specified stream.
1943 *
1944 * @param pSum The checksum.
1945 * @param pFile The output file stream
1946 */
1947static void kOCSumFPrintf(PCKOCSUM pSum, FILE *pFile)
1948{
1949 fprintf(pFile, "%#x:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
1950 pSum->crc32,
1951 pSum->md5[0], pSum->md5[1], pSum->md5[2], pSum->md5[3],
1952 pSum->md5[4], pSum->md5[5], pSum->md5[6], pSum->md5[7],
1953 pSum->md5[8], pSum->md5[9], pSum->md5[10], pSum->md5[11],
1954 pSum->md5[12], pSum->md5[13], pSum->md5[14], pSum->md5[15]);
1955}
1956
1957
1958/**
1959 * Displays the checksum (not chain!) using the InfoMsg() method.
1960 *
1961 * @param pSum The checksum.
1962 * @param uLevel The info message level.
1963 * @param pszMsg Message to prefix the info message with.
1964 */
1965static void kOCSumInfo(PCKOCSUM pSum, unsigned uLevel, const char *pszMsg)
1966{
1967 InfoMsg(uLevel,
1968 "%s: crc32=%#010x md5=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
1969 pszMsg,
1970 pSum->crc32,
1971 pSum->md5[0], pSum->md5[1], pSum->md5[2], pSum->md5[3],
1972 pSum->md5[4], pSum->md5[5], pSum->md5[6], pSum->md5[7],
1973 pSum->md5[8], pSum->md5[9], pSum->md5[10], pSum->md5[11],
1974 pSum->md5[12], pSum->md5[13], pSum->md5[14], pSum->md5[15]);
1975}
1976
1977
1978/**
1979 * Compares two check sum entries.
1980 *
1981 * @returns 1 if equal, 0 if not equal.
1982 *
1983 * @param pSum1 The first checksum.
1984 * @param pSum2 The second checksum.
1985 */
1986static int kOCSumIsEqual(PCKOCSUM pSum1, PCKOCSUM pSum2)
1987{
1988 if (pSum1 == pSum2)
1989 return 1;
1990 if (!pSum1 || !pSum2)
1991 return 0;
1992 if (pSum1->crc32 != pSum2->crc32)
1993 return 0;
1994 if (memcmp(&pSum1->md5[0], &pSum2->md5[0], sizeof(pSum1->md5)))
1995 return 0;
1996 return 1;
1997}
1998
1999
2000/**
2001 * Checks if the specified checksum equals one of the
2002 * checksums in the chain.
2003 *
2004 * @returns 1 if equals one of them, 0 if not.
2005 *
2006 * @param pSumHead The checksum chain too look in.
2007 * @param pSum The checksum to look for.
2008 * @todo ugly name. fix.
2009 */
2010static int kOCSumHasEqualInChain(PCKOCSUM pSumHead, PCKOCSUM pSum)
2011{
2012 for (; pSumHead; pSumHead = pSumHead->pNext)
2013 {
2014 if (pSumHead == pSum)
2015 return 1;
2016 if (pSumHead->crc32 != pSum->crc32)
2017 continue;
2018 if (memcmp(&pSumHead->md5[0], &pSum->md5[0], sizeof(pSumHead->md5)))
2019 continue;
2020 return 1;
2021 }
2022 return 0;
2023}
2024
2025
2026/**
2027 * Checks if the checksum (chain) empty.
2028 *
2029 * @returns 1 if empty, 0 if it there is one or more checksums.
2030 * @param pSum The checksum to test.
2031 */
2032static int kOCSumIsEmpty(PCKOCSUM pSum)
2033{
2034 return !pSum->fUsed;
2035}
2036
2037
2038
2039
2040
2041
2042/**
2043 * The representation of a cache entry.
2044 */
2045typedef struct KOCENTRY
2046{
2047 /** The name of the cache entry. */
2048 const char *pszName;
2049 /** The dir that all other names are relative to. */
2050 char *pszDir;
2051 /** The absolute path. */
2052 char *pszAbsPath;
2053 /** Set if the object needs to be (re)compiled. */
2054 unsigned fNeedCompiling;
2055 /** Whether the preprocessor runs in piped mode. If clear it's file
2056 * mode (it could be redirected stdout, but that's essentially the
2057 * same from our point of view). */
2058 unsigned fPipedPreComp;
2059 /** Whether the compiler runs in piped mode (preprocessor output on stdin). */
2060 unsigned fPipedCompile;
2061 /** The name of the pipe that we're feeding the preprocessed output to the
2062 * compiler via. This is a Windows thing. */
2063 char *pszNmPipeCompile;
2064 /** Name of the dependency file (generated from #line statements in the
2065 * preprocessor output). */
2066 char *pszMakeDepFilename;
2067 /** Whether to fix the case of the make depedencies. */
2068 int fMakeDepFixCase;
2069 /** Whether to do the make dependencies quietly. */
2070 int fMakeDepQuiet;
2071 /** Whether to generate stubs for headers files. */
2072 int fMakeDepGenStubs;
2073 /** The dependency collector state. */
2074 KOCDEP DepState;
2075 /** Whether the optimizations are enabled. */
2076 int fOptimizeCpp;
2077 /** Cache entry key that's used for some quick digest validation. */
2078 uint32_t uKey;
2079
2080 /** The file data. */
2081 struct KOCENTRYDATA
2082 {
2083 /** The name of file containing the preprocessor output. */
2084 char *pszCppName;
2085 /** Pointer to the preprocessor output. */
2086 char *pszCppMapping;
2087 /** The size of the preprocessor output. 0 if not determined. */
2088 size_t cbCpp;
2089 /** The preprocessor output checksums that will produce the cached object. */
2090 KOCSUM SumHead;
2091 /** The number of milliseconds spent precompiling. */
2092 uint32_t cMsCpp;
2093
2094 /** The object filename (relative to the cache file). */
2095 char *pszObjName;
2096 /** The compile argument vector used to build the object. */
2097 char **papszArgvCompile;
2098 /** The size of the compile */
2099 unsigned cArgvCompile;
2100 /** The checksum of the compiler argument vector. */
2101 KOCSUM SumCompArgv;
2102 /** The number of milliseconds spent compiling. */
2103 uint32_t cMsCompile;
2104 /** @todo need a list of additional output files for MSC. */
2105 /** @todo need compiler output (warnings). */
2106
2107 /** The target os/arch identifier. */
2108 char *pszTarget;
2109 }
2110 /** The old data.*/
2111 Old,
2112 /** The new data. */
2113 New;
2114} KOCENTRY;
2115/** Pointer to a KOCENTRY. */
2116typedef KOCENTRY *PKOCENTRY;
2117/** Pointer to a const KOCENTRY. */
2118typedef const KOCENTRY *PCKOCENTRY;
2119
2120
2121/**
2122 * Creates a cache entry for the given cache file name.
2123 *
2124 * @returns Pointer to a cache entry.
2125 * @param pszFilename The cache file name.
2126 */
2127static PKOCENTRY kOCEntryCreate(const char *pszFilename)
2128{
2129 PKOCENTRY pEntry;
2130 size_t off;
2131
2132 /*
2133 * Allocate an empty entry.
2134 */
2135 pEntry = xmallocz(sizeof(*pEntry));
2136
2137 kOCDepInit(&pEntry->DepState);
2138
2139 kOCSumInit(&pEntry->New.SumHead);
2140 kOCSumInit(&pEntry->Old.SumHead);
2141
2142 kOCSumInit(&pEntry->New.SumCompArgv);
2143 kOCSumInit(&pEntry->Old.SumCompArgv);
2144
2145 /*
2146 * Setup the directory and cache file name.
2147 */
2148 pEntry->pszAbsPath = AbsPath(pszFilename);
2149 pEntry->pszName = FindFilenameInPath(pEntry->pszAbsPath);
2150 off = pEntry->pszName - pEntry->pszAbsPath;
2151 if (!off)
2152 FatalDie("Failed to find abs path for '%s'!\n", pszFilename);
2153 pEntry->pszDir = xmalloc(off);
2154 memcpy(pEntry->pszDir, pEntry->pszAbsPath, off - 1);
2155 pEntry->pszDir[off - 1] = '\0';
2156
2157 return pEntry;
2158}
2159
2160
2161/**
2162 * Destroys the cache entry freeing up all it's resources.
2163 *
2164 * @param pEntry The entry to free.
2165 */
2166static void kOCEntryDestroy(PKOCENTRY pEntry)
2167{
2168 /** @todo free pEntry->pszName? */
2169 free(pEntry->pszDir);
2170 free(pEntry->pszAbsPath);
2171 free(pEntry->pszNmPipeCompile);
2172 free(pEntry->pszMakeDepFilename);
2173
2174 kOCDepDelete(&pEntry->DepState);
2175
2176 kOCSumDeleteChain(&pEntry->New.SumHead);
2177 kOCSumDeleteChain(&pEntry->Old.SumHead);
2178
2179 kOCSumDeleteChain(&pEntry->New.SumCompArgv);
2180 kOCSumDeleteChain(&pEntry->Old.SumCompArgv);
2181
2182 free(pEntry->New.pszCppName);
2183 free(pEntry->Old.pszCppName);
2184
2185 free(pEntry->New.pszCppMapping);
2186 free(pEntry->Old.pszCppMapping);
2187
2188 free(pEntry->New.pszObjName);
2189 free(pEntry->Old.pszObjName);
2190
2191 free(pEntry->New.pszTarget);
2192 free(pEntry->Old.pszTarget);
2193
2194 while (pEntry->New.cArgvCompile > 0)
2195 free(pEntry->New.papszArgvCompile[--pEntry->New.cArgvCompile]);
2196 while (pEntry->Old.cArgvCompile > 0)
2197 free(pEntry->Old.papszArgvCompile[--pEntry->Old.cArgvCompile]);
2198
2199 free(pEntry->New.papszArgvCompile);
2200 free(pEntry->Old.papszArgvCompile);
2201
2202 free(pEntry);
2203}
2204
2205
2206/**
2207 * Calculates the checksum of an compiler argument vector.
2208 *
2209 * @param pEntry The cache entry.
2210 * @param papszArgv The argument vector.
2211 * @param cArgc The number of entries in the vector.
2212 * @param pszIgnorePath1 Path to ignore when encountered at the end of
2213 * arguments. (Not quite safe for simple file names,
2214 * but what the heck.)
2215 * @param pszIgnorePath2 Path to ignore when encountered at the end of
2216 * arguments. (Not quite safe for simple file names,
2217 * but what the heck.)
2218 * @param pSum Where to store the check sum.
2219 */
2220static void kOCEntryCalcArgvSum(PKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgc,
2221 const char *pszIgnorePath1, const char *pszIgnorePath2, PKOCSUM pSum)
2222{
2223 size_t cchIgnorePath1 = strlen(pszIgnorePath1);
2224 size_t cchIgnorePath2 = pszIgnorePath2 ? strlen(pszIgnorePath2) : ~(size_t)0;
2225 KOCSUMCTX Ctx;
2226 unsigned i;
2227
2228 kOCSumInitWithCtx(pSum, &Ctx);
2229 for (i = 0; i < cArgc; i++)
2230 {
2231 size_t cch = strlen(papszArgv[i]);
2232 if ( ( cch < cchIgnorePath1
2233 || !ArePathsIdenticalN(papszArgv[i] + cch - cchIgnorePath1, pszIgnorePath1, cch))
2234 && ( cch < cchIgnorePath2
2235 || !ArePathsIdenticalN(papszArgv[i] + cch - cchIgnorePath2, pszIgnorePath2, cch)) )
2236 kOCSumUpdate(pSum, &Ctx, papszArgv[i], cch + 1);
2237 }
2238 kOCSumFinalize(pSum, &Ctx);
2239
2240 (void)pEntry;
2241}
2242
2243
2244/**
2245 * Reads and parses the cache file.
2246 *
2247 * @param pEntry The entry to read it into.
2248 */
2249static void kOCEntryRead(PKOCENTRY pEntry)
2250{
2251 FILE *pFile;
2252 pFile = FOpenFileInDir(pEntry->pszName, pEntry->pszDir, "rb");
2253 if (pFile)
2254 {
2255 InfoMsg(4, "reading cache entry...\n");
2256
2257 /*
2258 * Check the magic.
2259 */
2260 if ( !fgets(g_szLine, sizeof(g_szLine), pFile)
2261 || ( strcmp(g_szLine, "magic=kObjCacheEntry-v0.1.0\n")
2262 && strcmp(g_szLine, "magic=kObjCacheEntry-v0.1.1\n"))
2263 )
2264 {
2265 InfoMsg(2, "bad cache file (magic)\n");
2266 pEntry->fNeedCompiling = 1;
2267 }
2268 else
2269 {
2270 /*
2271 * Parse the rest of the file (relaxed order).
2272 */
2273 unsigned i;
2274 int fBad = 0;
2275 int fBadBeforeMissing = 1;
2276 while (fgets(g_szLine, sizeof(g_szLine), pFile))
2277 {
2278 char *pszNl;
2279 char *pszVal;
2280
2281 /* Split the line and drop the trailing newline. */
2282 pszVal = strchr(g_szLine, '=');
2283 if ((fBad = pszVal == NULL))
2284 break;
2285 *pszVal++ = '\0';
2286
2287 pszNl = strchr(pszVal, '\n');
2288 if (pszNl)
2289 *pszNl = '\0';
2290
2291 /* string case on variable name */
2292 if (!strcmp(g_szLine, "obj"))
2293 {
2294 if ((fBad = pEntry->Old.pszObjName != NULL))
2295 break;
2296 pEntry->Old.pszObjName = xstrdup(pszVal);
2297 }
2298 else if (!strcmp(g_szLine, "cpp"))
2299 {
2300 if ((fBad = pEntry->Old.pszCppName != NULL))
2301 break;
2302 pEntry->Old.pszCppName = xstrdup(pszVal);
2303 }
2304 else if (!strcmp(g_szLine, "cpp-size"))
2305 {
2306 char *pszNext;
2307 if ((fBad = pEntry->Old.cbCpp != 0))
2308 break;
2309 pEntry->Old.cbCpp = strtoul(pszVal, &pszNext, 0);
2310 if ((fBad = pszNext && *pszNext))
2311 break;
2312 }
2313 else if (!strcmp(g_szLine, "cpp-sum"))
2314 {
2315 KOCSUM Sum;
2316 if ((fBad = kOCSumInitFromString(&Sum, pszVal)))
2317 break;
2318 kOCSumAdd(&pEntry->Old.SumHead, &Sum);
2319 }
2320 else if (!strcmp(g_szLine, "cpp-ms"))
2321 {
2322 char *pszNext;
2323 if ((fBad = pEntry->Old.cMsCpp != 0))
2324 break;
2325 pEntry->Old.cMsCpp = strtoul(pszVal, &pszNext, 0);
2326 if ((fBad = pszNext && *pszNext))
2327 break;
2328 }
2329 else if (!strcmp(g_szLine, "cc-argc"))
2330 {
2331 if ((fBad = pEntry->Old.papszArgvCompile != NULL))
2332 break;
2333 pEntry->Old.cArgvCompile = atoi(pszVal); /* if wrong, we'll fail below. */
2334 pEntry->Old.papszArgvCompile = xmallocz((pEntry->Old.cArgvCompile + 1) * sizeof(pEntry->Old.papszArgvCompile[0]));
2335 }
2336 else if (!strncmp(g_szLine, "cc-argv-#", sizeof("cc-argv-#") - 1))
2337 {
2338 char *pszNext;
2339 unsigned i = strtoul(&g_szLine[sizeof("cc-argv-#") - 1], &pszNext, 0);
2340 if ((fBad = i >= pEntry->Old.cArgvCompile || pEntry->Old.papszArgvCompile[i] || (pszNext && *pszNext)))
2341 break;
2342 pEntry->Old.papszArgvCompile[i] = xstrdup(pszVal);
2343 }
2344 else if (!strcmp(g_szLine, "cc-argv-sum"))
2345 {
2346 if ((fBad = !kOCSumIsEmpty(&pEntry->Old.SumCompArgv)))
2347 break;
2348 if ((fBad = kOCSumInitFromString(&pEntry->Old.SumCompArgv, pszVal)))
2349 break;
2350 }
2351 else if (!strcmp(g_szLine, "cc-ms"))
2352 {
2353 char *pszNext;
2354 if ((fBad = pEntry->Old.cMsCompile != 0))
2355 break;
2356 pEntry->Old.cMsCompile = strtoul(pszVal, &pszNext, 0);
2357 if ((fBad = pszNext && *pszNext))
2358 break;
2359 }
2360 else if (!strcmp(g_szLine, "target"))
2361 {
2362 if ((fBad = pEntry->Old.pszTarget != NULL))
2363 break;
2364 pEntry->Old.pszTarget = xstrdup(pszVal);
2365 }
2366 else if (!strcmp(g_szLine, "key"))
2367 {
2368 char *pszNext;
2369 if ((fBad = pEntry->uKey != 0))
2370 break;
2371 pEntry->uKey = strtoul(pszVal, &pszNext, 0);
2372 if ((fBad = pszNext && *pszNext))
2373 break;
2374 }
2375 else if (!strcmp(g_szLine, "the-end"))
2376 {
2377 fBadBeforeMissing = fBad = strcmp(pszVal, "fine");
2378 break;
2379 }
2380 else
2381 {
2382 fBad = 1;
2383 break;
2384 }
2385 } /* parse loop */
2386
2387 /*
2388 * Did we find everything and does it add up correctly?
2389 */
2390 if (!fBad && fBadBeforeMissing)
2391 {
2392 InfoMsg(2, "bad cache file (no end)\n");
2393 fBad = 1;
2394 }
2395 else
2396 {
2397 fBadBeforeMissing = fBad;
2398 if ( !fBad
2399 && ( !pEntry->Old.papszArgvCompile
2400 || !pEntry->Old.pszObjName
2401 || !pEntry->Old.pszCppName
2402 || kOCSumIsEmpty(&pEntry->Old.SumHead)))
2403 fBad = 1;
2404 if (!fBad)
2405 for (i = 0; i < pEntry->Old.cArgvCompile; i++)
2406 if ((fBad = !pEntry->Old.papszArgvCompile[i]))
2407 break;
2408 if (!fBad)
2409 {
2410 KOCSUM Sum;
2411 kOCEntryCalcArgvSum(pEntry, (const char * const *)pEntry->Old.papszArgvCompile,
2412 pEntry->Old.cArgvCompile, pEntry->Old.pszObjName, pEntry->Old.pszCppName,
2413 &Sum);
2414 fBad = !kOCSumIsEqual(&pEntry->Old.SumCompArgv, &Sum);
2415 }
2416 if (fBad)
2417 InfoMsg(2, "bad cache file (%s)\n", fBadBeforeMissing ? g_szLine : "missing stuff");
2418 else if (ferror(pFile))
2419 {
2420 InfoMsg(2, "cache file read error\n");
2421 fBad = 1;
2422 }
2423
2424 /*
2425 * Verify the existance of the object file.
2426 */
2427 if (!fBad)
2428 {
2429 struct stat st;
2430 char *pszPath = MakePathFromDirAndFile(pEntry->Old.pszObjName, pEntry->pszDir);
2431 if (stat(pszPath, &st) != 0)
2432 {
2433 InfoMsg(2, "failed to stat object file: %s\n", strerror(errno));
2434 fBad = 1;
2435 }
2436 else
2437 {
2438 /** @todo verify size and the timestamp. */
2439 }
2440 }
2441 }
2442 pEntry->fNeedCompiling = fBad;
2443 }
2444 fclose(pFile);
2445 }
2446 else
2447 {
2448 InfoMsg(2, "no cache file\n");
2449 pEntry->fNeedCompiling = 1;
2450 }
2451}
2452
2453
2454/**
2455 * Writes the cache file.
2456 *
2457 * @param pEntry The entry to write.
2458 */
2459static void kOCEntryWrite(PKOCENTRY pEntry)
2460{
2461 FILE *pFile;
2462 PCKOCSUM pSum;
2463 unsigned i;
2464
2465 InfoMsg(4, "writing cache entry '%s'...\n", pEntry->pszName);
2466 pFile = FOpenFileInDir(pEntry->pszName, pEntry->pszDir, "wb");
2467 if (!pFile)
2468 FatalDie("Failed to open '%s' in '%s': %s\n",
2469 pEntry->pszName, pEntry->pszDir, strerror(errno));
2470
2471#define CHECK_LEN(expr) \
2472 do { int cch = expr; if (cch >= KOBJCACHE_MAX_LINE_LEN) FatalDie("Line too long: %d (max %d)\nexpr: %s\n", cch, KOBJCACHE_MAX_LINE_LEN, #expr); } while (0)
2473
2474 fprintf(pFile, "magic=kObjCacheEntry-v0.1.1\n");
2475 CHECK_LEN(fprintf(pFile, "target=%s\n", pEntry->New.pszTarget ? pEntry->New.pszTarget : pEntry->Old.pszTarget));
2476 CHECK_LEN(fprintf(pFile, "key=%lu\n", (unsigned long)pEntry->uKey));
2477 CHECK_LEN(fprintf(pFile, "obj=%s\n", pEntry->New.pszObjName ? pEntry->New.pszObjName : pEntry->Old.pszObjName));
2478 CHECK_LEN(fprintf(pFile, "cpp=%s\n", pEntry->New.pszCppName ? pEntry->New.pszCppName : pEntry->Old.pszCppName));
2479 CHECK_LEN(fprintf(pFile, "cpp-size=%lu\n", pEntry->New.pszCppName ? pEntry->New.cbCpp : pEntry->Old.cbCpp));
2480 CHECK_LEN(fprintf(pFile, "cpp-ms=%lu\n", pEntry->New.pszCppName ? pEntry->New.cMsCpp : pEntry->Old.cMsCpp));
2481 CHECK_LEN(fprintf(pFile, "cc-ms=%lu\n", pEntry->New.pszCppName ? pEntry->New.cMsCompile : pEntry->Old.cMsCompile));
2482
2483 if (!kOCSumIsEmpty(&pEntry->New.SumCompArgv))
2484 {
2485 CHECK_LEN(fprintf(pFile, "cc-argc=%u\n", pEntry->New.cArgvCompile));
2486 for (i = 0; i < pEntry->New.cArgvCompile; i++)
2487 CHECK_LEN(fprintf(pFile, "cc-argv-#%u=%s\n", i, pEntry->New.papszArgvCompile[i]));
2488 fprintf(pFile, "cc-argv-sum=");
2489 kOCSumFPrintf(&pEntry->New.SumCompArgv, pFile);
2490 }
2491 else
2492 {
2493 CHECK_LEN(fprintf(pFile, "cc-argc=%u\n", pEntry->Old.cArgvCompile));
2494 for (i = 0; i < pEntry->Old.cArgvCompile; i++)
2495 CHECK_LEN(fprintf(pFile, "cc-argv-#%u=%s\n", i, pEntry->Old.papszArgvCompile[i]));
2496 fprintf(pFile, "cc-argv-sum=");
2497 kOCSumFPrintf(&pEntry->Old.SumCompArgv, pFile);
2498 }
2499
2500
2501 for (pSum = !kOCSumIsEmpty(&pEntry->New.SumHead) ? &pEntry->New.SumHead : &pEntry->Old.SumHead;
2502 pSum;
2503 pSum = pSum->pNext)
2504 {
2505 fprintf(pFile, "cpp-sum=");
2506 kOCSumFPrintf(pSum, pFile);
2507 }
2508
2509 fprintf(pFile, "the-end=fine\n");
2510
2511#undef CHECK_LEN
2512
2513 /*
2514 * Flush the file and check for errors.
2515 * On failure delete the file so we won't be seeing any invalid
2516 * files the next time or upset make with new timestamps.
2517 */
2518 errno = 0;
2519 if ( fflush(pFile) < 0
2520 || ferror(pFile))
2521 {
2522 int iErr = errno;
2523 fclose(pFile);
2524 UnlinkFileInDir(pEntry->pszName, pEntry->pszDir);
2525 FatalDie("Stream error occured while writing '%s' in '%s': %s\n",
2526 pEntry->pszName, pEntry->pszDir, strerror(iErr));
2527 }
2528 fclose(pFile);
2529}
2530
2531
2532/**
2533 * Checks that the read cache entry is valid.
2534 * It sets fNeedCompiling if it isn't.
2535 *
2536 * @returns 1 valid, 0 invalid.
2537 * @param pEntry The cache entry.
2538 */
2539static int kOCEntryCheck(PKOCENTRY pEntry)
2540{
2541 return !pEntry->fNeedCompiling;
2542}
2543
2544
2545/**
2546 * Sets the object name and compares it with the old name if present.
2547 *
2548 * @param pEntry The cache entry.
2549 * @param pszObjName The new object name.
2550 */
2551static void kOCEntrySetCompileObjName(PKOCENTRY pEntry, const char *pszObjName)
2552{
2553 assert(!pEntry->New.pszObjName);
2554 pEntry->New.pszObjName = CalcRelativeName(pszObjName, pEntry->pszDir);
2555
2556 if ( !pEntry->fNeedCompiling
2557 && ( !pEntry->Old.pszObjName
2558 || strcmp(pEntry->New.pszObjName, pEntry->Old.pszObjName)))
2559 {
2560 InfoMsg(2, "object file name differs\n");
2561 pEntry->fNeedCompiling = 1;
2562 }
2563
2564 if ( !pEntry->fNeedCompiling
2565 && !DoesFileInDirExist(pEntry->New.pszObjName, pEntry->pszDir))
2566 {
2567 InfoMsg(2, "object file doesn't exist\n");
2568 pEntry->fNeedCompiling = 1;
2569 }
2570}
2571
2572
2573/**
2574 * Set the new compiler args, calc their checksum, and comparing them with any old ones.
2575 *
2576 * @param pEntry The cache entry.
2577 * @param papszArgvCompile The new argument vector for compilation.
2578 * @param cArgvCompile The number of arguments in the vector.
2579 *
2580 * @remark Must call kOCEntrySetCompileObjName before this function!
2581 */
2582static void kOCEntrySetCompileArgv(PKOCENTRY pEntry, const char * const *papszArgvCompile, unsigned cArgvCompile)
2583{
2584 unsigned i;
2585
2586 /* call me only once! */
2587 assert(!pEntry->New.cArgvCompile);
2588 /* call kOCEntrySetCompilerObjName first! */
2589 assert(pEntry->New.pszObjName);
2590
2591 /*
2592 * Copy the argument vector and calculate the checksum.
2593 */
2594 pEntry->New.cArgvCompile = cArgvCompile;
2595 pEntry->New.papszArgvCompile = xmalloc((cArgvCompile + 1) * sizeof(pEntry->New.papszArgvCompile[0]));
2596 for (i = 0; i < cArgvCompile; i++)
2597 pEntry->New.papszArgvCompile[i] = xstrdup(papszArgvCompile[i]);
2598 pEntry->New.papszArgvCompile[i] = NULL; /* for exev/spawnv */
2599
2600 kOCEntryCalcArgvSum(pEntry, papszArgvCompile, cArgvCompile, pEntry->New.pszObjName, pEntry->New.pszCppName,
2601 &pEntry->New.SumCompArgv);
2602 kOCSumInfo(&pEntry->New.SumCompArgv, 4, "comp-argv");
2603
2604 /*
2605 * Compare with the old argument vector.
2606 */
2607 if ( !pEntry->fNeedCompiling
2608 && !kOCSumIsEqual(&pEntry->New.SumCompArgv, &pEntry->Old.SumCompArgv))
2609 {
2610 InfoMsg(2, "compiler args differs\n");
2611 pEntry->fNeedCompiling = 1;
2612 }
2613}
2614
2615
2616/**
2617 * Sets the arch/os target and compares it with the old name if present.
2618 *
2619 * @param pEntry The cache entry.
2620 * @param pszObjName The new object name.
2621 */
2622static void kOCEntrySetTarget(PKOCENTRY pEntry, const char *pszTarget)
2623{
2624 assert(!pEntry->New.pszTarget);
2625 pEntry->New.pszTarget = xstrdup(pszTarget);
2626
2627 if ( !pEntry->fNeedCompiling
2628 && ( !pEntry->Old.pszTarget
2629 || strcmp(pEntry->New.pszTarget, pEntry->Old.pszTarget)))
2630 {
2631 InfoMsg(2, "target differs\n");
2632 pEntry->fNeedCompiling = 1;
2633 }
2634}
2635
2636
2637/**
2638 * Sets the preprocessor output filename. We don't generally care if this
2639 * matches the old name or not.
2640 *
2641 * @param pEntry The cache entry.
2642 * @param pszCppName The preprocessor output filename.
2643 */
2644static void kOCEntrySetCppName(PKOCENTRY pEntry, const char *pszCppName)
2645{
2646 assert(!pEntry->New.pszCppName);
2647 pEntry->New.pszCppName = CalcRelativeName(pszCppName, pEntry->pszDir);
2648}
2649
2650
2651/**
2652 * Sets the piped mode of the preprocessor and compiler.
2653 *
2654 * @param pEntry The cache entry.
2655 * @param fRedirPreCompStdOut Whether the preprocessor is in piped mode.
2656 * @param fRedirCompileStdIn Whether the compiler is in piped mode.
2657 * @param pszNmPipeCompile The name of the named pipe to use to feed
2658 * the microsoft compiler.
2659 */
2660static void kOCEntrySetPipedMode(PKOCENTRY pEntry, int fRedirPreCompStdOut, int fRedirCompileStdIn,
2661 const char *pszNmPipeCompile)
2662{
2663 pEntry->fPipedPreComp = fRedirPreCompStdOut;
2664 pEntry->fPipedCompile = fRedirCompileStdIn || pszNmPipeCompile;
2665 pEntry->pszNmPipeCompile = xstrdup(pszNmPipeCompile);
2666}
2667
2668
2669/**
2670 * Sets the dependency file.
2671 *
2672 * @param pEntry The cache entry.
2673 * @param pszMakeDepFilename The dependency filename.
2674 * @param fMakeDepFixCase Whether to fix the case of dependency files.
2675 * @param fMakeDepQuiet Whether to be quiet about the dependencies.
2676 * @param fMakeDepGenStubs Whether to generate stubs.
2677 */
2678static void kOCEntrySetDepFilename(PKOCENTRY pEntry, const char *pszMakeDepFilename,
2679 int fMakeDepFixCase, int fMakeDepQuiet, int fMakeDepGenStubs)
2680{
2681 pEntry->pszMakeDepFilename = xstrdup(pszMakeDepFilename);
2682 pEntry->fMakeDepFixCase = fMakeDepFixCase;
2683 pEntry->fMakeDepQuiet = fMakeDepQuiet;
2684 pEntry->fMakeDepGenStubs = fMakeDepGenStubs;
2685}
2686
2687
2688/**
2689 * Configures the preprocessor output optimizations.
2690 *
2691 * @param pEntry The cache entry.
2692 * @param fOptimizeCpp The one and only flag, so far.
2693 */
2694static void kOCEntrySetOptimizations(PKOCENTRY pEntry, int fOptimizeCpp)
2695{
2696 pEntry->fOptimizeCpp = fOptimizeCpp;
2697}
2698
2699
2700/**
2701 * Spawns a child in a synchronous fashion.
2702 * Terminating on failure.
2703 *
2704 * @param papszArgv Argument vector. The cArgv element is NULL.
2705 * @param pcMs The cache entry member use for time keeping. This
2706 * will be set to the current timestamp.
2707 * @param cArgv The number of arguments in the vector.
2708 * @param pszMsg Which operation this is, for use in messages.
2709 * @param pszStdOut Where to redirect standard out.
2710 */
2711static void kOCEntrySpawn(PCKOCENTRY pEntry, uint32_t *pcMs, const char * const *papszArgv, unsigned cArgv,
2712 const char *pszMsg, const char *pszStdOut)
2713{
2714#if defined(__OS2__) || defined(__WIN__)
2715 intptr_t rc;
2716 int fdStdOut = -1;
2717 if (pszStdOut)
2718 {
2719 int fdReDir;
2720 fdStdOut = dup(STDOUT_FILENO);
2721 close(STDOUT_FILENO);
2722 fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0666);
2723 if (fdReDir < 0)
2724 FatalDie("%s - failed to create stdout redirection file '%s': %s\n",
2725 pszMsg, pszStdOut, strerror(errno));
2726
2727 if (fdReDir != STDOUT_FILENO)
2728 {
2729 if (dup2(fdReDir, STDOUT_FILENO) < 0)
2730 FatalDie("%s - dup2 failed: %s\n", pszMsg, strerror(errno));
2731 close(fdReDir);
2732 }
2733 }
2734
2735 *pcMs = NowMs();
2736 errno = 0;
2737# ifdef __WIN__
2738 rc = quoted_spawnvp(_P_WAIT, papszArgv[0], papszArgv);
2739# else
2740 rc = _spawnvp(_P_WAIT, papszArgv[0], papszArgv);
2741# endif
2742 *pcMs = NowMs() - *pcMs;
2743 if (rc < 0)
2744 FatalDie("%s - _spawnvp failed (rc=0x%p): %s\n", pszMsg, rc, strerror(errno));
2745 if (rc > 0)
2746 FatalDie("%s - failed rc=%d\n", pszMsg, (int)rc);
2747 if (fdStdOut != -1)
2748 {
2749 close(STDOUT_FILENO);
2750 fdStdOut = dup2(fdStdOut, STDOUT_FILENO);
2751 close(fdStdOut);
2752 }
2753
2754#else
2755 int iStatus;
2756 pid_t pidWait;
2757 pid_t pid;
2758
2759 *pcMs = NowMs();
2760 pid = fork();
2761 if (!pid)
2762 {
2763 if (pszStdOut)
2764 {
2765 int fdReDir;
2766
2767 close(STDOUT_FILENO);
2768 fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0666);
2769 if (fdReDir < 0)
2770 FatalDie("%s - failed to create stdout redirection file '%s': %s\n",
2771 pszMsg, pszStdOut, strerror(errno));
2772 if (fdReDir != STDOUT_FILENO)
2773 {
2774 if (dup2(fdReDir, STDOUT_FILENO) < 0)
2775 FatalDie("%s - dup2 failed: %s\n", pszMsg, strerror(errno));
2776 close(fdReDir);
2777 }
2778 }
2779
2780 execvp(papszArgv[0], (char **)papszArgv);
2781 FatalDie("%s - execvp failed: %s\n",
2782 pszMsg, strerror(errno));
2783 }
2784 if (pid == -1)
2785 FatalDie("%s - fork() failed: %s\n", pszMsg, strerror(errno));
2786
2787 pidWait = waitpid(pid, &iStatus, 0);
2788 while (pidWait < 0 && errno == EINTR)
2789 pidWait = waitpid(pid, &iStatus, 0);
2790 *pcMs = NowMs() - *pcMs;
2791 if (pidWait != pid)
2792 FatalDie("%s - waitpid failed rc=%d: %s\n",
2793 pszMsg, pidWait, strerror(errno));
2794 if (!WIFEXITED(iStatus))
2795 FatalDie("%s - abended (iStatus=%#x)\n", pszMsg, iStatus);
2796 if (WEXITSTATUS(iStatus))
2797 FatalDie("%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus));
2798#endif
2799
2800 (void)pEntry; (void)cArgv;
2801}
2802
2803
2804/**
2805 * Spawns child with optional redirection of stdin and stdout.
2806 *
2807 * @param pEntry The cache entry.
2808 * @param pcMs The cache entry member use for time keeping. This
2809 * will be set to the current timestamp.
2810 * @param papszArgv Argument vector. The cArgv element is NULL.
2811 * @param cArgv The number of arguments in the vector.
2812 * @param fdStdIn Child stdin, -1 if it should inherit our stdin. Will be closed.
2813 * @param fdStdOut Child stdout, -1 if it should inherit our stdout. Will be closed.
2814 * @param pszMsg Message to start the info/error messages with.
2815 */
2816static pid_t kOCEntrySpawnChild(PCKOCENTRY pEntry, uint32_t *pcMs, const char * const *papszArgv, unsigned cArgv,
2817 int fdStdIn, int fdStdOut, const char *pszMsg)
2818{
2819 pid_t pid;
2820 int fdSavedStdOut = -1;
2821 int fdSavedStdIn = -1;
2822
2823 /*
2824 * Setup redirection.
2825 */
2826 if (fdStdOut != -1 && fdStdOut != STDOUT_FILENO)
2827 {
2828 fdSavedStdOut = dup(STDOUT_FILENO);
2829 if (dup2(fdStdOut, STDOUT_FILENO) < 0)
2830 FatalDie("%s - dup2(,1) failed: %s\n", pszMsg, strerror(errno));
2831 close(fdStdOut);
2832#ifndef __WIN__
2833 fcntl(fdSavedStdOut, F_SETFD, FD_CLOEXEC);
2834#endif
2835 }
2836 if (fdStdIn != -1 && fdStdIn != STDIN_FILENO)
2837 {
2838 fdSavedStdIn = dup(STDIN_FILENO);
2839 if (dup2(fdStdIn, STDIN_FILENO) < 0)
2840 FatalDie("%s - dup2(,0) failed: %s\n", pszMsg, strerror(errno));
2841 close(fdStdIn);
2842#ifndef __WIN__
2843 fcntl(fdSavedStdIn, F_SETFD, FD_CLOEXEC);
2844#endif
2845 }
2846
2847 /*
2848 * Create the child process.
2849 */
2850 *pcMs = NowMs();
2851#if defined(__OS2__) || defined(__WIN__)
2852 errno = 0;
2853# ifdef __WIN__
2854 pid = quoted_spawnvp(_P_NOWAIT, papszArgv[0], papszArgv);
2855# else
2856 pid = _spawnvp(_P_NOWAIT, papszArgv[0], papszArgv);
2857# endif
2858 if (pid == -1)
2859 FatalDie("preprocess - _spawnvp failed: %s\n", strerror(errno));
2860
2861#else
2862 pid = fork();
2863 if (!pid)
2864 {
2865 execvp(papszArgv[0], (char **)papszArgv);
2866 FatalDie("preprocess - execvp failed: %s\n", strerror(errno));
2867 }
2868 if (pid == -1)
2869 FatalDie("preprocess - fork() failed: %s\n", strerror(errno));
2870#endif
2871
2872 /*
2873 * Restore stdout & stdin.
2874 */
2875 if (fdSavedStdIn != -1)
2876 {
2877 close(STDIN_FILENO);
2878 dup2(fdStdOut, STDIN_FILENO);
2879 close(fdSavedStdIn);
2880 }
2881 if (fdSavedStdOut != -1)
2882 {
2883 close(STDOUT_FILENO);
2884 dup2(fdSavedStdOut, STDOUT_FILENO);
2885 close(fdSavedStdOut);
2886 }
2887
2888 InfoMsg(3, "%s - spawned %ld\n", pszMsg, (long)pid);
2889 (void)cArgv;
2890 (void)pEntry;
2891 return pid;
2892}
2893
2894
2895/**
2896 * Waits for a child and exits fatally if the child failed in any way.
2897 *
2898 * @param pEntry The cache entry.
2899 * @param pcMs The millisecond timestamp that should be convert to
2900 * elapsed time.
2901 * @param pid The child to wait for.
2902 * @param pszMsg Message to start the info/error messages with.
2903 */
2904static void kOCEntryWaitChild(PCKOCENTRY pEntry, uint32_t *pcMs, pid_t pid, const char *pszMsg)
2905{
2906 int iStatus = -1;
2907 pid_t pidWait;
2908 InfoMsg(3, "%s - wait-child %ld\n", pszMsg, (long)pid);
2909
2910#ifdef __WIN__
2911 pidWait = _cwait(&iStatus, pid, _WAIT_CHILD);
2912 *pcMs = NowMs() - *pcMs;
2913 if (pidWait == -1)
2914 FatalDie("%s - waitpid failed: %s\n", pszMsg, strerror(errno));
2915 if (iStatus)
2916 FatalDie("%s - failed with rc %d\n", pszMsg, iStatus);
2917#else
2918 pidWait = waitpid(pid, &iStatus, 0);
2919 while (pidWait < 0 && errno == EINTR)
2920 pidWait = waitpid(pid, &iStatus, 0);
2921 *pcMs = NowMs() - *pcMs;
2922 if (pidWait != pid)
2923 FatalDie("%s - waitpid failed rc=%d: %s\n", pidWait, strerror(errno));
2924 if (!WIFEXITED(iStatus))
2925 FatalDie("%s - abended (iStatus=%#x)\n", pszMsg, iStatus);
2926 if (WEXITSTATUS(iStatus))
2927 FatalDie("%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus));
2928#endif
2929 (void)pEntry;
2930}
2931
2932
2933/**
2934 * Creates a pipe for setting up redirected stdin/stdout.
2935 *
2936 * @param pEntry The cache entry.
2937 * @param paFDs Where to store the two file descriptors.
2938 * @param pszMsg The operation message for info/error messages.
2939 * @param pszPipeName The pipe name if it is supposed to be named. (Windows only.)
2940 * @param fText Whether to read text mode or binary mode.
2941 */
2942static void kOCEntryCreatePipe(PKOCENTRY pEntry, int *paFDs, const char *pszPipeName, const char *pszMsg, int fText)
2943{
2944 paFDs[0] = paFDs[1] = -1;
2945#if defined(__WIN__)
2946 if (pszPipeName)
2947 {
2948 HANDLE hPipe = CreateNamedPipeA(pszPipeName,
2949 /*PIPE_ACCESS_OUTBOUND*/ PIPE_ACCESS_DUPLEX,
2950 PIPE_READMODE_BYTE | PIPE_WAIT,
2951 10 /* nMaxInstances */,
2952 0x10000 /* nOutBuffer */,
2953 0x10000 /* nInBuffer */,
2954 NMPWAIT_WAIT_FOREVER,
2955 NULL /* pSecurityAttributes */);
2956
2957 if (hPipe == INVALID_HANDLE_VALUE)
2958 FatalDie("%s - CreateNamedPipe(%s) failed: %d\n", pszMsg, pszPipeName, GetLastError());
2959
2960 paFDs[1 /* write */] = _open_osfhandle((intptr_t)hPipe, _O_WRONLY | _O_TEXT | _O_NOINHERIT);
2961 if (paFDs[1 /* write */] == -1)
2962 FatalDie("%s - _open_osfhandle failed: %d\n", pszMsg, strerror(errno));
2963 }
2964 else if ( _pipe(paFDs, 256*1024, _O_NOINHERIT | (fText ? _O_BINARY : _O_TEXT)) < 0
2965 && _pipe(paFDs, 0, _O_NOINHERIT | (fText ? _O_BINARY : _O_TEXT)) < 0)
2966#else
2967 if (pipe(paFDs) < 0)
2968#endif
2969 FatalDie("%s - pipe failed: %s\n", pszMsg, strerror(errno));
2970#if !defined(__WIN__)
2971 fcntl(paFDs[0], F_SETFD, FD_CLOEXEC);
2972 fcntl(paFDs[1], F_SETFD, FD_CLOEXEC);
2973#endif
2974
2975 (void)pEntry;
2976}
2977
2978
2979/**
2980 * Spawns a child that produces output to stdout.
2981 *
2982 * @param papszArgv Argument vector. The cArgv element is NULL.
2983 * @param cArgv The number of arguments in the vector.
2984 * @param pszMsg The operation message for info/error messages.
2985 * @param pfnConsumer Pointer to a consumer callback function that is responsible
2986 * for servicing the child output and closing the pipe.
2987 */
2988static void kOCEntrySpawnProducer(PKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgv, const char *pszMsg,
2989 void (*pfnConsumer)(PKOCENTRY, int))
2990{
2991 int fds[2];
2992 pid_t pid;
2993
2994 kOCEntryCreatePipe(pEntry, fds, NULL, pszMsg, pEntry->fOptimizeCpp);
2995 pid = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCpp, papszArgv, cArgv, -1, fds[1 /* write */], pszMsg);
2996
2997 pfnConsumer(pEntry, fds[0 /* read */]);
2998
2999 kOCEntryWaitChild(pEntry, &pEntry->New.cMsCpp, pid, pszMsg);
3000}
3001
3002
3003/**
3004 * Spawns a child that consumes input on stdin or via a named pipe.
3005 *
3006 * @param papszArgv Argument vector. The cArgv element is NULL.
3007 * @param cArgv The number of arguments in the vector.
3008 * @param pszMsg The operation message for info/error messages.
3009 * @param pfnProducer Pointer to a producer callback function that is responsible
3010 * for serving the child input and closing the pipe.
3011 */
3012static void kOCEntrySpawnConsumer(PKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgv, const char *pszMsg,
3013 void (*pfnProducer)(PKOCENTRY, int))
3014{
3015 int fds[2];
3016 pid_t pid;
3017
3018 kOCEntryCreatePipe(pEntry, fds, pEntry->pszNmPipeCompile, pszMsg, 0 /*fText*/);
3019 pid = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCompile, papszArgv, cArgv, fds[0 /* read */], -1, pszMsg);
3020#ifdef __WIN__
3021 if (pEntry->pszNmPipeCompile && !ConnectNamedPipe((HANDLE)_get_osfhandle(fds[1 /* write */]), NULL))
3022 FatalDie("compile - ConnectNamedPipe failed: %d\n", GetLastError());
3023#endif
3024
3025 pfnProducer(pEntry, fds[1 /* write */]);
3026
3027 kOCEntryWaitChild(pEntry, &pEntry->New.cMsCompile, pid, pszMsg);
3028}
3029
3030
3031/**
3032 * Spawns two child processes, one producing output and one consuming.
3033 * Terminating on failure.
3034 *
3035 * @param papszArgv Argument vector. The cArgv element is NULL.
3036 * @param cArgv The number of arguments in the vector.
3037 * @param pszMsg The operation message for info/error messages.
3038 * @param pfnConsumer Pointer to a consumer callback function that is responsible
3039 * for servicing the child output and closing the pipe.
3040 */
3041static void kOCEntrySpawnTee(PKOCENTRY pEntry, const char * const *papszProdArgv, unsigned cProdArgv,
3042 const char * const *papszConsArgv, unsigned cConsArgv,
3043 const char *pszMsg, void (*pfnTeeConsumer)(PKOCENTRY, int, int))
3044{
3045 int fds[2];
3046 int fdIn, fdOut;
3047 pid_t pidProducer, pidConsumer;
3048
3049 /*
3050 * The producer.
3051 */
3052 kOCEntryCreatePipe(pEntry, fds, NULL, pszMsg, pEntry->fOptimizeCpp);
3053 pidConsumer = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCpp, papszProdArgv, cProdArgv, -1, fds[1 /* write */], pszMsg);
3054 fdIn = fds[0 /* read */];
3055
3056 /*
3057 * The consumer.
3058 */
3059 kOCEntryCreatePipe(pEntry, fds, pEntry->pszNmPipeCompile, pszMsg, 0 /*fText*/);
3060 pidProducer = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCompile, papszConsArgv, cConsArgv, fds[0 /* read */], -1, pszMsg);
3061 fdOut = fds[1 /* write */];
3062
3063 /*
3064 * Hand it on to the tee consumer.
3065 */
3066 pfnTeeConsumer(pEntry, fdIn, fdOut);
3067
3068 /*
3069 * Reap the children.
3070 */
3071 kOCEntryWaitChild(pEntry, &pEntry->New.cMsCpp, pidProducer, pszMsg);
3072 kOCEntryWaitChild(pEntry, &pEntry->New.cMsCompile, pidConsumer, pszMsg);
3073}
3074
3075
3076/**
3077 * Reads the output from the preprocessor.
3078 *
3079 * @param pEntry The cache entry. New.cbCpp and New.pszCppMapping will be updated.
3080 * @param pWhich Specifies what to read (old/new).
3081 * @param fNonFatal Whether failure is fatal or not.
3082 */
3083static int kOCEntryReadCppOutput(PKOCENTRY pEntry, struct KOCENTRYDATA *pWhich, int fNonFatal)
3084{
3085 pWhich->pszCppMapping = ReadFileInDir(pWhich->pszCppName, pEntry->pszDir, &pWhich->cbCpp);
3086 if (!pWhich->pszCppMapping)
3087 {
3088 if (!fNonFatal)
3089 FatalDie("failed to open/read '%s' in '%s': %s\n",
3090 pWhich->pszCppName, pEntry->pszDir, strerror(errno));
3091 InfoMsg(2, "failed to open/read '%s' in '%s': %s\n",
3092 pWhich->pszCppName, pEntry->pszDir, strerror(errno));
3093 return -1;
3094 }
3095
3096 InfoMsg(3, "preprocessed file is %lu bytes long\n", (unsigned long)pWhich->cbCpp);
3097 return 0;
3098}
3099
3100
3101/**
3102 * Worker for kOCEntryPreProcess and calculates the checksum of
3103 * the preprocessor output.
3104 *
3105 * @param pEntry The cache entry. NewSum will be updated.
3106 */
3107static void kOCEntryCalcChecksum(PKOCENTRY pEntry)
3108{
3109 KOCSUMCTX Ctx;
3110 kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx);
3111 kOCSumUpdate(&pEntry->New.SumHead, &Ctx, pEntry->New.pszCppMapping, pEntry->New.cbCpp);
3112 kOCSumFinalize(&pEntry->New.SumHead, &Ctx);
3113 kOCSumInfo(&pEntry->New.SumHead, 4, "cpp (file)");
3114}
3115
3116
3117/**
3118 * This consumes the preprocessor output and checksums it.
3119 *
3120 * @param pEntry The cache entry.
3121 * @param fdIn The preprocessor output pipe.
3122 * @param fdOut The compiler input pipe, -1 if no compiler.
3123 */
3124static void kOCEntryPreProcessConsumer(PKOCENTRY pEntry, int fdIn)
3125{
3126 KOCSUMCTX Ctx;
3127 KOCCPPRD CppRd;
3128
3129 kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx);
3130 kOCCppRdInit(&CppRd, pEntry->Old.cbCpp, pEntry->fOptimizeCpp);
3131
3132 for (;;)
3133 {
3134 /*
3135 * Read data from the pipe.
3136 */
3137 const char *psz;
3138 long cbRead = kOCCppRdRead(&CppRd, fdIn, &psz);
3139 if (!cbRead)
3140 break;
3141
3142 /*
3143 * Process the data.
3144 */
3145 kOCSumUpdate(&pEntry->New.SumHead, &Ctx, psz, cbRead);
3146 if (pEntry->pszMakeDepFilename)
3147 kOCDepConsumer(&pEntry->DepState, psz, cbRead);
3148 }
3149
3150 close(fdIn);
3151 kOCCppRdGrabOutput(&CppRd, &pEntry->New.pszCppMapping, &pEntry->New.cbCpp);
3152 kOCCppRdDelete(&CppRd);
3153 kOCSumFinalize(&pEntry->New.SumHead, &Ctx);
3154 kOCSumInfo(&pEntry->New.SumHead, 4, "cpp (pipe)");
3155}
3156
3157
3158
3159
3160/**
3161 * Run the preprocessor and calculate the checksum of the output.
3162 *
3163 * @param pEntry The cache entry.
3164 * @param papszArgvPreComp The argument vector for executing preprocessor.
3165 * The cArgvPreComp'th argument must be NULL.
3166 * @param cArgvPreComp The number of arguments.
3167 */
3168static void kOCEntryPreProcess(PKOCENTRY pEntry, const char * const *papszArgvPreComp, unsigned cArgvPreComp)
3169{
3170 /*
3171 * If we're executing the preprocessor in piped mode, it's relatively simple.
3172 */
3173 if (pEntry->fPipedPreComp)
3174 kOCEntrySpawnProducer(pEntry, papszArgvPreComp, cArgvPreComp, "preprocess",
3175 kOCEntryPreProcessConsumer);
3176 else
3177 {
3178 /*
3179 * Rename the old preprocessed output to '-old' so the preprocessor won't
3180 * overwrite it when we execute it.
3181 */
3182 if ( pEntry->Old.pszCppName
3183 && DoesFileInDirExist(pEntry->Old.pszCppName, pEntry->pszDir))
3184 {
3185 size_t cch = strlen(pEntry->Old.pszCppName);
3186 char *psz = xmalloc(cch + sizeof("-old"));
3187 memcpy(psz, pEntry->Old.pszCppName, cch);
3188 memcpy(psz + cch, "-old", sizeof("-old"));
3189
3190 InfoMsg(3, "renaming '%s' to '%s' in '%s'\n", pEntry->Old.pszCppName, psz, pEntry->pszDir);
3191 UnlinkFileInDir(psz, pEntry->pszDir);
3192 if (RenameFileInDir(pEntry->Old.pszCppName, psz, pEntry->pszDir))
3193 FatalDie("failed to rename '%s' -> '%s' in '%s': %s\n",
3194 pEntry->Old.pszCppName, psz, pEntry->pszDir, strerror(errno));
3195 free(pEntry->Old.pszCppName);
3196 pEntry->Old.pszCppName = psz;
3197 }
3198
3199 /*
3200 * Preprocess it and calculate the checksum on the output.
3201 */
3202 InfoMsg(3, "precompiling -> '%s'...\n", pEntry->New.pszCppName);
3203 kOCEntrySpawn(pEntry, &pEntry->New.cMsCpp, papszArgvPreComp, cArgvPreComp, "preprocess", NULL);
3204 kOCEntryReadCppOutput(pEntry, &pEntry->New, 0 /* fatal */);
3205 kOCEntryCalcChecksum(pEntry);
3206 if (pEntry->pszMakeDepFilename)
3207 kOCDepConsumer(&pEntry->DepState, pEntry->New.pszCppMapping, pEntry->New.cbCpp);
3208 }
3209
3210 if (pEntry->pszMakeDepFilename)
3211 kOCDepWriteToFile(&pEntry->DepState, pEntry->pszMakeDepFilename, pEntry->New.pszObjName, pEntry->pszDir,
3212 pEntry->fMakeDepFixCase, pEntry->fMakeDepQuiet, pEntry->fMakeDepGenStubs);
3213}
3214
3215
3216/**
3217 * Worker function for kOCEntryTeeConsumer and kOCEntryCompileIt that
3218 * writes the preprocessor output to disk.
3219 *
3220 * @param pEntry The cache entry.
3221 * @param fFreeIt Whether we can free it after writing it or not.
3222 */
3223static void kOCEntryWriteCppOutput(PKOCENTRY pEntry, int fFreeIt)
3224{
3225 /*
3226 * Remove old files.
3227 */
3228 if (pEntry->Old.pszCppName)
3229 UnlinkFileInDir(pEntry->Old.pszCppName, pEntry->pszDir);
3230 if (pEntry->New.pszCppName)
3231 UnlinkFileInDir(pEntry->New.pszCppName, pEntry->pszDir);
3232
3233 /*
3234 * Write it to disk if we've got a file name.
3235 */
3236 if (pEntry->New.pszCppName)
3237 {
3238 long cbLeft;
3239 char *psz;
3240 int fd = OpenFileInDir(pEntry->New.pszCppName, pEntry->pszDir,
3241 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
3242 if (fd == -1)
3243 FatalDie("Failed to create '%s' in '%s': %s\n",
3244 pEntry->New.pszCppName, pEntry->pszDir, strerror(errno));
3245 psz = pEntry->New.pszCppMapping;
3246 cbLeft = (long)pEntry->New.cbCpp;
3247 while (cbLeft > 0)
3248 {
3249 long cbWritten = write(fd, psz, cbLeft);
3250 if (cbWritten < 0)
3251 {
3252 int iErr = errno;
3253 if (iErr == EINTR)
3254 continue;
3255 close(fd);
3256 UnlinkFileInDir(pEntry->New.pszCppName, pEntry->pszDir);
3257 FatalDie("error writing '%s' in '%s': %s\n",
3258 pEntry->New.pszCppName, pEntry->pszDir, strerror(iErr));
3259 }
3260
3261 psz += cbWritten;
3262 cbLeft -= cbWritten;
3263 }
3264 close(fd);
3265 }
3266
3267 /*
3268 * Free it.
3269 */
3270 if (fFreeIt)
3271 {
3272 free(pEntry->New.pszCppMapping);
3273 pEntry->New.pszCppMapping = NULL;
3274 }
3275}
3276
3277
3278/**
3279 * kOCEntrySpawnConsumer callback that passes the preprocessor output to the
3280 * compiler and writes it to the disk (latter only when necesary).
3281 *
3282 * @param pEntry The cache entry.
3283 * @param fdOut The pipe handle connected to the childs stdin.
3284 */
3285static void kOCEntryCompileProducer(PKOCENTRY pEntry, int fdOut)
3286{
3287 const char *psz = pEntry->New.pszCppMapping;
3288 long cbLeft = (long)pEntry->New.cbCpp;
3289 while (cbLeft > 0)
3290 {
3291 long cbWritten = write(fdOut, psz, cbLeft);
3292 if (cbWritten < 0)
3293 {
3294 if (errno == EINTR)
3295 continue;
3296#ifdef __WIN__ /* HACK */
3297 if ( errno == EINVAL
3298 && pEntry->pszNmPipeCompile
3299 && DisconnectNamedPipe((HANDLE)_get_osfhandle(fdOut))
3300 && ConnectNamedPipe((HANDLE)_get_osfhandle(fdOut), NULL))
3301 {
3302 psz = pEntry->New.pszCppMapping;
3303 cbLeft = (long)pEntry->New.cbCpp;
3304 }
3305 FatalDie("compile - write(%d,,%ld) failed: %s - _doserrno=%d\n", fdOut, cbLeft, strerror(errno), _doserrno);
3306#else
3307 FatalDie("compile - write(%d,,%ld) failed: %s\n", fdOut, cbLeft, strerror(errno));
3308#endif
3309 }
3310 psz += cbWritten;
3311 cbLeft -= cbWritten;
3312 }
3313 close(fdOut);
3314
3315 if (pEntry->fPipedPreComp)
3316 kOCEntryWriteCppOutput(pEntry, 1 /* free it */);
3317}
3318
3319
3320/**
3321 * Does the actual compiling.
3322 *
3323 * @param pEntry The cache entry.
3324 */
3325static void kOCEntryCompileIt(PKOCENTRY pEntry)
3326{
3327 /*
3328 * Delete the object files and free old cpp output that's no longer needed.
3329 */
3330 if (pEntry->Old.pszObjName)
3331 UnlinkFileInDir(pEntry->Old.pszObjName, pEntry->pszDir);
3332 UnlinkFileInDir(pEntry->New.pszObjName, pEntry->pszDir);
3333
3334 free(pEntry->Old.pszCppMapping);
3335 pEntry->Old.pszCppMapping = NULL;
3336 if (!pEntry->fPipedPreComp && !pEntry->fPipedCompile)
3337 {
3338 free(pEntry->New.pszCppMapping);
3339 pEntry->New.pszCppMapping = NULL;
3340 }
3341
3342 /*
3343 * Do the (re-)compile job.
3344 */
3345 if (pEntry->fPipedCompile)
3346 {
3347 if ( !pEntry->fPipedPreComp
3348 && !pEntry->New.pszCppMapping)
3349 kOCEntryReadCppOutput(pEntry, &pEntry->New, 0 /* fatal */);
3350 InfoMsg(3, "compiling -> '%s'...\n", pEntry->New.pszObjName);
3351 kOCEntrySpawnConsumer(pEntry, (const char * const *)pEntry->New.papszArgvCompile,
3352 pEntry->New.cArgvCompile, "compile", kOCEntryCompileProducer);
3353 }
3354 else
3355 {
3356 if (pEntry->fPipedPreComp)
3357 kOCEntryWriteCppOutput(pEntry, 1 /* free it */);
3358 InfoMsg(3, "compiling -> '%s'...\n", pEntry->New.pszObjName);
3359 kOCEntrySpawn(pEntry, &pEntry->New.cMsCompile, (const char * const *)pEntry->New.papszArgvCompile,
3360 pEntry->New.cArgvCompile, "compile", NULL);
3361 }
3362}
3363
3364
3365/**
3366 * kOCEntrySpawnTee callback that works sort of like 'tee'.
3367 *
3368 * It will calculate the preprocessed output checksum and
3369 * write it to disk while the compiler is busy compiling it.
3370 *
3371 * @param pEntry The cache entry.
3372 * @param fdIn The input handle (connected to the preprocessor).
3373 * @param fdOut The output handle (connected to the compiler).
3374 */
3375static void kOCEntryTeeConsumer(PKOCENTRY pEntry, int fdIn, int fdOut)
3376{
3377#ifdef __WIN__
3378 unsigned fConnectedToCompiler = fdOut == -1 || pEntry->pszNmPipeCompile == NULL;
3379#endif
3380 KOCSUMCTX Ctx;
3381 KOCCPPRD CppRd;
3382
3383 kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx);
3384 kOCCppRdInit(&CppRd, pEntry->Old.cbCpp, pEntry->fOptimizeCpp);
3385 InfoMsg(3, "preprocessor|compile - starting passhtru...\n");
3386 for (;;)
3387 {
3388 /*
3389 * Read data from the pipe.
3390 */
3391 const char *psz;
3392 long cbRead = kOCCppRdRead(&CppRd, fdIn, &psz);
3393 if (!cbRead)
3394 break;
3395 InfoMsg(3, "preprocessor|compile - read %d\n", cbRead);
3396
3397 /*
3398 * Process the data.
3399 */
3400 kOCSumUpdate(&pEntry->New.SumHead, &Ctx, psz, cbRead);
3401 if (pEntry->pszMakeDepFilename)
3402 kOCDepConsumer(&pEntry->DepState, psz, cbRead);
3403
3404#ifdef __WIN__
3405 if ( !fConnectedToCompiler
3406 && !(fConnectedToCompiler = ConnectNamedPipe((HANDLE)_get_osfhandle(fdOut), NULL)))
3407 FatalDie("preprocess|compile - ConnectNamedPipe failed: %d\n", GetLastError());
3408#endif
3409 do
3410 {
3411 long cbWritten = write(fdOut, psz, cbRead);
3412 if (cbWritten < 0)
3413 {
3414 if (errno == EINTR)
3415 continue;
3416 FatalDie("preprocess|compile - write(%d,,%ld) failed: %s\n", fdOut, cbRead, strerror(errno));
3417 }
3418 psz += cbWritten;
3419 cbRead -= cbWritten;
3420 } while (cbRead > 0);
3421
3422 }
3423 InfoMsg(3, "preprocessor|compile - done passhtru\n");
3424
3425 close(fdIn);
3426 close(fdOut);
3427 kOCCppRdGrabOutput(&CppRd, &pEntry->New.pszCppMapping, &pEntry->New.cbCpp);
3428 kOCCppRdDelete(&CppRd);
3429 kOCSumFinalize(&pEntry->New.SumHead, &Ctx);
3430 kOCSumInfo(&pEntry->New.SumHead, 4, "cpp (tee)");
3431
3432 /*
3433 * Write the preprocessor output to disk and free the memory it
3434 * occupies while the compiler is busy compiling.
3435 */
3436 kOCEntryWriteCppOutput(pEntry, 1 /* free it */);
3437}
3438
3439
3440/**
3441 * Performs pre-compile and compile in one go (typical clean build scenario).
3442 *
3443 * @param pEntry The cache entry.
3444 * @param papszArgvPreComp The argument vector for executing preprocessor.
3445 * The cArgvPreComp'th argument must be NULL.
3446 * @param cArgvPreComp The number of arguments.
3447 */
3448static void kOCEntryPreProcessAndCompile(PKOCENTRY pEntry, const char * const *papszArgvPreComp, unsigned cArgvPreComp)
3449{
3450 if ( pEntry->fPipedCompile
3451 && pEntry->fPipedPreComp)
3452 {
3453 /*
3454 * Clean up old stuff first.
3455 */
3456 if (pEntry->Old.pszObjName)
3457 UnlinkFileInDir(pEntry->Old.pszObjName, pEntry->pszDir);
3458 if (pEntry->New.pszObjName)
3459 UnlinkFileInDir(pEntry->New.pszObjName, pEntry->pszDir);
3460 if (pEntry->Old.pszCppName)
3461 UnlinkFileInDir(pEntry->Old.pszCppName, pEntry->pszDir);
3462 if (pEntry->New.pszCppName)
3463 UnlinkFileInDir(pEntry->New.pszCppName, pEntry->pszDir);
3464
3465 /*
3466 * Do the actual compile and write the preprocessor output to disk.
3467 */
3468 kOCEntrySpawnTee(pEntry, papszArgvPreComp, cArgvPreComp,
3469 (const char * const *)pEntry->New.papszArgvCompile, pEntry->New.cArgvCompile,
3470 "preprocess|compile", kOCEntryTeeConsumer);
3471 if (pEntry->pszMakeDepFilename)
3472 kOCDepWriteToFile(&pEntry->DepState, pEntry->pszMakeDepFilename, pEntry->New.pszObjName, pEntry->pszDir,
3473 pEntry->fMakeDepFixCase, pEntry->fMakeDepQuiet, pEntry->fMakeDepGenStubs);
3474 }
3475 else
3476 {
3477 kOCEntryPreProcess(pEntry, papszArgvPreComp, cArgvPreComp);
3478 kOCEntryCompileIt(pEntry);
3479 }
3480}
3481
3482
3483/**
3484 * Check whether the string is a '#line' statement.
3485 *
3486 * @returns 1 if it is, 0 if it isn't.
3487 * @param psz The line to examin.
3488 * @parma piLine Where to store the line number.
3489 * @parma ppszFile Where to store the start of the filename.
3490 */
3491static int kOCEntryIsLineStatement(const char *psz, unsigned *piLine, const char **ppszFile)
3492{
3493 unsigned iLine;
3494
3495 /* Expect a hash. */
3496 if (*psz++ != '#')
3497 return 0;
3498
3499 /* Skip blanks between '#' and the line / number */
3500 while (*psz == ' ' || *psz == '\t')
3501 psz++;
3502
3503 /* Skip the 'line' if present. */
3504 if (!strncmp(psz, "line", sizeof("line") - 1))
3505 psz += sizeof("line");
3506
3507 /* Expect a line number now. */
3508 if ((unsigned char)(*psz - '0') > 9)
3509 return 0;
3510 iLine = 0;
3511 do
3512 {
3513 iLine *= 10;
3514 iLine += (*psz - '0');
3515 psz++;
3516 }
3517 while ((unsigned char)(*psz - '0') <= 9);
3518
3519 /* Expect one or more space now. */
3520 if (*psz != ' ' && *psz != '\t')
3521 return 0;
3522 do psz++;
3523 while (*psz == ' ' || *psz == '\t');
3524
3525 /* that's good enough. */
3526 *piLine = iLine;
3527 *ppszFile = psz;
3528 return 1;
3529}
3530
3531
3532/**
3533 * Scan backwards for the previous #line statement.
3534 *
3535 * @returns The filename in the previous statement.
3536 * @param pszStart Where to start.
3537 * @param pszStop Where to stop. Less than pszStart.
3538 * @param piLine The line number count to adjust.
3539 */
3540static const char *kOCEntryFindFileStatement(const char *pszStart, const char *pszStop, unsigned *piLine)
3541{
3542 unsigned iLine = *piLine;
3543 assert(pszStart >= pszStop);
3544 while (pszStart >= pszStop)
3545 {
3546 if (*pszStart == '\n')
3547 iLine++;
3548 else if (*pszStart == '#')
3549 {
3550 unsigned iLineTmp;
3551 const char *pszFile;
3552 const char *psz = pszStart - 1;
3553 while (psz >= pszStop && (*psz == ' ' || *psz =='\t'))
3554 psz--;
3555 if ( (psz < pszStop || *psz == '\n')
3556 && kOCEntryIsLineStatement(pszStart, &iLineTmp, &pszFile))
3557 {
3558 *piLine = iLine + iLineTmp - 1;
3559 return pszFile;
3560 }
3561 }
3562 pszStart--;
3563 }
3564 return NULL;
3565}
3566
3567
3568/**
3569 * Worker for kOCEntryCompareOldAndNewOutput() that compares the
3570 * preprocessed output using a fast but not very good method.
3571 *
3572 * @returns 1 if matching, 0 if not matching.
3573 * @param pEntry The entry containing the names of the files to compare.
3574 * The entry is not updated in any way.
3575 */
3576static int kOCEntryCompareFast(PCKOCENTRY pEntry)
3577{
3578 const char * psz1 = pEntry->New.pszCppMapping;
3579 const char * const pszEnd1 = psz1 + pEntry->New.cbCpp;
3580 const char * psz2 = pEntry->Old.pszCppMapping;
3581 const char * const pszEnd2 = psz2 + pEntry->Old.cbCpp;
3582
3583 assert(*pszEnd1 == '\0');
3584 assert(*pszEnd2 == '\0');
3585
3586 /*
3587 * Iterate block by block and backtrack when we find a difference.
3588 */
3589 for (;;)
3590 {
3591 size_t cch = pszEnd1 - psz1;
3592 if (cch > (size_t)(pszEnd2 - psz2))
3593 cch = pszEnd2 - psz2;
3594 if (cch > 4096)
3595 cch = 4096;
3596 if ( cch
3597 && !memcmp(psz1, psz2, cch))
3598 {
3599 /* no differences */
3600 psz1 += cch;
3601 psz2 += cch;
3602 }
3603 else
3604 {
3605 /*
3606 * Pinpoint the difference exactly and the try find the start
3607 * of that line. Then skip forward until we find something to
3608 * work on that isn't spaces, #line statements or closing curly
3609 * braces.
3610 *
3611 * The closing curly braces are ignored because they are frequently
3612 * found at the end of header files (__END_DECLS) and the worst
3613 * thing that may happen if it isn't one of these braces we're
3614 * ignoring is that the final line in a function block is a little
3615 * bit off in the debug info.
3616 *
3617 * Since we might be skipping a few new empty headers, it is
3618 * possible that we will omit this header from the dependencies
3619 * when using VCC. This might not be a problem, since it seems
3620 * we'll have to use the preprocessor output to generate the deps
3621 * anyway.
3622 */
3623 const char *psz;
3624 const char *pszMismatch1;
3625 const char *pszFile1 = NULL;
3626 unsigned iLine1 = 0;
3627 unsigned cCurlyBraces1 = 0;
3628 const char *pszMismatch2;
3629 const char *pszFile2 = NULL;
3630 unsigned iLine2 = 0;
3631 unsigned cCurlyBraces2 = 0;
3632
3633 /* locate the difference. */
3634 while (cch >= 512 && !memcmp(psz1, psz2, 512))
3635 psz1 += 512, psz2 += 512, cch -= 512;
3636 while (cch >= 64 && !memcmp(psz1, psz2, 64))
3637 psz1 += 64, psz2 += 64, cch -= 64;
3638 while (*psz1 == *psz2 && cch > 0)
3639 psz1++, psz2++, cch--;
3640
3641 /* locate the start of that line. */
3642 psz = psz1;
3643 while ( psz > pEntry->New.pszCppMapping
3644 && psz[-1] != '\n')
3645 psz--;
3646 psz2 -= (psz1 - psz);
3647 pszMismatch2 = psz2;
3648 pszMismatch1 = psz1 = psz;
3649
3650 /* Parse the 1st file line by line. */
3651 while (psz1 < pszEnd1)
3652 {
3653 if (*psz1 == '\n')
3654 {
3655 psz1++;
3656 iLine1++;
3657 }
3658 else
3659 {
3660 psz = psz1;
3661 while (isspace(*psz) && *psz != '\n')
3662 psz++;
3663 if (*psz == '\n')
3664 {
3665 psz1 = psz + 1;
3666 iLine1++;
3667 }
3668 else if (*psz == '#' && kOCEntryIsLineStatement(psz, &iLine1, &pszFile1))
3669 {
3670 psz1 = memchr(psz, '\n', pszEnd1 - psz);
3671 if (!psz1++)
3672 psz1 = pszEnd1;
3673 }
3674 else if (*psz == '}')
3675 {
3676 do psz++;
3677 while (isspace(*psz) && *psz != '\n');
3678 if (*psz == '\n')
3679 iLine1++;
3680 else if (psz != pszEnd1)
3681 break;
3682 cCurlyBraces1++;
3683 psz1 = psz;
3684 }
3685 else if (psz == pszEnd1)
3686 psz1 = psz;
3687 else /* found something that can be compared. */
3688 break;
3689 }
3690 }
3691
3692 /* Ditto for the 2nd file. */
3693 while (psz2 < pszEnd2)
3694 {
3695 if (*psz2 == '\n')
3696 {
3697 psz2++;
3698 iLine2++;
3699 }
3700 else
3701 {
3702 psz = psz2;
3703 while (isspace(*psz) && *psz != '\n')
3704 psz++;
3705 if (*psz == '\n')
3706 {
3707 psz2 = psz + 1;
3708 iLine2++;
3709 }
3710 else if (*psz == '#' && kOCEntryIsLineStatement(psz, &iLine2, &pszFile2))
3711 {
3712 psz2 = memchr(psz, '\n', pszEnd2 - psz);
3713 if (!psz2++)
3714 psz2 = pszEnd2;
3715 }
3716 else if (*psz == '}')
3717 {
3718 do psz++;
3719 while (isspace(*psz) && *psz != '\n');
3720 if (*psz == '\n')
3721 iLine2++;
3722 else if (psz != pszEnd2)
3723 break;
3724 cCurlyBraces2++;
3725 psz2 = psz;
3726 }
3727 else if (psz == pszEnd2)
3728 psz2 = psz;
3729 else /* found something that can be compared. */
3730 break;
3731 }
3732 }
3733
3734 /* Match the number of ignored closing curly braces. */
3735 if (cCurlyBraces1 != cCurlyBraces2)
3736 return 0;
3737
3738 /* Reaching the end of any of them means the return statement can decide. */
3739 if ( psz1 == pszEnd1
3740 || psz2 == pszEnd2)
3741 break;
3742
3743 /* Match the current line. */
3744 psz = memchr(psz1, '\n', pszEnd1 - psz1);
3745 if (!psz++)
3746 psz = pszEnd1;
3747 cch = psz - psz1;
3748 if (psz2 + cch > pszEnd2)
3749 break;
3750 if (memcmp(psz1, psz2, cch))
3751 break;
3752
3753 /* Check that we're at the same location now. */
3754 if (!pszFile1)
3755 pszFile1 = kOCEntryFindFileStatement(pszMismatch1, pEntry->New.pszCppMapping, &iLine1);
3756 if (!pszFile2)
3757 pszFile2 = kOCEntryFindFileStatement(pszMismatch2, pEntry->Old.pszCppMapping, &iLine2);
3758 if (pszFile1 && pszFile2)
3759 {
3760 if (iLine1 != iLine2)
3761 break;
3762 while (*pszFile1 == *pszFile2 && *pszFile1 != '\n' && *pszFile1)
3763 pszFile1++, pszFile2++;
3764 if (*pszFile1 != *pszFile2)
3765 break;
3766 }
3767 else if (pszFile1 || pszFile2)
3768 {
3769 assert(0); /* this shouldn't happen. */
3770 break;
3771 }
3772
3773 /* Advance. We might now have a misaligned buffer, but that's memcmps problem... */
3774 psz1 += cch;
3775 psz2 += cch;
3776 }
3777 }
3778
3779 return psz1 == pszEnd1
3780 && psz2 == pszEnd2;
3781}
3782
3783
3784/**
3785 * Worker for kOCEntryCompileIfNeeded that compares the
3786 * preprocessed output.
3787 *
3788 * @returns 1 if matching, 0 if not matching.
3789 * @param pEntry The entry containing the names of the files to compare.
3790 * This will load the old cpp output (changing pszOldCppName and Old.cbCpp).
3791 */
3792static int kOCEntryCompareOldAndNewOutput(PKOCENTRY pEntry)
3793{
3794 /*
3795 * I may implement a more sophisticated alternative method later... maybe.
3796 */
3797 if (kOCEntryReadCppOutput(pEntry, &pEntry->Old, 1 /* nonfatal */) == -1)
3798 return 0;
3799 /*if ()
3800 return kOCEntryCompareBest(pEntry);*/
3801 return kOCEntryCompareFast(pEntry);
3802}
3803
3804
3805/**
3806 * Check if re-compilation is required.
3807 * This sets the fNeedCompile flag.
3808 *
3809 * @param pEntry The cache entry.
3810 */
3811static void kOCEntryCalcRecompile(PKOCENTRY pEntry)
3812{
3813 if (pEntry->fNeedCompiling)
3814 return;
3815
3816 /*
3817 * Check if the preprocessor output differ in any significant way?
3818 */
3819 if (!kOCSumHasEqualInChain(&pEntry->Old.SumHead, &pEntry->New.SumHead))
3820 {
3821 InfoMsg(2, "no checksum match - comparing output\n");
3822 if (!kOCEntryCompareOldAndNewOutput(pEntry))
3823 pEntry->fNeedCompiling = 1;
3824 else
3825 kOCSumAddChain(&pEntry->New.SumHead, &pEntry->Old.SumHead);
3826 }
3827}
3828
3829
3830/**
3831 * Does this cache entry need compiling or what?
3832 *
3833 * @returns 1 if it does, 0 if it doesn't.
3834 * @param pEntry The cache entry in question.
3835 */
3836static int kOCEntryNeedsCompiling(PCKOCENTRY pEntry)
3837{
3838 return pEntry->fNeedCompiling;
3839}
3840
3841
3842/**
3843 * Worker function for kOCEntryCopy.
3844 *
3845 * @param pEntry The entry we're coping to, which pszTo is relative to.
3846 * @param pszTo The destination.
3847 * @param pszFrom The source. This path will be freed.
3848 */
3849static void kOCEntryCopyFile(PCKOCENTRY pEntry, const char *pszTo, char *pszSrc)
3850{
3851 char *pszDst = MakePathFromDirAndFile(pszTo, pEntry->pszDir);
3852 char *pszBuf = xmalloc(256 * 1024);
3853 char *psz;
3854 int fdSrc;
3855 int fdDst;
3856
3857 /*
3858 * Open the files.
3859 */
3860 fdSrc = open(pszSrc, O_RDONLY | O_BINARY);
3861 if (fdSrc == -1)
3862 FatalDie("failed to open '%s': %s\n", pszSrc, strerror(errno));
3863
3864 unlink(pszDst);
3865 fdDst = open(pszDst, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
3866 if (fdDst == -1)
3867 FatalDie("failed to create '%s': %s\n", pszDst, strerror(errno));
3868
3869 /*
3870 * Copy them.
3871 */
3872 for (;;)
3873 {
3874 /* read a chunk. */
3875 long cbRead = read(fdSrc, pszBuf, 256*1024);
3876 if (cbRead < 0)
3877 {
3878 if (errno == EINTR)
3879 continue;
3880 FatalDie("read '%s' failed: %s\n", pszSrc, strerror(errno));
3881 }
3882 if (!cbRead)
3883 break; /* eof */
3884
3885 /* write the chunk. */
3886 psz = pszBuf;
3887 do
3888 {
3889 long cbWritten = write(fdDst, psz, cbRead);
3890 if (cbWritten < 0)
3891 {
3892 if (errno == EINTR)
3893 continue;
3894 FatalDie("write '%s' failed: %s\n", pszSrc, strerror(errno));
3895 }
3896 psz += cbWritten;
3897 cbRead -= cbWritten;
3898 } while (cbRead > 0);
3899 }
3900
3901 /* cleanup */
3902 if (close(fdDst) != 0)
3903 FatalDie("closing '%s' failed: %s\n", pszDst, strerror(errno));
3904 close(fdSrc);
3905 free(pszBuf);
3906 free(pszDst);
3907 free(pszSrc);
3908}
3909
3910
3911/**
3912 * Copies the object (and whatever else) from one cache entry to another.
3913 *
3914 * This is called when a matching cache entry has been found and we don't
3915 * need to recompile anything.
3916 *
3917 * @param pEntry The entry to copy to.
3918 * @param pFrom The entry to copy from.
3919 */
3920static void kOCEntryCopy(PKOCENTRY pEntry, PCKOCENTRY pFrom)
3921{
3922 kOCEntryCopyFile(pEntry, pEntry->New.pszObjName,
3923 MakePathFromDirAndFile(pFrom->New.pszObjName
3924 ? pFrom->New.pszObjName : pFrom->Old.pszObjName,
3925 pFrom->pszDir));
3926}
3927
3928
3929/**
3930 * Gets the absolute path to the cache entry.
3931 *
3932 * @returns absolute path to the cache entry.
3933 * @param pEntry The cache entry in question.
3934 */
3935static const char *kOCEntryAbsPath(PCKOCENTRY pEntry)
3936{
3937 return pEntry->pszAbsPath;
3938}
3939
3940
3941
3942
3943
3944
3945/**
3946 * Digest of one cache entry.
3947 *
3948 * This contains all the information required to find a matching
3949 * cache entry without having to open each of the files.
3950 */
3951typedef struct KOCDIGEST
3952{
3953 /** The relative path to the entry. Optional if pszAbsPath is set. */
3954 char *pszRelPath;
3955 /** The absolute path to the entry. Optional if pszRelPath is set. */
3956 char *pszAbsPath;
3957 /** The target os/arch identifier. */
3958 char *pszTarget;
3959 /** A unique number assigned to the entry when it's (re)-inserted
3960 * into the cache. This is used for simple consitency checking. */
3961 uint32_t uKey;
3962 /** The checksum of the compile argument vector. */
3963 KOCSUM SumCompArgv;
3964 /** The list of preprocessor output checksums that's . */
3965 KOCSUM SumHead;
3966} KOCDIGEST;
3967/** Pointer to a file digest. */
3968typedef KOCDIGEST *PKOCDIGEST;
3969/** Pointer to a const file digest. */
3970typedef KOCDIGEST *PCKOCDIGEST;
3971
3972
3973/**
3974 * Initializes the specified digest.
3975 *
3976 * @param pDigest The digest.
3977 */
3978static void kOCDigestInit(PKOCDIGEST pDigest)
3979{
3980 memset(pDigest, 0, sizeof(*pDigest));
3981 kOCSumInit(&pDigest->SumHead);
3982}
3983
3984
3985/**
3986 * Initializes the digest for the specified entry.
3987 *
3988 * @param pDigest The (uninitialized) digest.
3989 * @param pEntry The entry.
3990 */
3991static void kOCDigestInitFromEntry(PKOCDIGEST pDigest, PCKOCENTRY pEntry)
3992{
3993 kOCDigestInit(pDigest);
3994
3995 pDigest->uKey = pEntry->uKey;
3996 pDigest->pszTarget = xstrdup(pEntry->New.pszTarget ? pEntry->New.pszTarget : pEntry->Old.pszTarget);
3997
3998 kOCSumInit(&pDigest->SumCompArgv);
3999 if (!kOCSumIsEmpty(&pEntry->New.SumCompArgv))
4000 kOCSumAdd(&pDigest->SumCompArgv, &pEntry->New.SumCompArgv);
4001 else
4002 kOCSumAdd(&pDigest->SumCompArgv, &pEntry->Old.SumCompArgv);
4003
4004 kOCSumInit(&pDigest->SumHead);
4005 if (!kOCSumIsEmpty(&pEntry->New.SumHead))
4006 kOCSumAddChain(&pDigest->SumHead, &pEntry->New.SumHead);
4007 else
4008 kOCSumAddChain(&pDigest->SumHead, &pEntry->Old.SumHead);
4009
4010 /** @todo implement selective relative path support. */
4011 pDigest->pszRelPath = NULL;
4012 pDigest->pszAbsPath = xstrdup(kOCEntryAbsPath(pEntry));
4013}
4014
4015
4016/**
4017 * Purges a digest, freeing all resources and returning
4018 * it to the initial state.
4019 *
4020 * @param pDigest The digest.
4021 */
4022static void kOCDigestPurge(PKOCDIGEST pDigest)
4023{
4024 free(pDigest->pszRelPath);
4025 free(pDigest->pszAbsPath);
4026 free(pDigest->pszTarget);
4027 pDigest->pszTarget = pDigest->pszAbsPath = pDigest->pszRelPath = NULL;
4028 pDigest->uKey = 0;
4029 kOCSumDeleteChain(&pDigest->SumCompArgv);
4030 kOCSumDeleteChain(&pDigest->SumHead);
4031}
4032
4033
4034/**
4035 * Returns the absolute path to the entry, calculating
4036 * the path if necessary.
4037 *
4038 * @returns absolute path.
4039 * @param pDigest The digest.
4040 * @param pszDir The cache directory that it might be relative to.
4041 */
4042static const char *kOCDigestAbsPath(PCKOCDIGEST pDigest, const char *pszDir)
4043{
4044 if (!pDigest->pszAbsPath)
4045 {
4046 char *pszPath = MakePathFromDirAndFile(pDigest->pszRelPath, pszDir);
4047 ((PKOCDIGEST)pDigest)->pszAbsPath = AbsPath(pszPath);
4048 free(pszPath);
4049 }
4050 return pDigest->pszAbsPath;
4051}
4052
4053
4054/**
4055 * Checks that the digest matches the
4056 *
4057 * @returns 1 if valid, 0 if invalid in some way.
4058 *
4059 * @param pDigest The digest to validate.
4060 * @param pEntry What to validate it against.
4061 */
4062static int kOCDigestIsValid(PCKOCDIGEST pDigest, PCKOCENTRY pEntry)
4063{
4064 PCKOCSUM pSum;
4065 PCKOCSUM pSumEntry;
4066
4067 if (pDigest->uKey != pEntry->uKey)
4068 return 0;
4069
4070 if (!kOCSumIsEqual(&pDigest->SumCompArgv,
4071 kOCSumIsEmpty(&pEntry->New.SumCompArgv)
4072 ? &pEntry->Old.SumCompArgv : &pEntry->New.SumCompArgv))
4073 return 0;
4074
4075 if (strcmp(pDigest->pszTarget, pEntry->New.pszTarget ? pEntry->New.pszTarget : pEntry->Old.pszTarget))
4076 return 0;
4077
4078 /* match the checksums */
4079 pSumEntry = kOCSumIsEmpty(&pEntry->New.SumHead)
4080 ? &pEntry->Old.SumHead : &pEntry->New.SumHead;
4081 for (pSum = &pDigest->SumHead; pSum; pSum = pSum->pNext)
4082 if (!kOCSumHasEqualInChain(pSumEntry, pSum))
4083 return 0;
4084
4085 return 1;
4086}
4087
4088
4089
4090
4091
4092/**
4093 * The structure for the central cache entry.
4094 */
4095typedef struct KOBJCACHE
4096{
4097 /** The entry name. */
4098 const char *pszName;
4099 /** The dir that relative names in the digest are relative to. */
4100 char *pszDir;
4101 /** The absolute path. */
4102 char *pszAbsPath;
4103
4104 /** The cache file descriptor. */
4105 int fd;
4106 /** The stream associated with fd. */
4107 FILE *pFile;
4108 /** Whether it's currently locked or not. */
4109 unsigned fLocked;
4110 /** Whether the cache file is dirty and needs writing back. */
4111 unsigned fDirty;
4112 /** Whether this is a new cache or not. */
4113 unsigned fNewCache;
4114
4115 /** The cache file generation. */
4116 uint32_t uGeneration;
4117 /** The next valid key. (Determin at load time.) */
4118 uint32_t uNextKey;
4119
4120 /** Number of digests in paDigests. */
4121 unsigned cDigests;
4122 /** Array of digests for the KOCENTRY objects in the cache. */
4123 PKOCDIGEST paDigests;
4124
4125} KOBJCACHE;
4126/** Pointer to a cache. */
4127typedef KOBJCACHE *PKOBJCACHE;
4128/** Pointer to a const cache. */
4129typedef KOBJCACHE const *PCKOBJCACHE;
4130
4131
4132/**
4133 * Creates an empty cache.
4134 *
4135 * This doesn't touch the file system, it just create the data structure.
4136 *
4137 * @returns Pointer to a cache.
4138 * @param pszCacheFile The cache file.
4139 */
4140static PKOBJCACHE kObjCacheCreate(const char *pszCacheFile)
4141{
4142 PKOBJCACHE pCache;
4143 size_t off;
4144
4145 /*
4146 * Allocate an empty entry.
4147 */
4148 pCache = xmallocz(sizeof(*pCache));
4149 pCache->fd = -1;
4150
4151 /*
4152 * Setup the directory and cache file name.
4153 */
4154 pCache->pszAbsPath = AbsPath(pszCacheFile);
4155 pCache->pszName = FindFilenameInPath(pCache->pszAbsPath);
4156 off = pCache->pszName - pCache->pszAbsPath;
4157 if (!off)
4158 FatalDie("Failed to find abs path for '%s'!\n", pszCacheFile);
4159 pCache->pszDir = xmalloc(off);
4160 memcpy(pCache->pszDir, pCache->pszAbsPath, off - 1);
4161 pCache->pszDir[off - 1] = '\0';
4162
4163 return pCache;
4164}
4165
4166
4167/**
4168 * Destroys the cache - closing any open files, freeing up heap memory and such.
4169 *
4170 * @param pCache The cache.
4171 */
4172static void kObjCacheDestroy(PKOBJCACHE pCache)
4173{
4174 if (pCache->pFile)
4175 {
4176 errno = 0;
4177 if (fclose(pCache->pFile) != 0)
4178 FatalMsg("fclose failed: %s\n", strerror(errno));
4179 pCache->pFile = NULL;
4180 pCache->fd = -1;
4181 }
4182 free(pCache->paDigests);
4183 free(pCache->pszAbsPath);
4184 free(pCache->pszDir);
4185 free(pCache);
4186}
4187
4188
4189/**
4190 * Purges the data in the cache object.
4191 *
4192 * @param pCache The cache object.
4193 */
4194static void kObjCachePurge(PKOBJCACHE pCache)
4195{
4196 while (pCache->cDigests > 0)
4197 kOCDigestPurge(&pCache->paDigests[--pCache->cDigests]);
4198 free(pCache->paDigests);
4199 pCache->paDigests = NULL;
4200 pCache->uGeneration = 0;
4201 pCache->uNextKey = 0;
4202}
4203
4204
4205/**
4206 * (Re-)reads the file.
4207 *
4208 * @param pCache The cache to (re)-read.
4209 */
4210static void kObjCacheRead(PKOBJCACHE pCache)
4211{
4212 unsigned i;
4213 char szBuf[8192];
4214 int fBad = 0;
4215
4216 InfoMsg(4, "reading cache file...\n");
4217
4218 /*
4219 * Rewind the file & stream, and associate a temporary buffer
4220 * with the stream to speed up reading.
4221 */
4222 if (lseek(pCache->fd, 0, SEEK_SET) == -1)
4223 FatalDie("lseek(cache-fd) failed: %s\n", strerror(errno));
4224 rewind(pCache->pFile);
4225 if (setvbuf(pCache->pFile, szBuf, _IOFBF, sizeof(szBuf)) != 0)
4226 FatalDie("fdopen(cache-fd,rb) failed: %s\n", strerror(errno));
4227
4228 /*
4229 * Read magic and generation.
4230 */
4231 if ( !fgets(g_szLine, sizeof(g_szLine), pCache->pFile)
4232 || strcmp(g_szLine, "magic=kObjCache-v0.1.0\n"))
4233 {
4234 InfoMsg(2, "bad cache file (magic)\n");
4235 fBad = 1;
4236 }
4237 else if ( !fgets(g_szLine, sizeof(g_szLine), pCache->pFile)
4238 || strncmp(g_szLine, "generation=", sizeof("generation=") - 1))
4239 {
4240 InfoMsg(2, "bad cache file (generation)\n");
4241 fBad = 1;
4242 }
4243 else if ( pCache->uGeneration
4244 && (long)pCache->uGeneration == atol(&g_szLine[sizeof("generation=") - 1]))
4245 {
4246 InfoMsg(3, "drop re-read unmodified cache file\n");
4247 fBad = 0;
4248 }
4249 else
4250 {
4251 int fBadBeforeMissing;
4252
4253 /*
4254 * Read everything (anew).
4255 */
4256 kObjCachePurge(pCache);
4257 do
4258 {
4259 PKOCDIGEST pDigest;
4260 char *pszNl;
4261 char *pszVal;
4262 char *psz;
4263
4264 /* Split the line and drop the trailing newline. */
4265 pszVal = strchr(g_szLine, '=');
4266 if ((fBad = pszVal == NULL))
4267 break;
4268 *pszVal++ = '\0';
4269
4270 pszNl = strchr(pszVal, '\n');
4271 if (pszNl)
4272 *pszNl = '\0';
4273
4274 /* digest '#'? */
4275 psz = strchr(g_szLine, '#');
4276 if (psz)
4277 {
4278 char *pszNext;
4279 i = strtoul(++psz, &pszNext, 0);
4280 if ((fBad = pszNext && *pszNext))
4281 break;
4282 if ((fBad = i >= pCache->cDigests))
4283 break;
4284 pDigest = &pCache->paDigests[i];
4285 *psz = '\0';
4286 }
4287 else
4288 pDigest = NULL;
4289
4290
4291 /* string case on value name. */
4292 if (!strcmp(g_szLine, "sum-#"))
4293 {
4294 KOCSUM Sum;
4295 if ((fBad = kOCSumInitFromString(&Sum, pszVal) != 0))
4296 break;
4297 kOCSumAdd(&pDigest->SumHead, &Sum);
4298 }
4299 else if (!strcmp(g_szLine, "digest-abs-#"))
4300 {
4301 if ((fBad = pDigest->pszAbsPath != NULL))
4302 break;
4303 pDigest->pszAbsPath = xstrdup(pszVal);
4304 }
4305 else if (!strcmp(g_szLine, "digest-rel-#"))
4306 {
4307 if ((fBad = pDigest->pszRelPath != NULL))
4308 break;
4309 pDigest->pszRelPath = xstrdup(pszVal);
4310 }
4311 else if (!strcmp(g_szLine, "key-#"))
4312 {
4313 if ((fBad = pDigest->uKey != 0))
4314 break;
4315 pDigest->uKey = strtoul(pszVal, &psz, 0);
4316 if ((fBad = psz && *psz))
4317 break;
4318 if (pDigest->uKey >= pCache->uNextKey)
4319 pCache->uNextKey = pDigest->uKey + 1;
4320 }
4321 else if (!strcmp(g_szLine, "comp-argv-sum-#"))
4322 {
4323 if ((fBad = !kOCSumIsEmpty(&pDigest->SumCompArgv)))
4324 break;
4325 if ((fBad = kOCSumInitFromString(&pDigest->SumCompArgv, pszVal) != 0))
4326 break;
4327 }
4328 else if (!strcmp(g_szLine, "target-#"))
4329 {
4330 if ((fBad = pDigest->pszTarget != NULL))
4331 break;
4332 pDigest->pszTarget = xstrdup(pszVal);
4333 }
4334 else if (!strcmp(g_szLine, "digests"))
4335 {
4336 if ((fBad = pCache->paDigests != NULL))
4337 break;
4338 pCache->cDigests = strtoul(pszVal, &psz, 0);
4339 if ((fBad = psz && *psz))
4340 break;
4341 i = (pCache->cDigests + 4) & ~3;
4342 pCache->paDigests = xmalloc(i * sizeof(pCache->paDigests[0]));
4343 for (i = 0; i < pCache->cDigests; i++)
4344 kOCDigestInit(&pCache->paDigests[i]);
4345 }
4346 else if (!strcmp(g_szLine, "generation"))
4347 {
4348 if ((fBad = pCache->uGeneration != 0))
4349 break;
4350 pCache->uGeneration = strtoul(pszVal, &psz, 0);
4351 if ((fBad = psz && *psz))
4352 break;
4353 }
4354 else if (!strcmp(g_szLine, "the-end"))
4355 {
4356 fBad = strcmp(pszVal, "fine");
4357 break;
4358 }
4359 else
4360 {
4361 fBad = 1;
4362 break;
4363 }
4364 } while (fgets(g_szLine, sizeof(g_szLine), pCache->pFile));
4365
4366 /*
4367 * Did we find everything?
4368 */
4369 fBadBeforeMissing = fBad;
4370 if ( !fBad
4371 && !pCache->uGeneration)
4372 fBad = 1;
4373 if (!fBad)
4374 for (i = 0; i < pCache->cDigests; i++)
4375 {
4376 if ((fBad = kOCSumIsEmpty(&pCache->paDigests[i].SumCompArgv)))
4377 break;
4378 if ((fBad = kOCSumIsEmpty(&pCache->paDigests[i].SumHead)))
4379 break;
4380 if ((fBad = pCache->paDigests[i].uKey == 0))
4381 break;
4382 if ((fBad = pCache->paDigests[i].pszAbsPath == NULL
4383 && pCache->paDigests[i].pszRelPath == NULL))
4384 break;
4385 if ((fBad = pCache->paDigests[i].pszTarget == NULL))
4386 break;
4387 InfoMsg(4, "digest-%u: %s\n", i, pCache->paDigests[i].pszAbsPath
4388 ? pCache->paDigests[i].pszAbsPath : pCache->paDigests[i].pszRelPath);
4389 }
4390 if (fBad)
4391 InfoMsg(2, "bad cache file (%s)\n", fBadBeforeMissing ? g_szLine : "missing stuff");
4392 else if (ferror(pCache->pFile))
4393 {
4394 InfoMsg(2, "cache file read error\n");
4395 fBad = 1;
4396 }
4397 }
4398 if (fBad)
4399 {
4400 kObjCachePurge(pCache);
4401 pCache->fNewCache = 1;
4402 }
4403
4404 /*
4405 * Disassociate the buffer from the stream changing
4406 * it to non-buffered mode.
4407 */
4408 if (setvbuf(pCache->pFile, NULL, _IONBF, 0) != 0)
4409 FatalDie("setvbuf(,0,,0) failed: %s\n", strerror(errno));
4410}
4411
4412
4413/**
4414 * Re-writes the cache file.
4415 *
4416 * @param pCache The cache to commit and unlock.
4417 */
4418static void kObjCacheWrite(PKOBJCACHE pCache)
4419{
4420 unsigned i;
4421 off_t cb;
4422 char szBuf[8192];
4423 assert(pCache->fLocked);
4424 assert(pCache->fDirty);
4425
4426 /*
4427 * Rewind the file & stream, and associate a temporary buffer
4428 * with the stream to speed up the writing.
4429 */
4430 if (lseek(pCache->fd, 0, SEEK_SET) == -1)
4431 FatalDie("lseek(cache-fd) failed: %s\n", strerror(errno));
4432 rewind(pCache->pFile);
4433 if (setvbuf(pCache->pFile, szBuf, _IOFBF, sizeof(szBuf)) != 0)
4434 FatalDie("setvbuf failed: %s\n", strerror(errno));
4435
4436 /*
4437 * Write the header.
4438 */
4439 pCache->uGeneration++;
4440 fprintf(pCache->pFile,
4441 "magic=kObjCache-v0.1.0\n"
4442 "generation=%d\n"
4443 "digests=%d\n",
4444 pCache->uGeneration,
4445 pCache->cDigests);
4446
4447 /*
4448 * Write the digests.
4449 */
4450 for (i = 0; i < pCache->cDigests; i++)
4451 {
4452 PCKOCDIGEST pDigest = &pCache->paDigests[i];
4453 PKOCSUM pSum;
4454
4455 if (pDigest->pszAbsPath)
4456 fprintf(pCache->pFile, "digest-abs-#%u=%s\n", i, pDigest->pszAbsPath);
4457 if (pDigest->pszRelPath)
4458 fprintf(pCache->pFile, "digest-rel-#%u=%s\n", i, pDigest->pszRelPath);
4459 fprintf(pCache->pFile, "key-#%u=%u\n", i, pDigest->uKey);
4460 fprintf(pCache->pFile, "target-#%u=%s\n", i, pDigest->pszTarget);
4461 fprintf(pCache->pFile, "comp-argv-sum-#%u=", i);
4462 kOCSumFPrintf(&pDigest->SumCompArgv, pCache->pFile);
4463 for (pSum = &pDigest->SumHead; pSum; pSum = pSum->pNext)
4464 {
4465 fprintf(pCache->pFile, "sum-#%u=", i);
4466 kOCSumFPrintf(pSum, pCache->pFile);
4467 }
4468 }
4469
4470 /*
4471 * Close the stream and unlock fhe file.
4472 * (Closing the stream shouldn't close the file handle IIRC...)
4473 */
4474 fprintf(pCache->pFile, "the-end=fine\n");
4475 errno = 0;
4476 if ( fflush(pCache->pFile) < 0
4477 || ferror(pCache->pFile))
4478 {
4479 int iErr = errno;
4480 fclose(pCache->pFile);
4481 UnlinkFileInDir(pCache->pszName, pCache->pszDir);
4482 FatalDie("Stream error occured while writing '%s' in '%s': %s\n",
4483 pCache->pszName, pCache->pszDir, strerror(iErr));
4484 }
4485 if (setvbuf(pCache->pFile, NULL, _IONBF, 0) != 0)
4486 FatalDie("setvbuf(,0,,0) failed: %s\n", strerror(errno));
4487
4488 cb = lseek(pCache->fd, 0, SEEK_CUR);
4489 if (cb == -1)
4490 FatalDie("lseek(cache-file,0,CUR) failed: %s\n", strerror(errno));
4491#if defined(__WIN__)
4492 if (_chsize(pCache->fd, cb) == -1)
4493#else
4494 if (ftruncate(pCache->fd, cb) == -1)
4495#endif
4496 FatalDie("file truncation failed: %s\n", strerror(errno));
4497 InfoMsg(4, "wrote '%s' in '%s', %d bytes\n", pCache->pszName, pCache->pszDir, cb);
4498}
4499
4500
4501/**
4502 * Cleans out all invalid digests.s
4503 *
4504 * This is done periodically from the unlock routine to make
4505 * sure we don't accidentally accumulate stale digests.
4506 *
4507 * @param pCache The cache to chek.
4508 */
4509static void kObjCacheClean(PKOBJCACHE pCache)
4510{
4511 unsigned i = pCache->cDigests;
4512 while (i-- > 0)
4513 {
4514 /*
4515 * Try open it and purge it if it's bad.
4516 * (We don't kill the entry file because that's kmk clean's job.)
4517 */
4518 PCKOCDIGEST pDigest = &pCache->paDigests[i];
4519 PKOCENTRY pEntry = kOCEntryCreate(kOCDigestAbsPath(pDigest, pCache->pszDir));
4520 kOCEntryRead(pEntry);
4521 if ( !kOCEntryCheck(pEntry)
4522 || !kOCDigestIsValid(pDigest, pEntry))
4523 {
4524 unsigned cLeft;
4525 kOCDigestPurge(pDigest);
4526
4527 pCache->cDigests--;
4528 cLeft = pCache->cDigests - i;
4529 if (cLeft)
4530 memmove(pDigest, pDigest + 1, cLeft * sizeof(*pDigest));
4531
4532 pCache->fDirty = 1;
4533 }
4534 kOCEntryDestroy(pEntry);
4535 }
4536}
4537
4538
4539/**
4540 * Locks the cache for exclusive access.
4541 *
4542 * This will open the file if necessary and lock the entire file
4543 * using the best suitable platform API (tricky).
4544 *
4545 * @param pCache The cache to lock.
4546 */
4547static void kObjCacheLock(PKOBJCACHE pCache)
4548{
4549 struct stat st;
4550#if defined(__WIN__)
4551 OVERLAPPED OverLapped;
4552#endif
4553
4554 assert(!pCache->fLocked);
4555
4556 /*
4557 * Open it?
4558 */
4559 if (pCache->fd < 0)
4560 {
4561 pCache->fd = OpenFileInDir(pCache->pszName, pCache->pszDir, O_CREAT | O_RDWR | O_BINARY, 0666);
4562 if (pCache->fd == -1)
4563 {
4564 MakePath(pCache->pszDir);
4565 pCache->fd = OpenFileInDir(pCache->pszName, pCache->pszDir, O_CREAT | O_RDWR | O_BINARY, 0666);
4566 if (pCache->fd == -1)
4567 FatalDie("Failed to create '%s' in '%s': %s\n", pCache->pszName, pCache->pszDir, strerror(errno));
4568 }
4569
4570 pCache->pFile = fdopen(pCache->fd, "r+b");
4571 if (!pCache->pFile)
4572 FatalDie("fdopen failed: %s\n", strerror(errno));
4573 if (setvbuf(pCache->pFile, NULL, _IONBF, 0) != 0)
4574 FatalDie("setvbuf(,0,,0) failed: %s\n", strerror(errno));
4575 }
4576
4577 /*
4578 * Lock it.
4579 */
4580#if defined(__WIN__)
4581 memset(&OverLapped, 0, sizeof(OverLapped));
4582 if (!LockFileEx((HANDLE)_get_osfhandle(pCache->fd), LOCKFILE_EXCLUSIVE_LOCK, 0, ~0, 0, &OverLapped))
4583 FatalDie("Failed to lock the cache file: Windows Error %d\n", GetLastError());
4584#elif defined(__sun__)
4585 {
4586 struct flock fl;
4587 fl.l_whence = 0;
4588 fl.l_start = 0;
4589 fl.l_len = 0;
4590 fl.l_type = F_WRLCK;
4591 if (fcntl(pCache->fd, F_SETLKW, &fl) != 0)
4592 FatalDie("Failed to lock the cache file: %s\n", strerror(errno));
4593 }
4594#else
4595 if (flock(pCache->fd, LOCK_EX) != 0)
4596 FatalDie("Failed to lock the cache file: %s\n", strerror(errno));
4597#endif
4598 pCache->fLocked = 1;
4599
4600 /*
4601 * Check for new cache and read it it's an existing cache.
4602 *
4603 * There is no point in initializing a new cache until we've finished
4604 * compiling and has something to put into it, so we'll leave it as a
4605 * 0 byte file.
4606 */
4607 if (fstat(pCache->fd, &st) == -1)
4608 FatalDie("fstat(cache-fd) failed: %s\n", strerror(errno));
4609 if (st.st_size)
4610 kObjCacheRead(pCache);
4611 else
4612 {
4613 pCache->fNewCache = 1;
4614 InfoMsg(2, "the cache file is empty\n");
4615 }
4616}
4617
4618
4619/**
4620 * Unlocks the cache (without writing anything back).
4621 *
4622 * @param pCache The cache to unlock.
4623 */
4624static void kObjCacheUnlock(PKOBJCACHE pCache)
4625{
4626#if defined(__WIN__)
4627 OVERLAPPED OverLapped;
4628#endif
4629 assert(pCache->fLocked);
4630
4631 /*
4632 * Write it back if it's dirty.
4633 */
4634 if (pCache->fDirty)
4635 {
4636 if ( pCache->cDigests >= 16
4637 && (pCache->uGeneration % 19) == 19)
4638 kObjCacheClean(pCache);
4639 kObjCacheWrite(pCache);
4640 pCache->fDirty = 0;
4641 }
4642
4643 /*
4644 * Lock it.
4645 */
4646#if defined(__WIN__)
4647 memset(&OverLapped, 0, sizeof(OverLapped));
4648 if (!UnlockFileEx((HANDLE)_get_osfhandle(pCache->fd), 0, ~0U, 0, &OverLapped))
4649 FatalDie("Failed to unlock the cache file: Windows Error %d\n", GetLastError());
4650#elif defined(__sun__)
4651 {
4652 struct flock fl;
4653 fl.l_whence = 0;
4654 fl.l_start = 0;
4655 fl.l_len = 0;
4656 fl.l_type = F_UNLCK;
4657 if (fcntl(pCache->fd, F_SETLKW, &fl) != 0)
4658 FatalDie("Failed to lock the cache file: %s\n", strerror(errno));
4659 }
4660#else
4661 if (flock(pCache->fd, LOCK_UN) != 0)
4662 FatalDie("Failed to unlock the cache file: %s\n", strerror(errno));
4663#endif
4664 pCache->fLocked = 0;
4665}
4666
4667
4668/**
4669 * Removes the entry from the cache.
4670 *
4671 * The entry doesn't need to be in the cache.
4672 * The cache entry (file) itself is not touched.
4673 *
4674 * @param pCache The cache.
4675 * @param pEntry The entry.
4676 */
4677static void kObjCacheRemoveEntry(PKOBJCACHE pCache, PCKOCENTRY pEntry)
4678{
4679 unsigned i = pCache->cDigests;
4680 while (i-- > 0)
4681 {
4682 PKOCDIGEST pDigest = &pCache->paDigests[i];
4683 if (ArePathsIdentical(kOCDigestAbsPath(pDigest, pCache->pszDir),
4684 kOCEntryAbsPath(pEntry)))
4685 {
4686 unsigned cLeft;
4687 kOCDigestPurge(pDigest);
4688
4689 pCache->cDigests--;
4690 cLeft = pCache->cDigests - i;
4691 if (cLeft)
4692 memmove(pDigest, pDigest + 1, cLeft * sizeof(*pDigest));
4693
4694 pCache->fDirty = 1;
4695 InfoMsg(3, "removing entry '%s'; %d left.\n", kOCEntryAbsPath(pEntry), pCache->cDigests);
4696 }
4697 }
4698}
4699
4700
4701/**
4702 * Inserts the entry into the cache.
4703 *
4704 * The cache entry (file) itself is not touched by this operation,
4705 * the pEntry object otoh is.
4706 *
4707 * @param pCache The cache.
4708 * @param pEntry The entry.
4709 */
4710static void kObjCacheInsertEntry(PKOBJCACHE pCache, PKOCENTRY pEntry)
4711{
4712 unsigned i;
4713
4714 /*
4715 * Find a new key.
4716 */
4717 pEntry->uKey = pCache->uNextKey++;
4718 if (!pEntry->uKey)
4719 pEntry->uKey = pCache->uNextKey++;
4720 i = pCache->cDigests;
4721 while (i-- > 0)
4722 if (pCache->paDigests[i].uKey == pEntry->uKey)
4723 {
4724 pEntry->uKey = pCache->uNextKey++;
4725 if (!pEntry->uKey)
4726 pEntry->uKey = pCache->uNextKey++;
4727 i = pCache->cDigests;
4728 }
4729
4730 /*
4731 * Reallocate the digest array?
4732 */
4733 if ( !(pCache->cDigests & 3)
4734 && (pCache->cDigests || !pCache->paDigests))
4735 pCache->paDigests = xrealloc(pCache->paDigests, sizeof(pCache->paDigests[0]) * (pCache->cDigests + 4));
4736
4737 /*
4738 * Create a new digest.
4739 */
4740 kOCDigestInitFromEntry(&pCache->paDigests[pCache->cDigests], pEntry);
4741 pCache->cDigests++;
4742 InfoMsg(4, "Inserted digest #%u: %s\n", pCache->cDigests - 1, kOCEntryAbsPath(pEntry));
4743
4744 pCache->fDirty = 1;
4745}
4746
4747
4748/**
4749 * Find a matching cache entry.
4750 */
4751static PKOCENTRY kObjCacheFindMatchingEntry(PKOBJCACHE pCache, PCKOCENTRY pEntry)
4752{
4753 unsigned i = pCache->cDigests;
4754
4755 assert(pEntry->fNeedCompiling);
4756 assert(!kOCSumIsEmpty(&pEntry->New.SumCompArgv));
4757 assert(!kOCSumIsEmpty(&pEntry->New.SumHead));
4758
4759 while (i-- > 0)
4760 {
4761 /*
4762 * Matching?
4763 */
4764 PCKOCDIGEST pDigest = &pCache->paDigests[i];
4765 if ( kOCSumIsEqual(&pDigest->SumCompArgv, &pEntry->New.SumCompArgv)
4766 && kOCSumHasEqualInChain(&pDigest->SumHead, &pEntry->New.SumHead))
4767 {
4768 /*
4769 * Try open it.
4770 */
4771 unsigned cLeft;
4772 PKOCENTRY pRetEntry = kOCEntryCreate(kOCDigestAbsPath(pDigest, pCache->pszDir));
4773 kOCEntryRead(pRetEntry);
4774 if ( kOCEntryCheck(pRetEntry)
4775 && kOCDigestIsValid(pDigest, pRetEntry))
4776 return pRetEntry;
4777 kOCEntryDestroy(pRetEntry);
4778
4779 /* bad entry, purge it. */
4780 InfoMsg(3, "removing bad digest '%s'\n", kOCDigestAbsPath(pDigest, pCache->pszDir));
4781 kOCDigestPurge(pDigest);
4782
4783 pCache->cDigests--;
4784 cLeft = pCache->cDigests - i;
4785 if (cLeft)
4786 memmove(pDigest, pDigest + 1, cLeft * sizeof(*pDigest));
4787
4788 pCache->fDirty = 1;
4789 }
4790 }
4791
4792 return NULL;
4793}
4794
4795
4796/**
4797 * Is this a new cache?
4798 *
4799 * @returns 1 if new, 0 if not new.
4800 * @param pEntry The entry.
4801 */
4802static int kObjCacheIsNew(PKOBJCACHE pCache)
4803{
4804 return pCache->fNewCache;
4805}
4806
4807
4808/**
4809 * Prints a syntax error and returns the appropriate exit code
4810 *
4811 * @returns approriate exit code.
4812 * @param pszFormat The syntax error message.
4813 * @param ... Message args.
4814 */
4815static int SyntaxError(const char *pszFormat, ...)
4816{
4817 va_list va;
4818 fprintf(stderr, "kObjCache: syntax error: ");
4819 va_start(va, pszFormat);
4820 vfprintf(stderr, pszFormat, va);
4821 va_end(va);
4822 return 1;
4823}
4824
4825
4826/**
4827 * Prints the usage.
4828 * @returns 0.
4829 */
4830static int usage(FILE *pOut)
4831{
4832 fprintf(pOut,
4833 "syntax: kObjCache [--kObjCache-options] [-v|--verbose]\n"
4834 " < [-c|--cache-file <cache-file>]\n"
4835 " | [-n|--name <name-in-cache>] [[-d|--cache-dir <cache-dir>]] >\n"
4836 " <-f|--file <local-cache-file>>\n"
4837 " <-t|--target <target-name>>\n"
4838 " [-r|--redir-stdout] [-p|--passthru] [--named-pipe-compile <pipename>]\n"
4839 " --kObjCache-cpp <filename> <preprocessor + args>\n"
4840 " --kObjCache-cc <object> <compiler + args>\n"
4841 " [--kObjCache-both [args]]\n"
4842 );
4843 fprintf(pOut,
4844 " [--kObjCache-cpp|--kObjCache-cc [more args]]\n"
4845 " kObjCache <-V|--version>\n"
4846 " kObjCache [-?|/?|-h|/h|--help|/help]\n"
4847 "\n"
4848 "The env.var. KOBJCACHE_DIR sets the default cache diretory (-d).\n"
4849 "The env.var. KOBJCACHE_OPTS allow you to specifie additional options\n"
4850 "without having to mess with the makefiles. These are appended with "
4851 "a --kObjCache-options between them and the command args.\n"
4852 "\n");
4853 return 0;
4854}
4855
4856
4857int main(int argc, char **argv)
4858{
4859 PKOBJCACHE pCache;
4860 PKOCENTRY pEntry;
4861
4862 const char *pszCacheDir = getenv("KOBJCACHE_DIR");
4863 const char *pszCacheName = NULL;
4864 const char *pszCacheFile = NULL;
4865 const char *pszEntryFile = NULL;
4866
4867 const char **papszArgvPreComp = NULL;
4868 unsigned cArgvPreComp = 0;
4869 const char *pszPreCompName = NULL;
4870 int fRedirPreCompStdOut = 0;
4871
4872 const char **papszArgvCompile = NULL;
4873 unsigned cArgvCompile = 0;
4874 const char *pszObjName = NULL;
4875 int fRedirCompileStdIn = 0;
4876 const char *pszNmPipeCompile = NULL;
4877
4878 const char *pszMakeDepFilename = NULL;
4879 int fMakeDepFixCase = 0;
4880 int fMakeDepGenStubs = 0;
4881 int fMakeDepQuiet = 0;
4882 int fOptimizePreprocessorOutput = 0;
4883
4884 const char *pszTarget = NULL;
4885
4886 enum { kOC_Options, kOC_CppArgv, kOC_CcArgv, kOC_BothArgv } enmMode = kOC_Options;
4887
4888 size_t cch;
4889 char *psz;
4890 int i;
4891
4892 SetErrorPrefix("kObjCache");
4893
4894 /*
4895 * Arguments passed in the environmnet?
4896 */
4897 psz = getenv("KOBJCACHE_OPTS");
4898 if (psz)
4899 AppendArgs(&argc, &argv, psz, "--kObjCache-options");
4900
4901 /*
4902 * Parse the arguments.
4903 */
4904 if (argc <= 1)
4905 return usage(stderr);
4906 for (i = 1; i < argc; i++)
4907 {
4908 if (!strcmp(argv[i], "--kObjCache-cpp"))
4909 {
4910 enmMode = kOC_CppArgv;
4911 if (!pszPreCompName)
4912 {
4913 if (++i >= argc)
4914 return SyntaxError("--kObjCache-cpp requires an object filename!\n");
4915 pszPreCompName = argv[i];
4916 }
4917 }
4918 else if (!strcmp(argv[i], "--kObjCache-cc"))
4919 {
4920 enmMode = kOC_CcArgv;
4921 if (!pszObjName)
4922 {
4923 if (++i >= argc)
4924 return SyntaxError("--kObjCache-cc requires an preprocessor output filename!\n");
4925 pszObjName = argv[i];
4926 }
4927 }
4928 else if (!strcmp(argv[i], "--kObjCache-both"))
4929 enmMode = kOC_BothArgv;
4930 else if (!strcmp(argv[i], "--kObjCache-options"))
4931 enmMode = kOC_Options;
4932 else if (!strcmp(argv[i], "--help"))
4933 return usage(stderr);
4934 else if (enmMode != kOC_Options)
4935 {
4936 if (enmMode == kOC_CppArgv || enmMode == kOC_BothArgv)
4937 {
4938 if (!(cArgvPreComp % 16))
4939 papszArgvPreComp = xrealloc((void *)papszArgvPreComp, (cArgvPreComp + 17) * sizeof(papszArgvPreComp[0]));
4940 papszArgvPreComp[cArgvPreComp++] = argv[i];
4941 papszArgvPreComp[cArgvPreComp] = NULL;
4942 }
4943 if (enmMode == kOC_CcArgv || enmMode == kOC_BothArgv)
4944 {
4945 if (!(cArgvCompile % 16))
4946 papszArgvCompile = xrealloc((void *)papszArgvCompile, (cArgvCompile + 17) * sizeof(papszArgvCompile[0]));
4947 papszArgvCompile[cArgvCompile++] = argv[i];
4948 papszArgvCompile[cArgvCompile] = NULL;
4949 }
4950 }
4951 else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--entry-file"))
4952 {
4953 if (i + 1 >= argc)
4954 return SyntaxError("%s requires a cache entry filename!\n", argv[i]);
4955 pszEntryFile = argv[++i];
4956 }
4957 else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--cache-file"))
4958 {
4959 if (i + 1 >= argc)
4960 return SyntaxError("%s requires a cache filename!\n", argv[i]);
4961 pszCacheFile = argv[++i];
4962 }
4963 else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--name"))
4964 {
4965 if (i + 1 >= argc)
4966 return SyntaxError("%s requires a cache name!\n", argv[i]);
4967 pszCacheName = argv[++i];
4968 }
4969 else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--cache-dir"))
4970 {
4971 if (i + 1 >= argc)
4972 return SyntaxError("%s requires a cache directory!\n", argv[i]);
4973 pszCacheDir = argv[++i];
4974 }
4975 else if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--target"))
4976 {
4977 if (i + 1 >= argc)
4978 return SyntaxError("%s requires a target platform/arch name!\n", argv[i]);
4979 pszTarget = argv[++i];
4980 }
4981 else if (!strcmp(argv[i], "--named-pipe-compile"))
4982 {
4983 if (i + 1 >= argc)
4984 return SyntaxError("%s requires a pipe name!\n", argv[i]);
4985 pszNmPipeCompile = argv[++i];
4986 fRedirCompileStdIn = 0;
4987 }
4988 else if (!strcmp(argv[i], "-m") || !strcmp(argv[i], "--make-dep-file"))
4989 {
4990 if (i + 1 >= argc)
4991 return SyntaxError("%s requires a filename!\n", argv[i]);
4992 pszMakeDepFilename = argv[++i];
4993 }
4994 else if (!strcmp(argv[i], "--make-dep-fix-case"))
4995 fMakeDepFixCase = 1;
4996 else if (!strcmp(argv[i], "--make-dep-gen-stubs"))
4997 fMakeDepGenStubs = 1;
4998 else if (!strcmp(argv[i], "--make-dep-quiet"))
4999 fMakeDepQuiet = 1;
5000 else if (!strcmp(argv[i], "-O1") || !strcmp(argv[i], "--optimize-1"))
5001 fOptimizePreprocessorOutput = 1;
5002 else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--passthru"))
5003 fRedirPreCompStdOut = fRedirCompileStdIn = 1;
5004 else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--redir-stdout"))
5005 fRedirPreCompStdOut = 1;
5006 else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))
5007 g_cVerbosityLevel++;
5008 else if (!strcmp(argv[i], "-q") || !strcmp(argv[i], "--quiet"))
5009 g_cVerbosityLevel = 0;
5010 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-?")
5011 || !strcmp(argv[i], "/h") || !strcmp(argv[i], "/?") || !strcmp(argv[i], "/help"))
5012 {
5013 usage(stdout);
5014 return 0;
5015 }
5016 else if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version"))
5017 {
5018 printf("kObjCache - kBuild version %d.%d.%d ($Revision: 2617 $)\n"
5019 "Copyright (c) 2007-2012 knut st. osmundsen\n",
5020 KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH);
5021 return 0;
5022 }
5023 else
5024 return SyntaxError("Doesn't grok '%s'!\n", argv[i]);
5025 }
5026 if (!pszEntryFile)
5027 return SyntaxError("No cache entry filename (-f)!\n");
5028 if (!pszTarget)
5029 return SyntaxError("No target name (-t)!\n");
5030 if (!cArgvCompile)
5031 return SyntaxError("No compiler arguments (--kObjCache-cc)!\n");
5032 if (!cArgvPreComp)
5033 return SyntaxError("No preprocessor arguments (--kObjCache-cc)!\n");
5034
5035 /*
5036 * Calc the cache file name.
5037 * It's a bit messy since the extension has to be replaced.
5038 */
5039 if (!pszCacheFile)
5040 {
5041 if (!pszCacheDir)
5042 return SyntaxError("No cache dir (-d / KOBJCACHE_DIR) and no cache filename!\n");
5043 if (!pszCacheName)
5044 {
5045 psz = (char *)FindFilenameInPath(pszEntryFile);
5046 if (!*psz)
5047 return SyntaxError("The cache file (-f) specifies a directory / nothing!\n");
5048 cch = psz - pszEntryFile;
5049 pszCacheName = memcpy(xmalloc(cch + 5), psz, cch + 1);
5050 psz = strrchr(pszCacheName, '.');
5051 if (!psz || psz <= pszCacheName)
5052 psz = (char *)pszCacheName + cch;
5053 memcpy(psz, ".koc", sizeof(".koc"));
5054 }
5055 pszCacheFile = MakePathFromDirAndFile(pszCacheName, pszCacheDir);
5056 }
5057
5058 /*
5059 * Create and initialize the two objects we'll be working on.
5060 *
5061 * We're supposed to be the only ones actually writing to the local file,
5062 * so it's perfectly fine to read it here before we lock it. This simplifies
5063 * the detection of object name and compiler argument changes.
5064 */
5065 SetErrorPrefix("kObjCache - %s", FindFilenameInPath(pszCacheFile));
5066 pCache = kObjCacheCreate(pszCacheFile);
5067
5068 pEntry = kOCEntryCreate(pszEntryFile);
5069 kOCEntryRead(pEntry);
5070 kOCEntrySetCppName(pEntry, pszPreCompName);
5071 kOCEntrySetCompileObjName(pEntry, pszObjName);
5072 kOCEntrySetCompileArgv(pEntry, papszArgvCompile, cArgvCompile);
5073 kOCEntrySetTarget(pEntry, pszTarget);
5074 kOCEntrySetPipedMode(pEntry, fRedirPreCompStdOut, fRedirCompileStdIn, pszNmPipeCompile);
5075 kOCEntrySetDepFilename(pEntry, pszMakeDepFilename, fMakeDepFixCase, fMakeDepQuiet, fMakeDepGenStubs);
5076 kOCEntrySetOptimizations(pEntry, fOptimizePreprocessorOutput);
5077
5078 /*
5079 * Open (& lock) the two files and do validity checks and such.
5080 */
5081 kObjCacheLock(pCache);
5082 if ( kObjCacheIsNew(pCache)
5083 && kOCEntryNeedsCompiling(pEntry))
5084 {
5085 /*
5086 * Both files are missing/invalid.
5087 * Optimize this path as it is frequently used when making a clean build.
5088 */
5089 kObjCacheUnlock(pCache);
5090 InfoMsg(1, "doing full compile\n");
5091 kOCEntryPreProcessAndCompile(pEntry, papszArgvPreComp, cArgvPreComp);
5092 kObjCacheLock(pCache);
5093 }
5094 else
5095 {
5096 /*
5097 * Do the preprocess (don't need to lock the cache file for this).
5098 */
5099 kObjCacheUnlock(pCache);
5100 kOCEntryPreProcess(pEntry, papszArgvPreComp, cArgvPreComp);
5101
5102 /*
5103 * Check if we need to recompile. If we do, try see if the is a cache entry first.
5104 */
5105 kOCEntryCalcRecompile(pEntry);
5106 if (kOCEntryNeedsCompiling(pEntry))
5107 {
5108 PKOCENTRY pUseEntry;
5109 kObjCacheLock(pCache);
5110 kObjCacheRemoveEntry(pCache, pEntry);
5111 pUseEntry = kObjCacheFindMatchingEntry(pCache, pEntry);
5112 if (pUseEntry)
5113 {
5114 InfoMsg(1, "using cache entry '%s'\n", kOCEntryAbsPath(pUseEntry));
5115 kOCEntryCopy(pEntry, pUseEntry);
5116 kOCEntryDestroy(pUseEntry);
5117 }
5118 else
5119 {
5120 kObjCacheUnlock(pCache);
5121 InfoMsg(1, "recompiling\n");
5122 kOCEntryCompileIt(pEntry);
5123 kObjCacheLock(pCache);
5124 }
5125 }
5126 else
5127 {
5128 InfoMsg(1, "no need to recompile\n");
5129 kObjCacheLock(pCache);
5130 }
5131 }
5132
5133 /*
5134 * Update the cache files.
5135 */
5136 kObjCacheRemoveEntry(pCache, pEntry);
5137 kObjCacheInsertEntry(pCache, pEntry);
5138 kOCEntryWrite(pEntry);
5139 kObjCacheUnlock(pCache);
5140 kObjCacheDestroy(pCache);
5141 if (fOptimizePreprocessorOutput)
5142 {
5143 InfoMsg(1, "g_cbMemMoved=%#x (%d)\n", g_cbMemMoved, g_cbMemMoved);
5144 InfoMsg(1, "g_cMemMoves=%#x (%d)\n", g_cMemMoves, g_cMemMoves);
5145 }
5146
5147 return 0;
5148}
5149
5150
5151/** @page kObjCache Benchmarks.
5152 *
5153 * (2007-06-10)
5154 *
5155 * Mac OS X debug -j 3 cached clobber build (rm -Rf out ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 USE_KOBJCACHE=1):
5156 * real 11m28.811s
5157 * user 13m59.291s
5158 * sys 3m24.590s
5159 *
5160 * Mac OS X debug -j 3 cached depend build [cdefs.h] (touch include/iprt/cdefs.h ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 USE_KOBJCACHE=1):
5161 * real 1m26.895s
5162 * user 1m26.971s
5163 * sys 0m32.532s
5164 *
5165 * Mac OS X debug -j 3 cached depend build [err.h] (touch include/iprt/err.h ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 USE_KOBJCACHE=1):
5166 * real 1m18.049s
5167 * user 1m20.462s
5168 * sys 0m27.887s
5169 *
5170 * Mac OS X release -j 3 cached clobber build (rm -Rf out/darwin.x86/release ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 USE_KOBJCACHE=1 BUILD_TYPE=release):
5171 * real 13m27.751s
5172 * user 18m12.654s
5173 * sys 3m25.170s
5174 *
5175 * Mac OS X profile -j 3 cached clobber build (rm -Rf out/darwin.x86/profile ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 USE_KOBJCACHE=1 BUILD_TYPE=profile):
5176 * real 9m9.720s
5177 * user 8m53.005s
5178 * sys 2m13.110s
5179 *
5180 * Mac OS X debug -j 3 clobber build (rm -Rf out/darwin.x86/debug ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 BUILD_TYPE=debug):
5181 * real 10m18.129s
5182 * user 12m52.687s
5183 * sys 2m51.277s
5184 *
5185 * Mac OS X debug -j 3 debug build [cdefs.h] (touch include/iprt/cdefs.h ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 BUILD_TYPE=debug):
5186 * real 4m46.147s
5187 * user 5m27.087s
5188 * sys 1m11.775s
5189 *
5190 * Mac OS X debug -j 3 debug build [err.h] (touch include/iprt/cdefs.h ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 BUILD_TYPE=debug):
5191 * real 4m17.572s
5192 * user 5m7.450s
5193 * sys 1m3.450s
5194 *
5195 * Mac OS X release -j 3 clobber build (rm -Rf out/darwin.x86/release ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 BUILD_TYPE=release):
5196 * real 12m14.742s
5197 * user 17m11.794s
5198 * sys 2m51.454s
5199 *
5200 * Mac OS X profile -j 3 clobber build (rm -Rf out/darwin.x86/profile ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 BUILD_TYPE=profile):
5201 * real 12m33.821s
5202 * user 17m35.086s
5203 * sys 2m53.312s
5204 *
5205 * Note. The profile build can pick object files from the release build.
5206 * (all with KOBJCACHE_OPTS=-v; which means a bit more output and perhaps a second or two slower.)
5207 */
5208
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette