VirtualBox

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

Last change on this file since 3101 was 3065, checked in by bird, 8 years ago

misc gcc warning fixes

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

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