VirtualBox

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

Last change on this file since 2997 was 2955, checked in by bird, 8 years ago

kDepObj: Added -e<ignored .ext> option for avoid circular dependencies when compiling the precompiled header file (pch).

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