VirtualBox

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

Last change on this file since 3239 was 3239, checked in by bird, 6 years ago

kObjCache: GCC warnings.

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

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