VirtualBox

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

Last change on this file since 2618 was 2618, checked in by bird, 13 years ago

kObjCache: Redid the buffering in the optimizer.

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

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