source: trunk/src/kObjCache/kObjCache.c

Last change on this file 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
RevLine 
[1001]1/* $Id: kObjCache.c 3315 2020-03-31 01:12:19Z bird $ */
2/** @file
3 * kObjCache - Object Cache.
[1184]4 */
5
6/*
[2612]7 * Copyright (c) 2007-2012 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
[1001]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
[2019]13 * the Free Software Foundation; either version 3 of the License, or
[1001]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
[2019]22 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
[1001]23 *
24 */
25
26/*******************************************************************************
27* Header Files *
28*******************************************************************************/
[1042]29#if 0
30# define ELECTRIC_HEAP
31# include "../kmk/electric.h"
[2627]32# include "../kmk/electric.c"
[1042]33#endif
[1001]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>
[1002]42#include <limits.h>
[1017]43#include <ctype.h>
[1002]44#ifndef PATH_MAX
[3110]45# ifdef _MAX_PATH
46# define PATH_MAX _MAX_PATH /* windows */
47# else
48# define PATH_MAX 4096 /* gnu hurd */
49# endif
[1038]50#endif
[1001]51#if defined(__OS2__) || defined(__WIN__)
52# include <process.h>
53# include <io.h>
54# ifdef __OS2__
55# include <unistd.h>
[1093]56# include <sys/wait.h>
[2456]57# include <sys/time.h>
[1038]58# endif
[1040]59# if defined(_MSC_VER)
[1055]60# include <direct.h>
[1040]61 typedef intptr_t pid_t;
62# endif
[1093]63# ifndef _P_WAIT
64# define _P_WAIT P_WAIT
[1184]65# endif
[1093]66# ifndef _P_NOWAIT
67# define _P_NOWAIT P_NOWAIT
68# endif
[1001]69#else
70# include <unistd.h>
71# include <sys/wait.h>
[2456]72# include <sys/time.h>
[1001]73# ifndef O_BINARY
74# define O_BINARY 0
75# endif
[3065]76# ifndef __sun__
77# include <sys/file.h> /* flock */
78# endif
[1038]79#endif
[1040]80#if defined(__WIN__)
81# include <Windows.h>
[2409]82# include "quoted_spawn.h"
[1040]83#endif
[2546]84#if defined(__HAIKU__)
85# include <posix/sys/file.h>
86#endif
[1040]87
[1001]88#include "crc32.h"
89#include "md5.h"
[2612]90#include "kDep.h"
[1001]91
[1040]92
[1003]93/*******************************************************************************
94* Defined Constants And Macros *
95*******************************************************************************/
96/** The max line length in a cache file. */
97#define KOBJCACHE_MAX_LINE_LEN 16384
[1005]98#if defined(__WIN__)
99# define PATH_SLASH '\\'
100#else
101# define PATH_SLASH '/'
[1038]102#endif
[1005]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) == '/')
[1038]109#endif
[1001]110
[1044]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
[1003]120
[2612]121#define MY_IS_BLANK(a_ch) ((a_ch) == ' ' || (a_ch) == '\t')
[1044]122
[2616]123#define KOC_BUF_MIN KOC_BUF_ALIGNMENT
124#define KOC_BUF_INCR KOC_BUF_ALIGNMENT
125#define KOC_BUF_ALIGNMENT (4U*1024U*1024U)
[2612]126
[2616]127
[1040]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];
[1005]135
[1040]136/** Read buffer shared by the cache components. */
137static char g_szLine[KOBJCACHE_MAX_LINE_LEN + 16];
138
[2617]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;
[1040]143
[2617]144
[1001]145/*******************************************************************************
[1040]146* Internal Functions *
[1001]147*******************************************************************************/
[1040]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);
[2612]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);
[1040]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
[1732]189#if 0 /* unused */
[1040]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}
[1732]203#endif /* unused */
[1040]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
[1042]241#ifndef ELECTRIC_HEAP
[1040]242void *xmalloc(size_t cb)
243{
244 void *pv = malloc(cb);
245 if (!pv)
[1044]246 FatalDie("out of memory (%d)\n", (int)cb);
[1040]247 return pv;
248}
249
250
251void *xrealloc(void *pvOld, size_t cb)
252{
253 void *pv = realloc(pvOld, cb);
254 if (!pv)
[1044]255 FatalDie("out of memory (%d)\n", (int)cb);
[1040]256 return pv;
257}
258
259
260char *xstrdup(const char *pszIn)
261{
[2456]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;
[1040]271 return psz;
272}
[1042]273#endif
[1040]274
275
[1042]276void *xmallocz(size_t cb)
277{
278 void *pv = xmalloc(cb);
279 memset(pv, 0, cb);
280 return pv;
281}
[1040]282
[1042]283
284
285
286
[1040]287/**
[2456]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/**
[1040]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{
[1093]316/** @todo this isn't really working as it should... */
[1040]317 char szTmp[PATH_MAX];
[1093]318#if defined(__OS2__)
319 if ( _fullpath(szTmp, *pszPath ? pszPath : ".", sizeof(szTmp))
320 && !realpath(pszPath, szTmp))
321 return xstrdup(pszPath);
322#elif defined(__WIN__)
[1040]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;
[1093]342 if (pszFilename < pszPath)
343 return pszPath;
[1040]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.
[2409]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.
[1040]422 * @param cch The number of characters to compare.
423 */
[2409]424static int ArePathsIdenticalN(const char *pszPath1, const char *pszPath2, size_t cch)
[1040]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;
[2409]432 for ( ; cch; psz1++, psz2++, cch--)
[1040]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 */
[2409]469 if (ArePathsIdenticalN(pszPath, pszDir, cchDir))
[1040]470 {
471 if (pszPath[cchDir])
472 pszRet = (char *)pszPath + cchDir;
473 else
474 pszRet = "./";
475 }
476 else
477 {
478 pszAbsPath = AbsPath(pszPath);
[2409]479 if (ArePathsIdenticalN(pszAbsPath, pszDir, cchDir))
[1040]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{
[1044]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)
[2568]685 && errno != EEXIST
686 && errno != ENOSYS /* Solaris nonsensical mkdir crap. */
687 && errno != EACCES /* Solaris nonsensical mkdir crap. */
688 )
[1044]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;
[1040]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;
[1044]746 *pcArgs = i + cExtraArgs + !!pszWedgeArg;
747 papszArgs = xmalloc((*pcArgs + 1) * sizeof(char *));
[1040]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 {
[1044]756 size_t cch;
[1040]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
[1044]766 cch = pszEnd - psz;
767 papszArgs[i] = xmalloc(cch + 1);
768 memcpy(papszArgs[i], psz, cch);
769 papszArgs[i][cch] = '\0';
770
[1040]771 i++;
[1044]772 psz = pszEnd;
[1040]773 }
[1044]774
775 papszArgs[i] = NULL;
[1040]776}
777
778
[2612]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;
[3167]809 /** The core dependency collector state. */
810 DEPGLOBALS Core;
[2612]811} KOCDEP;
812/** Pointer to a KOCDEP. */
813typedef KOCDEP *PKOCDEP;
[1040]814
815
[2612]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;
[3167]828 depInit(&pDepState->Core);
[2612]829}
[1040]830
[2612]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;
[3167]842 depCleanup(&pDepState->Core);
[2612]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
[2618]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))
[3167]913 pDepState->pCurDep = depAdd(&pDepState->Core, pDepState->pszFilename, cchFilename);
[2618]914}
915
916
[2612]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;
[3239]945 /* fall thru */
[2612]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;
[3239]958 /* fall thru */
[2612]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;
[3239]983 /* fall thru */
[2612]984
985 case kOCDepState_NeedLine_i:
986 if (*pszInput != 'i')
987 break;
988 pszInput++;
989 if (!--cchInput)
990 return pDepState->enmState = kOCDepState_NeedLine_n;
[3239]991 /* fall thru */
[2612]992
993 case kOCDepState_NeedLine_n:
994 if (*pszInput != 'n')
995 break;
996 pszInput++;
997 if (!--cchInput)
998 return pDepState->enmState = kOCDepState_NeedLine_e;
[3239]999 /* fall thru */
[2612]1000
1001 case kOCDepState_NeedLine_e:
1002 if (*pszInput != 'e')
1003 break;
1004 pszInput++;
1005 if (!--cchInput)
1006 return pDepState->enmState = kOCDepState_NeedSpaceBeforeDigit;
[3239]1007 /* fall thru */
[2612]1008
1009 case kOCDepState_NeedSpaceBeforeDigit:
1010 if (!MY_IS_BLANK(*pszInput))
1011 break;
1012 pszInput++;
1013 cchInput--;
[3239]1014 /* fall thru */
[2612]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--;
[3239]1026 /* fall thru */
[2612]1027
1028 case kOCDepState_NeedMoreDigits:
1029 while (cchInput > 0 && isdigit(*pszInput))
1030 cchInput--, pszInput++;
1031 if (!cchInput)
1032 return pDepState->enmState = kOCDepState_NeedMoreDigits;
[3239]1033 /* fall thru */
[2612]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--;
[3239]1045 /* fall thru */
[2612]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))
[3167]1085 pDepState->pCurDep = depAdd(&pDepState->Core, pDepState->pszFilename, cchFilename);
[2612]1086 pDepState->offFilename = 0;
1087 break;
1088 }
1089
1090 off++;
1091 }
1092 }
[3239]1093 /* fall thru */
[3065]1094
1095 case kOCDepState_Invalid:
1096 assert(0);
1097 break;
[2612]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;
[2613]1123 char *psz;
[2612]1124 FILE *pFile = fopen(pszFilename, "w");
1125 if (!pFile)
1126 FatalMsg("Failed to open dependency file '%s': %s\n", pszFilename, strerror(errno));
1127
[3167]1128 depOptimize(&pDepState->Core, fFixCase, fQuiet, NULL /*pszIgnoredExt*/);
[2612]1129
[2613]1130 /* Make object file name with unix slashes. */
[2612]1131 pszObjFileAbs = MakePathFromDirAndFile(pszObjFile, pszObjDir);
[2613]1132 psz = pszObjFileAbs;
1133 while ((psz = strchr(psz, '\\')) != NULL)
1134 *psz++ = '/';
1135
[3315]1136 depPrintTargetWithDeps(&pDepState->Core, pFile, pszObjFileAbs, 1 /*fEscapeTarget*/);
[2612]1137 free(pszObjFileAbs);
1138 if (fGenStubs)
[3167]1139 depPrintStubs(&pDepState->Core, pFile);
[2612]1140
1141 if (fclose(pFile) != 0)
1142 FatalMsg("Failed to write dependency file '%s': %s\n", pszFilename, strerror(errno));
1143}
1144
1145
[2616]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. */
[2618]1156 size_t cchDstOptimized;
1157 /** Offset to the start of the unoptimized source. */
1158 size_t offSrcUnoptimized;
[2616]1159 /** The offset of the next bits to process. */
[2618]1160 size_t offSrcCur;
[2616]1161 /** The offset where to put more raw preprocessor output. */
[2618]1162 size_t offSrcRead;
[2616]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;
[2612]1173
[2616]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;
[2612]1180
[2616]1181 /** Line directive / new line sequence buffer. */
1182 char *pszLineBuf;
1183 /** The size of the buffer pointed to by pszLineBuf. */
1184 size_t cbLineBuf;
[2618]1185
1186 /** Set if we should work the dependency generator as well. */
1187 PKOCDEP pDepState;
[2616]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.
[2618]1200 * @param pDepState Pointer to the dependency generator. Must only be set
1201 * if @a fOptimize is also set.
[2616]1202 */
[2618]1203static void kOCCppRdInit(PKOCCPPRD pCppRd, size_t cbOldCpp, int fOptimize, PKOCDEP pDepState)
[2616]1204{
[2618]1205 assert(!pDepState || fOptimize);
1206
[2616]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;
[2618]1210 pCppRd->cchDstOptimized = 0;
1211 pCppRd->offSrcUnoptimized = 0;
1212 pCppRd->offSrcCur = 0;
1213 pCppRd->offSrcRead = 0;
[2616]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;
[2618]1226
1227 pCppRd->pDepState = pDepState;
[2616]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
[2618]1254 return pCppRd->cbBufAlloc - pCppRd->offSrcRead;
[2616]1255}
1256
1257
[2618]1258static size_t kOCCppRdOptInsert(PKOCCPPRD pCppRd, size_t cchSrcReplaced, const char *pchInsert, size_t cchInsert)
[2616]1259{
[2618]1260 size_t offDelta = 0;
1261 size_t cchAvail;
[2616]1262
[2618]1263 pCppRd->offSrcUnoptimized += cchSrcReplaced;
1264 assert(pCppRd->offSrcUnoptimized <= pCppRd->offSrcCur);
1265 cchAvail = pCppRd->offSrcUnoptimized - pCppRd->cchDstOptimized;
1266 if (cchAvail < cchInsert)
[2616]1267 {
[2618]1268 size_t const cbToMove = pCppRd->offSrcRead - pCppRd->offSrcUnoptimized;
1269 assert(cbToMove <= pCppRd->offSrcRead);
1270 offDelta = cchInsert - cchAvail;
[2616]1271
[2618]1272 while (pCppRd->offSrcRead + offDelta >= pCppRd->cbBufAlloc)
1273 kOCCppRdGrowBuffer(pCppRd);
[2616]1274
[2617]1275 g_cMemMoves++;
1276 g_cbMemMoved += cbToMove + 1;
[2618]1277 memmove(pCppRd->pszBuf + pCppRd->offSrcUnoptimized + offDelta,
1278 pCppRd->pszBuf + pCppRd->offSrcUnoptimized,
[2617]1279 cbToMove + 1);
[2616]1280
[2618]1281 pCppRd->offSrcRead += offDelta;
1282 pCppRd->offSrcUnoptimized += offDelta;
1283 pCppRd->offSrcCur += offDelta;
1284 assert(pCppRd->offSrcRead < 1 || pCppRd->pszBuf[pCppRd->offSrcRead - 1] != '\0');
[2616]1285 }
1286
[2618]1287 memcpy(pCppRd->pszBuf + pCppRd->cchDstOptimized, pchInsert, cchInsert);
1288 pCppRd->cchDstOptimized += cchInsert;
[2616]1289
1290 return offDelta;
1291}
1292
1293
[2618]1294static void kOCCppRdOptCommit(PKOCCPPRD pCppRd)
[2617]1295{
[2618]1296 size_t cchToCommit = pCppRd->offSrcCur - pCppRd->offSrcUnoptimized;
1297 assert(pCppRd->offSrcUnoptimized <= pCppRd->offSrcCur);
[2617]1298
[2618]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 }
[2617]1305
1306 pCppRd->uOptLineNo = pCppRd->uCurLineNo;
1307}
1308
1309
1310
[2616]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 {
[2627]1328 pCppRd->cbFileNmBuf = (cchFile + 15 + 1) & ~(size_t)15;
[2616]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
[2618]1381static size_t kOCCppRdOptFlush(PKOCCPPRD pCppRd, size_t offSrcCur, int fLineDirNext)
[2616]1382{
[2618]1383 size_t offDelta = 0;
1384 size_t const offSrcUnoptimized = pCppRd->offSrcUnoptimized;
1385 assert(offSrcUnoptimized <= offSrcCur);
1386
1387 if (offSrcCur > offSrcUnoptimized)
[2616]1388 {
1389 /*
1390 * We've got unflushed whitelines.
1391 */
[2618]1392 size_t const cchSrcInQuestion = offSrcCur - offSrcUnoptimized;
[2616]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
[2618]1400 offDelta = kOCCppRdOptInsert(pCppRd, cchSrcInQuestion, pCppRd->pszLineBuf, cchLineDir);
[2616]1401 }
1402
1403 (void)fLineDirNext; /* Use later if required. */
[2618]1404 return offDelta;
[2616]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{
[2618]1502 size_t const offSrcLine = pCppRd->offSrcCur;
[2619]1503 size_t const cchSrcLine = pszEol - pCppRd->pszBuf - (pCppRd->fOptimize & 2 ? pCppRd->offSrcUnoptimized : pCppRd->offSrcCur);
[2618]1504 size_t const cbLeftAssert = *pcbLeft;
[2616]1505 char *pszNewFile;
1506 size_t cchNewFile;
1507 uint32_t uNewLineNo;
1508 assert(*pszEol == '\r' || *pszEol == '\n' || *pszEol == '\0');
1509
[2618]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 */
[2616]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 */
[2618]1525 kOCCppRdOptFlush(pCppRd, offSrcLine, 0);
[2616]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;
[2619]1547 if (!(pCppRd->fOptimize & 2))
1548 kOCCppRdOptFlush(pCppRd, offSrcLine, 1);
[2616]1549
1550 cchLineDir = kOCCppRdOptFmtLine(pCppRd, uNewLineNo, NULL, 0) - 1; /* sans \n */
[2618]1551 kOCCppRdOptInsert(pCppRd, cchSrcLine, pCppRd->pszLineBuf, cchLineDir);
[2616]1552
1553 *pfEmptyLine = 0;
1554 }
1555 }
1556 else
1557 {
1558 /*
1559 * The #line directive changed the file.
1560 */
1561 size_t cchLineDir;
1562
[2618]1563 kOCCppRdOptSetFile(pCppRd, pszNewFile, cchNewFile); /* save to do this early */
[2619]1564 if (!(pCppRd->fOptimize & 2))
1565 kOCCppRdOptFlush(pCppRd, offSrcLine, 1);
[2616]1566
[2618]1567 cchLineDir = kOCCppRdOptFmtLine(pCppRd, uNewLineNo, pCppRd->pszFileNmBuf, cchNewFile) - 1; /* sans \n */
1568 kOCCppRdOptInsert(pCppRd, cchSrcLine, pCppRd->pszLineBuf, cchLineDir);
[2616]1569
[2618]1570 if (pCppRd->pDepState)
1571 kOCDepEnter(pCppRd->pDepState, pCppRd->pszFileNmBuf, cchNewFile);
[2616]1572
1573 *pfEmptyLine = 0;
1574 }
1575
1576 pCppRd->uCurLineNo = uNewLineNo - 1;
1577 }
1578
[2618]1579 return pCppRd->pszBuf + pCppRd->offSrcCur;
[2616]1580}
1581
1582
1583static void kOCCppRdOpt(PKOCCPPRD pCppRd)
1584{
1585 size_t cch;
1586 char *pszEol;
[2618]1587 char *pszCur = pCppRd->pszBuf + pCppRd->offSrcCur;
1588 size_t cbTodo = pCppRd->offSrcRead - pCppRd->offSrcCur;
[2616]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':
[2618]1600 pCppRd->offSrcCur = pszCur - pCppRd->pszBuf + 1;
[2616]1601 pCppRd->uCurLineNo++;
1602 if (!fEmptyLine)
[2617]1603 kOCCppRdOptCommit(pCppRd);
[2616]1604 fEmptyLine = 1;
1605 break;
1606
1607 case '\r': /* "\r\n" -> "\n" */
[2618]1608 if (cbTodo <= 1 && !pCppRd->fDone)
[2616]1609 return;
[2618]1610 if (pszCur[1] == '\n' && !fEmptyLine)
[2616]1611 {
[2618]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;
[2616]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
[2618]1646 pszCur += kOCCppRdOptFlush(pCppRd, pCppRd->offSrcCur, 0);
[2616]1647
[2618]1648 assert(cch <= cbTodo);
[2616]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;
[2618]1663 assert(pCppRd->offSrcRead < 1 || pCppRd->pszBuf[pCppRd->offSrcRead - 1] != '\0');
1664 pCppRd->pszBuf[pCppRd->offSrcRead] = '\0';
[2616]1665 kOCCppRdOpt(pCppRd);
[2618]1666
1667 assert(pCppRd->offSrcCur == pCppRd->offSrcRead);
1668 kOCCppRdOptFlush(pCppRd, pCppRd->offSrcCur, 0);
[2616]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 */
[2618]1696 size_t const cchOldOptimized = pCppRd->cchDstOptimized;
[2616]1697 if (pCppRd->chSaved)
[2618]1698 pCppRd->pszBuf[pCppRd->cchDstOptimized] = pCppRd->chSaved;
[2616]1699
1700 do
1701 {
1702 /* Read more raw C preprocessor output. */
[2618]1703 cbLeft = pCppRd->cbBufAlloc - pCppRd->offSrcRead;
[2616]1704 if (cbLeft <= 1)
1705 cbLeft = kOCCppRdGrowBuffer(pCppRd);
1706
1707 do
[2618]1708 cbRead = read(fdIn, pCppRd->pszBuf + pCppRd->offSrcRead, (long)(cbLeft - 1));
[2616]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));
[2618]1713 pCppRd->offSrcRead += cbRead;
[2616]1714
1715 /* Optimize it. */
1716 if (!cbRead)
1717 {
1718 kOCCppRdOptFinalize(pCppRd);
1719 break;
1720 }
1721 kOCCppRdOpt(pCppRd);
[2618]1722 } while (pCppRd->cchDstOptimized == cchOldOptimized);
[2616]1723
1724 *ppszRet = &pCppRd->pszBuf[cchOldOptimized];
[2618]1725 pCppRd->chSaved = pCppRd->pszBuf[pCppRd->cchDstOptimized];
1726 pCppRd->pszBuf[pCppRd->cchDstOptimized] = '\0';
1727 cbRead = (long)(pCppRd->cchDstOptimized - cchOldOptimized);
[2616]1728 }
1729 else
1730 {
1731 /*
1732 * Pass thru.
1733 */
1734 char *pszBuf;
[2618]1735 cbLeft = pCppRd->cbBufAlloc - pCppRd->offSrcRead;
[2616]1736 if (cbLeft <= 1)
1737 cbLeft = kOCCppRdGrowBuffer(pCppRd);
[2618]1738 pszBuf = pCppRd->pszBuf + pCppRd->offSrcRead;
[2616]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;
[2618]1748 pCppRd->offSrcRead += cbRead;
[2616]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{
[2618]1765 assert(pCppRd->offSrcRead < 1 || pCppRd->pszBuf[pCppRd->offSrcRead - 1] != '\0');
[2616]1766 *ppszRet = pCppRd->pszBuf;
[2618]1767 *pcbRet = pCppRd->fOptimize ? pCppRd->cchDstOptimized : pCppRd->offSrcRead;
[2616]1768 pCppRd->pszBuf = NULL;
[2618]1769 pCppRd->offSrcRead = 0;
[2616]1770}
1771
1772
1773
1774
1775
1776
[1001]1777/** A checksum list entry.
[2612]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.
[1001]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];
[1040]1791 /** Valid or not. */
1792 unsigned fUsed;
1793} KOCSUM;
1794/** Pointer to a KOCSUM. */
1795typedef KOCSUM *PKOCSUM;
[1001]1796/** Pointer to a const KOCSUM. */
1797typedef const KOCSUM *PCKOCSUM;
1798
[1040]1799
[1001]1800/**
[2612]1801 * Temporary context record used when calculating the checksum of some data.
[1001]1802 */
[1040]1803typedef struct KOCSUMCTX
[1001]1804{
[1040]1805 /** The MD5 context. */
1806 struct MD5Context MD5Ctx;
1807} KOCSUMCTX;
1808/** Pointer to a check context record. */
1809typedef KOCSUMCTX *PKOCSUMCTX;
[1001]1810
1811
1812
[1040]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{
[1041]1821 memset(pSum, 0, sizeof(*pSum));
[1040]1822 MD5Init(&pCtx->MD5Ctx);
1823}
[1001]1824
1825
[1040]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);
[1184]1844 MD5Update(&pCtx->MD5Ctx, pb, (unsigned)cb);
[1040]1845 cbBuf -= cb;
1846 }
1847}
[1001]1848
1849
[1040]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);
[1042]1859 pSum->fUsed = 1;
[1040]1860}
[1001]1861
[1002]1862
[1001]1863/**
[1040]1864 * Init a check sum chain head.
1865 *
1866 * @param pSumHead The checksum head to init.
1867 */
1868static void kOCSumInit(PKOCSUM pSumHead)
1869{
[1041]1870 memset(pSumHead, 0, sizeof(*pSumHead));
[1040]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{
[1095]1930 PKOCSUM pSum = pSumHead->pNext;
1931 while (pSum)
[1040]1932 {
[1095]1933 void *pvFree = pSum;
1934 pSum = pSum->pNext;
1935 free(pvFree);
[1040]1936 }
[1041]1937 memset(pSumHead, 0, sizeof(*pSumHead));
[1040]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/**
[1038]2021 * Compares two check sum entries.
2022 *
2023 * @returns 1 if equal, 0 if not equal.
2024 *
[1001]2025 * @param pSum1 The first checksum.
2026 * @param pSum2 The second checksum.
2027 */
[1040]2028static int kOCSumIsEqual(PCKOCSUM pSum1, PCKOCSUM pSum2)
[1001]2029{
2030 if (pSum1 == pSum2)
2031 return 1;
[1003]2032 if (!pSum1 || !pSum2)
[1001]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/**
[1040]2043 * Checks if the specified checksum equals one of the
2044 * checksums in the chain.
[1038]2045 *
[1040]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.
[1002]2051 */
[1040]2052static int kOCSumHasEqualInChain(PCKOCSUM pSumHead, PCKOCSUM pSum)
[1002]2053{
[1040]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}
[1002]2066
2067
[1040]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;
[1002]2077}
2078
2079
[1040]2080
2081
2082
2083
[1002]2084/**
[1040]2085 * The representation of a cache entry.
[1002]2086 */
[1040]2087typedef struct KOCENTRY
[1002]2088{
[1040]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;
[2612]2097 /** Whether the preprocessor runs in piped mode. If clear it's file
[1040]2098 * mode (it could be redirected stdout, but that's essentially the
2099 * same from our point of view). */
2100 unsigned fPipedPreComp;
[2612]2101 /** Whether the compiler runs in piped mode (preprocessor output on stdin). */
[1040]2102 unsigned fPipedCompile;
[2612]2103 /** The name of the pipe that we're feeding the preprocessed output to the
[2456]2104 * compiler via. This is a Windows thing. */
2105 char *pszNmPipeCompile;
[2612]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;
[2616]2117 /** Whether the optimizations are enabled. */
2118 int fOptimizeCpp;
[1040]2119 /** Cache entry key that's used for some quick digest validation. */
2120 uint32_t uKey;
2121
2122 /** The file data. */
2123 struct KOCENTRYDATA
[1002]2124 {
[2612]2125 /** The name of file containing the preprocessor output. */
[1040]2126 char *pszCppName;
[2612]2127 /** Pointer to the preprocessor output. */
[1040]2128 char *pszCppMapping;
[2612]2129 /** The size of the preprocessor output. 0 if not determined. */
[1040]2130 size_t cbCpp;
[2612]2131 /** The preprocessor output checksums that will produce the cached object. */
[1040]2132 KOCSUM SumHead;
[2456]2133 /** The number of milliseconds spent precompiling. */
2134 uint32_t cMsCpp;
2135
[1040]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;
[2456]2144 /** The number of milliseconds spent compiling. */
2145 uint32_t cMsCompile;
2146 /** @todo need a list of additional output files for MSC. */
[2612]2147 /** @todo need compiler output (warnings). */
[2456]2148
[1040]2149 /** The target os/arch identifier. */
2150 char *pszTarget;
[1002]2151 }
[1040]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;
[1002]2161
2162
2163/**
[1001]2164 * Creates a cache entry for the given cache file name.
[1038]2165 *
[1001]2166 * @returns Pointer to a cache entry.
2167 * @param pszFilename The cache file name.
2168 */
[1040]2169static PKOCENTRY kOCEntryCreate(const char *pszFilename)
[1001]2170{
[1040]2171 PKOCENTRY pEntry;
2172 size_t off;
[1001]2173
2174 /*
2175 * Allocate an empty entry.
2176 */
[1040]2177 pEntry = xmallocz(sizeof(*pEntry));
[1001]2178
[2612]2179 kOCDepInit(&pEntry->DepState);
2180
[1040]2181 kOCSumInit(&pEntry->New.SumHead);
2182 kOCSumInit(&pEntry->Old.SumHead);
2183
2184 kOCSumInit(&pEntry->New.SumCompArgv);
2185 kOCSumInit(&pEntry->Old.SumCompArgv);
2186
[1001]2187 /*
2188 * Setup the directory and cache file name.
2189 */
[1040]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);
[1042]2195 pEntry->pszDir = xmalloc(off);
2196 memcpy(pEntry->pszDir, pEntry->pszAbsPath, off - 1);
[1040]2197 pEntry->pszDir[off - 1] = '\0';
[1001]2198
2199 return pEntry;
2200}
2201
2202
2203/**
[1038]2204 * Destroys the cache entry freeing up all it's resources.
2205 *
[1001]2206 * @param pEntry The entry to free.
2207 */
[1040]2208static void kOCEntryDestroy(PKOCENTRY pEntry)
[1001]2209{
[2456]2210 /** @todo free pEntry->pszName? */
[1001]2211 free(pEntry->pszDir);
[1040]2212 free(pEntry->pszAbsPath);
[2456]2213 free(pEntry->pszNmPipeCompile);
[2612]2214 free(pEntry->pszMakeDepFilename);
[1040]2215
[2612]2216 kOCDepDelete(&pEntry->DepState);
2217
[1040]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
[1045]2233 free(pEntry->New.pszTarget);
2234 free(pEntry->Old.pszTarget);
2235
[1040]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
[1001]2244 free(pEntry);
2245}
2246
2247
2248/**
[1045]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.
[2614]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.)
[1045]2260 * @param pSum Where to store the check sum.
2261 */
2262static void kOCEntryCalcArgvSum(PKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgc,
[2614]2263 const char *pszIgnorePath1, const char *pszIgnorePath2, PKOCSUM pSum)
[1045]2264{
[2614]2265 size_t cchIgnorePath1 = strlen(pszIgnorePath1);
2266 size_t cchIgnorePath2 = pszIgnorePath2 ? strlen(pszIgnorePath2) : ~(size_t)0;
[1045]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]);
[2614]2274 if ( ( cch < cchIgnorePath1
2275 || !ArePathsIdenticalN(papszArgv[i] + cch - cchIgnorePath1, pszIgnorePath1, cch))
2276 && ( cch < cchIgnorePath2
2277 || !ArePathsIdenticalN(papszArgv[i] + cch - cchIgnorePath2, pszIgnorePath2, cch)) )
[1045]2278 kOCSumUpdate(pSum, &Ctx, papszArgv[i], cch + 1);
2279 }
2280 kOCSumFinalize(pSum, &Ctx);
[1732]2281
2282 (void)pEntry;
[1045]2283}
2284
2285
2286/**
[1001]2287 * Reads and parses the cache file.
[1038]2288 *
[1001]2289 * @param pEntry The entry to read it into.
2290 */
[1040]2291static void kOCEntryRead(PKOCENTRY pEntry)
[1001]2292{
[1003]2293 FILE *pFile;
2294 pFile = FOpenFileInDir(pEntry->pszName, pEntry->pszDir, "rb");
2295 if (pFile)
2296 {
[1045]2297 InfoMsg(4, "reading cache entry...\n");
[1003]2298
[1038]2299 /*
[1003]2300 * Check the magic.
2301 */
[1040]2302 if ( !fgets(g_szLine, sizeof(g_szLine), pFile)
[2456]2303 || ( strcmp(g_szLine, "magic=kObjCacheEntry-v0.1.0\n")
2304 && strcmp(g_szLine, "magic=kObjCacheEntry-v0.1.1\n"))
2305 )
[1003]2306 {
[1045]2307 InfoMsg(2, "bad cache file (magic)\n");
[1003]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;
[1040]2317 int fBadBeforeMissing = 1;
2318 while (fgets(g_szLine, sizeof(g_szLine), pFile))
[1003]2319 {
[1040]2320 char *pszNl;
2321 char *pszVal;
2322
[1003]2323 /* Split the line and drop the trailing newline. */
[1040]2324 pszVal = strchr(g_szLine, '=');
[1003]2325 if ((fBad = pszVal == NULL))
2326 break;
[1040]2327 *pszVal++ = '\0';
2328
2329 pszNl = strchr(pszVal, '\n');
[1003]2330 if (pszNl)
2331 *pszNl = '\0';
2332
2333 /* string case on variable name */
[1040]2334 if (!strcmp(g_szLine, "obj"))
[1003]2335 {
[1040]2336 if ((fBad = pEntry->Old.pszObjName != NULL))
[1003]2337 break;
[1040]2338 pEntry->Old.pszObjName = xstrdup(pszVal);
[1003]2339 }
[1040]2340 else if (!strcmp(g_szLine, "cpp"))
[1003]2341 {
[1040]2342 if ((fBad = pEntry->Old.pszCppName != NULL))
[1003]2343 break;
[1040]2344 pEntry->Old.pszCppName = xstrdup(pszVal);
[1003]2345 }
[1040]2346 else if (!strcmp(g_szLine, "cpp-size"))
[1008]2347 {
2348 char *pszNext;
[1040]2349 if ((fBad = pEntry->Old.cbCpp != 0))
[1008]2350 break;
[1040]2351 pEntry->Old.cbCpp = strtoul(pszVal, &pszNext, 0);
[1008]2352 if ((fBad = pszNext && *pszNext))
2353 break;
2354 }
[1042]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 }
[2456]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 }
[1040]2371 else if (!strcmp(g_szLine, "cc-argc"))
[1003]2372 {
[1040]2373 if ((fBad = pEntry->Old.papszArgvCompile != NULL))
[1003]2374 break;
[1040]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]));
[1003]2377 }
[1040]2378 else if (!strncmp(g_szLine, "cc-argv-#", sizeof("cc-argv-#") - 1))
[1003]2379 {
2380 char *pszNext;
[3065]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)))
[1003]2383 break;
[3065]2384 pEntry->Old.papszArgvCompile[iArg] = xstrdup(pszVal);
[1003]2385 }
[1042]2386 else if (!strcmp(g_szLine, "cc-argv-sum"))
[1003]2387 {
[1042]2388 if ((fBad = !kOCSumIsEmpty(&pEntry->Old.SumCompArgv)))
[1003]2389 break;
[1042]2390 if ((fBad = kOCSumInitFromString(&pEntry->Old.SumCompArgv, pszVal)))
2391 break;
[1040]2392 }
[2456]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 }
[1040]2402 else if (!strcmp(g_szLine, "target"))
2403 {
2404 if ((fBad = pEntry->Old.pszTarget != NULL))
[1003]2405 break;
[1040]2406 pEntry->Old.pszTarget = xstrdup(pszVal);
2407 }
[1042]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 }
[1040]2417 else if (!strcmp(g_szLine, "the-end"))
2418 {
[1042]2419 fBadBeforeMissing = fBad = strcmp(pszVal, "fine");
[1040]2420 break;
2421 }
2422 else
2423 {
2424 fBad = 1;
2425 break;
2426 }
2427 } /* parse loop */
[1003]2428
[1040]2429 /*
2430 * Did we find everything and does it add up correctly?
2431 */
2432 if (!fBad && fBadBeforeMissing)
2433 {
[1045]2434 InfoMsg(2, "bad cache file (no end)\n");
[1040]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
[1042]2444 || kOCSumIsEmpty(&pEntry->Old.SumHead)))
[1040]2445 fBad = 1;
2446 if (!fBad)
2447 for (i = 0; i < pEntry->Old.cArgvCompile; i++)
2448 if ((fBad = !pEntry->Old.papszArgvCompile[i]))
[1003]2449 break;
[1045]2450 if (!fBad)
2451 {
2452 KOCSUM Sum;
2453 kOCEntryCalcArgvSum(pEntry, (const char * const *)pEntry->Old.papszArgvCompile,
[2614]2454 pEntry->Old.cArgvCompile, pEntry->Old.pszObjName, pEntry->Old.pszCppName,
2455 &Sum);
[1045]2456 fBad = !kOCSumIsEqual(&pEntry->Old.SumCompArgv, &Sum);
[1040]2457 }
2458 if (fBad)
[1045]2459 InfoMsg(2, "bad cache file (%s)\n", fBadBeforeMissing ? g_szLine : "missing stuff");
[1040]2460 else if (ferror(pFile))
2461 {
[1045]2462 InfoMsg(2, "cache file read error\n");
[1040]2463 fBad = 1;
2464 }
[1038]2465
[1040]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)
[1003]2474 {
[1045]2475 InfoMsg(2, "failed to stat object file: %s\n", strerror(errno));
[1040]2476 fBad = 1;
[1003]2477 }
2478 else
2479 {
[1040]2480 /** @todo verify size and the timestamp. */
[1003]2481 }
2482 }
[1040]2483 }
[1003]2484 pEntry->fNeedCompiling = fBad;
2485 }
2486 fclose(pFile);
2487 }
2488 else
2489 {
[1045]2490 InfoMsg(2, "no cache file\n");
[1003]2491 pEntry->fNeedCompiling = 1;
2492 }
[1001]2493}
2494
2495
2496/**
2497 * Writes the cache file.
[1038]2498 *
[1001]2499 * @param pEntry The entry to write.
2500 */
[1040]2501static void kOCEntryWrite(PKOCENTRY pEntry)
[1001]2502{
[1003]2503 FILE *pFile;
2504 PCKOCSUM pSum;
2505 unsigned i;
2506
[1045]2507 InfoMsg(4, "writing cache entry '%s'...\n", pEntry->pszName);
[1003]2508 pFile = FOpenFileInDir(pEntry->pszName, pEntry->pszDir, "wb");
2509 if (!pFile)
[1040]2510 FatalDie("Failed to open '%s' in '%s': %s\n",
2511 pEntry->pszName, pEntry->pszDir, strerror(errno));
[1003]2512
2513#define CHECK_LEN(expr) \
[1040]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)
[1003]2515
[2456]2516 fprintf(pFile, "magic=kObjCacheEntry-v0.1.1\n");
[3065]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));
[2456]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));
[3065]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)));
[1040]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
[1042]2543 for (pSum = !kOCSumIsEmpty(&pEntry->New.SumHead) ? &pEntry->New.SumHead : &pEntry->Old.SumHead;
[1003]2544 pSum;
2545 pSum = pSum->pNext)
[1040]2546 {
[1042]2547 fprintf(pFile, "cpp-sum=");
[1040]2548 kOCSumFPrintf(pSum, pFile);
2549 }
[1038]2550
[1040]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 */
[1042]2560 errno = 0;
[1003]2561 if ( fflush(pFile) < 0
2562 || ferror(pFile))
2563 {
2564 int iErr = errno;
2565 fclose(pFile);
2566 UnlinkFileInDir(pEntry->pszName, pEntry->pszDir);
[1040]2567 FatalDie("Stream error occured while writing '%s' in '%s': %s\n",
2568 pEntry->pszName, pEntry->pszDir, strerror(iErr));
[1003]2569 }
2570 fclose(pFile);
[1001]2571}
2572
2573
2574/**
[1040]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{
[1045]2583 return !pEntry->fNeedCompiling;
[1040]2584}
2585
2586
2587/**
[1045]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/**
[1040]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.
[1045]2621 *
2622 * @remark Must call kOCEntrySetCompileObjName before this function!
[1040]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);
[1045]2630 /* call kOCEntrySetCompilerObjName first! */
2631 assert(pEntry->New.pszObjName);
[1040]2632
2633 /*
[1045]2634 * Copy the argument vector and calculate the checksum.
[1040]2635 */
2636 pEntry->New.cArgvCompile = cArgvCompile;
[1042]2637 pEntry->New.papszArgvCompile = xmalloc((cArgvCompile + 1) * sizeof(pEntry->New.papszArgvCompile[0]));
[1040]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
[2614]2642 kOCEntryCalcArgvSum(pEntry, papszArgvCompile, cArgvCompile, pEntry->New.pszObjName, pEntry->New.pszCppName,
2643 &pEntry->New.SumCompArgv);
[1045]2644 kOCSumInfo(&pEntry->New.SumCompArgv, 4, "comp-argv");
2645
[1040]2646 /*
2647 * Compare with the old argument vector.
2648 */
2649 if ( !pEntry->fNeedCompiling
2650 && !kOCSumIsEqual(&pEntry->New.SumCompArgv, &pEntry->Old.SumCompArgv))
2651 {
[1045]2652 InfoMsg(2, "compiler args differs\n");
[1040]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 {
[1045]2673 InfoMsg(2, "target differs\n");
[1040]2674 pEntry->fNeedCompiling = 1;
2675 }
2676}
2677
2678
2679/**
[2612]2680 * Sets the preprocessor output filename. We don't generally care if this
2681 * matches the old name or not.
[1040]2682 *
2683 * @param pEntry The cache entry.
[2612]2684 * @param pszCppName The preprocessor output filename.
[1040]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/**
[2612]2694 * Sets the piped mode of the preprocessor and compiler.
[1040]2695 *
2696 * @param pEntry The cache entry.
[2612]2697 * @param fRedirPreCompStdOut Whether the preprocessor is in piped mode.
[1040]2698 * @param fRedirCompileStdIn Whether the compiler is in piped mode.
[2456]2699 * @param pszNmPipeCompile The name of the named pipe to use to feed
2700 * the microsoft compiler.
[1040]2701 */
[2456]2702static void kOCEntrySetPipedMode(PKOCENTRY pEntry, int fRedirPreCompStdOut, int fRedirCompileStdIn,
2703 const char *pszNmPipeCompile)
[1040]2704{
2705 pEntry->fPipedPreComp = fRedirPreCompStdOut;
[2456]2706 pEntry->fPipedCompile = fRedirCompileStdIn || pszNmPipeCompile;
2707 pEntry->pszNmPipeCompile = xstrdup(pszNmPipeCompile);
[1040]2708}
2709
2710
2711/**
[2612]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/**
[2616]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/**
[1001]2743 * Spawns a child in a synchronous fashion.
2744 * Terminating on failure.
[1038]2745 *
[1001]2746 * @param papszArgv Argument vector. The cArgv element is NULL.
[2456]2747 * @param pcMs The cache entry member use for time keeping. This
2748 * will be set to the current timestamp.
[1001]2749 * @param cArgv The number of arguments in the vector.
[2456]2750 * @param pszMsg Which operation this is, for use in messages.
2751 * @param pszStdOut Where to redirect standard out.
[1001]2752 */
[2456]2753static void kOCEntrySpawn(PCKOCENTRY pEntry, uint32_t *pcMs, const char * const *papszArgv, unsigned cArgv,
2754 const char *pszMsg, const char *pszStdOut)
[1001]2755{
[1002]2756#if defined(__OS2__) || defined(__WIN__)
2757 intptr_t rc;
2758 int fdStdOut = -1;
2759 if (pszStdOut)
2760 {
2761 int fdReDir;
[1044]2762 fdStdOut = dup(STDOUT_FILENO);
2763 close(STDOUT_FILENO);
[1040]2764 fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0666);
[1002]2765 if (fdReDir < 0)
[1040]2766 FatalDie("%s - failed to create stdout redirection file '%s': %s\n",
2767 pszMsg, pszStdOut, strerror(errno));
[1008]2768
[1044]2769 if (fdReDir != STDOUT_FILENO)
[1002]2770 {
[1044]2771 if (dup2(fdReDir, STDOUT_FILENO) < 0)
[1040]2772 FatalDie("%s - dup2 failed: %s\n", pszMsg, strerror(errno));
[1002]2773 close(fdReDir);
2774 }
2775 }
[1001]2776
[2456]2777 *pcMs = NowMs();
[1002]2778 errno = 0;
[2409]2779# ifdef __WIN__
2780 rc = quoted_spawnvp(_P_WAIT, papszArgv[0], papszArgv);
2781# else
[1002]2782 rc = _spawnvp(_P_WAIT, papszArgv[0], papszArgv);
[2409]2783# endif
[2456]2784 *pcMs = NowMs() - *pcMs;
[1002]2785 if (rc < 0)
[1040]2786 FatalDie("%s - _spawnvp failed (rc=0x%p): %s\n", pszMsg, rc, strerror(errno));
[1002]2787 if (rc > 0)
[1040]2788 FatalDie("%s - failed rc=%d\n", pszMsg, (int)rc);
[2409]2789 if (fdStdOut != -1)
[1002]2790 {
[1044]2791 close(STDOUT_FILENO);
2792 fdStdOut = dup2(fdStdOut, STDOUT_FILENO);
[1002]2793 close(fdStdOut);
2794 }
[1001]2795
2796#else
2797 int iStatus;
2798 pid_t pidWait;
[2456]2799 pid_t pid;
2800
2801 *pcMs = NowMs();
2802 pid = fork();
[1001]2803 if (!pid)
2804 {
[1002]2805 if (pszStdOut)
2806 {
[1016]2807 int fdReDir;
2808
[1044]2809 close(STDOUT_FILENO);
[1040]2810 fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0666);
[1002]2811 if (fdReDir < 0)
[1040]2812 FatalDie("%s - failed to create stdout redirection file '%s': %s\n",
2813 pszMsg, pszStdOut, strerror(errno));
[1044]2814 if (fdReDir != STDOUT_FILENO)
[1002]2815 {
[1044]2816 if (dup2(fdReDir, STDOUT_FILENO) < 0)
[1040]2817 FatalDie("%s - dup2 failed: %s\n", pszMsg, strerror(errno));
[1002]2818 close(fdReDir);
2819 }
2820 }
2821
[1016]2822 execvp(papszArgv[0], (char **)papszArgv);
[1040]2823 FatalDie("%s - execvp failed: %s\n",
2824 pszMsg, strerror(errno));
[1001]2825 }
[1008]2826 if (pid == -1)
[1040]2827 FatalDie("%s - fork() failed: %s\n", pszMsg, strerror(errno));
[1008]2828
[1016]2829 pidWait = waitpid(pid, &iStatus, 0);
[1001]2830 while (pidWait < 0 && errno == EINTR)
[1016]2831 pidWait = waitpid(pid, &iStatus, 0);
[2456]2832 *pcMs = NowMs() - *pcMs;
[1001]2833 if (pidWait != pid)
[1040]2834 FatalDie("%s - waitpid failed rc=%d: %s\n",
2835 pszMsg, pidWait, strerror(errno));
[1008]2836 if (!WIFEXITED(iStatus))
[1040]2837 FatalDie("%s - abended (iStatus=%#x)\n", pszMsg, iStatus);
[1008]2838 if (WEXITSTATUS(iStatus))
[1040]2839 FatalDie("%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus));
[1008]2840#endif
[1732]2841
2842 (void)pEntry; (void)cArgv;
[1008]2843}
2844
2845
2846/**
[1040]2847 * Spawns child with optional redirection of stdin and stdout.
[1038]2848 *
[1040]2849 * @param pEntry The cache entry.
[2456]2850 * @param pcMs The cache entry member use for time keeping. This
2851 * will be set to the current timestamp.
[1008]2852 * @param papszArgv Argument vector. The cArgv element is NULL.
2853 * @param cArgv The number of arguments in the vector.
[1040]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.
[1008]2857 */
[2456]2858static pid_t kOCEntrySpawnChild(PCKOCENTRY pEntry, uint32_t *pcMs, const char * const *papszArgv, unsigned cArgv,
2859 int fdStdIn, int fdStdOut, const char *pszMsg)
[1008]2860{
[1040]2861 pid_t pid;
2862 int fdSavedStdOut = -1;
2863 int fdSavedStdIn = -1;
[1008]2864
2865 /*
[1040]2866 * Setup redirection.
[1008]2867 */
[1044]2868 if (fdStdOut != -1 && fdStdOut != STDOUT_FILENO)
[1040]2869 {
[1044]2870 fdSavedStdOut = dup(STDOUT_FILENO);
2871 if (dup2(fdStdOut, STDOUT_FILENO) < 0)
[1040]2872 FatalDie("%s - dup2(,1) failed: %s\n", pszMsg, strerror(errno));
2873 close(fdStdOut);
2874#ifndef __WIN__
2875 fcntl(fdSavedStdOut, F_SETFD, FD_CLOEXEC);
[1008]2876#endif
[1040]2877 }
[1044]2878 if (fdStdIn != -1 && fdStdIn != STDIN_FILENO)
[1040]2879 {
[1044]2880 fdSavedStdIn = dup(STDIN_FILENO);
2881 if (dup2(fdStdIn, STDIN_FILENO) < 0)
[1040]2882 FatalDie("%s - dup2(,0) failed: %s\n", pszMsg, strerror(errno));
2883 close(fdStdIn);
[1008]2884#ifndef __WIN__
[1040]2885 fcntl(fdSavedStdIn, F_SETFD, FD_CLOEXEC);
[1038]2886#endif
[1040]2887 }
[1008]2888
2889 /*
2890 * Create the child process.
2891 */
[2456]2892 *pcMs = NowMs();
[1008]2893#if defined(__OS2__) || defined(__WIN__)
2894 errno = 0;
[2409]2895# ifdef __WIN__
2896 pid = quoted_spawnvp(_P_NOWAIT, papszArgv[0], papszArgv);
2897# else
[1008]2898 pid = _spawnvp(_P_NOWAIT, papszArgv[0], papszArgv);
[2409]2899# endif
[1008]2900 if (pid == -1)
[2612]2901 FatalDie("preprocess - _spawnvp failed: %s\n", strerror(errno));
[1008]2902
2903#else
2904 pid = fork();
2905 if (!pid)
2906 {
[1016]2907 execvp(papszArgv[0], (char **)papszArgv);
[2612]2908 FatalDie("preprocess - execvp failed: %s\n", strerror(errno));
[1008]2909 }
2910 if (pid == -1)
[2612]2911 FatalDie("preprocess - fork() failed: %s\n", strerror(errno));
[1038]2912#endif
[1008]2913
2914 /*
[1040]2915 * Restore stdout & stdin.
[1008]2916 */
[1044]2917 if (fdSavedStdIn != -1)
[1008]2918 {
[1044]2919 close(STDIN_FILENO);
2920 dup2(fdStdOut, STDIN_FILENO);
[1040]2921 close(fdSavedStdIn);
[1038]2922 }
[1044]2923 if (fdSavedStdOut != -1)
[1040]2924 {
[1044]2925 close(STDOUT_FILENO);
2926 dup2(fdSavedStdOut, STDOUT_FILENO);
[1040]2927 close(fdSavedStdOut);
2928 }
[1008]2929
[1044]2930 InfoMsg(3, "%s - spawned %ld\n", pszMsg, (long)pid);
[1040]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.
[2456]2941 * @param pcMs The millisecond timestamp that should be convert to
2942 * elapsed time.
[1040]2943 * @param pid The child to wait for.
2944 * @param pszMsg Message to start the info/error messages with.
2945 */
[2456]2946static void kOCEntryWaitChild(PCKOCENTRY pEntry, uint32_t *pcMs, pid_t pid, const char *pszMsg)
[1040]2947{
2948 int iStatus = -1;
2949 pid_t pidWait;
[1045]2950 InfoMsg(3, "%s - wait-child %ld\n", pszMsg, (long)pid);
[1044]2951
[1008]2952#ifdef __WIN__
2953 pidWait = _cwait(&iStatus, pid, _WAIT_CHILD);
[2456]2954 *pcMs = NowMs() - *pcMs;
[1008]2955 if (pidWait == -1)
[1040]2956 FatalDie("%s - waitpid failed: %s\n", pszMsg, strerror(errno));
[1008]2957 if (iStatus)
[1040]2958 FatalDie("%s - failed with rc %d\n", pszMsg, iStatus);
[1008]2959#else
[1016]2960 pidWait = waitpid(pid, &iStatus, 0);
[1008]2961 while (pidWait < 0 && errno == EINTR)
[1016]2962 pidWait = waitpid(pid, &iStatus, 0);
[2456]2963 *pcMs = NowMs() - *pcMs;
[1008]2964 if (pidWait != pid)
[1040]2965 FatalDie("%s - waitpid failed rc=%d: %s\n", pidWait, strerror(errno));
[1001]2966 if (!WIFEXITED(iStatus))
[1040]2967 FatalDie("%s - abended (iStatus=%#x)\n", pszMsg, iStatus);
[1001]2968 if (WEXITSTATUS(iStatus))
[1040]2969 FatalDie("%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus));
[1001]2970#endif
[1040]2971 (void)pEntry;
[1001]2972}
2973
2974
2975/**
[1040]2976 * Creates a pipe for setting up redirected stdin/stdout.
[1038]2977 *
[1040]2978 * @param pEntry The cache entry.
[2612]2979 * @param paFDs Where to store the two file descriptors.
[1040]2980 * @param pszMsg The operation message for info/error messages.
[2456]2981 * @param pszPipeName The pipe name if it is supposed to be named. (Windows only.)
[2617]2982 * @param fText Whether to read text mode or binary mode.
[1040]2983 */
[2617]2984static void kOCEntryCreatePipe(PKOCENTRY pEntry, int *paFDs, const char *pszPipeName, const char *pszMsg, int fText)
[1040]2985{
[2612]2986 paFDs[0] = paFDs[1] = -1;
[1040]2987#if defined(__WIN__)
[2456]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
[2612]3002 paFDs[1 /* write */] = _open_osfhandle((intptr_t)hPipe, _O_WRONLY | _O_TEXT | _O_NOINHERIT);
3003 if (paFDs[1 /* write */] == -1)
[2456]3004 FatalDie("%s - _open_osfhandle failed: %d\n", pszMsg, strerror(errno));
3005 }
[2618]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)
[1040]3008#else
[2612]3009 if (pipe(paFDs) < 0)
[1040]3010#endif
3011 FatalDie("%s - pipe failed: %s\n", pszMsg, strerror(errno));
3012#if !defined(__WIN__)
[2612]3013 fcntl(paFDs[0], F_SETFD, FD_CLOEXEC);
3014 fcntl(paFDs[1], F_SETFD, FD_CLOEXEC);
[1040]3015#endif
[1732]3016
3017 (void)pEntry;
[1040]3018}
3019
3020
3021/**
3022 * Spawns a child that produces output to stdout.
[1038]3023 *
[1040]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.
[1008]3029 */
[1043]3030static void kOCEntrySpawnProducer(PKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgv, const char *pszMsg,
[1040]3031 void (*pfnConsumer)(PKOCENTRY, int))
[1008]3032{
[1040]3033 int fds[2];
3034 pid_t pid;
3035
[2617]3036 kOCEntryCreatePipe(pEntry, fds, NULL, pszMsg, pEntry->fOptimizeCpp);
[2456]3037 pid = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCpp, papszArgv, cArgv, -1, fds[1 /* write */], pszMsg);
[1040]3038
3039 pfnConsumer(pEntry, fds[0 /* read */]);
3040
[2456]3041 kOCEntryWaitChild(pEntry, &pEntry->New.cMsCpp, pid, pszMsg);
[1008]3042}
3043
3044
3045/**
[2456]3046 * Spawns a child that consumes input on stdin or via a named pipe.
[1040]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 */
[1043]3054static void kOCEntrySpawnConsumer(PKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgv, const char *pszMsg,
[1040]3055 void (*pfnProducer)(PKOCENTRY, int))
3056{
3057 int fds[2];
3058 pid_t pid;
3059
[2617]3060 kOCEntryCreatePipe(pEntry, fds, pEntry->pszNmPipeCompile, pszMsg, 0 /*fText*/);
[2456]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
[1040]3066
3067 pfnProducer(pEntry, fds[1 /* write */]);
3068
[2456]3069 kOCEntryWaitChild(pEntry, &pEntry->New.cMsCompile, pid, pszMsg);
[1040]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 */
[1043]3083static void kOCEntrySpawnTee(PKOCENTRY pEntry, const char * const *papszProdArgv, unsigned cProdArgv,
3084 const char * const *papszConsArgv, unsigned cConsArgv,
[1040]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 */
[2617]3094 kOCEntryCreatePipe(pEntry, fds, NULL, pszMsg, pEntry->fOptimizeCpp);
[2456]3095 pidConsumer = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCpp, papszProdArgv, cProdArgv, -1, fds[1 /* write */], pszMsg);
[1040]3096 fdIn = fds[0 /* read */];
3097
3098 /*
3099 * The consumer.
3100 */
[2617]3101 kOCEntryCreatePipe(pEntry, fds, pEntry->pszNmPipeCompile, pszMsg, 0 /*fText*/);
[2456]3102 pidProducer = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCompile, papszConsArgv, cConsArgv, fds[0 /* read */], -1, pszMsg);
[1040]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 */
[2456]3113 kOCEntryWaitChild(pEntry, &pEntry->New.cMsCpp, pidProducer, pszMsg);
3114 kOCEntryWaitChild(pEntry, &pEntry->New.cMsCompile, pidConsumer, pszMsg);
[1040]3115}
3116
3117
3118/**
[2612]3119 * Reads the output from the preprocessor.
[1040]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));
[1045]3133 InfoMsg(2, "failed to open/read '%s' in '%s': %s\n",
[1040]3134 pWhich->pszCppName, pEntry->pszDir, strerror(errno));
3135 return -1;
3136 }
3137
[2612]3138 InfoMsg(3, "preprocessed file is %lu bytes long\n", (unsigned long)pWhich->cbCpp);
[1040]3139 return 0;
3140}
3141
3142
3143/**
[2612]3144 * Worker for kOCEntryPreProcess and calculates the checksum of
3145 * the preprocessor output.
[1038]3146 *
[1001]3147 * @param pEntry The cache entry. NewSum will be updated.
3148 */
[1040]3149static void kOCEntryCalcChecksum(PKOCENTRY pEntry)
[1001]3150{
[1040]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);
[1045]3155 kOCSumInfo(&pEntry->New.SumHead, 4, "cpp (file)");
[1040]3156}
[1001]3157
[1003]3158
[1040]3159/**
[2612]3160 * This consumes the preprocessor output and checksums it.
[1040]3161 *
3162 * @param pEntry The cache entry.
[2612]3163 * @param fdIn The preprocessor output pipe.
[1040]3164 * @param fdOut The compiler input pipe, -1 if no compiler.
3165 */
[2612]3166static void kOCEntryPreProcessConsumer(PKOCENTRY pEntry, int fdIn)
[1040]3167{
3168 KOCSUMCTX Ctx;
[2616]3169 KOCCPPRD CppRd;
[1040]3170
3171 kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx);
[2618]3172 kOCCppRdInit(&CppRd, pEntry->Old.cbCpp, pEntry->fOptimizeCpp,
3173 pEntry->pszMakeDepFilename ? &pEntry->DepState : NULL);
[2616]3174
[1040]3175 for (;;)
3176 {
3177 /*
3178 * Read data from the pipe.
3179 */
[2616]3180 const char *psz;
3181 long cbRead = kOCCppRdRead(&CppRd, fdIn, &psz);
[1040]3182 if (!cbRead)
3183 break;
3184
3185 /*
3186 * Process the data.
3187 */
3188 kOCSumUpdate(&pEntry->New.SumHead, &Ctx, psz, cbRead);
[2618]3189 if (pEntry->pszMakeDepFilename && !pEntry->fOptimizeCpp)
[2612]3190 kOCDepConsumer(&pEntry->DepState, psz, cbRead);
[1040]3191 }
3192
3193 close(fdIn);
[2616]3194 kOCCppRdGrabOutput(&CppRd, &pEntry->New.pszCppMapping, &pEntry->New.cbCpp);
3195 kOCCppRdDelete(&CppRd);
[1040]3196 kOCSumFinalize(&pEntry->New.SumHead, &Ctx);
[1045]3197 kOCSumInfo(&pEntry->New.SumHead, 4, "cpp (pipe)");
[1001]3198}
3199
3200
[1040]3201
3202
[1001]3203/**
[2612]3204 * Run the preprocessor and calculate the checksum of the output.
[1038]3205 *
[1001]3206 * @param pEntry The cache entry.
[2612]3207 * @param papszArgvPreComp The argument vector for executing preprocessor.
3208 * The cArgvPreComp'th argument must be NULL.
[1001]3209 * @param cArgvPreComp The number of arguments.
3210 */
[2612]3211static void kOCEntryPreProcess(PKOCENTRY pEntry, const char * const *papszArgvPreComp, unsigned cArgvPreComp)
[1001]3212{
3213 /*
[2612]3214 * If we're executing the preprocessor in piped mode, it's relatively simple.
[1008]3215 */
[1040]3216 if (pEntry->fPipedPreComp)
[2612]3217 kOCEntrySpawnProducer(pEntry, papszArgvPreComp, cArgvPreComp, "preprocess",
3218 kOCEntryPreProcessConsumer);
[1008]3219 else
[1040]3220 {
3221 /*
[2612]3222 * Rename the old preprocessed output to '-old' so the preprocessor won't
[1040]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"));
[1008]3232
[1045]3233 InfoMsg(3, "renaming '%s' to '%s' in '%s'\n", pEntry->Old.pszCppName, psz, pEntry->pszDir);
[1040]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 /*
[2616]3243 * Preprocess it and calculate the checksum on the output.
[1040]3244 */
[1045]3245 InfoMsg(3, "precompiling -> '%s'...\n", pEntry->New.pszCppName);
[2612]3246 kOCEntrySpawn(pEntry, &pEntry->New.cMsCpp, papszArgvPreComp, cArgvPreComp, "preprocess", NULL);
[1040]3247 kOCEntryReadCppOutput(pEntry, &pEntry->New, 0 /* fatal */);
3248 kOCEntryCalcChecksum(pEntry);
[2612]3249 if (pEntry->pszMakeDepFilename)
3250 kOCDepConsumer(&pEntry->DepState, pEntry->New.pszCppMapping, pEntry->New.cbCpp);
[1040]3251 }
[2612]3252
3253 if (pEntry->pszMakeDepFilename)
3254 kOCDepWriteToFile(&pEntry->DepState, pEntry->pszMakeDepFilename, pEntry->New.pszObjName, pEntry->pszDir,
3255 pEntry->fMakeDepFixCase, pEntry->fMakeDepQuiet, pEntry->fMakeDepGenStubs);
[1040]3256}
3257
3258
3259/**
3260 * Worker function for kOCEntryTeeConsumer and kOCEntryCompileIt that
[2612]3261 * writes the preprocessor output to disk.
[1040]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{
[1008]3268 /*
[1040]3269 * Remove old files.
[1001]3270 */
[1040]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)
[1001]3280 {
[1040]3281 long cbLeft;
3282 char *psz;
[1042]3283 int fd = OpenFileInDir(pEntry->New.pszCppName, pEntry->pszDir,
3284 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
[1040]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;
[1184]3289 cbLeft = (long)pEntry->New.cbCpp;
[1040]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 }
[1042]3303
3304 psz += cbWritten;
3305 cbLeft -= cbWritten;
[1040]3306 }
3307 close(fd);
3308 }
[1001]3309
[1040]3310 /*
3311 * Free it.
3312 */
3313 if (fFreeIt)
3314 {
3315 free(pEntry->New.pszCppMapping);
3316 pEntry->New.pszCppMapping = NULL;
[1001]3317 }
[1040]3318}
[1001]3319
[1040]3320
3321/**
[2612]3322 * kOCEntrySpawnConsumer callback that passes the preprocessor output to the
3323 * compiler and writes it to the disk (latter only when necesary).
[1040]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;
[1184]3331 long cbLeft = (long)pEntry->New.cbCpp;
[1040]3332 while (cbLeft > 0)
3333 {
3334 long cbWritten = write(fdOut, psz, cbLeft);
3335 if (cbWritten < 0)
3336 {
3337 if (errno == EINTR)
3338 continue;
[2456]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
[1040]3350 FatalDie("compile - write(%d,,%ld) failed: %s\n", fdOut, cbLeft, strerror(errno));
[2456]3351#endif
[1040]3352 }
3353 psz += cbWritten;
3354 cbLeft -= cbWritten;
3355 }
[1044]3356 close(fdOut);
[1040]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{
[1001]3370 /*
[1040]3371 * Delete the object files and free old cpp output that's no longer needed.
[1001]3372 */
[1040]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 */);
[1045]3393 InfoMsg(3, "compiling -> '%s'...\n", pEntry->New.pszObjName);
[2456]3394 kOCEntrySpawnConsumer(pEntry, (const char * const *)pEntry->New.papszArgvCompile,
3395 pEntry->New.cArgvCompile, "compile", kOCEntryCompileProducer);
[1040]3396 }
[1002]3397 else
[1008]3398 {
[1040]3399 if (pEntry->fPipedPreComp)
3400 kOCEntryWriteCppOutput(pEntry, 1 /* free it */);
[1045]3401 InfoMsg(3, "compiling -> '%s'...\n", pEntry->New.pszObjName);
[2456]3402 kOCEntrySpawn(pEntry, &pEntry->New.cMsCompile, (const char * const *)pEntry->New.papszArgvCompile,
3403 pEntry->New.cArgvCompile, "compile", NULL);
[1008]3404 }
[1001]3405}
3406
3407
3408/**
[1040]3409 * kOCEntrySpawnTee callback that works sort of like 'tee'.
3410 *
[2612]3411 * It will calculate the preprocessed output checksum and
[1040]3412 * write it to disk while the compiler is busy compiling it.
3413 *
3414 * @param pEntry The cache entry.
[2612]3415 * @param fdIn The input handle (connected to the preprocessor).
[1040]3416 * @param fdOut The output handle (connected to the compiler).
3417 */
3418static void kOCEntryTeeConsumer(PKOCENTRY pEntry, int fdIn, int fdOut)
3419{
[2456]3420#ifdef __WIN__
3421 unsigned fConnectedToCompiler = fdOut == -1 || pEntry->pszNmPipeCompile == NULL;
3422#endif
[1040]3423 KOCSUMCTX Ctx;
[2616]3424 KOCCPPRD CppRd;
[1040]3425
3426 kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx);
[2618]3427 kOCCppRdInit(&CppRd, pEntry->Old.cbCpp, pEntry->fOptimizeCpp,
3428 pEntry->pszMakeDepFilename ? &pEntry->DepState : NULL);
[2612]3429 InfoMsg(3, "preprocessor|compile - starting passhtru...\n");
[1040]3430 for (;;)
3431 {
3432 /*
3433 * Read data from the pipe.
3434 */
[2616]3435 const char *psz;
3436 long cbRead = kOCCppRdRead(&CppRd, fdIn, &psz);
[1040]3437 if (!cbRead)
3438 break;
[2612]3439 InfoMsg(3, "preprocessor|compile - read %d\n", cbRead);
[1040]3440
3441 /*
3442 * Process the data.
3443 */
3444 kOCSumUpdate(&pEntry->New.SumHead, &Ctx, psz, cbRead);
[2618]3445 if (pEntry->pszMakeDepFilename && !pEntry->fOptimizeCpp)
[2612]3446 kOCDepConsumer(&pEntry->DepState, psz, cbRead);
3447
[2456]3448#ifdef __WIN__
3449 if ( !fConnectedToCompiler
3450 && !(fConnectedToCompiler = ConnectNamedPipe((HANDLE)_get_osfhandle(fdOut), NULL)))
[2612]3451 FatalDie("preprocess|compile - ConnectNamedPipe failed: %d\n", GetLastError());
[2456]3452#endif
[1040]3453 do
3454 {
3455 long cbWritten = write(fdOut, psz, cbRead);
3456 if (cbWritten < 0)
3457 {
3458 if (errno == EINTR)
3459 continue;
[2612]3460 FatalDie("preprocess|compile - write(%d,,%ld) failed: %s\n", fdOut, cbRead, strerror(errno));
[1040]3461 }
3462 psz += cbWritten;
3463 cbRead -= cbWritten;
3464 } while (cbRead > 0);
3465
3466 }
[2612]3467 InfoMsg(3, "preprocessor|compile - done passhtru\n");
[1040]3468
3469 close(fdIn);
3470 close(fdOut);
[2616]3471 kOCCppRdGrabOutput(&CppRd, &pEntry->New.pszCppMapping, &pEntry->New.cbCpp);
3472 kOCCppRdDelete(&CppRd);
[1040]3473 kOCSumFinalize(&pEntry->New.SumHead, &Ctx);
[1045]3474 kOCSumInfo(&pEntry->New.SumHead, 4, "cpp (tee)");
[1040]3475
3476 /*
[2612]3477 * Write the preprocessor output to disk and free the memory it
[1040]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.
[2612]3488 * @param papszArgvPreComp The argument vector for executing preprocessor.
3489 * The cArgvPreComp'th argument must be NULL.
[1040]3490 * @param cArgvPreComp The number of arguments.
3491 */
[2612]3492static void kOCEntryPreProcessAndCompile(PKOCENTRY pEntry, const char * const *papszArgvPreComp, unsigned cArgvPreComp)
[1040]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 /*
[2612]3510 * Do the actual compile and write the preprocessor output to disk.
[1040]3511 */
3512 kOCEntrySpawnTee(pEntry, papszArgvPreComp, cArgvPreComp,
[1043]3513 (const char * const *)pEntry->New.papszArgvCompile, pEntry->New.cArgvCompile,
[2612]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);
[1040]3518 }
3519 else
3520 {
[2612]3521 kOCEntryPreProcess(pEntry, papszArgvPreComp, cArgvPreComp);
[1040]3522 kOCEntryCompileIt(pEntry);
3523 }
3524}
3525
3526
3527/**
[1017]3528 * Check whether the string is a '#line' statement.
[1038]3529 *
[1017]3530 * @returns 1 if it is, 0 if it isn't.
[1038]3531 * @param psz The line to examin.
[1017]3532 * @parma piLine Where to store the line number.
3533 * @parma ppszFile Where to store the start of the filename.
3534 */
[1040]3535static int kOCEntryIsLineStatement(const char *psz, unsigned *piLine, const char **ppszFile)
[1017]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;
[1038]3555 do
[1017]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.
[1038]3578 *
[1017]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 */
[1040]3584static const char *kOCEntryFindFileStatement(const char *pszStart, const char *pszStop, unsigned *piLine)
[1017]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')
[1040]3600 && kOCEntryIsLineStatement(pszStart, &iLineTmp, &pszFile))
[1017]3601 {
3602 *piLine = iLine + iLineTmp - 1;
3603 return pszFile;
3604 }
3605 }
3606 pszStart--;
3607 }
3608 return NULL;
3609}
3610
3611
3612/**
[1040]3613 * Worker for kOCEntryCompareOldAndNewOutput() that compares the
[2612]3614 * preprocessed output using a fast but not very good method.
[1038]3615 *
3616 * @returns 1 if matching, 0 if not matching.
[1017]3617 * @param pEntry The entry containing the names of the files to compare.
3618 * The entry is not updated in any way.
3619 */
[1040]3620static int kOCEntryCompareFast(PCKOCENTRY pEntry)
[1017]3621{
[1040]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;
[1017]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
[1038]3652 * work on that isn't spaces, #line statements or closing curly
3653 * braces.
[1036]3654 *
[1038]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.
[1036]3660 *
[1038]3661 * Since we might be skipping a few new empty headers, it is
3662 * possible that we will omit this header from the dependencies
[1036]3663 * when using VCC. This might not be a problem, since it seems
[2612]3664 * we'll have to use the preprocessor output to generate the deps
[1036]3665 * anyway.
[1017]3666 */
3667 const char *psz;
3668 const char *pszMismatch1;
3669 const char *pszFile1 = NULL;
3670 unsigned iLine1 = 0;
[1036]3671 unsigned cCurlyBraces1 = 0;
[1017]3672 const char *pszMismatch2;
3673 const char *pszFile2 = NULL;
3674 unsigned iLine2 = 0;
[1036]3675 unsigned cCurlyBraces2 = 0;
[1017]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;
[1040]3687 while ( psz > pEntry->New.pszCppMapping
[1017]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 }
[1040]3712 else if (*psz == '#' && kOCEntryIsLineStatement(psz, &iLine1, &pszFile1))
[1017]3713 {
3714 psz1 = memchr(psz, '\n', pszEnd1 - psz);
3715 if (!psz1++)
3716 psz1 = pszEnd1;
3717 }
[1036]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 }
[1017]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 }
[1040]3754 else if (*psz == '#' && kOCEntryIsLineStatement(psz, &iLine2, &pszFile2))
[1017]3755 {
3756 psz2 = memchr(psz, '\n', pszEnd2 - psz);
3757 if (!psz2++)
3758 psz2 = pszEnd2;
3759 }
[1036]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 }
[1017]3771 else if (psz == pszEnd2)
3772 psz2 = psz;
3773 else /* found something that can be compared. */
3774 break;
3775 }
3776 }
3777
[1036]3778 /* Match the number of ignored closing curly braces. */
3779 if (cCurlyBraces1 != cCurlyBraces2)
3780 return 0;
3781
[1017]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);
[1087]3789 if (!psz++)
[1017]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)
[1040]3799 pszFile1 = kOCEntryFindFileStatement(pszMismatch1, pEntry->New.pszCppMapping, &iLine1);
[1017]3800 if (!pszFile2)
[1040]3801 pszFile2 = kOCEntryFindFileStatement(pszMismatch2, pEntry->Old.pszCppMapping, &iLine2);
[1017]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
[1087]3817 /* Advance. We might now have a misaligned buffer, but that's memcmps problem... */
[1017]3818 psz1 += cch;
3819 psz2 += cch;
3820 }
3821 }
3822
[1038]3823 return psz1 == pszEnd1
[1017]3824 && psz2 == pszEnd2;
3825}
3826
3827
3828/**
[1040]3829 * Worker for kOCEntryCompileIfNeeded that compares the
[2612]3830 * preprocessed output.
[1038]3831 *
3832 * @returns 1 if matching, 0 if not matching.
[1001]3833 * @param pEntry The entry containing the names of the files to compare.
[1040]3834 * This will load the old cpp output (changing pszOldCppName and Old.cbCpp).
[1001]3835 */
[1040]3836static int kOCEntryCompareOldAndNewOutput(PKOCENTRY pEntry)
[1001]3837{
[1017]3838 /*
[1040]3839 * I may implement a more sophisticated alternative method later... maybe.
[1017]3840 */
[1040]3841 if (kOCEntryReadCppOutput(pEntry, &pEntry->Old, 1 /* nonfatal */) == -1)
[1017]3842 return 0;
[1732]3843 /*if ()
3844 return kOCEntryCompareBest(pEntry);*/
[1040]3845 return kOCEntryCompareFast(pEntry);
[1001]3846}
3847
3848
3849/**
[1040]3850 * Check if re-compilation is required.
3851 * This sets the fNeedCompile flag.
[1038]3852 *
[1001]3853 * @param pEntry The cache entry.
3854 */
[1040]3855static void kOCEntryCalcRecompile(PKOCENTRY pEntry)
[1001]3856{
[1040]3857 if (pEntry->fNeedCompiling)
3858 return;
[1001]3859
3860 /*
[2612]3861 * Check if the preprocessor output differ in any significant way?
[1008]3862 */
[1040]3863 if (!kOCSumHasEqualInChain(&pEntry->Old.SumHead, &pEntry->New.SumHead))
[1008]3864 {
[2619]3865 if (pEntry->fOptimizeCpp & 2)
3866 {
3867 InfoMsg(2, "no checksum match - no need to compare output, -O2.\n");
[1040]3868 pEntry->fNeedCompiling = 1;
[2619]3869 }
[1040]3870 else
[2619]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 }
[1008]3878 }
[1040]3879}
[1008]3880
[1001]3881
[1040]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;
[1001]3891}
3892
3893
3894/**
[2620]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/**
[1040]3934 * Worker function for kOCEntryCopy.
[1038]3935 *
[1040]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.
[1001]3939 */
[1040]3940static void kOCEntryCopyFile(PCKOCENTRY pEntry, const char *pszTo, char *pszSrc)
[1001]3941{
[1040]3942 char *pszDst = MakePathFromDirAndFile(pszTo, pEntry->pszDir);
[2620]3943 unlink(pszDst);
3944 if (!kOCEntryTryHardlink(pszDst, pszSrc))
3945 {
3946 char *pszBuf = xmalloc(256 * 1024);
3947 char *psz;
3948 int fdSrc;
3949 int fdDst;
[1001]3950
[2620]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));
[1001]3957
[2620]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));
[1001]3961
[2620]3962 /*
3963 * Copy them.
3964 */
3965 for (;;)
[1040]3966 {
[2620]3967 /* read a chunk. */
3968 long cbRead = read(fdSrc, pszBuf, 256*1024);
3969 if (cbRead < 0)
[1001]3970 {
[1040]3971 if (errno == EINTR)
3972 continue;
[2620]3973 FatalDie("read '%s' failed: %s\n", pszSrc, strerror(errno));
[1001]3974 }
[2620]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);
[1001]3999 }
[1040]4000 free(pszDst);
4001 free(pszSrc);
4002}
[1001]4003
[1040]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));
[1001]4020}
4021
4022
4023/**
[1040]4024 * Gets the absolute path to the cache entry.
[1038]4025 *
[1040]4026 * @returns absolute path to the cache entry.
4027 * @param pEntry The cache entry in question.
[1002]4028 */
[1040]4029static const char *kOCEntryAbsPath(PCKOCENTRY pEntry)
[1002]4030{
[1040]4031 return pEntry->pszAbsPath;
[1002]4032}
4033
4034
[1040]4035
4036
4037
4038
[1002]4039/**
[1040]4040 * Digest of one cache entry.
[1038]4041 *
[1040]4042 * This contains all the information required to find a matching
4043 * cache entry without having to open each of the files.
[1001]4044 */
[1040]4045typedef struct KOCDIGEST
[1001]4046{
[1040]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;
[2612]4058 /** The list of preprocessor output checksums that's . */
[1040]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);
[1001]4076}
4077
4078
4079/**
[1040]4080 * Initializes the digest for the specified entry.
[1038]4081 *
[1040]4082 * @param pDigest The (uninitialized) digest.
4083 * @param pEntry The entry.
[1001]4084 */
[1040]4085static void kOCDigestInitFromEntry(PKOCDIGEST pDigest, PCKOCENTRY pEntry)
[1001]4086{
[1040]4087 kOCDigestInit(pDigest);
4088
4089 pDigest->uKey = pEntry->uKey;
[1042]4090 pDigest->pszTarget = xstrdup(pEntry->New.pszTarget ? pEntry->New.pszTarget : pEntry->Old.pszTarget);
[1040]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));
[1001]4107}
4108
4109
4110/**
[1040]4111 * Purges a digest, freeing all resources and returning
4112 * it to the initial state.
[1038]4113 *
[1040]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.
[1038]4131 *
[1040]4132 * @returns absolute path.
4133 * @param pDigest The digest.
4134 * @param pszDir The cache directory that it might be relative to.
[1001]4135 */
[1040]4136static const char *kOCDigestAbsPath(PCKOCDIGEST pDigest, const char *pszDir)
[1001]4137{
[1040]4138 if (!pDigest->pszAbsPath)
[1001]4139 {
[1040]4140 char *pszPath = MakePathFromDirAndFile(pDigest->pszRelPath, pszDir);
4141 ((PKOCDIGEST)pDigest)->pszAbsPath = AbsPath(pszPath);
4142 free(pszPath);
[1001]4143 }
[1040]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
[1002]4179 return 1;
4180}
4181
4182
[1040]4183
4184
4185
[1002]4186/**
[1040]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;
[1042]4200 /** The stream associated with fd. */
4201 FILE *pFile;
[1040]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.
[1038]4228 *
[1040]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.
[1002]4233 */
[1040]4234static PKOBJCACHE kObjCacheCreate(const char *pszCacheFile)
[1002]4235{
[1040]4236 PKOBJCACHE pCache;
4237 size_t off;
[1002]4238
4239 /*
[1040]4240 * Allocate an empty entry.
[1002]4241 */
[1040]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);
[1042]4253 pCache->pszDir = xmalloc(off);
4254 memcpy(pCache->pszDir, pCache->pszAbsPath, off - 1);
[1040]4255 pCache->pszDir[off - 1] = '\0';
4256
4257 return pCache;
4258}
4259
4260
4261/**
[1042]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/**
[1040]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;
[1042]4307 char szBuf[8192];
[1040]4308 int fBad = 0;
4309
[1045]4310 InfoMsg(4, "reading cache file...\n");
4311
[1040]4312 /*
[1042]4313 * Rewind the file & stream, and associate a temporary buffer
4314 * with the stream to speed up reading.
[1040]4315 */
4316 if (lseek(pCache->fd, 0, SEEK_SET) == -1)
4317 FatalDie("lseek(cache-fd) failed: %s\n", strerror(errno));
[1042]4318 rewind(pCache->pFile);
4319 if (setvbuf(pCache->pFile, szBuf, _IOFBF, sizeof(szBuf)) != 0)
[1040]4320 FatalDie("fdopen(cache-fd,rb) failed: %s\n", strerror(errno));
4321
4322 /*
4323 * Read magic and generation.
4324 */
[1042]4325 if ( !fgets(g_szLine, sizeof(g_szLine), pCache->pFile)
[1040]4326 || strcmp(g_szLine, "magic=kObjCache-v0.1.0\n"))
[1001]4327 {
[1045]4328 InfoMsg(2, "bad cache file (magic)\n");
[1040]4329 fBad = 1;
[1001]4330 }
[1042]4331 else if ( !fgets(g_szLine, sizeof(g_szLine), pCache->pFile)
[1040]4332 || strncmp(g_szLine, "generation=", sizeof("generation=") - 1))
4333 {
[1045]4334 InfoMsg(2, "bad cache file (generation)\n");
[1040]4335 fBad = 1;
4336 }
4337 else if ( pCache->uGeneration
[1732]4338 && (long)pCache->uGeneration == atol(&g_szLine[sizeof("generation=") - 1]))
[1040]4339 {
[1045]4340 InfoMsg(3, "drop re-read unmodified cache file\n");
[1040]4341 fBad = 0;
4342 }
[1002]4343 else
4344 {
[1040]4345 int fBadBeforeMissing;
4346
4347 /*
4348 * Read everything (anew).
4349 */
4350 kObjCachePurge(pCache);
4351 do
[1002]4352 {
[1040]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 {
[1042]4372 char *pszNext;
4373 i = strtoul(++psz, &pszNext, 0);
4374 if ((fBad = pszNext && *pszNext))
[1040]4375 break;
4376 if ((fBad = i >= pCache->cDigests))
4377 break;
4378 pDigest = &pCache->paDigests[i];
[1042]4379 *psz = '\0';
[1040]4380 }
[1002]4381 else
[1040]4382 pDigest = NULL;
4383
4384
4385 /* string case on value name. */
[1042]4386 if (!strcmp(g_szLine, "sum-#"))
[1040]4387 {
4388 KOCSUM Sum;
4389 if ((fBad = kOCSumInitFromString(&Sum, pszVal) != 0))
4390 break;
4391 kOCSumAdd(&pDigest->SumHead, &Sum);
4392 }
[1042]4393 else if (!strcmp(g_szLine, "digest-abs-#"))
[1040]4394 {
4395 if ((fBad = pDigest->pszAbsPath != NULL))
4396 break;
4397 pDigest->pszAbsPath = xstrdup(pszVal);
4398 }
[1042]4399 else if (!strcmp(g_szLine, "digest-rel-#"))
[1040]4400 {
4401 if ((fBad = pDigest->pszRelPath != NULL))
4402 break;
4403 pDigest->pszRelPath = xstrdup(pszVal);
4404 }
[1042]4405 else if (!strcmp(g_szLine, "key-#"))
[1040]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 }
[1042]4415 else if (!strcmp(g_szLine, "comp-argv-sum-#"))
[1040]4416 {
4417 if ((fBad = !kOCSumIsEmpty(&pDigest->SumCompArgv)))
4418 break;
4419 if ((fBad = kOCSumInitFromString(&pDigest->SumCompArgv, pszVal) != 0))
4420 break;
4421 }
[1042]4422 else if (!strcmp(g_szLine, "target-#"))
[1040]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 }
[1042]4458 } while (fgets(g_szLine, sizeof(g_szLine), pCache->pFile));
[1040]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;
[1045]4481 InfoMsg(4, "digest-%u: %s\n", i, pCache->paDigests[i].pszAbsPath
4482 ? pCache->paDigests[i].pszAbsPath : pCache->paDigests[i].pszRelPath);
[1040]4483 }
4484 if (fBad)
[1045]4485 InfoMsg(2, "bad cache file (%s)\n", fBadBeforeMissing ? g_szLine : "missing stuff");
[1042]4486 else if (ferror(pCache->pFile))
[1040]4487 {
[1045]4488 InfoMsg(2, "cache file read error\n");
[1040]4489 fBad = 1;
[1002]4490 }
4491 }
[1040]4492 if (fBad)
[1002]4493 {
[1040]4494 kObjCachePurge(pCache);
4495 pCache->fNewCache = 1;
[1002]4496 }
[1001]4497
4498 /*
[1042]4499 * Disassociate the buffer from the stream changing
4500 * it to non-buffered mode.
[1001]4501 */
[1042]4502 if (setvbuf(pCache->pFile, NULL, _IONBF, 0) != 0)
4503 FatalDie("setvbuf(,0,,0) failed: %s\n", strerror(errno));
[1001]4504}
4505
4506
4507/**
[1040]4508 * Re-writes the cache file.
[1038]4509 *
[1040]4510 * @param pCache The cache to commit and unlock.
[1001]4511 */
[1040]4512static void kObjCacheWrite(PKOBJCACHE pCache)
[1001]4513{
[1040]4514 unsigned i;
4515 off_t cb;
[1042]4516 char szBuf[8192];
[1040]4517 assert(pCache->fLocked);
4518 assert(pCache->fDirty);
4519
4520 /*
[1042]4521 * Rewind the file & stream, and associate a temporary buffer
4522 * with the stream to speed up the writing.
[1040]4523 */
4524 if (lseek(pCache->fd, 0, SEEK_SET) == -1)
4525 FatalDie("lseek(cache-fd) failed: %s\n", strerror(errno));
[1042]4526 rewind(pCache->pFile);
4527 if (setvbuf(pCache->pFile, szBuf, _IOFBF, sizeof(szBuf)) != 0)
4528 FatalDie("setvbuf failed: %s\n", strerror(errno));
[1040]4529
4530 /*
4531 * Write the header.
4532 */
4533 pCache->uGeneration++;
[1042]4534 fprintf(pCache->pFile,
[1040]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 */
[1042]4544 for (i = 0; i < pCache->cDigests; i++)
[1040]4545 {
4546 PCKOCDIGEST pDigest = &pCache->paDigests[i];
4547 PKOCSUM pSum;
4548
4549 if (pDigest->pszAbsPath)
[1042]4550 fprintf(pCache->pFile, "digest-abs-#%u=%s\n", i, pDigest->pszAbsPath);
[1040]4551 if (pDigest->pszRelPath)
[1042]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);
[1040]4557 for (pSum = &pDigest->SumHead; pSum; pSum = pSum->pNext)
4558 {
[1042]4559 fprintf(pCache->pFile, "sum-#%u=", i);
4560 kOCSumFPrintf(pSum, pCache->pFile);
[1040]4561 }
4562 }
4563
4564 /*
4565 * Close the stream and unlock fhe file.
4566 * (Closing the stream shouldn't close the file handle IIRC...)
4567 */
[1042]4568 fprintf(pCache->pFile, "the-end=fine\n");
4569 errno = 0;
4570 if ( fflush(pCache->pFile) < 0
4571 || ferror(pCache->pFile))
[1040]4572 {
4573 int iErr = errno;
[1042]4574 fclose(pCache->pFile);
[1040]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 }
[1042]4579 if (setvbuf(pCache->pFile, NULL, _IONBF, 0) != 0)
4580 FatalDie("setvbuf(,0,,0) failed: %s\n", strerror(errno));
[1040]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));
[1045]4591 InfoMsg(4, "wrote '%s' in '%s', %d bytes\n", pCache->pszName, pCache->pszDir, cb);
[1001]4592}
4593
4594
4595/**
[1040]4596 * Cleans out all invalid digests.s
[1038]4597 *
[1040]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.
[1001]4602 */
[1040]4603static void kObjCacheClean(PKOBJCACHE pCache)
[1001]4604{
[1040]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 }
[1001]4630}
4631
4632
4633/**
[1040]4634 * Locks the cache for exclusive access.
[1038]4635 *
[1040]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.
[1001]4640 */
[1040]4641static void kObjCacheLock(PKOBJCACHE pCache)
[1001]4642{
[1040]4643 struct stat st;
[1042]4644#if defined(__WIN__)
4645 OVERLAPPED OverLapped;
4646#endif
4647
[1040]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 }
[1042]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));
[1040]4669 }
4670
4671 /*
4672 * Lock it.
4673 */
4674#if defined(__WIN__)
[1042]4675 memset(&OverLapped, 0, sizeof(OverLapped));
4676 if (!LockFileEx((HANDLE)_get_osfhandle(pCache->fd), LOCKFILE_EXCLUSIVE_LOCK, 0, ~0, 0, &OverLapped))
[1040]4677 FatalDie("Failed to lock the cache file: Windows Error %d\n", GetLastError());
[1089]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 }
[1040]4688#else
[1043]4689 if (flock(pCache->fd, LOCK_EX) != 0)
4690 FatalDie("Failed to lock the cache file: %s\n", strerror(errno));
[1040]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
[1045]4706 {
[1040]4707 pCache->fNewCache = 1;
[1045]4708 InfoMsg(2, "the cache file is empty\n");
4709 }
[1001]4710}
4711
4712
4713/**
[1040]4714 * Unlocks the cache (without writing anything back).
[1038]4715 *
[1040]4716 * @param pCache The cache to unlock.
[1001]4717 */
[1040]4718static void kObjCacheUnlock(PKOBJCACHE pCache)
[1001]4719{
[1042]4720#if defined(__WIN__)
4721 OVERLAPPED OverLapped;
4722#endif
[1040]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__)
[1042]4741 memset(&OverLapped, 0, sizeof(OverLapped));
4742 if (!UnlockFileEx((HANDLE)_get_osfhandle(pCache->fd), 0, ~0U, 0, &OverLapped))
[1040]4743 FatalDie("Failed to unlock the cache file: Windows Error %d\n", GetLastError());
[1089]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 }
[1001]4754#else
[1043]4755 if (flock(pCache->fd, LOCK_UN) != 0)
4756 FatalDie("Failed to unlock the cache file: %s\n", strerror(errno));
[1038]4757#endif
[1040]4758 pCache->fLocked = 0;
[1001]4759}
4760
4761
4762/**
[1040]4763 * Removes the entry from the cache.
[1038]4764 *
[1040]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.
[1001]4770 */
[1040]4771static void kObjCacheRemoveEntry(PKOBJCACHE pCache, PCKOCENTRY pEntry)
[1001]4772{
[1040]4773 unsigned i = pCache->cDigests;
4774 while (i-- > 0)
[1001]4775 {
[1040]4776 PKOCDIGEST pDigest = &pCache->paDigests[i];
4777 if (ArePathsIdentical(kOCDigestAbsPath(pDigest, pCache->pszDir),
[2409]4778 kOCEntryAbsPath(pEntry)))
[1001]4779 {
[1040]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;
[1045]4789 InfoMsg(3, "removing entry '%s'; %d left.\n", kOCEntryAbsPath(pEntry), pCache->cDigests);
[1001]4790 }
4791 }
4792}
4793
4794
[1040]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)
[1002]4805{
[1040]4806 unsigned i;
4807
4808 /*
4809 * Find a new key.
4810 */
4811 pEntry->uKey = pCache->uNextKey++;
[1042]4812 if (!pEntry->uKey)
4813 pEntry->uKey = pCache->uNextKey++;
[1040]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))
[1042]4829 pCache->paDigests = xrealloc(pCache->paDigests, sizeof(pCache->paDigests[0]) * (pCache->cDigests + 4));
[1040]4830
4831 /*
4832 * Create a new digest.
4833 */
[1045]4834 kOCDigestInitFromEntry(&pCache->paDigests[pCache->cDigests], pEntry);
4835 pCache->cDigests++;
4836 InfoMsg(4, "Inserted digest #%u: %s\n", pCache->cDigests - 1, kOCEntryAbsPath(pEntry));
[1040]4837
4838 pCache->fDirty = 1;
[1002]4839}
[1001]4840
[1002]4841
[1040]4842/**
4843 * Find a matching cache entry.
4844 */
4845static PKOCENTRY kObjCacheFindMatchingEntry(PKOBJCACHE pCache, PCKOCENTRY pEntry)
[1002]4846{
[1040]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. */
[1045]4874 InfoMsg(3, "removing bad digest '%s'\n", kOCDigestAbsPath(pDigest, pCache->pszDir));
[1040]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;
[1002]4887}
4888
4889
[1040]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)
[1002]4897{
[1040]4898 return pCache->fNewCache;
[1002]4899}
4900
4901
[1001]4902/**
4903 * Prints a syntax error and returns the appropriate exit code
[1038]4904 *
[1001]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 */
[1184]4924static int usage(FILE *pOut)
[1001]4925{
[1184]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"
[2456]4932 " [-r|--redir-stdout] [-p|--passthru] [--named-pipe-compile <pipename>]\n"
[2612]4933 " --kObjCache-cpp <filename> <preprocessor + args>\n"
[1184]4934 " --kObjCache-cc <object> <compiler + args>\n"
4935 " [--kObjCache-both [args]]\n"
[1732]4936 );
4937 fprintf(pOut,
[1184]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");
[1001]4947 return 0;
4948}
4949
4950
4951int main(int argc, char **argv)
4952{
[1040]4953 PKOBJCACHE pCache;
4954 PKOCENTRY pEntry;
[1001]4955
[1040]4956 const char *pszCacheDir = getenv("KOBJCACHE_DIR");
4957 const char *pszCacheName = NULL;
4958 const char *pszCacheFile = NULL;
4959 const char *pszEntryFile = NULL;
[1001]4960
4961 const char **papszArgvPreComp = NULL;
4962 unsigned cArgvPreComp = 0;
4963 const char *pszPreCompName = NULL;
[1040]4964 int fRedirPreCompStdOut = 0;
[1001]4965
4966 const char **papszArgvCompile = NULL;
4967 unsigned cArgvCompile = 0;
4968 const char *pszObjName = NULL;
[1040]4969 int fRedirCompileStdIn = 0;
[2463]4970 const char *pszNmPipeCompile = NULL;
[1001]4971
[2612]4972 const char *pszMakeDepFilename = NULL;
4973 int fMakeDepFixCase = 0;
4974 int fMakeDepGenStubs = 0;
4975 int fMakeDepQuiet = 0;
[2616]4976 int fOptimizePreprocessorOutput = 0;
[2612]4977
[1040]4978 const char *pszTarget = NULL;
4979
[1001]4980 enum { kOC_Options, kOC_CppArgv, kOC_CcArgv, kOC_BothArgv } enmMode = kOC_Options;
4981
[1046]4982 size_t cch;
[1040]4983 char *psz;
[1001]4984 int i;
4985
[1040]4986 SetErrorPrefix("kObjCache");
4987
[1001]4988 /*
[1040]4989 * Arguments passed in the environmnet?
4990 */
4991 psz = getenv("KOBJCACHE_OPTS");
4992 if (psz)
4993 AppendArgs(&argc, &argv, psz, "--kObjCache-options");
4994
4995 /*
[1001]4996 * Parse the arguments.
4997 */
[1041]4998 if (argc <= 1)
[1184]4999 return usage(stderr);
[1001]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;
[1002]5015 if (!pszObjName)
[1001]5016 {
5017 if (++i >= argc)
[2612]5018 return SyntaxError("--kObjCache-cc requires an preprocessor output filename!\n");
[1001]5019 pszObjName = argv[i];
5020 }
5021 }
5022 else if (!strcmp(argv[i], "--kObjCache-both"))
5023 enmMode = kOC_BothArgv;
[1040]5024 else if (!strcmp(argv[i], "--kObjCache-options"))
5025 enmMode = kOC_Options;
[1001]5026 else if (!strcmp(argv[i], "--help"))
[1184]5027 return usage(stderr);
[1001]5028 else if (enmMode != kOC_Options)
5029 {
5030 if (enmMode == kOC_CppArgv || enmMode == kOC_BothArgv)
5031 {
5032 if (!(cArgvPreComp % 16))
[1002]5033 papszArgvPreComp = xrealloc((void *)papszArgvPreComp, (cArgvPreComp + 17) * sizeof(papszArgvPreComp[0]));
[1001]5034 papszArgvPreComp[cArgvPreComp++] = argv[i];
5035 papszArgvPreComp[cArgvPreComp] = NULL;
5036 }
5037 if (enmMode == kOC_CcArgv || enmMode == kOC_BothArgv)
5038 {
5039 if (!(cArgvCompile % 16))
[1002]5040 papszArgvCompile = xrealloc((void *)papszArgvCompile, (cArgvCompile + 17) * sizeof(papszArgvCompile[0]));
[1001]5041 papszArgvCompile[cArgvCompile++] = argv[i];
5042 papszArgvCompile[cArgvCompile] = NULL;
5043 }
5044 }
[1040]5045 else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--entry-file"))
[1001]5046 {
5047 if (i + 1 >= argc)
[1040]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)
[1001]5054 return SyntaxError("%s requires a cache filename!\n", argv[i]);
5055 pszCacheFile = argv[++i];
5056 }
[1040]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 }
[2456]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 }
[2612]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;
[2616]5094 else if (!strcmp(argv[i], "-O1") || !strcmp(argv[i], "--optimize-1"))
5095 fOptimizePreprocessorOutput = 1;
[2619]5096 else if (!strcmp(argv[i], "-O2") || !strcmp(argv[i], "--optimize-2"))
5097 fOptimizePreprocessorOutput = 1 | 2;
[1040]5098 else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--passthru"))
5099 fRedirPreCompStdOut = fRedirCompileStdIn = 1;
[1002]5100 else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--redir-stdout"))
[1040]5101 fRedirPreCompStdOut = 1;
[1001]5102 else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))
[1040]5103 g_cVerbosityLevel++;
[1001]5104 else if (!strcmp(argv[i], "-q") || !strcmp(argv[i], "--quiet"))
[1040]5105 g_cVerbosityLevel = 0;
[1041]5106 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-?")
5107 || !strcmp(argv[i], "/h") || !strcmp(argv[i], "/?") || !strcmp(argv[i], "/help"))
[1184]5108 {
5109 usage(stdout);
5110 return 0;
5111 }
[1001]5112 else if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version"))
5113 {
[1184]5114 printf("kObjCache - kBuild version %d.%d.%d ($Revision: 3315 $)\n"
[2612]5115 "Copyright (c) 2007-2012 knut st. osmundsen\n",
[1184]5116 KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH);
[1001]5117 return 0;
5118 }
5119 else
5120 return SyntaxError("Doesn't grok '%s'!\n", argv[i]);
5121 }
[1040]5122 if (!pszEntryFile)
5123 return SyntaxError("No cache entry filename (-f)!\n");
[1041]5124 if (!pszTarget)
5125 return SyntaxError("No target name (-t)!\n");
[1001]5126 if (!cArgvCompile)
[1040]5127 return SyntaxError("No compiler arguments (--kObjCache-cc)!\n");
[1001]5128 if (!cArgvPreComp)
[2612]5129 return SyntaxError("No preprocessor arguments (--kObjCache-cc)!\n");
[1001]5130
5131 /*
[1040]5132 * Calc the cache file name.
[1046]5133 * It's a bit messy since the extension has to be replaced.
[1001]5134 */
[1040]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 {
[1046]5141 psz = (char *)FindFilenameInPath(pszEntryFile);
5142 if (!*psz)
[1040]5143 return SyntaxError("The cache file (-f) specifies a directory / nothing!\n");
[1046]5144 cch = psz - pszEntryFile;
5145 pszCacheName = memcpy(xmalloc(cch + 5), psz, cch + 1);
[1040]5146 psz = strrchr(pszCacheName, '.');
[1046]5147 if (!psz || psz <= pszCacheName)
5148 psz = (char *)pszCacheName + cch;
[2616]5149 memcpy(psz, ".koc", sizeof(".koc"));
[1040]5150 }
[1044]5151 pszCacheFile = MakePathFromDirAndFile(pszCacheName, pszCacheDir);
[1040]5152 }
[1001]5153
5154 /*
[1046]5155 * Create and initialize the two objects we'll be working on.
[1040]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.
[1001]5160 */
[1040]5161 SetErrorPrefix("kObjCache - %s", FindFilenameInPath(pszCacheFile));
5162 pCache = kObjCacheCreate(pszCacheFile);
[1001]5163
[1042]5164 pEntry = kOCEntryCreate(pszEntryFile);
[1040]5165 kOCEntryRead(pEntry);
[2614]5166 kOCEntrySetCppName(pEntry, pszPreCompName);
[1045]5167 kOCEntrySetCompileObjName(pEntry, pszObjName);
[1040]5168 kOCEntrySetCompileArgv(pEntry, papszArgvCompile, cArgvCompile);
5169 kOCEntrySetTarget(pEntry, pszTarget);
[2456]5170 kOCEntrySetPipedMode(pEntry, fRedirPreCompStdOut, fRedirCompileStdIn, pszNmPipeCompile);
[2612]5171 kOCEntrySetDepFilename(pEntry, pszMakeDepFilename, fMakeDepFixCase, fMakeDepQuiet, fMakeDepGenStubs);
[2616]5172 kOCEntrySetOptimizations(pEntry, fOptimizePreprocessorOutput);
[1040]5173
[1001]5174 /*
[1040]5175 * Open (& lock) the two files and do validity checks and such.
[1001]5176 */
[1040]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 */
[1042]5185 kObjCacheUnlock(pCache);
[1045]5186 InfoMsg(1, "doing full compile\n");
[2612]5187 kOCEntryPreProcessAndCompile(pEntry, papszArgvPreComp, cArgvPreComp);
[1042]5188 kObjCacheLock(pCache);
[1040]5189 }
5190 else
5191 {
5192 /*
[2612]5193 * Do the preprocess (don't need to lock the cache file for this).
[1040]5194 */
5195 kObjCacheUnlock(pCache);
[2612]5196 kOCEntryPreProcess(pEntry, papszArgvPreComp, cArgvPreComp);
[1040]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 {
[1045]5210 InfoMsg(1, "using cache entry '%s'\n", kOCEntryAbsPath(pUseEntry));
[1040]5211 kOCEntryCopy(pEntry, pUseEntry);
5212 kOCEntryDestroy(pUseEntry);
5213 }
5214 else
5215 {
5216 kObjCacheUnlock(pCache);
[1045]5217 InfoMsg(1, "recompiling\n");
[1040]5218 kOCEntryCompileIt(pEntry);
5219 kObjCacheLock(pCache);
5220 }
5221 }
[1042]5222 else
[1045]5223 {
5224 InfoMsg(1, "no need to recompile\n");
[1042]5225 kObjCacheLock(pCache);
[1045]5226 }
[1040]5227 }
5228
5229 /*
5230 * Update the cache files.
5231 */
[1044]5232 kObjCacheRemoveEntry(pCache, pEntry);
[1040]5233 kObjCacheInsertEntry(pCache, pEntry);
5234 kOCEntryWrite(pEntry);
5235 kObjCacheUnlock(pCache);
[1042]5236 kObjCacheDestroy(pCache);
[2617]5237 if (fOptimizePreprocessorOutput)
5238 {
[2619]5239 InfoMsg(3, "g_cbMemMoved=%#x (%d)\n", g_cbMemMoved, g_cbMemMoved);
5240 InfoMsg(3, "g_cMemMoves=%#x (%d)\n", g_cMemMoves, g_cMemMoves);
[2617]5241 }
5242
[1001]5243 return 0;
5244}
5245
[1017]5246
5247/** @page kObjCache Benchmarks.
[1038]5248 *
[1049]5249 * (2007-06-10)
[1038]5250 *
[1048]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 *
[1049]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):
[1048]5257 * real 1m26.895s
5258 * user 1m26.971s
5259 * sys 0m32.532s
5260 *
[1049]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):
[1048]5262 * real 1m18.049s
5263 * user 1m20.462s
5264 * sys 0m27.887s
5265 *
[1049]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.)
[1017]5303 */
[1049]5304
Note: See TracBrowser for help on using the repository browser.