VirtualBox

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

Last change on this file since 2413 was 2413, checked in by bird, 14 years ago

copyright year update.

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

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