VirtualBox

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

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

fixed uninitialized variable.

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