VirtualBox

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

Last change on this file since 3477 was 3315, checked in by bird, 5 years ago

lib/kDep+users: Escape spaces and such in dependency files (experimental).

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

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