VirtualBox

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

Last change on this file since 3110 was 3110, checked in by bird, 7 years ago

More GNU hurd adjustments.

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