VirtualBox

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

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

kObjCache: More VC2010 related fixes.

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