VirtualBox

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

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

kDep*: no globals; dir-nt-bird.c: only main thread

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

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