VirtualBox

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

Last change on this file since 1223 was 1184, checked in by bird, 17 years ago

Usage and version. Shut up a couple of warnings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 116.8 KB
Line 
1/* $Id: kObjCache.c 1184 2007-10-05 22:25:54Z bird $ */
2/** @file
3 * kObjCache - Object Cache.
4 */
5
6/*
7 * Copyright (c) 2007 knut st. osmundsen <[email protected]>
8 *
9 * This file is part of kBuild.
10 *
11 * kBuild is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * kBuild is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with kBuild; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 *
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#if 0
32# define ELECTRIC_HEAP
33# include "../kmk/electric.h"
34#endif
35#include <string.h>
36#include <stdlib.h>
37#include <stdarg.h>
38#include <stdio.h>
39#include <errno.h>
40#include <assert.h>
41#include <sys/stat.h>
42#include <fcntl.h>
43#include <limits.h>
44#include <ctype.h>
45#ifndef PATH_MAX
46# define PATH_MAX _MAX_PATH /* windows */
47#endif
48#if defined(__OS2__) || defined(__WIN__)
49# include <process.h>
50# include <io.h>
51# ifdef __OS2__
52# include <unistd.h>
53# include <sys/wait.h>
54# endif
55# if defined(_MSC_VER)
56# include <direct.h>
57 typedef intptr_t pid_t;
58# endif
59# ifndef _P_WAIT
60# define _P_WAIT P_WAIT
61# endif
62# ifndef _P_NOWAIT
63# define _P_NOWAIT P_NOWAIT
64# endif
65#else
66# include <unistd.h>
67# include <sys/wait.h>
68# ifndef O_BINARY
69# define O_BINARY 0
70# endif
71#endif
72#if defined(__WIN__)
73# include <Windows.h>
74#endif
75
76#include "crc32.h"
77#include "md5.h"
78
79
80/*******************************************************************************
81* Defined Constants And Macros *
82*******************************************************************************/
83/** The max line length in a cache file. */
84#define KOBJCACHE_MAX_LINE_LEN 16384
85#if defined(__WIN__)
86# define PATH_SLASH '\\'
87#else
88# define PATH_SLASH '/'
89#endif
90#if defined(__OS2__) || defined(__WIN__)
91# define IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
92# define IS_SLASH_DRV(ch) ((ch) == '/' || (ch) == '\\' || (ch) == ':')
93#else
94# define IS_SLASH(ch) ((ch) == '/')
95# define IS_SLASH_DRV(ch) ((ch) == '/')
96#endif
97
98#ifndef STDIN_FILENO
99# define STDIN_FILENO 0
100#endif
101#ifndef STDOUT_FILENO
102# define STDOUT_FILENO 1
103#endif
104#ifndef STDERR_FILENO
105# define STDERR_FILENO 2
106#endif
107
108
109/*******************************************************************************
110* Global Variables *
111*******************************************************************************/
112/** Whether verbose output is enabled. */
113static unsigned g_cVerbosityLevel = 0;
114/** What to prefix the errors with. */
115static char g_szErrorPrefix[128];
116
117/** Read buffer shared by the cache components. */
118static char g_szLine[KOBJCACHE_MAX_LINE_LEN + 16];
119
120
121/*******************************************************************************
122* Internal Functions *
123*******************************************************************************/
124static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir);
125static char *CalcRelativeName(const char *pszPath, const char *pszDir);
126static FILE *FOpenFileInDir(const char *pszName, const char *pszDir, const char *pszMode);
127static int UnlinkFileInDir(const char *pszName, const char *pszDir);
128static int RenameFileInDir(const char *pszOldName, const char *pszNewName, const char *pszDir);
129static int DoesFileInDirExist(const char *pszName, const char *pszDir);
130static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile);
131
132
133void FatalMsg(const char *pszFormat, ...)
134{
135 va_list va;
136
137 if (g_szErrorPrefix[0])
138 fprintf(stderr, "%s - fatal error: ", g_szErrorPrefix);
139 else
140 fprintf(stderr, "fatal error: ");
141
142 va_start(va, pszFormat);
143 vfprintf(stderr, pszFormat, va);
144 va_end(va);
145}
146
147
148void FatalDie(const char *pszFormat, ...)
149{
150 va_list va;
151
152 if (g_szErrorPrefix[0])
153 fprintf(stderr, "%s - fatal error: ", g_szErrorPrefix);
154 else
155 fprintf(stderr, "fatal error: ");
156
157 va_start(va, pszFormat);
158 vfprintf(stderr, pszFormat, va);
159 va_end(va);
160
161 exit(1);
162}
163
164
165static void ErrorMsg(const char *pszFormat, ...)
166{
167 va_list va;
168
169 if (g_szErrorPrefix[0])
170 fprintf(stderr, "%s - error: ", g_szErrorPrefix);
171 else
172 fprintf(stderr, "error: ");
173
174 va_start(va, pszFormat);
175 vfprintf(stderr, pszFormat, va);
176 va_end(va);
177}
178
179
180static void InfoMsg(unsigned uLevel, const char *pszFormat, ...)
181{
182 if (uLevel <= g_cVerbosityLevel)
183 {
184 va_list va;
185
186 if (g_szErrorPrefix[0])
187 fprintf(stderr, "%s - info: ", g_szErrorPrefix);
188 else
189 fprintf(stderr, "info: ");
190
191 va_start(va, pszFormat);
192 vfprintf(stderr, pszFormat, va);
193 va_end(va);
194 }
195}
196
197
198static void SetErrorPrefix(const char *pszPrefix, ...)
199{
200 int cch;
201 va_list va;
202
203 va_start(va, pszPrefix);
204#if defined(_MSC_VER) || defined(__sun__)
205 cch = vsprintf(g_szErrorPrefix, pszPrefix, va);
206 if (cch >= sizeof(g_szErrorPrefix))
207 FatalDie("Buffer overflow setting error prefix!\n");
208#else
209 vsnprintf(g_szErrorPrefix, sizeof(g_szErrorPrefix), pszPrefix, va);
210#endif
211 va_end(va);
212 (void)cch;
213}
214
215#ifndef ELECTRIC_HEAP
216void *xmalloc(size_t cb)
217{
218 void *pv = malloc(cb);
219 if (!pv)
220 FatalDie("out of memory (%d)\n", (int)cb);
221 return pv;
222}
223
224
225void *xrealloc(void *pvOld, size_t cb)
226{
227 void *pv = realloc(pvOld, cb);
228 if (!pv)
229 FatalDie("out of memory (%d)\n", (int)cb);
230 return pv;
231}
232
233
234char *xstrdup(const char *pszIn)
235{
236 char *psz = strdup(pszIn);
237 if (!psz)
238 FatalDie("out of memory (%d)\n", (int)strlen(pszIn));
239 return psz;
240}
241#endif
242
243
244void *xmallocz(size_t cb)
245{
246 void *pv = xmalloc(cb);
247 memset(pv, 0, cb);
248 return pv;
249}
250
251
252
253
254
255/**
256 * Gets the absolute path
257 *
258 * @returns A new heap buffer containing the absolute path.
259 * @param pszPath The path to make absolute. (Readonly)
260 */
261static char *AbsPath(const char *pszPath)
262{
263/** @todo this isn't really working as it should... */
264 char szTmp[PATH_MAX];
265#if defined(__OS2__)
266 if ( _fullpath(szTmp, *pszPath ? pszPath : ".", sizeof(szTmp))
267 && !realpath(pszPath, szTmp))
268 return xstrdup(pszPath);
269#elif defined(__WIN__)
270 if (!_fullpath(szTmp, *pszPath ? pszPath : ".", sizeof(szTmp)))
271 return xstrdup(pszPath);
272#else
273 if (!realpath(pszPath, szTmp))
274 return xstrdup(pszPath);
275#endif
276 return xstrdup(szTmp);
277}
278
279
280/**
281 * Utility function that finds the filename part in a path.
282 *
283 * @returns Pointer to the file name part (this may be "").
284 * @param pszPath The path to parse.
285 */
286static const char *FindFilenameInPath(const char *pszPath)
287{
288 const char *pszFilename = strchr(pszPath, '\0') - 1;
289 if (pszFilename < pszPath)
290 return pszPath;
291 while ( pszFilename > pszPath
292 && !IS_SLASH_DRV(pszFilename[-1]))
293 pszFilename--;
294 return pszFilename;
295}
296
297
298/**
299 * Utility function that combines a filename and a directory into a path.
300 *
301 * @returns malloced buffer containing the result.
302 * @param pszName The file name.
303 * @param pszDir The directory path.
304 */
305static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir)
306{
307 size_t cchName = strlen(pszName);
308 size_t cchDir = strlen(pszDir);
309 char *pszBuf = xmalloc(cchName + cchDir + 2);
310 memcpy(pszBuf, pszDir, cchDir);
311 if (cchDir > 0 && !IS_SLASH_DRV(pszDir[cchDir - 1]))
312 pszBuf[cchDir++] = PATH_SLASH;
313 memcpy(pszBuf + cchDir, pszName, cchName + 1);
314 return pszBuf;
315}
316
317
318/**
319 * Compares two path strings to see if they are identical.
320 *
321 * This doesn't do anything fancy, just the case ignoring and
322 * slash unification.
323 *
324 * @returns 1 if equal, 0 otherwise.
325 * @param pszPath1 The first path.
326 * @param pszPath2 The second path.
327 * @param cch The number of characters to compare.
328 */
329static int ArePathsIdentical(const char *pszPath1, const char *pszPath2, size_t cch)
330{
331#if defined(__OS2__) || defined(__WIN__)
332 if (strnicmp(pszPath1, pszPath2, cch))
333 {
334 /* Slashes may differ, compare char by char. */
335 const char *psz1 = pszPath1;
336 const char *psz2 = pszPath2;
337 for (;cch; psz1++, psz2++, cch--)
338 {
339 if (*psz1 != *psz2)
340 {
341 if ( tolower(*psz1) != tolower(*psz2)
342 && toupper(*psz1) != toupper(*psz2)
343 && *psz1 != '/'
344 && *psz1 != '\\'
345 && *psz2 != '/'
346 && *psz2 != '\\')
347 return 0;
348 }
349 }
350 }
351 return 1;
352#else
353 return !strncmp(pszPath1, pszPath2, cch);
354#endif
355}
356
357
358/**
359 * Calculate how to get to pszPath from pszDir.
360 *
361 * @returns The relative path from pszDir to path pszPath.
362 * @param pszPath The path to the object.
363 * @param pszDir The directory it shall be relative to.
364 */
365static char *CalcRelativeName(const char *pszPath, const char *pszDir)
366{
367 char *pszRet = NULL;
368 char *pszAbsPath = NULL;
369 size_t cchDir = strlen(pszDir);
370
371 /*
372 * This is indeed a bit tricky, so we'll try the easy way first...
373 */
374 if (ArePathsIdentical(pszPath, pszDir, cchDir))
375 {
376 if (pszPath[cchDir])
377 pszRet = (char *)pszPath + cchDir;
378 else
379 pszRet = "./";
380 }
381 else
382 {
383 pszAbsPath = AbsPath(pszPath);
384 if (ArePathsIdentical(pszAbsPath, pszDir, cchDir))
385 {
386 if (pszPath[cchDir])
387 pszRet = pszAbsPath + cchDir;
388 else
389 pszRet = "./";
390 }
391 }
392 if (pszRet)
393 {
394 while (IS_SLASH_DRV(*pszRet))
395 pszRet++;
396 pszRet = xstrdup(pszRet);
397 free(pszAbsPath);
398 return pszRet;
399 }
400
401 /*
402 * Damn, it's gonna be complicated. Deal with that later.
403 */
404 FatalDie("complicated relative path stuff isn't implemented yet. sorry.\n");
405 return NULL;
406}
407
408
409/**
410 * Utility function that combines a filename and directory and passes it onto fopen.
411 *
412 * @returns fopen return value.
413 * @param pszName The file name.
414 * @param pszDir The directory path.
415 * @param pszMode The fopen mode string.
416 */
417static FILE *FOpenFileInDir(const char *pszName, const char *pszDir, const char *pszMode)
418{
419 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
420 FILE *pFile = fopen(pszPath, pszMode);
421 free(pszPath);
422 return pFile;
423}
424
425
426/**
427 * Utility function that combines a filename and directory and passes it onto open.
428 *
429 * @returns open return value.
430 * @param pszName The file name.
431 * @param pszDir The directory path.
432 * @param fFlags The open flags.
433 * @param fCreateMode The file creation mode.
434 */
435static int OpenFileInDir(const char *pszName, const char *pszDir, int fFlags, int fCreateMode)
436{
437 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
438 int fd = open(pszPath, fFlags, fCreateMode);
439 free(pszPath);
440 return fd;
441}
442
443
444
445/**
446 * Deletes a file in a directory.
447 *
448 * @returns whatever unlink returns.
449 * @param pszName The file name.
450 * @param pszDir The directory path.
451 */
452static int UnlinkFileInDir(const char *pszName, const char *pszDir)
453{
454 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
455 int rc = unlink(pszPath);
456 free(pszPath);
457 return rc;
458}
459
460
461/**
462 * Renames a file in a directory.
463 *
464 * @returns whatever rename returns.
465 * @param pszOldName The new file name.
466 * @param pszNewName The old file name.
467 * @param pszDir The directory path.
468 */
469static int RenameFileInDir(const char *pszOldName, const char *pszNewName, const char *pszDir)
470{
471 char *pszOldPath = MakePathFromDirAndFile(pszOldName, pszDir);
472 char *pszNewPath = MakePathFromDirAndFile(pszNewName, pszDir);
473 int rc = rename(pszOldPath, pszNewPath);
474 free(pszOldPath);
475 free(pszNewPath);
476 return rc;
477}
478
479
480/**
481 * Check if a (regular) file exists in a directory.
482 *
483 * @returns 1 if it exists and is a regular file, 0 if not.
484 * @param pszName The file name.
485 * @param pszDir The directory path.
486 */
487static int DoesFileInDirExist(const char *pszName, const char *pszDir)
488{
489 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
490 struct stat st;
491 int rc = stat(pszPath, &st);
492 free(pszPath);
493#ifdef S_ISREG
494 return !rc && S_ISREG(st.st_mode);
495#elif defined(_MSC_VER)
496 return !rc && (st.st_mode & _S_IFMT) == _S_IFREG;
497#else
498#error "Port me"
499#endif
500}
501
502
503/**
504 * Reads into memory an entire file.
505 *
506 * @returns Pointer to the heap allocation containing the file.
507 * On failure NULL and errno is returned.
508 * @param pszName The file.
509 * @param pszDir The directory the file resides in.
510 * @param pcbFile Where to store the file size.
511 */
512static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile)
513{
514 int SavedErrno;
515 char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
516 int fd = open(pszPath, O_RDONLY | O_BINARY);
517 if (fd >= 0)
518 {
519 off_t cbFile = lseek(fd, 0, SEEK_END);
520 if ( cbFile >= 0
521 && lseek(fd, 0, SEEK_SET) == 0)
522 {
523 char *pb = malloc(cbFile + 1);
524 if (pb)
525 {
526 if (read(fd, pb, cbFile) == cbFile)
527 {
528 close(fd);
529 pb[cbFile] = '\0';
530 *pcbFile = (size_t)cbFile;
531 return pb;
532 }
533 SavedErrno = errno;
534 free(pb);
535 }
536 else
537 SavedErrno = ENOMEM;
538 }
539 else
540 SavedErrno = errno;
541 close(fd);
542 }
543 else
544 SavedErrno = errno;
545 free(pszPath);
546 errno = SavedErrno;
547 return NULL;
548}
549
550
551/**
552 * Creates a directory including all necessary parent directories.
553 *
554 * @returns 0 on success, -1 + errno on failure.
555 * @param pszDir The directory.
556 */
557static int MakePath(const char *pszPath)
558{
559 int iErr = 0;
560 char *pszAbsPath = AbsPath(pszPath);
561 char *psz = pszAbsPath;
562
563 /* Skip to the root slash (PC). */
564 while (!IS_SLASH(*psz) && *psz)
565 psz++;
566/** @todo UNC */
567 for (;;)
568 {
569 char chSaved;
570
571 /* skip slashes */
572 while (IS_SLASH(*psz))
573 psz++;
574 if (!*psz)
575 break;
576
577 /* find the next slash or end and terminate the string. */
578 while (!IS_SLASH(*psz) && *psz)
579 psz++;
580 chSaved = *psz;
581 *psz = '\0';
582
583 /* try create the directory, ignore failure because the directory already exists. */
584 errno = 0;
585#ifdef _MSC_VER
586 if ( _mkdir(pszAbsPath)
587 && errno != EEXIST)
588#else
589 if ( mkdir(pszAbsPath, 0777)
590 && errno != EEXIST)
591#endif
592 {
593 iErr = errno;
594 break;
595 }
596
597 /* restore the slash/terminator */
598 *psz = chSaved;
599 }
600
601 free(pszAbsPath);
602 return iErr ? -1 : 0;
603}
604
605
606/**
607 * Adds the arguments found in the pszCmdLine string to argument vector.
608 *
609 * The parsing of the pszCmdLine string isn't very sophisticated, no
610 * escaping or quotes.
611 *
612 * @param pcArgs Pointer to the argument counter.
613 * @param ppapszArgs Pointer to the argument vector pointer.
614 * @param pszCmdLine The command line to parse and append.
615 * @param pszWedgeArg Argument to put infront of anything found in pszCmdLine.
616 */
617static void AppendArgs(int *pcArgs, char ***ppapszArgs, const char *pszCmdLine, const char *pszWedgeArg)
618{
619 int i;
620 int cExtraArgs;
621 const char *psz;
622 char **papszArgs;
623
624 /*
625 * Count the new arguments.
626 */
627 cExtraArgs = 0;
628 psz = pszCmdLine;
629 while (*psz)
630 {
631 while (isspace(*psz))
632 psz++;
633 if (!psz)
634 break;
635 cExtraArgs++;
636 while (!isspace(*psz) && *psz)
637 psz++;
638 }
639 if (!cExtraArgs)
640 return;
641
642 /*
643 * Allocate a new vector that can hold the arguments.
644 * (Reallocating might not work since the argv might not be allocated
645 * from the heap but off the stack or somewhere... )
646 */
647 i = *pcArgs;
648 *pcArgs = i + cExtraArgs + !!pszWedgeArg;
649 papszArgs = xmalloc((*pcArgs + 1) * sizeof(char *));
650 *ppapszArgs = memcpy(papszArgs, *ppapszArgs, i * sizeof(char *));
651
652 if (pszWedgeArg)
653 papszArgs[i++] = xstrdup(pszWedgeArg);
654
655 psz = pszCmdLine;
656 while (*psz)
657 {
658 size_t cch;
659 const char *pszEnd;
660 while (isspace(*psz))
661 psz++;
662 if (!psz)
663 break;
664 pszEnd = psz;
665 while (!isspace(*pszEnd) && *pszEnd)
666 pszEnd++;
667
668 cch = pszEnd - psz;
669 papszArgs[i] = xmalloc(cch + 1);
670 memcpy(papszArgs[i], psz, cch);
671 papszArgs[i][cch] = '\0';
672
673 i++;
674 psz = pszEnd;
675 }
676
677 papszArgs[i] = NULL;
678}
679
680
681
682
683
684/** A checksum list entry.
685 * We keep a list checksums (of precompiler output) that matches, The planned
686 * matching algorithm doesn't require the precompiler output to be indentical,
687 * only to produce the same object files.
688 */
689typedef struct KOCSUM
690{
691 /** The next checksum. */
692 struct KOCSUM *pNext;
693 /** The crc32 checksum. */
694 uint32_t crc32;
695 /** The MD5 digest. */
696 unsigned char md5[16];
697 /** Valid or not. */
698 unsigned fUsed;
699} KOCSUM;
700/** Pointer to a KOCSUM. */
701typedef KOCSUM *PKOCSUM;
702/** Pointer to a const KOCSUM. */
703typedef const KOCSUM *PCKOCSUM;
704
705
706/**
707 * Temporary context record used when calculating
708 * the checksum of some data.
709 */
710typedef struct KOCSUMCTX
711{
712 /** The MD5 context. */
713 struct MD5Context MD5Ctx;
714} KOCSUMCTX;
715/** Pointer to a check context record. */
716typedef KOCSUMCTX *PKOCSUMCTX;
717
718
719
720/**
721 * Initializes a checksum object with an associated context.
722 *
723 * @param pSum The checksum object.
724 * @param pCtx The checksum context.
725 */
726static void kOCSumInitWithCtx(PKOCSUM pSum, PKOCSUMCTX pCtx)
727{
728 memset(pSum, 0, sizeof(*pSum));
729 MD5Init(&pCtx->MD5Ctx);
730}
731
732
733/**
734 * Updates the checksum calculation.
735 *
736 * @param pSum The checksum.
737 * @param pCtx The checksum calcuation context.
738 * @param pvBuf The input data to checksum.
739 * @param cbBuf The size of the input data.
740 */
741static void kOCSumUpdate(PKOCSUM pSum, PKOCSUMCTX pCtx, const void *pvBuf, size_t cbBuf)
742{
743 /*
744 * Take in relativly small chunks to try keep it in the cache.
745 */
746 const unsigned char *pb = (const unsigned char *)pvBuf;
747 while (cbBuf > 0)
748 {
749 size_t cb = cbBuf >= 128*1024 ? 128*1024 : cbBuf;
750 pSum->crc32 = crc32(pSum->crc32, pb, cb);
751 MD5Update(&pCtx->MD5Ctx, pb, (unsigned)cb);
752 cbBuf -= cb;
753 }
754}
755
756
757/**
758 * Finalizes a checksum calculation.
759 *
760 * @param pSum The checksum.
761 * @param pCtx The checksum calcuation context.
762 */
763static void kOCSumFinalize(PKOCSUM pSum, PKOCSUMCTX pCtx)
764{
765 MD5Final(&pSum->md5[0], &pCtx->MD5Ctx);
766 pSum->fUsed = 1;
767}
768
769
770/**
771 * Init a check sum chain head.
772 *
773 * @param pSumHead The checksum head to init.
774 */
775static void kOCSumInit(PKOCSUM pSumHead)
776{
777 memset(pSumHead, 0, sizeof(*pSumHead));
778}
779
780
781/**
782 * Parses the given string into a checksum head object.
783 *
784 * @returns 0 on success, -1 on format error.
785 * @param pSumHead The checksum head to init.
786 * @param pszVal The string to initialized it from.
787 */
788static int kOCSumInitFromString(PKOCSUM pSumHead, const char *pszVal)
789{
790 unsigned i;
791 char *pszNext;
792 char *pszMD5;
793
794 memset(pSumHead, 0, sizeof(*pSumHead));
795
796 pszMD5 = strchr(pszVal, ':');
797 if (pszMD5 == NULL)
798 return -1;
799 *pszMD5++ = '\0';
800
801 /* crc32 */
802 pSumHead->crc32 = (uint32_t)strtoul(pszVal, &pszNext, 16);
803 if (pszNext && *pszNext)
804 return -1;
805
806 /* md5 */
807 for (i = 0; i < sizeof(pSumHead->md5) * 2; i++)
808 {
809 unsigned char ch = pszMD5[i];
810 int x;
811 if ((unsigned char)(ch - '0') <= 9)
812 x = ch - '0';
813 else if ((unsigned char)(ch - 'a') <= 5)
814 x = ch - 'a' + 10;
815 else if ((unsigned char)(ch - 'A') <= 5)
816 x = ch - 'A' + 10;
817 else
818 return -1;
819 if (!(i & 1))
820 pSumHead->md5[i >> 1] = x << 4;
821 else
822 pSumHead->md5[i >> 1] |= x;
823 }
824
825 pSumHead->fUsed = 1;
826 return 0;
827}
828
829
830/**
831 * Delete a check sum chain.
832 *
833 * @param pSumHead The head of the checksum chain.
834 */
835static void kOCSumDeleteChain(PKOCSUM pSumHead)
836{
837 PKOCSUM pSum = pSumHead->pNext;
838 while (pSum)
839 {
840 void *pvFree = pSum;
841 pSum = pSum->pNext;
842 free(pvFree);
843 }
844 memset(pSumHead, 0, sizeof(*pSumHead));
845}
846
847
848/**
849 * Insert a check sum into the chain.
850 *
851 * @param pSumHead The head of the checksum list.
852 * @param pSumAdd The checksum to add (duplicate).
853 */
854static void kOCSumAdd(PKOCSUM pSumHead, PCKOCSUM pSumAdd)
855{
856 if (pSumHead->fUsed)
857 {
858 PKOCSUM pNew = xmalloc(sizeof(*pNew));
859 *pNew = *pSumAdd;
860 pNew->pNext = pSumHead->pNext;
861 pNew->fUsed = 1;
862 pSumHead->pNext = pNew;
863 }
864 else
865 {
866 *pSumHead = *pSumAdd;
867 pSumHead->pNext = NULL;
868 pSumHead->fUsed = 1;
869 }
870}
871
872
873/**
874 * Inserts an entrie chain into the given check sum chain.
875 *
876 * @param pSumHead The head of the checksum list.
877 * @param pSumHeadAdd The head of the checksum list to be added.
878 */
879static void kOCSumAddChain(PKOCSUM pSumHead, PCKOCSUM pSumHeadAdd)
880{
881 while (pSumHeadAdd)
882 {
883 kOCSumAdd(pSumHead, pSumHeadAdd);
884 pSumHeadAdd = pSumHeadAdd->pNext;
885 }
886}
887
888
889
890/**
891 * Prints the checksum to the specified stream.
892 *
893 * @param pSum The checksum.
894 * @param pFile The output file stream
895 */
896static void kOCSumFPrintf(PCKOCSUM pSum, FILE *pFile)
897{
898 fprintf(pFile, "%#x:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
899 pSum->crc32,
900 pSum->md5[0], pSum->md5[1], pSum->md5[2], pSum->md5[3],
901 pSum->md5[4], pSum->md5[5], pSum->md5[6], pSum->md5[7],
902 pSum->md5[8], pSum->md5[9], pSum->md5[10], pSum->md5[11],
903 pSum->md5[12], pSum->md5[13], pSum->md5[14], pSum->md5[15]);
904}
905
906
907/**
908 * Displays the checksum (not chain!) using the InfoMsg() method.
909 *
910 * @param pSum The checksum.
911 * @param uLevel The info message level.
912 * @param pszMsg Message to prefix the info message with.
913 */
914static void kOCSumInfo(PCKOCSUM pSum, unsigned uLevel, const char *pszMsg)
915{
916 InfoMsg(uLevel,
917 "%s: crc32=%#010x md5=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
918 pszMsg,
919 pSum->crc32,
920 pSum->md5[0], pSum->md5[1], pSum->md5[2], pSum->md5[3],
921 pSum->md5[4], pSum->md5[5], pSum->md5[6], pSum->md5[7],
922 pSum->md5[8], pSum->md5[9], pSum->md5[10], pSum->md5[11],
923 pSum->md5[12], pSum->md5[13], pSum->md5[14], pSum->md5[15]);
924}
925
926
927/**
928 * Compares two check sum entries.
929 *
930 * @returns 1 if equal, 0 if not equal.
931 *
932 * @param pSum1 The first checksum.
933 * @param pSum2 The second checksum.
934 */
935static int kOCSumIsEqual(PCKOCSUM pSum1, PCKOCSUM pSum2)
936{
937 if (pSum1 == pSum2)
938 return 1;
939 if (!pSum1 || !pSum2)
940 return 0;
941 if (pSum1->crc32 != pSum2->crc32)
942 return 0;
943 if (memcmp(&pSum1->md5[0], &pSum2->md5[0], sizeof(pSum1->md5)))
944 return 0;
945 return 1;
946}
947
948
949/**
950 * Checks if the specified checksum equals one of the
951 * checksums in the chain.
952 *
953 * @returns 1 if equals one of them, 0 if not.
954 *
955 * @param pSumHead The checksum chain too look in.
956 * @param pSum The checksum to look for.
957 * @todo ugly name. fix.
958 */
959static int kOCSumHasEqualInChain(PCKOCSUM pSumHead, PCKOCSUM pSum)
960{
961 for (; pSumHead; pSumHead = pSumHead->pNext)
962 {
963 if (pSumHead == pSum)
964 return 1;
965 if (pSumHead->crc32 != pSum->crc32)
966 continue;
967 if (memcmp(&pSumHead->md5[0], &pSum->md5[0], sizeof(pSumHead->md5)))
968 continue;
969 return 1;
970 }
971 return 0;
972}
973
974
975/**
976 * Checks if the checksum (chain) empty.
977 *
978 * @returns 1 if empty, 0 if it there is one or more checksums.
979 * @param pSum The checksum to test.
980 */
981static int kOCSumIsEmpty(PCKOCSUM pSum)
982{
983 return !pSum->fUsed;
984}
985
986
987
988
989
990
991/**
992 * The representation of a cache entry.
993 */
994typedef struct KOCENTRY
995{
996 /** The name of the cache entry. */
997 const char *pszName;
998 /** The dir that all other names are relative to. */
999 char *pszDir;
1000 /** The absolute path. */
1001 char *pszAbsPath;
1002 /** Set if the object needs to be (re)compiled. */
1003 unsigned fNeedCompiling;
1004 /** Whether the precompiler runs in piped mode. If clear it's file
1005 * mode (it could be redirected stdout, but that's essentially the
1006 * same from our point of view). */
1007 unsigned fPipedPreComp;
1008 /** Whether the compiler runs in piped mode (precompiler output on stdin). */
1009 unsigned fPipedCompile;
1010 /** Cache entry key that's used for some quick digest validation. */
1011 uint32_t uKey;
1012
1013 /** The file data. */
1014 struct KOCENTRYDATA
1015 {
1016 /** The name of file containing the precompiler output. */
1017 char *pszCppName;
1018 /** Pointer to the precompiler output. */
1019 char *pszCppMapping;
1020 /** The size of the precompiler output. 0 if not determined. */
1021 size_t cbCpp;
1022 /** The precompiler output checksums that will produce the cached object. */
1023 KOCSUM SumHead;
1024 /** The object filename (relative to the cache file). */
1025 char *pszObjName;
1026 /** The compile argument vector used to build the object. */
1027 char **papszArgvCompile;
1028 /** The size of the compile */
1029 unsigned cArgvCompile;
1030 /** The checksum of the compiler argument vector. */
1031 KOCSUM SumCompArgv;
1032 /** The target os/arch identifier. */
1033 char *pszTarget;
1034 }
1035 /** The old data.*/
1036 Old,
1037 /** The new data. */
1038 New;
1039} KOCENTRY;
1040/** Pointer to a KOCENTRY. */
1041typedef KOCENTRY *PKOCENTRY;
1042/** Pointer to a const KOCENTRY. */
1043typedef const KOCENTRY *PCKOCENTRY;
1044
1045
1046/**
1047 * Creates a cache entry for the given cache file name.
1048 *
1049 * @returns Pointer to a cache entry.
1050 * @param pszFilename The cache file name.
1051 */
1052static PKOCENTRY kOCEntryCreate(const char *pszFilename)
1053{
1054 PKOCENTRY pEntry;
1055 size_t off;
1056
1057 /*
1058 * Allocate an empty entry.
1059 */
1060 pEntry = xmallocz(sizeof(*pEntry));
1061
1062 kOCSumInit(&pEntry->New.SumHead);
1063 kOCSumInit(&pEntry->Old.SumHead);
1064
1065 kOCSumInit(&pEntry->New.SumCompArgv);
1066 kOCSumInit(&pEntry->Old.SumCompArgv);
1067
1068 /*
1069 * Setup the directory and cache file name.
1070 */
1071 pEntry->pszAbsPath = AbsPath(pszFilename);
1072 pEntry->pszName = FindFilenameInPath(pEntry->pszAbsPath);
1073 off = pEntry->pszName - pEntry->pszAbsPath;
1074 if (!off)
1075 FatalDie("Failed to find abs path for '%s'!\n", pszFilename);
1076 pEntry->pszDir = xmalloc(off);
1077 memcpy(pEntry->pszDir, pEntry->pszAbsPath, off - 1);
1078 pEntry->pszDir[off - 1] = '\0';
1079
1080 return pEntry;
1081}
1082
1083
1084/**
1085 * Destroys the cache entry freeing up all it's resources.
1086 *
1087 * @param pEntry The entry to free.
1088 */
1089static void kOCEntryDestroy(PKOCENTRY pEntry)
1090{
1091 free(pEntry->pszDir);
1092 free(pEntry->pszAbsPath);
1093
1094 kOCSumDeleteChain(&pEntry->New.SumHead);
1095 kOCSumDeleteChain(&pEntry->Old.SumHead);
1096
1097 kOCSumDeleteChain(&pEntry->New.SumCompArgv);
1098 kOCSumDeleteChain(&pEntry->Old.SumCompArgv);
1099
1100 free(pEntry->New.pszCppName);
1101 free(pEntry->Old.pszCppName);
1102
1103 free(pEntry->New.pszCppMapping);
1104 free(pEntry->Old.pszCppMapping);
1105
1106 free(pEntry->New.pszObjName);
1107 free(pEntry->Old.pszObjName);
1108
1109 free(pEntry->New.pszTarget);
1110 free(pEntry->Old.pszTarget);
1111
1112 while (pEntry->New.cArgvCompile > 0)
1113 free(pEntry->New.papszArgvCompile[--pEntry->New.cArgvCompile]);
1114 while (pEntry->Old.cArgvCompile > 0)
1115 free(pEntry->Old.papszArgvCompile[--pEntry->Old.cArgvCompile]);
1116
1117 free(pEntry->New.papszArgvCompile);
1118 free(pEntry->Old.papszArgvCompile);
1119
1120 free(pEntry);
1121}
1122
1123
1124/**
1125 * Calculates the checksum of an compiler argument vector.
1126 *
1127 * @param pEntry The cache entry.
1128 * @param papszArgv The argument vector.
1129 * @param cArgc The number of entries in the vector.
1130 * @param pszIgnorePath Path to ignore when encountered at the end of arguments.
1131 * (Not quite safe for simple file names, but what the heck.)
1132 * @param pSum Where to store the check sum.
1133 */
1134static void kOCEntryCalcArgvSum(PKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgc,
1135 const char *pszIgnorePath, PKOCSUM pSum)
1136{
1137 size_t cchIgnorePath = strlen(pszIgnorePath);
1138 KOCSUMCTX Ctx;
1139 unsigned i;
1140
1141 kOCSumInitWithCtx(pSum, &Ctx);
1142 for (i = 0; i < cArgc; i++)
1143 {
1144 size_t cch = strlen(papszArgv[i]);
1145 if ( cch < cchIgnorePath
1146 || !ArePathsIdentical(papszArgv[i] + cch - cchIgnorePath, pszIgnorePath, cch))
1147 kOCSumUpdate(pSum, &Ctx, papszArgv[i], cch + 1);
1148 }
1149 kOCSumFinalize(pSum, &Ctx);
1150}
1151
1152
1153/**
1154 * Reads and parses the cache file.
1155 *
1156 * @param pEntry The entry to read it into.
1157 */
1158static void kOCEntryRead(PKOCENTRY pEntry)
1159{
1160 FILE *pFile;
1161 pFile = FOpenFileInDir(pEntry->pszName, pEntry->pszDir, "rb");
1162 if (pFile)
1163 {
1164 InfoMsg(4, "reading cache entry...\n");
1165
1166 /*
1167 * Check the magic.
1168 */
1169 if ( !fgets(g_szLine, sizeof(g_szLine), pFile)
1170 || strcmp(g_szLine, "magic=kObjCacheEntry-v0.1.0\n"))
1171 {
1172 InfoMsg(2, "bad cache file (magic)\n");
1173 pEntry->fNeedCompiling = 1;
1174 }
1175 else
1176 {
1177 /*
1178 * Parse the rest of the file (relaxed order).
1179 */
1180 unsigned i;
1181 int fBad = 0;
1182 int fBadBeforeMissing = 1;
1183 while (fgets(g_szLine, sizeof(g_szLine), pFile))
1184 {
1185 char *pszNl;
1186 char *pszVal;
1187
1188 /* Split the line and drop the trailing newline. */
1189 pszVal = strchr(g_szLine, '=');
1190 if ((fBad = pszVal == NULL))
1191 break;
1192 *pszVal++ = '\0';
1193
1194 pszNl = strchr(pszVal, '\n');
1195 if (pszNl)
1196 *pszNl = '\0';
1197
1198 /* string case on variable name */
1199 if (!strcmp(g_szLine, "obj"))
1200 {
1201 if ((fBad = pEntry->Old.pszObjName != NULL))
1202 break;
1203 pEntry->Old.pszObjName = xstrdup(pszVal);
1204 }
1205 else if (!strcmp(g_szLine, "cpp"))
1206 {
1207 if ((fBad = pEntry->Old.pszCppName != NULL))
1208 break;
1209 pEntry->Old.pszCppName = xstrdup(pszVal);
1210 }
1211 else if (!strcmp(g_szLine, "cpp-size"))
1212 {
1213 char *pszNext;
1214 if ((fBad = pEntry->Old.cbCpp != 0))
1215 break;
1216 pEntry->Old.cbCpp = strtoul(pszVal, &pszNext, 0);
1217 if ((fBad = pszNext && *pszNext))
1218 break;
1219 }
1220 else if (!strcmp(g_szLine, "cpp-sum"))
1221 {
1222 KOCSUM Sum;
1223 if ((fBad = kOCSumInitFromString(&Sum, pszVal)))
1224 break;
1225 kOCSumAdd(&pEntry->Old.SumHead, &Sum);
1226 }
1227 else if (!strcmp(g_szLine, "cc-argc"))
1228 {
1229 if ((fBad = pEntry->Old.papszArgvCompile != NULL))
1230 break;
1231 pEntry->Old.cArgvCompile = atoi(pszVal); /* if wrong, we'll fail below. */
1232 pEntry->Old.papszArgvCompile = xmallocz((pEntry->Old.cArgvCompile + 1) * sizeof(pEntry->Old.papszArgvCompile[0]));
1233 }
1234 else if (!strncmp(g_szLine, "cc-argv-#", sizeof("cc-argv-#") - 1))
1235 {
1236 char *pszNext;
1237 unsigned i = strtoul(&g_szLine[sizeof("cc-argv-#") - 1], &pszNext, 0);
1238 if ((fBad = i >= pEntry->Old.cArgvCompile || pEntry->Old.papszArgvCompile[i] || (pszNext && *pszNext)))
1239 break;
1240 pEntry->Old.papszArgvCompile[i] = xstrdup(pszVal);
1241 }
1242 else if (!strcmp(g_szLine, "cc-argv-sum"))
1243 {
1244 if ((fBad = !kOCSumIsEmpty(&pEntry->Old.SumCompArgv)))
1245 break;
1246 if ((fBad = kOCSumInitFromString(&pEntry->Old.SumCompArgv, pszVal)))
1247 break;
1248 }
1249 else if (!strcmp(g_szLine, "target"))
1250 {
1251 if ((fBad = pEntry->Old.pszTarget != NULL))
1252 break;
1253 pEntry->Old.pszTarget = xstrdup(pszVal);
1254 }
1255 else if (!strcmp(g_szLine, "key"))
1256 {
1257 char *pszNext;
1258 if ((fBad = pEntry->uKey != 0))
1259 break;
1260 pEntry->uKey = strtoul(pszVal, &pszNext, 0);
1261 if ((fBad = pszNext && *pszNext))
1262 break;
1263 }
1264 else if (!strcmp(g_szLine, "the-end"))
1265 {
1266 fBadBeforeMissing = fBad = strcmp(pszVal, "fine");
1267 break;
1268 }
1269 else
1270 {
1271 fBad = 1;
1272 break;
1273 }
1274 } /* parse loop */
1275
1276 /*
1277 * Did we find everything and does it add up correctly?
1278 */
1279 if (!fBad && fBadBeforeMissing)
1280 {
1281 InfoMsg(2, "bad cache file (no end)\n");
1282 fBad = 1;
1283 }
1284 else
1285 {
1286 fBadBeforeMissing = fBad;
1287 if ( !fBad
1288 && ( !pEntry->Old.papszArgvCompile
1289 || !pEntry->Old.pszObjName
1290 || !pEntry->Old.pszCppName
1291 || kOCSumIsEmpty(&pEntry->Old.SumHead)))
1292 fBad = 1;
1293 if (!fBad)
1294 for (i = 0; i < pEntry->Old.cArgvCompile; i++)
1295 if ((fBad = !pEntry->Old.papszArgvCompile[i]))
1296 break;
1297 if (!fBad)
1298 {
1299 KOCSUM Sum;
1300 kOCEntryCalcArgvSum(pEntry, (const char * const *)pEntry->Old.papszArgvCompile,
1301 pEntry->Old.cArgvCompile, pEntry->Old.pszObjName, &Sum);
1302 fBad = !kOCSumIsEqual(&pEntry->Old.SumCompArgv, &Sum);
1303 }
1304 if (fBad)
1305 InfoMsg(2, "bad cache file (%s)\n", fBadBeforeMissing ? g_szLine : "missing stuff");
1306 else if (ferror(pFile))
1307 {
1308 InfoMsg(2, "cache file read error\n");
1309 fBad = 1;
1310 }
1311
1312 /*
1313 * Verify the existance of the object file.
1314 */
1315 if (!fBad)
1316 {
1317 struct stat st;
1318 char *pszPath = MakePathFromDirAndFile(pEntry->Old.pszObjName, pEntry->pszDir);
1319 if (stat(pszPath, &st) != 0)
1320 {
1321 InfoMsg(2, "failed to stat object file: %s\n", strerror(errno));
1322 fBad = 1;
1323 }
1324 else
1325 {
1326 /** @todo verify size and the timestamp. */
1327 }
1328 }
1329 }
1330 pEntry->fNeedCompiling = fBad;
1331 }
1332 fclose(pFile);
1333 }
1334 else
1335 {
1336 InfoMsg(2, "no cache file\n");
1337 pEntry->fNeedCompiling = 1;
1338 }
1339}
1340
1341
1342/**
1343 * Writes the cache file.
1344 *
1345 * @param pEntry The entry to write.
1346 */
1347static void kOCEntryWrite(PKOCENTRY pEntry)
1348{
1349 FILE *pFile;
1350 PCKOCSUM pSum;
1351 unsigned i;
1352
1353 InfoMsg(4, "writing cache entry '%s'...\n", pEntry->pszName);
1354 pFile = FOpenFileInDir(pEntry->pszName, pEntry->pszDir, "wb");
1355 if (!pFile)
1356 FatalDie("Failed to open '%s' in '%s': %s\n",
1357 pEntry->pszName, pEntry->pszDir, strerror(errno));
1358
1359#define CHECK_LEN(expr) \
1360 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)
1361
1362 fprintf(pFile, "magic=kObjCacheEntry-v0.1.0\n");
1363 CHECK_LEN(fprintf(pFile, "target=%s\n", pEntry->New.pszTarget ? pEntry->New.pszTarget : pEntry->Old.pszTarget));
1364 CHECK_LEN(fprintf(pFile, "key=%u\n", (unsigned long)pEntry->uKey));
1365 CHECK_LEN(fprintf(pFile, "obj=%s\n", pEntry->New.pszObjName ? pEntry->New.pszObjName : pEntry->Old.pszObjName));
1366 CHECK_LEN(fprintf(pFile, "cpp=%s\n", pEntry->New.pszCppName ? pEntry->New.pszCppName : pEntry->Old.pszCppName));
1367 CHECK_LEN(fprintf(pFile, "cpp-size=%lu\n", pEntry->New.pszCppName ? pEntry->New.cbCpp : pEntry->Old.cbCpp));
1368
1369 if (!kOCSumIsEmpty(&pEntry->New.SumCompArgv))
1370 {
1371 CHECK_LEN(fprintf(pFile, "cc-argc=%u\n", pEntry->New.cArgvCompile));
1372 for (i = 0; i < pEntry->New.cArgvCompile; i++)
1373 CHECK_LEN(fprintf(pFile, "cc-argv-#%u=%s\n", i, pEntry->New.papszArgvCompile[i]));
1374 fprintf(pFile, "cc-argv-sum=");
1375 kOCSumFPrintf(&pEntry->New.SumCompArgv, pFile);
1376 }
1377 else
1378 {
1379 CHECK_LEN(fprintf(pFile, "cc-argc=%u\n", pEntry->Old.cArgvCompile));
1380 for (i = 0; i < pEntry->Old.cArgvCompile; i++)
1381 CHECK_LEN(fprintf(pFile, "cc-argv-#%u=%s\n", i, pEntry->Old.papszArgvCompile[i]));
1382 fprintf(pFile, "cc-argv-sum=");
1383 kOCSumFPrintf(&pEntry->Old.SumCompArgv, pFile);
1384 }
1385
1386
1387 for (pSum = !kOCSumIsEmpty(&pEntry->New.SumHead) ? &pEntry->New.SumHead : &pEntry->Old.SumHead;
1388 pSum;
1389 pSum = pSum->pNext)
1390 {
1391 fprintf(pFile, "cpp-sum=");
1392 kOCSumFPrintf(pSum, pFile);
1393 }
1394
1395 fprintf(pFile, "the-end=fine\n");
1396
1397#undef CHECK_LEN
1398
1399 /*
1400 * Flush the file and check for errors.
1401 * On failure delete the file so we won't be seeing any invalid
1402 * files the next time or upset make with new timestamps.
1403 */
1404 errno = 0;
1405 if ( fflush(pFile) < 0
1406 || ferror(pFile))
1407 {
1408 int iErr = errno;
1409 fclose(pFile);
1410 UnlinkFileInDir(pEntry->pszName, pEntry->pszDir);
1411 FatalDie("Stream error occured while writing '%s' in '%s': %s\n",
1412 pEntry->pszName, pEntry->pszDir, strerror(iErr));
1413 }
1414 fclose(pFile);
1415}
1416
1417
1418/**
1419 * Checks that the read cache entry is valid.
1420 * It sets fNeedCompiling if it isn't.
1421 *
1422 * @returns 1 valid, 0 invalid.
1423 * @param pEntry The cache entry.
1424 */
1425static int kOCEntryCheck(PKOCENTRY pEntry)
1426{
1427 return !pEntry->fNeedCompiling;
1428}
1429
1430
1431/**
1432 * Sets the object name and compares it with the old name if present.
1433 *
1434 * @param pEntry The cache entry.
1435 * @param pszObjName The new object name.
1436 */
1437static void kOCEntrySetCompileObjName(PKOCENTRY pEntry, const char *pszObjName)
1438{
1439 assert(!pEntry->New.pszObjName);
1440 pEntry->New.pszObjName = CalcRelativeName(pszObjName, pEntry->pszDir);
1441
1442 if ( !pEntry->fNeedCompiling
1443 && ( !pEntry->Old.pszObjName
1444 || strcmp(pEntry->New.pszObjName, pEntry->Old.pszObjName)))
1445 {
1446 InfoMsg(2, "object file name differs\n");
1447 pEntry->fNeedCompiling = 1;
1448 }
1449
1450 if ( !pEntry->fNeedCompiling
1451 && !DoesFileInDirExist(pEntry->New.pszObjName, pEntry->pszDir))
1452 {
1453 InfoMsg(2, "object file doesn't exist\n");
1454 pEntry->fNeedCompiling = 1;
1455 }
1456}
1457
1458
1459/**
1460 * Set the new compiler args, calc their checksum, and comparing them with any old ones.
1461 *
1462 * @param pEntry The cache entry.
1463 * @param papszArgvCompile The new argument vector for compilation.
1464 * @param cArgvCompile The number of arguments in the vector.
1465 *
1466 * @remark Must call kOCEntrySetCompileObjName before this function!
1467 */
1468static void kOCEntrySetCompileArgv(PKOCENTRY pEntry, const char * const *papszArgvCompile, unsigned cArgvCompile)
1469{
1470 unsigned i;
1471
1472 /* call me only once! */
1473 assert(!pEntry->New.cArgvCompile);
1474 /* call kOCEntrySetCompilerObjName first! */
1475 assert(pEntry->New.pszObjName);
1476
1477 /*
1478 * Copy the argument vector and calculate the checksum.
1479 */
1480 pEntry->New.cArgvCompile = cArgvCompile;
1481 pEntry->New.papszArgvCompile = xmalloc((cArgvCompile + 1) * sizeof(pEntry->New.papszArgvCompile[0]));
1482 for (i = 0; i < cArgvCompile; i++)
1483 pEntry->New.papszArgvCompile[i] = xstrdup(papszArgvCompile[i]);
1484 pEntry->New.papszArgvCompile[i] = NULL; /* for exev/spawnv */
1485
1486 kOCEntryCalcArgvSum(pEntry, papszArgvCompile, cArgvCompile, pEntry->New.pszObjName, &pEntry->New.SumCompArgv);
1487 kOCSumInfo(&pEntry->New.SumCompArgv, 4, "comp-argv");
1488
1489 /*
1490 * Compare with the old argument vector.
1491 */
1492 if ( !pEntry->fNeedCompiling
1493 && !kOCSumIsEqual(&pEntry->New.SumCompArgv, &pEntry->Old.SumCompArgv))
1494 {
1495 InfoMsg(2, "compiler args differs\n");
1496 pEntry->fNeedCompiling = 1;
1497 }
1498}
1499
1500
1501/**
1502 * Sets the arch/os target and compares it with the old name if present.
1503 *
1504 * @param pEntry The cache entry.
1505 * @param pszObjName The new object name.
1506 */
1507static void kOCEntrySetTarget(PKOCENTRY pEntry, const char *pszTarget)
1508{
1509 assert(!pEntry->New.pszTarget);
1510 pEntry->New.pszTarget = xstrdup(pszTarget);
1511
1512 if ( !pEntry->fNeedCompiling
1513 && ( !pEntry->Old.pszTarget
1514 || strcmp(pEntry->New.pszTarget, pEntry->Old.pszTarget)))
1515 {
1516 InfoMsg(2, "target differs\n");
1517 pEntry->fNeedCompiling = 1;
1518 }
1519}
1520
1521
1522/**
1523 * Sets the precompiler output filename.
1524 * We don't generally care if this matches the old name or not.
1525 *
1526 * @param pEntry The cache entry.
1527 * @param pszCppName The precompiler output filename.
1528 */
1529static void kOCEntrySetCppName(PKOCENTRY pEntry, const char *pszCppName)
1530{
1531 assert(!pEntry->New.pszCppName);
1532 pEntry->New.pszCppName = CalcRelativeName(pszCppName, pEntry->pszDir);
1533}
1534
1535
1536/**
1537 * Sets the piped mode of the precompiler and compiler.
1538 *
1539 * @param pEntry The cache entry.
1540 * @param fRedirPreCompStdOut Whether the precompiler is in piped mode.
1541 * @param fRedirCompileStdIn Whether the compiler is in piped mode.
1542 */
1543static void kOCEntrySetPipedMode(PKOCENTRY pEntry, int fRedirPreCompStdOut, int fRedirCompileStdIn)
1544{
1545 pEntry->fPipedPreComp = fRedirPreCompStdOut;
1546 pEntry->fPipedCompile = fRedirCompileStdIn;
1547}
1548
1549
1550/**
1551 * Spawns a child in a synchronous fashion.
1552 * Terminating on failure.
1553 *
1554 * @param papszArgv Argument vector. The cArgv element is NULL.
1555 * @param cArgv The number of arguments in the vector.
1556 */
1557static void kOCEntrySpawn(PCKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgv, const char *pszMsg, const char *pszStdOut)
1558{
1559#if defined(__OS2__) || defined(__WIN__)
1560 intptr_t rc;
1561 int fdStdOut = -1;
1562 if (pszStdOut)
1563 {
1564 int fdReDir;
1565 fdStdOut = dup(STDOUT_FILENO);
1566 close(STDOUT_FILENO);
1567 fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0666);
1568 if (fdReDir < 0)
1569 FatalDie("%s - failed to create stdout redirection file '%s': %s\n",
1570 pszMsg, pszStdOut, strerror(errno));
1571
1572 if (fdReDir != STDOUT_FILENO)
1573 {
1574 if (dup2(fdReDir, STDOUT_FILENO) < 0)
1575 FatalDie("%s - dup2 failed: %s\n", pszMsg, strerror(errno));
1576 close(fdReDir);
1577 }
1578 }
1579
1580 errno = 0;
1581 rc = _spawnvp(_P_WAIT, papszArgv[0], papszArgv);
1582 if (rc < 0)
1583 FatalDie("%s - _spawnvp failed (rc=0x%p): %s\n", pszMsg, rc, strerror(errno));
1584 if (rc > 0)
1585 FatalDie("%s - failed rc=%d\n", pszMsg, (int)rc);
1586 if (fdStdOut)
1587 {
1588 close(STDOUT_FILENO);
1589 fdStdOut = dup2(fdStdOut, STDOUT_FILENO);
1590 close(fdStdOut);
1591 }
1592
1593#else
1594 int iStatus;
1595 pid_t pidWait;
1596 pid_t pid = fork();
1597 if (!pid)
1598 {
1599 if (pszStdOut)
1600 {
1601 int fdReDir;
1602
1603 close(STDOUT_FILENO);
1604 fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0666);
1605 if (fdReDir < 0)
1606 FatalDie("%s - failed to create stdout redirection file '%s': %s\n",
1607 pszMsg, pszStdOut, strerror(errno));
1608 if (fdReDir != STDOUT_FILENO)
1609 {
1610 if (dup2(fdReDir, STDOUT_FILENO) < 0)
1611 FatalDie("%s - dup2 failed: %s\n", pszMsg, strerror(errno));
1612 close(fdReDir);
1613 }
1614 }
1615
1616 execvp(papszArgv[0], (char **)papszArgv);
1617 FatalDie("%s - execvp failed: %s\n",
1618 pszMsg, strerror(errno));
1619 }
1620 if (pid == -1)
1621 FatalDie("%s - fork() failed: %s\n", pszMsg, strerror(errno));
1622
1623 pidWait = waitpid(pid, &iStatus, 0);
1624 while (pidWait < 0 && errno == EINTR)
1625 pidWait = waitpid(pid, &iStatus, 0);
1626 if (pidWait != pid)
1627 FatalDie("%s - waitpid failed rc=%d: %s\n",
1628 pszMsg, pidWait, strerror(errno));
1629 if (!WIFEXITED(iStatus))
1630 FatalDie("%s - abended (iStatus=%#x)\n", pszMsg, iStatus);
1631 if (WEXITSTATUS(iStatus))
1632 FatalDie("%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus));
1633#endif
1634 (void)cArgv;
1635}
1636
1637
1638/**
1639 * Spawns child with optional redirection of stdin and stdout.
1640 *
1641 * @param pEntry The cache entry.
1642 * @param papszArgv Argument vector. The cArgv element is NULL.
1643 * @param cArgv The number of arguments in the vector.
1644 * @param fdStdIn Child stdin, -1 if it should inherit our stdin. Will be closed.
1645 * @param fdStdOut Child stdout, -1 if it should inherit our stdout. Will be closed.
1646 * @param pszMsg Message to start the info/error messages with.
1647 */
1648static pid_t kOCEntrySpawnChild(PCKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgv, int fdStdIn, int fdStdOut, const char *pszMsg)
1649{
1650 pid_t pid;
1651 int fdSavedStdOut = -1;
1652 int fdSavedStdIn = -1;
1653
1654 /*
1655 * Setup redirection.
1656 */
1657 if (fdStdOut != -1 && fdStdOut != STDOUT_FILENO)
1658 {
1659 fdSavedStdOut = dup(STDOUT_FILENO);
1660 if (dup2(fdStdOut, STDOUT_FILENO) < 0)
1661 FatalDie("%s - dup2(,1) failed: %s\n", pszMsg, strerror(errno));
1662 close(fdStdOut);
1663#ifndef __WIN__
1664 fcntl(fdSavedStdOut, F_SETFD, FD_CLOEXEC);
1665#endif
1666 }
1667 if (fdStdIn != -1 && fdStdIn != STDIN_FILENO)
1668 {
1669 fdSavedStdIn = dup(STDIN_FILENO);
1670 if (dup2(fdStdIn, STDIN_FILENO) < 0)
1671 FatalDie("%s - dup2(,0) failed: %s\n", pszMsg, strerror(errno));
1672 close(fdStdIn);
1673#ifndef __WIN__
1674 fcntl(fdSavedStdIn, F_SETFD, FD_CLOEXEC);
1675#endif
1676 }
1677
1678 /*
1679 * Create the child process.
1680 */
1681#if defined(__OS2__) || defined(__WIN__)
1682 errno = 0;
1683 pid = _spawnvp(_P_NOWAIT, papszArgv[0], papszArgv);
1684 if (pid == -1)
1685 FatalDie("precompile - _spawnvp failed: %s\n", strerror(errno));
1686
1687#else
1688 pid = fork();
1689 if (!pid)
1690 {
1691 execvp(papszArgv[0], (char **)papszArgv);
1692 FatalDie("precompile - execvp failed: %s\n", strerror(errno));
1693 }
1694 if (pid == -1)
1695 FatalDie("precompile - fork() failed: %s\n", strerror(errno));
1696#endif
1697
1698 /*
1699 * Restore stdout & stdin.
1700 */
1701 if (fdSavedStdIn != -1)
1702 {
1703 close(STDIN_FILENO);
1704 dup2(fdStdOut, STDIN_FILENO);
1705 close(fdSavedStdIn);
1706 }
1707 if (fdSavedStdOut != -1)
1708 {
1709 close(STDOUT_FILENO);
1710 dup2(fdSavedStdOut, STDOUT_FILENO);
1711 close(fdSavedStdOut);
1712 }
1713
1714 InfoMsg(3, "%s - spawned %ld\n", pszMsg, (long)pid);
1715 (void)cArgv;
1716 (void)pEntry;
1717 return pid;
1718}
1719
1720
1721/**
1722 * Waits for a child and exits fatally if the child failed in any way.
1723 *
1724 * @param pEntry The cache entry.
1725 * @param pid The child to wait for.
1726 * @param pszMsg Message to start the info/error messages with.
1727 */
1728static void kOCEntryWaitChild(PCKOCENTRY pEntry, pid_t pid, const char *pszMsg)
1729{
1730 int iStatus = -1;
1731 pid_t pidWait;
1732 InfoMsg(3, "%s - wait-child %ld\n", pszMsg, (long)pid);
1733
1734#ifdef __WIN__
1735 pidWait = _cwait(&iStatus, pid, _WAIT_CHILD);
1736 if (pidWait == -1)
1737 FatalDie("%s - waitpid failed: %s\n", pszMsg, strerror(errno));
1738 if (iStatus)
1739 FatalDie("%s - failed with rc %d\n", pszMsg, iStatus);
1740#else
1741 pidWait = waitpid(pid, &iStatus, 0);
1742 while (pidWait < 0 && errno == EINTR)
1743 pidWait = waitpid(pid, &iStatus, 0);
1744 if (pidWait != pid)
1745 FatalDie("%s - waitpid failed rc=%d: %s\n", pidWait, strerror(errno));
1746 if (!WIFEXITED(iStatus))
1747 FatalDie("%s - abended (iStatus=%#x)\n", pszMsg, iStatus);
1748 if (WEXITSTATUS(iStatus))
1749 FatalDie("%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus));
1750#endif
1751 (void)pEntry;
1752}
1753
1754
1755/**
1756 * Creates a pipe for setting up redirected stdin/stdout.
1757 *
1758 * @param pEntry The cache entry.
1759 * @param pFDs Where to store the two file descriptors.
1760 * @param pszMsg The operation message for info/error messages.
1761 */
1762static void kOCEntryCreatePipe(PKOCENTRY pEntry, int *pFDs, const char *pszMsg)
1763{
1764 pFDs[0] = pFDs[1] = -1;
1765#if defined(__WIN__)
1766 if (_pipe(pFDs, 0, _O_NOINHERIT | _O_BINARY) < 0)
1767#else
1768 if (pipe(pFDs) < 0)
1769#endif
1770 FatalDie("%s - pipe failed: %s\n", pszMsg, strerror(errno));
1771#if !defined(__WIN__)
1772 fcntl(pFDs[0], F_SETFD, FD_CLOEXEC);
1773 fcntl(pFDs[1], F_SETFD, FD_CLOEXEC);
1774#endif
1775}
1776
1777
1778/**
1779 * Spawns a child that produces output to stdout.
1780 *
1781 * @param papszArgv Argument vector. The cArgv element is NULL.
1782 * @param cArgv The number of arguments in the vector.
1783 * @param pszMsg The operation message for info/error messages.
1784 * @param pfnConsumer Pointer to a consumer callback function that is responsible
1785 * for servicing the child output and closing the pipe.
1786 */
1787static void kOCEntrySpawnProducer(PKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgv, const char *pszMsg,
1788 void (*pfnConsumer)(PKOCENTRY, int))
1789{
1790 int fds[2];
1791 pid_t pid;
1792
1793 kOCEntryCreatePipe(pEntry, fds, pszMsg);
1794 pid = kOCEntrySpawnChild(pEntry, papszArgv, cArgv, -1, fds[1 /* write */], pszMsg);
1795
1796 pfnConsumer(pEntry, fds[0 /* read */]);
1797
1798 kOCEntryWaitChild(pEntry, pid, pszMsg);
1799}
1800
1801
1802/**
1803 * Spawns a child that consumes input on stdin.
1804 *
1805 * @param papszArgv Argument vector. The cArgv element is NULL.
1806 * @param cArgv The number of arguments in the vector.
1807 * @param pszMsg The operation message for info/error messages.
1808 * @param pfnProducer Pointer to a producer callback function that is responsible
1809 * for serving the child input and closing the pipe.
1810 */
1811static void kOCEntrySpawnConsumer(PKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgv, const char *pszMsg,
1812 void (*pfnProducer)(PKOCENTRY, int))
1813{
1814 int fds[2];
1815 pid_t pid;
1816
1817 kOCEntryCreatePipe(pEntry, fds, pszMsg);
1818 pid = kOCEntrySpawnChild(pEntry, papszArgv, cArgv, fds[0 /* read */], -1, pszMsg);
1819
1820 pfnProducer(pEntry, fds[1 /* write */]);
1821
1822 kOCEntryWaitChild(pEntry, pid, pszMsg);
1823}
1824
1825
1826/**
1827 * Spawns two child processes, one producing output and one consuming.
1828 * Terminating on failure.
1829 *
1830 * @param papszArgv Argument vector. The cArgv element is NULL.
1831 * @param cArgv The number of arguments in the vector.
1832 * @param pszMsg The operation message for info/error messages.
1833 * @param pfnConsumer Pointer to a consumer callback function that is responsible
1834 * for servicing the child output and closing the pipe.
1835 */
1836static void kOCEntrySpawnTee(PKOCENTRY pEntry, const char * const *papszProdArgv, unsigned cProdArgv,
1837 const char * const *papszConsArgv, unsigned cConsArgv,
1838 const char *pszMsg, void (*pfnTeeConsumer)(PKOCENTRY, int, int))
1839{
1840 int fds[2];
1841 int fdIn, fdOut;
1842 pid_t pidProducer, pidConsumer;
1843
1844 /*
1845 * The producer.
1846 */
1847 kOCEntryCreatePipe(pEntry, fds, pszMsg);
1848 pidConsumer = kOCEntrySpawnChild(pEntry, papszProdArgv, cProdArgv, -1, fds[1 /* write */], pszMsg);
1849 fdIn = fds[0 /* read */];
1850
1851 /*
1852 * The consumer.
1853 */
1854 kOCEntryCreatePipe(pEntry, fds, pszMsg);
1855 pidProducer = kOCEntrySpawnChild(pEntry, papszConsArgv, cConsArgv, fds[0 /* read */], -1, pszMsg);
1856 fdOut = fds[1 /* write */];
1857
1858 /*
1859 * Hand it on to the tee consumer.
1860 */
1861 pfnTeeConsumer(pEntry, fdIn, fdOut);
1862
1863 /*
1864 * Reap the children.
1865 */
1866 kOCEntryWaitChild(pEntry, pidProducer, pszMsg);
1867 kOCEntryWaitChild(pEntry, pidConsumer, pszMsg);
1868}
1869
1870
1871/**
1872 * Reads the output from the precompiler.
1873 *
1874 * @param pEntry The cache entry. New.cbCpp and New.pszCppMapping will be updated.
1875 * @param pWhich Specifies what to read (old/new).
1876 * @param fNonFatal Whether failure is fatal or not.
1877 */
1878static int kOCEntryReadCppOutput(PKOCENTRY pEntry, struct KOCENTRYDATA *pWhich, int fNonFatal)
1879{
1880 pWhich->pszCppMapping = ReadFileInDir(pWhich->pszCppName, pEntry->pszDir, &pWhich->cbCpp);
1881 if (!pWhich->pszCppMapping)
1882 {
1883 if (!fNonFatal)
1884 FatalDie("failed to open/read '%s' in '%s': %s\n",
1885 pWhich->pszCppName, pEntry->pszDir, strerror(errno));
1886 InfoMsg(2, "failed to open/read '%s' in '%s': %s\n",
1887 pWhich->pszCppName, pEntry->pszDir, strerror(errno));
1888 return -1;
1889 }
1890
1891 InfoMsg(3, "precompiled file is %lu bytes long\n", (unsigned long)pWhich->cbCpp);
1892 return 0;
1893}
1894
1895
1896/**
1897 * Worker for kOCEntryPreCompile and calculates the checksum of
1898 * the precompiler output.
1899 *
1900 * @param pEntry The cache entry. NewSum will be updated.
1901 */
1902static void kOCEntryCalcChecksum(PKOCENTRY pEntry)
1903{
1904 KOCSUMCTX Ctx;
1905 kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx);
1906 kOCSumUpdate(&pEntry->New.SumHead, &Ctx, pEntry->New.pszCppMapping, pEntry->New.cbCpp);
1907 kOCSumFinalize(&pEntry->New.SumHead, &Ctx);
1908 kOCSumInfo(&pEntry->New.SumHead, 4, "cpp (file)");
1909}
1910
1911
1912/**
1913 * This consumes the precompiler output and checksums it.
1914 *
1915 * @param pEntry The cache entry.
1916 * @param fdIn The precompiler output pipe.
1917 * @param fdOut The compiler input pipe, -1 if no compiler.
1918 */
1919static void kOCEntryPreCompileConsumer(PKOCENTRY pEntry, int fdIn)
1920{
1921 KOCSUMCTX Ctx;
1922 long cbLeft;
1923 long cbAlloc;
1924 char *psz;
1925
1926 kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx);
1927 cbAlloc = pEntry->Old.cbCpp ? ((long)pEntry->Old.cbCpp + 4*1024*1024 + 4096) & ~(4*1024*1024 - 1) : 4*1024*1024;
1928 cbLeft = cbAlloc;
1929 pEntry->New.pszCppMapping = psz = xmalloc(cbAlloc);
1930 for (;;)
1931 {
1932 /*
1933 * Read data from the pipe.
1934 */
1935 long cbRead = read(fdIn, psz, cbLeft - 1);
1936 if (!cbRead)
1937 break;
1938 if (cbRead < 0)
1939 {
1940 if (errno == EINTR)
1941 continue;
1942 FatalDie("precompile - read(%d,,%ld) failed: %s\n",
1943 fdIn, (long)cbLeft, strerror(errno));
1944 }
1945
1946 /*
1947 * Process the data.
1948 */
1949 psz[cbRead] = '\0';
1950 kOCSumUpdate(&pEntry->New.SumHead, &Ctx, psz, cbRead);
1951
1952 /*
1953 * Advance.
1954 */
1955 psz += cbRead;
1956 cbLeft -= cbRead;
1957 if (cbLeft <= 1)
1958 {
1959 size_t off = psz - pEntry->New.pszCppMapping;
1960 cbLeft = 4*1024*1024;
1961 cbAlloc += cbLeft;
1962 pEntry->New.pszCppMapping = xrealloc(pEntry->New.pszCppMapping, cbAlloc);
1963 psz = pEntry->New.pszCppMapping + off;
1964 }
1965 }
1966
1967 close(fdIn);
1968 pEntry->New.cbCpp = cbAlloc - cbLeft;
1969 kOCSumFinalize(&pEntry->New.SumHead, &Ctx);
1970 kOCSumInfo(&pEntry->New.SumHead, 4, "cpp (pipe)");
1971}
1972
1973
1974
1975
1976/**
1977 * Run the precompiler and calculate the checksum of the output.
1978 *
1979 * @param pEntry The cache entry.
1980 * @param papszArgvPreComp The argument vector for executing precompiler. The cArgvPreComp'th argument must be NULL.
1981 * @param cArgvPreComp The number of arguments.
1982 */
1983static void kOCEntryPreCompile(PKOCENTRY pEntry, const char * const *papszArgvPreComp, unsigned cArgvPreComp)
1984{
1985 /*
1986 * If we're executing the precompiler in piped mode, it's relatively simple.
1987 */
1988 if (pEntry->fPipedPreComp)
1989 kOCEntrySpawnProducer(pEntry, papszArgvPreComp, cArgvPreComp, "precompile",
1990 kOCEntryPreCompileConsumer);
1991 else
1992 {
1993 /*
1994 * Rename the old precompiled output to '-old' so the precompiler won't
1995 * overwrite it when we execute it.
1996 */
1997 if ( pEntry->Old.pszCppName
1998 && DoesFileInDirExist(pEntry->Old.pszCppName, pEntry->pszDir))
1999 {
2000 size_t cch = strlen(pEntry->Old.pszCppName);
2001 char *psz = xmalloc(cch + sizeof("-old"));
2002 memcpy(psz, pEntry->Old.pszCppName, cch);
2003 memcpy(psz + cch, "-old", sizeof("-old"));
2004
2005 InfoMsg(3, "renaming '%s' to '%s' in '%s'\n", pEntry->Old.pszCppName, psz, pEntry->pszDir);
2006 UnlinkFileInDir(psz, pEntry->pszDir);
2007 if (RenameFileInDir(pEntry->Old.pszCppName, psz, pEntry->pszDir))
2008 FatalDie("failed to rename '%s' -> '%s' in '%s': %s\n",
2009 pEntry->Old.pszCppName, psz, pEntry->pszDir, strerror(errno));
2010 free(pEntry->Old.pszCppName);
2011 pEntry->Old.pszCppName = psz;
2012 }
2013
2014 /*
2015 * Precompile it and calculate the checksum on the output.
2016 */
2017 InfoMsg(3, "precompiling -> '%s'...\n", pEntry->New.pszCppName);
2018 kOCEntrySpawn(pEntry, papszArgvPreComp, cArgvPreComp, "precompile", NULL);
2019 kOCEntryReadCppOutput(pEntry, &pEntry->New, 0 /* fatal */);
2020 kOCEntryCalcChecksum(pEntry);
2021 }
2022}
2023
2024
2025/**
2026 * Worker function for kOCEntryTeeConsumer and kOCEntryCompileIt that
2027 * writes the precompiler output to disk.
2028 *
2029 * @param pEntry The cache entry.
2030 * @param fFreeIt Whether we can free it after writing it or not.
2031 */
2032static void kOCEntryWriteCppOutput(PKOCENTRY pEntry, int fFreeIt)
2033{
2034 /*
2035 * Remove old files.
2036 */
2037 if (pEntry->Old.pszCppName)
2038 UnlinkFileInDir(pEntry->Old.pszCppName, pEntry->pszDir);
2039 if (pEntry->New.pszCppName)
2040 UnlinkFileInDir(pEntry->New.pszCppName, pEntry->pszDir);
2041
2042 /*
2043 * Write it to disk if we've got a file name.
2044 */
2045 if (pEntry->New.pszCppName)
2046 {
2047 long cbLeft;
2048 char *psz;
2049 int fd = OpenFileInDir(pEntry->New.pszCppName, pEntry->pszDir,
2050 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
2051 if (fd == -1)
2052 FatalDie("Failed to create '%s' in '%s': %s\n",
2053 pEntry->New.pszCppName, pEntry->pszDir, strerror(errno));
2054 psz = pEntry->New.pszCppMapping;
2055 cbLeft = (long)pEntry->New.cbCpp;
2056 while (cbLeft > 0)
2057 {
2058 long cbWritten = write(fd, psz, cbLeft);
2059 if (cbWritten < 0)
2060 {
2061 int iErr = errno;
2062 if (iErr == EINTR)
2063 continue;
2064 close(fd);
2065 UnlinkFileInDir(pEntry->New.pszCppName, pEntry->pszDir);
2066 FatalDie("error writing '%s' in '%s': %s\n",
2067 pEntry->New.pszCppName, pEntry->pszDir, strerror(iErr));
2068 }
2069
2070 psz += cbWritten;
2071 cbLeft -= cbWritten;
2072 }
2073 close(fd);
2074 }
2075
2076 /*
2077 * Free it.
2078 */
2079 if (fFreeIt)
2080 {
2081 free(pEntry->New.pszCppMapping);
2082 pEntry->New.pszCppMapping = NULL;
2083 }
2084}
2085
2086
2087/**
2088 * kOCEntrySpawnConsumer callback that passes the precompiler
2089 * output to the compiler and writes it to the disk (latter only when necesary).
2090 *
2091 * @param pEntry The cache entry.
2092 * @param fdOut The pipe handle connected to the childs stdin.
2093 */
2094static void kOCEntryCompileProducer(PKOCENTRY pEntry, int fdOut)
2095{
2096 const char *psz = pEntry->New.pszCppMapping;
2097 long cbLeft = (long)pEntry->New.cbCpp;
2098 while (cbLeft > 0)
2099 {
2100 long cbWritten = write(fdOut, psz, cbLeft);
2101 if (cbWritten < 0)
2102 {
2103 if (errno == EINTR)
2104 continue;
2105 FatalDie("compile - write(%d,,%ld) failed: %s\n", fdOut, cbLeft, strerror(errno));
2106 }
2107 psz += cbWritten;
2108 cbLeft -= cbWritten;
2109 }
2110 close(fdOut);
2111
2112 if (pEntry->fPipedPreComp)
2113 kOCEntryWriteCppOutput(pEntry, 1 /* free it */);
2114}
2115
2116
2117/**
2118 * Does the actual compiling.
2119 *
2120 * @param pEntry The cache entry.
2121 */
2122static void kOCEntryCompileIt(PKOCENTRY pEntry)
2123{
2124 /*
2125 * Delete the object files and free old cpp output that's no longer needed.
2126 */
2127 if (pEntry->Old.pszObjName)
2128 UnlinkFileInDir(pEntry->Old.pszObjName, pEntry->pszDir);
2129 UnlinkFileInDir(pEntry->New.pszObjName, pEntry->pszDir);
2130
2131 free(pEntry->Old.pszCppMapping);
2132 pEntry->Old.pszCppMapping = NULL;
2133 if (!pEntry->fPipedPreComp && !pEntry->fPipedCompile)
2134 {
2135 free(pEntry->New.pszCppMapping);
2136 pEntry->New.pszCppMapping = NULL;
2137 }
2138
2139 /*
2140 * Do the (re-)compile job.
2141 */
2142 if (pEntry->fPipedCompile)
2143 {
2144 if ( !pEntry->fPipedPreComp
2145 && !pEntry->New.pszCppMapping)
2146 kOCEntryReadCppOutput(pEntry, &pEntry->New, 0 /* fatal */);
2147 InfoMsg(3, "compiling -> '%s'...\n", pEntry->New.pszObjName);
2148 kOCEntrySpawnConsumer(pEntry, (const char * const *)pEntry->New.papszArgvCompile, pEntry->New.cArgvCompile,
2149 "compile", kOCEntryCompileProducer);
2150 }
2151 else
2152 {
2153 if (pEntry->fPipedPreComp)
2154 kOCEntryWriteCppOutput(pEntry, 1 /* free it */);
2155 InfoMsg(3, "compiling -> '%s'...\n", pEntry->New.pszObjName);
2156 kOCEntrySpawn(pEntry, (const char * const *)pEntry->New.papszArgvCompile, pEntry->New.cArgvCompile, "compile", NULL);
2157 }
2158}
2159
2160
2161/**
2162 * kOCEntrySpawnTee callback that works sort of like 'tee'.
2163 *
2164 * It will calculate the precompiled output checksum and
2165 * write it to disk while the compiler is busy compiling it.
2166 *
2167 * @param pEntry The cache entry.
2168 * @param fdIn The input handle (connected to the precompiler).
2169 * @param fdOut The output handle (connected to the compiler).
2170 */
2171static void kOCEntryTeeConsumer(PKOCENTRY pEntry, int fdIn, int fdOut)
2172{
2173 KOCSUMCTX Ctx;
2174 long cbLeft;
2175 long cbAlloc;
2176 char *psz;
2177
2178 kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx);
2179 cbAlloc = pEntry->Old.cbCpp ? ((long)pEntry->Old.cbCpp + 4*1024*1024 + 4096) & ~(4*1024*1024 - 1) : 4*1024*1024;
2180 cbLeft = cbAlloc;
2181 pEntry->New.pszCppMapping = psz = xmalloc(cbAlloc);
2182 InfoMsg(3, "precompiler|compile - starting passhtru...\n");
2183 for (;;)
2184 {
2185 /*
2186 * Read data from the pipe.
2187 */
2188 long cbRead = read(fdIn, psz, cbLeft - 1);
2189 if (!cbRead)
2190 break;
2191 if (cbRead < 0)
2192 {
2193 if (errno == EINTR)
2194 continue;
2195 FatalDie("precompile|compile - read(%d,,%ld) failed: %s\n",
2196 fdIn, (long)cbLeft, strerror(errno));
2197 }
2198 InfoMsg(3, "precompiler|compile - read %d\n", cbRead);
2199
2200 /*
2201 * Process the data.
2202 */
2203 psz[cbRead] = '\0';
2204 kOCSumUpdate(&pEntry->New.SumHead, &Ctx, psz, cbRead);
2205 do
2206 {
2207 long cbWritten = write(fdOut, psz, cbRead);
2208 if (cbWritten < 0)
2209 {
2210 if (errno == EINTR)
2211 continue;
2212 FatalDie("precompile|compile - write(%d,,%ld) failed: %s\n", fdOut, cbRead, strerror(errno));
2213 }
2214 psz += cbWritten;
2215 cbRead -= cbWritten;
2216 cbLeft -= cbWritten;
2217 } while (cbRead > 0);
2218
2219 /*
2220 * Expand the buffer?
2221 */
2222 if (cbLeft <= 1)
2223 {
2224 size_t off = psz - pEntry->New.pszCppMapping;
2225 cbLeft = 4*1024*1024;
2226 cbAlloc += cbLeft;
2227 pEntry->New.pszCppMapping = xrealloc(pEntry->New.pszCppMapping, cbAlloc);
2228 psz = pEntry->New.pszCppMapping + off;
2229 }
2230 }
2231 InfoMsg(3, "precompiler|compile - done passhtru\n");
2232
2233 close(fdIn);
2234 close(fdOut);
2235 pEntry->New.cbCpp = cbAlloc - cbLeft;
2236 kOCSumFinalize(&pEntry->New.SumHead, &Ctx);
2237 kOCSumInfo(&pEntry->New.SumHead, 4, "cpp (tee)");
2238
2239 /*
2240 * Write the precompiler output to disk and free the memory it
2241 * occupies while the compiler is busy compiling.
2242 */
2243 kOCEntryWriteCppOutput(pEntry, 1 /* free it */);
2244}
2245
2246
2247/**
2248 * Performs pre-compile and compile in one go (typical clean build scenario).
2249 *
2250 * @param pEntry The cache entry.
2251 * @param papszArgvPreComp The argument vector for executing precompiler. The cArgvPreComp'th argument must be NULL.
2252 * @param cArgvPreComp The number of arguments.
2253 */
2254static void kOCEntryPreCompileAndCompile(PKOCENTRY pEntry, const char * const *papszArgvPreComp, unsigned cArgvPreComp)
2255{
2256 if ( pEntry->fPipedCompile
2257 && pEntry->fPipedPreComp)
2258 {
2259 /*
2260 * Clean up old stuff first.
2261 */
2262 if (pEntry->Old.pszObjName)
2263 UnlinkFileInDir(pEntry->Old.pszObjName, pEntry->pszDir);
2264 if (pEntry->New.pszObjName)
2265 UnlinkFileInDir(pEntry->New.pszObjName, pEntry->pszDir);
2266 if (pEntry->Old.pszCppName)
2267 UnlinkFileInDir(pEntry->Old.pszCppName, pEntry->pszDir);
2268 if (pEntry->New.pszCppName)
2269 UnlinkFileInDir(pEntry->New.pszCppName, pEntry->pszDir);
2270
2271 /*
2272 * Do the actual compile and write the precompiler output to disk.
2273 */
2274 kOCEntrySpawnTee(pEntry, papszArgvPreComp, cArgvPreComp,
2275 (const char * const *)pEntry->New.papszArgvCompile, pEntry->New.cArgvCompile,
2276 "precompile|compile", kOCEntryTeeConsumer);
2277 }
2278 else
2279 {
2280 kOCEntryPreCompile(pEntry, papszArgvPreComp, cArgvPreComp);
2281 kOCEntryCompileIt(pEntry);
2282 }
2283}
2284
2285
2286/**
2287 * Check whether the string is a '#line' statement.
2288 *
2289 * @returns 1 if it is, 0 if it isn't.
2290 * @param psz The line to examin.
2291 * @parma piLine Where to store the line number.
2292 * @parma ppszFile Where to store the start of the filename.
2293 */
2294static int kOCEntryIsLineStatement(const char *psz, unsigned *piLine, const char **ppszFile)
2295{
2296 unsigned iLine;
2297
2298 /* Expect a hash. */
2299 if (*psz++ != '#')
2300 return 0;
2301
2302 /* Skip blanks between '#' and the line / number */
2303 while (*psz == ' ' || *psz == '\t')
2304 psz++;
2305
2306 /* Skip the 'line' if present. */
2307 if (!strncmp(psz, "line", sizeof("line") - 1))
2308 psz += sizeof("line");
2309
2310 /* Expect a line number now. */
2311 if ((unsigned char)(*psz - '0') > 9)
2312 return 0;
2313 iLine = 0;
2314 do
2315 {
2316 iLine *= 10;
2317 iLine += (*psz - '0');
2318 psz++;
2319 }
2320 while ((unsigned char)(*psz - '0') <= 9);
2321
2322 /* Expect one or more space now. */
2323 if (*psz != ' ' && *psz != '\t')
2324 return 0;
2325 do psz++;
2326 while (*psz == ' ' || *psz == '\t');
2327
2328 /* that's good enough. */
2329 *piLine = iLine;
2330 *ppszFile = psz;
2331 return 1;
2332}
2333
2334
2335/**
2336 * Scan backwards for the previous #line statement.
2337 *
2338 * @returns The filename in the previous statement.
2339 * @param pszStart Where to start.
2340 * @param pszStop Where to stop. Less than pszStart.
2341 * @param piLine The line number count to adjust.
2342 */
2343static const char *kOCEntryFindFileStatement(const char *pszStart, const char *pszStop, unsigned *piLine)
2344{
2345 unsigned iLine = *piLine;
2346 assert(pszStart >= pszStop);
2347 while (pszStart >= pszStop)
2348 {
2349 if (*pszStart == '\n')
2350 iLine++;
2351 else if (*pszStart == '#')
2352 {
2353 unsigned iLineTmp;
2354 const char *pszFile;
2355 const char *psz = pszStart - 1;
2356 while (psz >= pszStop && (*psz == ' ' || *psz =='\t'))
2357 psz--;
2358 if ( (psz < pszStop || *psz == '\n')
2359 && kOCEntryIsLineStatement(pszStart, &iLineTmp, &pszFile))
2360 {
2361 *piLine = iLine + iLineTmp - 1;
2362 return pszFile;
2363 }
2364 }
2365 pszStart--;
2366 }
2367 return NULL;
2368}
2369
2370
2371/**
2372 * Worker for kOCEntryCompareOldAndNewOutput() that compares the
2373 * precompiled output using a fast but not very good method.
2374 *
2375 * @returns 1 if matching, 0 if not matching.
2376 * @param pEntry The entry containing the names of the files to compare.
2377 * The entry is not updated in any way.
2378 */
2379static int kOCEntryCompareFast(PCKOCENTRY pEntry)
2380{
2381 const char * psz1 = pEntry->New.pszCppMapping;
2382 const char * const pszEnd1 = psz1 + pEntry->New.cbCpp;
2383 const char * psz2 = pEntry->Old.pszCppMapping;
2384 const char * const pszEnd2 = psz2 + pEntry->Old.cbCpp;
2385
2386 assert(*pszEnd1 == '\0');
2387 assert(*pszEnd2 == '\0');
2388
2389 /*
2390 * Iterate block by block and backtrack when we find a difference.
2391 */
2392 for (;;)
2393 {
2394 size_t cch = pszEnd1 - psz1;
2395 if (cch > (size_t)(pszEnd2 - psz2))
2396 cch = pszEnd2 - psz2;
2397 if (cch > 4096)
2398 cch = 4096;
2399 if ( cch
2400 && !memcmp(psz1, psz2, cch))
2401 {
2402 /* no differences */
2403 psz1 += cch;
2404 psz2 += cch;
2405 }
2406 else
2407 {
2408 /*
2409 * Pinpoint the difference exactly and the try find the start
2410 * of that line. Then skip forward until we find something to
2411 * work on that isn't spaces, #line statements or closing curly
2412 * braces.
2413 *
2414 * The closing curly braces are ignored because they are frequently
2415 * found at the end of header files (__END_DECLS) and the worst
2416 * thing that may happen if it isn't one of these braces we're
2417 * ignoring is that the final line in a function block is a little
2418 * bit off in the debug info.
2419 *
2420 * Since we might be skipping a few new empty headers, it is
2421 * possible that we will omit this header from the dependencies
2422 * when using VCC. This might not be a problem, since it seems
2423 * we'll have to use the precompiler output to generate the deps
2424 * anyway.
2425 */
2426 const char *psz;
2427 const char *pszMismatch1;
2428 const char *pszFile1 = NULL;
2429 unsigned iLine1 = 0;
2430 unsigned cCurlyBraces1 = 0;
2431 const char *pszMismatch2;
2432 const char *pszFile2 = NULL;
2433 unsigned iLine2 = 0;
2434 unsigned cCurlyBraces2 = 0;
2435
2436 /* locate the difference. */
2437 while (cch >= 512 && !memcmp(psz1, psz2, 512))
2438 psz1 += 512, psz2 += 512, cch -= 512;
2439 while (cch >= 64 && !memcmp(psz1, psz2, 64))
2440 psz1 += 64, psz2 += 64, cch -= 64;
2441 while (*psz1 == *psz2 && cch > 0)
2442 psz1++, psz2++, cch--;
2443
2444 /* locate the start of that line. */
2445 psz = psz1;
2446 while ( psz > pEntry->New.pszCppMapping
2447 && psz[-1] != '\n')
2448 psz--;
2449 psz2 -= (psz1 - psz);
2450 pszMismatch2 = psz2;
2451 pszMismatch1 = psz1 = psz;
2452
2453 /* Parse the 1st file line by line. */
2454 while (psz1 < pszEnd1)
2455 {
2456 if (*psz1 == '\n')
2457 {
2458 psz1++;
2459 iLine1++;
2460 }
2461 else
2462 {
2463 psz = psz1;
2464 while (isspace(*psz) && *psz != '\n')
2465 psz++;
2466 if (*psz == '\n')
2467 {
2468 psz1 = psz + 1;
2469 iLine1++;
2470 }
2471 else if (*psz == '#' && kOCEntryIsLineStatement(psz, &iLine1, &pszFile1))
2472 {
2473 psz1 = memchr(psz, '\n', pszEnd1 - psz);
2474 if (!psz1++)
2475 psz1 = pszEnd1;
2476 }
2477 else if (*psz == '}')
2478 {
2479 do psz++;
2480 while (isspace(*psz) && *psz != '\n');
2481 if (*psz == '\n')
2482 iLine1++;
2483 else if (psz != pszEnd1)
2484 break;
2485 cCurlyBraces1++;
2486 psz1 = psz;
2487 }
2488 else if (psz == pszEnd1)
2489 psz1 = psz;
2490 else /* found something that can be compared. */
2491 break;
2492 }
2493 }
2494
2495 /* Ditto for the 2nd file. */
2496 while (psz2 < pszEnd2)
2497 {
2498 if (*psz2 == '\n')
2499 {
2500 psz2++;
2501 iLine2++;
2502 }
2503 else
2504 {
2505 psz = psz2;
2506 while (isspace(*psz) && *psz != '\n')
2507 psz++;
2508 if (*psz == '\n')
2509 {
2510 psz2 = psz + 1;
2511 iLine2++;
2512 }
2513 else if (*psz == '#' && kOCEntryIsLineStatement(psz, &iLine2, &pszFile2))
2514 {
2515 psz2 = memchr(psz, '\n', pszEnd2 - psz);
2516 if (!psz2++)
2517 psz2 = pszEnd2;
2518 }
2519 else if (*psz == '}')
2520 {
2521 do psz++;
2522 while (isspace(*psz) && *psz != '\n');
2523 if (*psz == '\n')
2524 iLine2++;
2525 else if (psz != pszEnd2)
2526 break;
2527 cCurlyBraces2++;
2528 psz2 = psz;
2529 }
2530 else if (psz == pszEnd2)
2531 psz2 = psz;
2532 else /* found something that can be compared. */
2533 break;
2534 }
2535 }
2536
2537 /* Match the number of ignored closing curly braces. */
2538 if (cCurlyBraces1 != cCurlyBraces2)
2539 return 0;
2540
2541 /* Reaching the end of any of them means the return statement can decide. */
2542 if ( psz1 == pszEnd1
2543 || psz2 == pszEnd2)
2544 break;
2545
2546 /* Match the current line. */
2547 psz = memchr(psz1, '\n', pszEnd1 - psz1);
2548 if (!psz++)
2549 psz = pszEnd1;
2550 cch = psz - psz1;
2551 if (psz2 + cch > pszEnd2)
2552 break;
2553 if (memcmp(psz1, psz2, cch))
2554 break;
2555
2556 /* Check that we're at the same location now. */
2557 if (!pszFile1)
2558 pszFile1 = kOCEntryFindFileStatement(pszMismatch1, pEntry->New.pszCppMapping, &iLine1);
2559 if (!pszFile2)
2560 pszFile2 = kOCEntryFindFileStatement(pszMismatch2, pEntry->Old.pszCppMapping, &iLine2);
2561 if (pszFile1 && pszFile2)
2562 {
2563 if (iLine1 != iLine2)
2564 break;
2565 while (*pszFile1 == *pszFile2 && *pszFile1 != '\n' && *pszFile1)
2566 pszFile1++, pszFile2++;
2567 if (*pszFile1 != *pszFile2)
2568 break;
2569 }
2570 else if (pszFile1 || pszFile2)
2571 {
2572 assert(0); /* this shouldn't happen. */
2573 break;
2574 }
2575
2576 /* Advance. We might now have a misaligned buffer, but that's memcmps problem... */
2577 psz1 += cch;
2578 psz2 += cch;
2579 }
2580 }
2581
2582 return psz1 == pszEnd1
2583 && psz2 == pszEnd2;
2584}
2585
2586
2587/**
2588 * Worker for kOCEntryCompileIfNeeded that compares the
2589 * precompiled output.
2590 *
2591 * @returns 1 if matching, 0 if not matching.
2592 * @param pEntry The entry containing the names of the files to compare.
2593 * This will load the old cpp output (changing pszOldCppName and Old.cbCpp).
2594 */
2595static int kOCEntryCompareOldAndNewOutput(PKOCENTRY pEntry)
2596{
2597 /*
2598 * I may implement a more sophisticated alternative method later... maybe.
2599 */
2600 if (kOCEntryReadCppOutput(pEntry, &pEntry->Old, 1 /* nonfatal */) == -1)
2601 return 0;
2602 //if ()
2603 // return kOCEntryCompareBest(pEntry);
2604 return kOCEntryCompareFast(pEntry);
2605}
2606
2607
2608/**
2609 * Check if re-compilation is required.
2610 * This sets the fNeedCompile flag.
2611 *
2612 * @param pEntry The cache entry.
2613 */
2614static void kOCEntryCalcRecompile(PKOCENTRY pEntry)
2615{
2616 if (pEntry->fNeedCompiling)
2617 return;
2618
2619 /*
2620 * Check if the precompiler output differ in any significant way?
2621 */
2622 if (!kOCSumHasEqualInChain(&pEntry->Old.SumHead, &pEntry->New.SumHead))
2623 {
2624 InfoMsg(2, "no checksum match - comparing output\n");
2625 if (!kOCEntryCompareOldAndNewOutput(pEntry))
2626 pEntry->fNeedCompiling = 1;
2627 else
2628 kOCSumAddChain(&pEntry->New.SumHead, &pEntry->Old.SumHead);
2629 }
2630}
2631
2632
2633/**
2634 * Does this cache entry need compiling or what?
2635 *
2636 * @returns 1 if it does, 0 if it doesn't.
2637 * @param pEntry The cache entry in question.
2638 */
2639static int kOCEntryNeedsCompiling(PCKOCENTRY pEntry)
2640{
2641 return pEntry->fNeedCompiling;
2642}
2643
2644
2645/**
2646 * Worker function for kOCEntryCopy.
2647 *
2648 * @param pEntry The entry we're coping to, which pszTo is relative to.
2649 * @param pszTo The destination.
2650 * @param pszFrom The source. This path will be freed.
2651 */
2652static void kOCEntryCopyFile(PCKOCENTRY pEntry, const char *pszTo, char *pszSrc)
2653{
2654 char *pszDst = MakePathFromDirAndFile(pszTo, pEntry->pszDir);
2655 char *pszBuf = xmalloc(256 * 1024);
2656 char *psz;
2657 int fdSrc;
2658 int fdDst;
2659
2660 /*
2661 * Open the files.
2662 */
2663 fdSrc = open(pszSrc, O_RDONLY | O_BINARY);
2664 if (fdSrc == -1)
2665 FatalDie("failed to open '%s': %s\n", pszSrc, strerror(errno));
2666
2667 unlink(pszDst);
2668 fdDst = open(pszDst, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
2669 if (fdDst == -1)
2670 FatalDie("failed to create '%s': %s\n", pszDst, strerror(errno));
2671
2672 /*
2673 * Copy them.
2674 */
2675 for (;;)
2676 {
2677 /* read a chunk. */
2678 long cbRead = read(fdSrc, pszBuf, 256*1024);
2679 if (cbRead < 0)
2680 {
2681 if (errno == EINTR)
2682 continue;
2683 FatalDie("read '%s' failed: %s\n", pszSrc, strerror(errno));
2684 }
2685 if (!cbRead)
2686 break; /* eof */
2687
2688 /* write the chunk. */
2689 psz = pszBuf;
2690 do
2691 {
2692 long cbWritten = write(fdDst, psz, cbRead);
2693 if (cbWritten < 0)
2694 {
2695 if (errno == EINTR)
2696 continue;
2697 FatalDie("write '%s' failed: %s\n", pszSrc, strerror(errno));
2698 }
2699 psz += cbWritten;
2700 cbRead -= cbWritten;
2701 } while (cbRead > 0);
2702 }
2703
2704 /* cleanup */
2705 if (close(fdDst) != 0)
2706 FatalDie("closing '%s' failed: %s\n", pszDst, strerror(errno));
2707 close(fdSrc);
2708 free(pszBuf);
2709 free(pszDst);
2710 free(pszSrc);
2711}
2712
2713
2714/**
2715 * Copies the object (and whatever else) from one cache entry to another.
2716 *
2717 * This is called when a matching cache entry has been found and we don't
2718 * need to recompile anything.
2719 *
2720 * @param pEntry The entry to copy to.
2721 * @param pFrom The entry to copy from.
2722 */
2723static void kOCEntryCopy(PKOCENTRY pEntry, PCKOCENTRY pFrom)
2724{
2725 kOCEntryCopyFile(pEntry, pEntry->New.pszObjName,
2726 MakePathFromDirAndFile(pFrom->New.pszObjName
2727 ? pFrom->New.pszObjName : pFrom->Old.pszObjName,
2728 pFrom->pszDir));
2729}
2730
2731
2732/**
2733 * Gets the absolute path to the cache entry.
2734 *
2735 * @returns absolute path to the cache entry.
2736 * @param pEntry The cache entry in question.
2737 */
2738static const char *kOCEntryAbsPath(PCKOCENTRY pEntry)
2739{
2740 return pEntry->pszAbsPath;
2741}
2742
2743
2744
2745
2746
2747
2748/**
2749 * Digest of one cache entry.
2750 *
2751 * This contains all the information required to find a matching
2752 * cache entry without having to open each of the files.
2753 */
2754typedef struct KOCDIGEST
2755{
2756 /** The relative path to the entry. Optional if pszAbsPath is set. */
2757 char *pszRelPath;
2758 /** The absolute path to the entry. Optional if pszRelPath is set. */
2759 char *pszAbsPath;
2760 /** The target os/arch identifier. */
2761 char *pszTarget;
2762 /** A unique number assigned to the entry when it's (re)-inserted
2763 * into the cache. This is used for simple consitency checking. */
2764 uint32_t uKey;
2765 /** The checksum of the compile argument vector. */
2766 KOCSUM SumCompArgv;
2767 /** The list of precompiler output checksums that's . */
2768 KOCSUM SumHead;
2769} KOCDIGEST;
2770/** Pointer to a file digest. */
2771typedef KOCDIGEST *PKOCDIGEST;
2772/** Pointer to a const file digest. */
2773typedef KOCDIGEST *PCKOCDIGEST;
2774
2775
2776/**
2777 * Initializes the specified digest.
2778 *
2779 * @param pDigest The digest.
2780 */
2781static void kOCDigestInit(PKOCDIGEST pDigest)
2782{
2783 memset(pDigest, 0, sizeof(*pDigest));
2784 kOCSumInit(&pDigest->SumHead);
2785}
2786
2787
2788/**
2789 * Initializes the digest for the specified entry.
2790 *
2791 * @param pDigest The (uninitialized) digest.
2792 * @param pEntry The entry.
2793 */
2794static void kOCDigestInitFromEntry(PKOCDIGEST pDigest, PCKOCENTRY pEntry)
2795{
2796 kOCDigestInit(pDigest);
2797
2798 pDigest->uKey = pEntry->uKey;
2799 pDigest->pszTarget = xstrdup(pEntry->New.pszTarget ? pEntry->New.pszTarget : pEntry->Old.pszTarget);
2800
2801 kOCSumInit(&pDigest->SumCompArgv);
2802 if (!kOCSumIsEmpty(&pEntry->New.SumCompArgv))
2803 kOCSumAdd(&pDigest->SumCompArgv, &pEntry->New.SumCompArgv);
2804 else
2805 kOCSumAdd(&pDigest->SumCompArgv, &pEntry->Old.SumCompArgv);
2806
2807 kOCSumInit(&pDigest->SumHead);
2808 if (!kOCSumIsEmpty(&pEntry->New.SumHead))
2809 kOCSumAddChain(&pDigest->SumHead, &pEntry->New.SumHead);
2810 else
2811 kOCSumAddChain(&pDigest->SumHead, &pEntry->Old.SumHead);
2812
2813 /** @todo implement selective relative path support. */
2814 pDigest->pszRelPath = NULL;
2815 pDigest->pszAbsPath = xstrdup(kOCEntryAbsPath(pEntry));
2816}
2817
2818
2819/**
2820 * Purges a digest, freeing all resources and returning
2821 * it to the initial state.
2822 *
2823 * @param pDigest The digest.
2824 */
2825static void kOCDigestPurge(PKOCDIGEST pDigest)
2826{
2827 free(pDigest->pszRelPath);
2828 free(pDigest->pszAbsPath);
2829 free(pDigest->pszTarget);
2830 pDigest->pszTarget = pDigest->pszAbsPath = pDigest->pszRelPath = NULL;
2831 pDigest->uKey = 0;
2832 kOCSumDeleteChain(&pDigest->SumCompArgv);
2833 kOCSumDeleteChain(&pDigest->SumHead);
2834}
2835
2836
2837/**
2838 * Returns the absolute path to the entry, calculating
2839 * the path if necessary.
2840 *
2841 * @returns absolute path.
2842 * @param pDigest The digest.
2843 * @param pszDir The cache directory that it might be relative to.
2844 */
2845static const char *kOCDigestAbsPath(PCKOCDIGEST pDigest, const char *pszDir)
2846{
2847 if (!pDigest->pszAbsPath)
2848 {
2849 char *pszPath = MakePathFromDirAndFile(pDigest->pszRelPath, pszDir);
2850 ((PKOCDIGEST)pDigest)->pszAbsPath = AbsPath(pszPath);
2851 free(pszPath);
2852 }
2853 return pDigest->pszAbsPath;
2854}
2855
2856
2857/**
2858 * Checks that the digest matches the
2859 *
2860 * @returns 1 if valid, 0 if invalid in some way.
2861 *
2862 * @param pDigest The digest to validate.
2863 * @param pEntry What to validate it against.
2864 */
2865static int kOCDigestIsValid(PCKOCDIGEST pDigest, PCKOCENTRY pEntry)
2866{
2867 PCKOCSUM pSum;
2868 PCKOCSUM pSumEntry;
2869
2870 if (pDigest->uKey != pEntry->uKey)
2871 return 0;
2872
2873 if (!kOCSumIsEqual(&pDigest->SumCompArgv,
2874 kOCSumIsEmpty(&pEntry->New.SumCompArgv)
2875 ? &pEntry->Old.SumCompArgv : &pEntry->New.SumCompArgv))
2876 return 0;
2877
2878 if (strcmp(pDigest->pszTarget, pEntry->New.pszTarget ? pEntry->New.pszTarget : pEntry->Old.pszTarget))
2879 return 0;
2880
2881 /* match the checksums */
2882 pSumEntry = kOCSumIsEmpty(&pEntry->New.SumHead)
2883 ? &pEntry->Old.SumHead : &pEntry->New.SumHead;
2884 for (pSum = &pDigest->SumHead; pSum; pSum = pSum->pNext)
2885 if (!kOCSumHasEqualInChain(pSumEntry, pSum))
2886 return 0;
2887
2888 return 1;
2889}
2890
2891
2892
2893
2894
2895/**
2896 * The structure for the central cache entry.
2897 */
2898typedef struct KOBJCACHE
2899{
2900 /** The entry name. */
2901 const char *pszName;
2902 /** The dir that relative names in the digest are relative to. */
2903 char *pszDir;
2904 /** The absolute path. */
2905 char *pszAbsPath;
2906
2907 /** The cache file descriptor. */
2908 int fd;
2909 /** The stream associated with fd. */
2910 FILE *pFile;
2911 /** Whether it's currently locked or not. */
2912 unsigned fLocked;
2913 /** Whether the cache file is dirty and needs writing back. */
2914 unsigned fDirty;
2915 /** Whether this is a new cache or not. */
2916 unsigned fNewCache;
2917
2918 /** The cache file generation. */
2919 uint32_t uGeneration;
2920 /** The next valid key. (Determin at load time.) */
2921 uint32_t uNextKey;
2922
2923 /** Number of digests in paDigests. */
2924 unsigned cDigests;
2925 /** Array of digests for the KOCENTRY objects in the cache. */
2926 PKOCDIGEST paDigests;
2927
2928} KOBJCACHE;
2929/** Pointer to a cache. */
2930typedef KOBJCACHE *PKOBJCACHE;
2931/** Pointer to a const cache. */
2932typedef KOBJCACHE const *PCKOBJCACHE;
2933
2934
2935/**
2936 * Creates an empty cache.
2937 *
2938 * This doesn't touch the file system, it just create the data structure.
2939 *
2940 * @returns Pointer to a cache.
2941 * @param pszCacheFile The cache file.
2942 */
2943static PKOBJCACHE kObjCacheCreate(const char *pszCacheFile)
2944{
2945 PKOBJCACHE pCache;
2946 size_t off;
2947
2948 /*
2949 * Allocate an empty entry.
2950 */
2951 pCache = xmallocz(sizeof(*pCache));
2952 pCache->fd = -1;
2953
2954 /*
2955 * Setup the directory and cache file name.
2956 */
2957 pCache->pszAbsPath = AbsPath(pszCacheFile);
2958 pCache->pszName = FindFilenameInPath(pCache->pszAbsPath);
2959 off = pCache->pszName - pCache->pszAbsPath;
2960 if (!off)
2961 FatalDie("Failed to find abs path for '%s'!\n", pszCacheFile);
2962 pCache->pszDir = xmalloc(off);
2963 memcpy(pCache->pszDir, pCache->pszAbsPath, off - 1);
2964 pCache->pszDir[off - 1] = '\0';
2965
2966 return pCache;
2967}
2968
2969
2970/**
2971 * Destroys the cache - closing any open files, freeing up heap memory and such.
2972 *
2973 * @param pCache The cache.
2974 */
2975static void kObjCacheDestroy(PKOBJCACHE pCache)
2976{
2977 if (pCache->pFile)
2978 {
2979 errno = 0;
2980 if (fclose(pCache->pFile) != 0)
2981 FatalMsg("fclose failed: %s\n", strerror(errno));
2982 pCache->pFile = NULL;
2983 pCache->fd = -1;
2984 }
2985 free(pCache->paDigests);
2986 free(pCache->pszAbsPath);
2987 free(pCache->pszDir);
2988 free(pCache);
2989}
2990
2991
2992/**
2993 * Purges the data in the cache object.
2994 *
2995 * @param pCache The cache object.
2996 */
2997static void kObjCachePurge(PKOBJCACHE pCache)
2998{
2999 while (pCache->cDigests > 0)
3000 kOCDigestPurge(&pCache->paDigests[--pCache->cDigests]);
3001 free(pCache->paDigests);
3002 pCache->paDigests = NULL;
3003 pCache->uGeneration = 0;
3004 pCache->uNextKey = 0;
3005}
3006
3007
3008/**
3009 * (Re-)reads the file.
3010 *
3011 * @param pCache The cache to (re)-read.
3012 */
3013static void kObjCacheRead(PKOBJCACHE pCache)
3014{
3015 unsigned i;
3016 char szBuf[8192];
3017 int fBad = 0;
3018
3019 InfoMsg(4, "reading cache file...\n");
3020
3021 /*
3022 * Rewind the file & stream, and associate a temporary buffer
3023 * with the stream to speed up reading.
3024 */
3025 if (lseek(pCache->fd, 0, SEEK_SET) == -1)
3026 FatalDie("lseek(cache-fd) failed: %s\n", strerror(errno));
3027 rewind(pCache->pFile);
3028 if (setvbuf(pCache->pFile, szBuf, _IOFBF, sizeof(szBuf)) != 0)
3029 FatalDie("fdopen(cache-fd,rb) failed: %s\n", strerror(errno));
3030
3031 /*
3032 * Read magic and generation.
3033 */
3034 if ( !fgets(g_szLine, sizeof(g_szLine), pCache->pFile)
3035 || strcmp(g_szLine, "magic=kObjCache-v0.1.0\n"))
3036 {
3037 InfoMsg(2, "bad cache file (magic)\n");
3038 fBad = 1;
3039 }
3040 else if ( !fgets(g_szLine, sizeof(g_szLine), pCache->pFile)
3041 || strncmp(g_szLine, "generation=", sizeof("generation=") - 1))
3042 {
3043 InfoMsg(2, "bad cache file (generation)\n");
3044 fBad = 1;
3045 }
3046 else if ( pCache->uGeneration
3047 && pCache->uGeneration == atol(&g_szLine[sizeof("generation=") - 1]))
3048 {
3049 InfoMsg(3, "drop re-read unmodified cache file\n");
3050 fBad = 0;
3051 }
3052 else
3053 {
3054 int fBadBeforeMissing;
3055
3056 /*
3057 * Read everything (anew).
3058 */
3059 kObjCachePurge(pCache);
3060 do
3061 {
3062 PKOCDIGEST pDigest;
3063 char *pszNl;
3064 char *pszVal;
3065 char *psz;
3066
3067 /* Split the line and drop the trailing newline. */
3068 pszVal = strchr(g_szLine, '=');
3069 if ((fBad = pszVal == NULL))
3070 break;
3071 *pszVal++ = '\0';
3072
3073 pszNl = strchr(pszVal, '\n');
3074 if (pszNl)
3075 *pszNl = '\0';
3076
3077 /* digest '#'? */
3078 psz = strchr(g_szLine, '#');
3079 if (psz)
3080 {
3081 char *pszNext;
3082 i = strtoul(++psz, &pszNext, 0);
3083 if ((fBad = pszNext && *pszNext))
3084 break;
3085 if ((fBad = i >= pCache->cDigests))
3086 break;
3087 pDigest = &pCache->paDigests[i];
3088 *psz = '\0';
3089 }
3090 else
3091 pDigest = NULL;
3092
3093
3094 /* string case on value name. */
3095 if (!strcmp(g_szLine, "sum-#"))
3096 {
3097 KOCSUM Sum;
3098 if ((fBad = kOCSumInitFromString(&Sum, pszVal) != 0))
3099 break;
3100 kOCSumAdd(&pDigest->SumHead, &Sum);
3101 }
3102 else if (!strcmp(g_szLine, "digest-abs-#"))
3103 {
3104 if ((fBad = pDigest->pszAbsPath != NULL))
3105 break;
3106 pDigest->pszAbsPath = xstrdup(pszVal);
3107 }
3108 else if (!strcmp(g_szLine, "digest-rel-#"))
3109 {
3110 if ((fBad = pDigest->pszRelPath != NULL))
3111 break;
3112 pDigest->pszRelPath = xstrdup(pszVal);
3113 }
3114 else if (!strcmp(g_szLine, "key-#"))
3115 {
3116 if ((fBad = pDigest->uKey != 0))
3117 break;
3118 pDigest->uKey = strtoul(pszVal, &psz, 0);
3119 if ((fBad = psz && *psz))
3120 break;
3121 if (pDigest->uKey >= pCache->uNextKey)
3122 pCache->uNextKey = pDigest->uKey + 1;
3123 }
3124 else if (!strcmp(g_szLine, "comp-argv-sum-#"))
3125 {
3126 if ((fBad = !kOCSumIsEmpty(&pDigest->SumCompArgv)))
3127 break;
3128 if ((fBad = kOCSumInitFromString(&pDigest->SumCompArgv, pszVal) != 0))
3129 break;
3130 }
3131 else if (!strcmp(g_szLine, "target-#"))
3132 {
3133 if ((fBad = pDigest->pszTarget != NULL))
3134 break;
3135 pDigest->pszTarget = xstrdup(pszVal);
3136 }
3137 else if (!strcmp(g_szLine, "digests"))
3138 {
3139 if ((fBad = pCache->paDigests != NULL))
3140 break;
3141 pCache->cDigests = strtoul(pszVal, &psz, 0);
3142 if ((fBad = psz && *psz))
3143 break;
3144 i = (pCache->cDigests + 4) & ~3;
3145 pCache->paDigests = xmalloc(i * sizeof(pCache->paDigests[0]));
3146 for (i = 0; i < pCache->cDigests; i++)
3147 kOCDigestInit(&pCache->paDigests[i]);
3148 }
3149 else if (!strcmp(g_szLine, "generation"))
3150 {
3151 if ((fBad = pCache->uGeneration != 0))
3152 break;
3153 pCache->uGeneration = strtoul(pszVal, &psz, 0);
3154 if ((fBad = psz && *psz))
3155 break;
3156 }
3157 else if (!strcmp(g_szLine, "the-end"))
3158 {
3159 fBad = strcmp(pszVal, "fine");
3160 break;
3161 }
3162 else
3163 {
3164 fBad = 1;
3165 break;
3166 }
3167 } while (fgets(g_szLine, sizeof(g_szLine), pCache->pFile));
3168
3169 /*
3170 * Did we find everything?
3171 */
3172 fBadBeforeMissing = fBad;
3173 if ( !fBad
3174 && !pCache->uGeneration)
3175 fBad = 1;
3176 if (!fBad)
3177 for (i = 0; i < pCache->cDigests; i++)
3178 {
3179 if ((fBad = kOCSumIsEmpty(&pCache->paDigests[i].SumCompArgv)))
3180 break;
3181 if ((fBad = kOCSumIsEmpty(&pCache->paDigests[i].SumHead)))
3182 break;
3183 if ((fBad = pCache->paDigests[i].uKey == 0))
3184 break;
3185 if ((fBad = pCache->paDigests[i].pszAbsPath == NULL
3186 && pCache->paDigests[i].pszRelPath == NULL))
3187 break;
3188 if ((fBad = pCache->paDigests[i].pszTarget == NULL))
3189 break;
3190 InfoMsg(4, "digest-%u: %s\n", i, pCache->paDigests[i].pszAbsPath
3191 ? pCache->paDigests[i].pszAbsPath : pCache->paDigests[i].pszRelPath);
3192 }
3193 if (fBad)
3194 InfoMsg(2, "bad cache file (%s)\n", fBadBeforeMissing ? g_szLine : "missing stuff");
3195 else if (ferror(pCache->pFile))
3196 {
3197 InfoMsg(2, "cache file read error\n");
3198 fBad = 1;
3199 }
3200 }
3201 if (fBad)
3202 {
3203 kObjCachePurge(pCache);
3204 pCache->fNewCache = 1;
3205 }
3206
3207 /*
3208 * Disassociate the buffer from the stream changing
3209 * it to non-buffered mode.
3210 */
3211 if (setvbuf(pCache->pFile, NULL, _IONBF, 0) != 0)
3212 FatalDie("setvbuf(,0,,0) failed: %s\n", strerror(errno));
3213}
3214
3215
3216/**
3217 * Re-writes the cache file.
3218 *
3219 * @param pCache The cache to commit and unlock.
3220 */
3221static void kObjCacheWrite(PKOBJCACHE pCache)
3222{
3223 unsigned i;
3224 off_t cb;
3225 char szBuf[8192];
3226 assert(pCache->fLocked);
3227 assert(pCache->fDirty);
3228
3229 /*
3230 * Rewind the file & stream, and associate a temporary buffer
3231 * with the stream to speed up the writing.
3232 */
3233 if (lseek(pCache->fd, 0, SEEK_SET) == -1)
3234 FatalDie("lseek(cache-fd) failed: %s\n", strerror(errno));
3235 rewind(pCache->pFile);
3236 if (setvbuf(pCache->pFile, szBuf, _IOFBF, sizeof(szBuf)) != 0)
3237 FatalDie("setvbuf failed: %s\n", strerror(errno));
3238
3239 /*
3240 * Write the header.
3241 */
3242 pCache->uGeneration++;
3243 fprintf(pCache->pFile,
3244 "magic=kObjCache-v0.1.0\n"
3245 "generation=%d\n"
3246 "digests=%d\n",
3247 pCache->uGeneration,
3248 pCache->cDigests);
3249
3250 /*
3251 * Write the digests.
3252 */
3253 for (i = 0; i < pCache->cDigests; i++)
3254 {
3255 PCKOCDIGEST pDigest = &pCache->paDigests[i];
3256 PKOCSUM pSum;
3257
3258 if (pDigest->pszAbsPath)
3259 fprintf(pCache->pFile, "digest-abs-#%u=%s\n", i, pDigest->pszAbsPath);
3260 if (pDigest->pszRelPath)
3261 fprintf(pCache->pFile, "digest-rel-#%u=%s\n", i, pDigest->pszRelPath);
3262 fprintf(pCache->pFile, "key-#%u=%u\n", i, pDigest->uKey);
3263 fprintf(pCache->pFile, "target-#%u=%s\n", i, pDigest->pszTarget);
3264 fprintf(pCache->pFile, "comp-argv-sum-#%u=", i);
3265 kOCSumFPrintf(&pDigest->SumCompArgv, pCache->pFile);
3266 for (pSum = &pDigest->SumHead; pSum; pSum = pSum->pNext)
3267 {
3268 fprintf(pCache->pFile, "sum-#%u=", i);
3269 kOCSumFPrintf(pSum, pCache->pFile);
3270 }
3271 }
3272
3273 /*
3274 * Close the stream and unlock fhe file.
3275 * (Closing the stream shouldn't close the file handle IIRC...)
3276 */
3277 fprintf(pCache->pFile, "the-end=fine\n");
3278 errno = 0;
3279 if ( fflush(pCache->pFile) < 0
3280 || ferror(pCache->pFile))
3281 {
3282 int iErr = errno;
3283 fclose(pCache->pFile);
3284 UnlinkFileInDir(pCache->pszName, pCache->pszDir);
3285 FatalDie("Stream error occured while writing '%s' in '%s': %s\n",
3286 pCache->pszName, pCache->pszDir, strerror(iErr));
3287 }
3288 if (setvbuf(pCache->pFile, NULL, _IONBF, 0) != 0)
3289 FatalDie("setvbuf(,0,,0) failed: %s\n", strerror(errno));
3290
3291 cb = lseek(pCache->fd, 0, SEEK_CUR);
3292 if (cb == -1)
3293 FatalDie("lseek(cache-file,0,CUR) failed: %s\n", strerror(errno));
3294#if defined(__WIN__)
3295 if (_chsize(pCache->fd, cb) == -1)
3296#else
3297 if (ftruncate(pCache->fd, cb) == -1)
3298#endif
3299 FatalDie("file truncation failed: %s\n", strerror(errno));
3300 InfoMsg(4, "wrote '%s' in '%s', %d bytes\n", pCache->pszName, pCache->pszDir, cb);
3301}
3302
3303
3304/**
3305 * Cleans out all invalid digests.s
3306 *
3307 * This is done periodically from the unlock routine to make
3308 * sure we don't accidentally accumulate stale digests.
3309 *
3310 * @param pCache The cache to chek.
3311 */
3312static void kObjCacheClean(PKOBJCACHE pCache)
3313{
3314 unsigned i = pCache->cDigests;
3315 while (i-- > 0)
3316 {
3317 /*
3318 * Try open it and purge it if it's bad.
3319 * (We don't kill the entry file because that's kmk clean's job.)
3320 */
3321 PCKOCDIGEST pDigest = &pCache->paDigests[i];
3322 PKOCENTRY pEntry = kOCEntryCreate(kOCDigestAbsPath(pDigest, pCache->pszDir));
3323 kOCEntryRead(pEntry);
3324 if ( !kOCEntryCheck(pEntry)
3325 || !kOCDigestIsValid(pDigest, pEntry))
3326 {
3327 unsigned cLeft;
3328 kOCDigestPurge(pDigest);
3329
3330 pCache->cDigests--;
3331 cLeft = pCache->cDigests - i;
3332 if (cLeft)
3333 memmove(pDigest, pDigest + 1, cLeft * sizeof(*pDigest));
3334
3335 pCache->fDirty = 1;
3336 }
3337 kOCEntryDestroy(pEntry);
3338 }
3339}
3340
3341
3342/**
3343 * Locks the cache for exclusive access.
3344 *
3345 * This will open the file if necessary and lock the entire file
3346 * using the best suitable platform API (tricky).
3347 *
3348 * @param pCache The cache to lock.
3349 */
3350static void kObjCacheLock(PKOBJCACHE pCache)
3351{
3352 struct stat st;
3353#if defined(__WIN__)
3354 OVERLAPPED OverLapped;
3355#endif
3356
3357 assert(!pCache->fLocked);
3358
3359 /*
3360 * Open it?
3361 */
3362 if (pCache->fd < 0)
3363 {
3364 pCache->fd = OpenFileInDir(pCache->pszName, pCache->pszDir, O_CREAT | O_RDWR | O_BINARY, 0666);
3365 if (pCache->fd == -1)
3366 {
3367 MakePath(pCache->pszDir);
3368 pCache->fd = OpenFileInDir(pCache->pszName, pCache->pszDir, O_CREAT | O_RDWR | O_BINARY, 0666);
3369 if (pCache->fd == -1)
3370 FatalDie("Failed to create '%s' in '%s': %s\n", pCache->pszName, pCache->pszDir, strerror(errno));
3371 }
3372
3373 pCache->pFile = fdopen(pCache->fd, "r+b");
3374 if (!pCache->pFile)
3375 FatalDie("fdopen failed: %s\n", strerror(errno));
3376 if (setvbuf(pCache->pFile, NULL, _IONBF, 0) != 0)
3377 FatalDie("setvbuf(,0,,0) failed: %s\n", strerror(errno));
3378 }
3379
3380 /*
3381 * Lock it.
3382 */
3383#if defined(__WIN__)
3384 memset(&OverLapped, 0, sizeof(OverLapped));
3385 if (!LockFileEx((HANDLE)_get_osfhandle(pCache->fd), LOCKFILE_EXCLUSIVE_LOCK, 0, ~0, 0, &OverLapped))
3386 FatalDie("Failed to lock the cache file: Windows Error %d\n", GetLastError());
3387#elif defined(__sun__)
3388 {
3389 struct flock fl;
3390 fl.l_whence = 0;
3391 fl.l_start = 0;
3392 fl.l_len = 0;
3393 fl.l_type = F_WRLCK;
3394 if (fcntl(pCache->fd, F_SETLKW, &fl) != 0)
3395 FatalDie("Failed to lock the cache file: %s\n", strerror(errno));
3396 }
3397#else
3398 if (flock(pCache->fd, LOCK_EX) != 0)
3399 FatalDie("Failed to lock the cache file: %s\n", strerror(errno));
3400#endif
3401 pCache->fLocked = 1;
3402
3403 /*
3404 * Check for new cache and read it it's an existing cache.
3405 *
3406 * There is no point in initializing a new cache until we've finished
3407 * compiling and has something to put into it, so we'll leave it as a
3408 * 0 byte file.
3409 */
3410 if (fstat(pCache->fd, &st) == -1)
3411 FatalDie("fstat(cache-fd) failed: %s\n", strerror(errno));
3412 if (st.st_size)
3413 kObjCacheRead(pCache);
3414 else
3415 {
3416 pCache->fNewCache = 1;
3417 InfoMsg(2, "the cache file is empty\n");
3418 }
3419}
3420
3421
3422/**
3423 * Unlocks the cache (without writing anything back).
3424 *
3425 * @param pCache The cache to unlock.
3426 */
3427static void kObjCacheUnlock(PKOBJCACHE pCache)
3428{
3429#if defined(__WIN__)
3430 OVERLAPPED OverLapped;
3431#endif
3432 assert(pCache->fLocked);
3433
3434 /*
3435 * Write it back if it's dirty.
3436 */
3437 if (pCache->fDirty)
3438 {
3439 if ( pCache->cDigests >= 16
3440 && (pCache->uGeneration % 19) == 19)
3441 kObjCacheClean(pCache);
3442 kObjCacheWrite(pCache);
3443 pCache->fDirty = 0;
3444 }
3445
3446 /*
3447 * Lock it.
3448 */
3449#if defined(__WIN__)
3450 memset(&OverLapped, 0, sizeof(OverLapped));
3451 if (!UnlockFileEx((HANDLE)_get_osfhandle(pCache->fd), 0, ~0U, 0, &OverLapped))
3452 FatalDie("Failed to unlock the cache file: Windows Error %d\n", GetLastError());
3453#elif defined(__sun__)
3454 {
3455 struct flock fl;
3456 fl.l_whence = 0;
3457 fl.l_start = 0;
3458 fl.l_len = 0;
3459 fl.l_type = F_UNLCK;
3460 if (fcntl(pCache->fd, F_SETLKW, &fl) != 0)
3461 FatalDie("Failed to lock the cache file: %s\n", strerror(errno));
3462 }
3463#else
3464 if (flock(pCache->fd, LOCK_UN) != 0)
3465 FatalDie("Failed to unlock the cache file: %s\n", strerror(errno));
3466#endif
3467 pCache->fLocked = 0;
3468}
3469
3470
3471/**
3472 * Removes the entry from the cache.
3473 *
3474 * The entry doesn't need to be in the cache.
3475 * The cache entry (file) itself is not touched.
3476 *
3477 * @param pCache The cache.
3478 * @param pEntry The entry.
3479 */
3480static void kObjCacheRemoveEntry(PKOBJCACHE pCache, PCKOCENTRY pEntry)
3481{
3482 unsigned i = pCache->cDigests;
3483 while (i-- > 0)
3484 {
3485 PKOCDIGEST pDigest = &pCache->paDigests[i];
3486 if (ArePathsIdentical(kOCDigestAbsPath(pDigest, pCache->pszDir),
3487 kOCEntryAbsPath(pEntry), ~0U))
3488 {
3489 unsigned cLeft;
3490 kOCDigestPurge(pDigest);
3491
3492 pCache->cDigests--;
3493 cLeft = pCache->cDigests - i;
3494 if (cLeft)
3495 memmove(pDigest, pDigest + 1, cLeft * sizeof(*pDigest));
3496
3497 pCache->fDirty = 1;
3498 InfoMsg(3, "removing entry '%s'; %d left.\n", kOCEntryAbsPath(pEntry), pCache->cDigests);
3499 }
3500 }
3501}
3502
3503
3504/**
3505 * Inserts the entry into the cache.
3506 *
3507 * The cache entry (file) itself is not touched by this operation,
3508 * the pEntry object otoh is.
3509 *
3510 * @param pCache The cache.
3511 * @param pEntry The entry.
3512 */
3513static void kObjCacheInsertEntry(PKOBJCACHE pCache, PKOCENTRY pEntry)
3514{
3515 unsigned i;
3516
3517 /*
3518 * Find a new key.
3519 */
3520 pEntry->uKey = pCache->uNextKey++;
3521 if (!pEntry->uKey)
3522 pEntry->uKey = pCache->uNextKey++;
3523 i = pCache->cDigests;
3524 while (i-- > 0)
3525 if (pCache->paDigests[i].uKey == pEntry->uKey)
3526 {
3527 pEntry->uKey = pCache->uNextKey++;
3528 if (!pEntry->uKey)
3529 pEntry->uKey = pCache->uNextKey++;
3530 i = pCache->cDigests;
3531 }
3532
3533 /*
3534 * Reallocate the digest array?
3535 */
3536 if ( !(pCache->cDigests & 3)
3537 && (pCache->cDigests || !pCache->paDigests))
3538 pCache->paDigests = xrealloc(pCache->paDigests, sizeof(pCache->paDigests[0]) * (pCache->cDigests + 4));
3539
3540 /*
3541 * Create a new digest.
3542 */
3543 kOCDigestInitFromEntry(&pCache->paDigests[pCache->cDigests], pEntry);
3544 pCache->cDigests++;
3545 InfoMsg(4, "Inserted digest #%u: %s\n", pCache->cDigests - 1, kOCEntryAbsPath(pEntry));
3546
3547 pCache->fDirty = 1;
3548}
3549
3550
3551/**
3552 * Find a matching cache entry.
3553 */
3554static PKOCENTRY kObjCacheFindMatchingEntry(PKOBJCACHE pCache, PCKOCENTRY pEntry)
3555{
3556 unsigned i = pCache->cDigests;
3557
3558 assert(pEntry->fNeedCompiling);
3559 assert(!kOCSumIsEmpty(&pEntry->New.SumCompArgv));
3560 assert(!kOCSumIsEmpty(&pEntry->New.SumHead));
3561
3562 while (i-- > 0)
3563 {
3564 /*
3565 * Matching?
3566 */
3567 PCKOCDIGEST pDigest = &pCache->paDigests[i];
3568 if ( kOCSumIsEqual(&pDigest->SumCompArgv, &pEntry->New.SumCompArgv)
3569 && kOCSumHasEqualInChain(&pDigest->SumHead, &pEntry->New.SumHead))
3570 {
3571 /*
3572 * Try open it.
3573 */
3574 unsigned cLeft;
3575 PKOCENTRY pRetEntry = kOCEntryCreate(kOCDigestAbsPath(pDigest, pCache->pszDir));
3576 kOCEntryRead(pRetEntry);
3577 if ( kOCEntryCheck(pRetEntry)
3578 && kOCDigestIsValid(pDigest, pRetEntry))
3579 return pRetEntry;
3580 kOCEntryDestroy(pRetEntry);
3581
3582 /* bad entry, purge it. */
3583 InfoMsg(3, "removing bad digest '%s'\n", kOCDigestAbsPath(pDigest, pCache->pszDir));
3584 kOCDigestPurge(pDigest);
3585
3586 pCache->cDigests--;
3587 cLeft = pCache->cDigests - i;
3588 if (cLeft)
3589 memmove(pDigest, pDigest + 1, cLeft * sizeof(*pDigest));
3590
3591 pCache->fDirty = 1;
3592 }
3593 }
3594
3595 return NULL;
3596}
3597
3598
3599/**
3600 * Is this a new cache?
3601 *
3602 * @returns 1 if new, 0 if not new.
3603 * @param pEntry The entry.
3604 */
3605static int kObjCacheIsNew(PKOBJCACHE pCache)
3606{
3607 return pCache->fNewCache;
3608}
3609
3610
3611/**
3612 * Prints a syntax error and returns the appropriate exit code
3613 *
3614 * @returns approriate exit code.
3615 * @param pszFormat The syntax error message.
3616 * @param ... Message args.
3617 */
3618static int SyntaxError(const char *pszFormat, ...)
3619{
3620 va_list va;
3621 fprintf(stderr, "kObjCache: syntax error: ");
3622 va_start(va, pszFormat);
3623 vfprintf(stderr, pszFormat, va);
3624 va_end(va);
3625 return 1;
3626}
3627
3628
3629/**
3630 * Prints the usage.
3631 * @returns 0.
3632 */
3633static int usage(FILE *pOut)
3634{
3635 fprintf(pOut,
3636 "syntax: kObjCache [--kObjCache-options] [-v|--verbose]\n"
3637 " < [-c|--cache-file <cache-file>]\n"
3638 " | [-n|--name <name-in-cache>] [[-d|--cache-dir <cache-dir>]] >\n"
3639 " <-f|--file <local-cache-file>>\n"
3640 " <-t|--target <target-name>>\n"
3641 " [-r|--redir-stdout] [-p|--passthru]\n"
3642 " --kObjCache-cpp <filename> <precompiler + args>\n"
3643 " --kObjCache-cc <object> <compiler + args>\n"
3644 " [--kObjCache-both [args]]\n"
3645 " [--kObjCache-cpp|--kObjCache-cc [more args]]\n"
3646 " kObjCache <-V|--version>\n"
3647 " kObjCache [-?|/?|-h|/h|--help|/help]\n"
3648 "\n"
3649 "The env.var. KOBJCACHE_DIR sets the default cache diretory (-d).\n"
3650 "The env.var. KOBJCACHE_OPTS allow you to specifie additional options\n"
3651 "without having to mess with the makefiles. These are appended with "
3652 "a --kObjCache-options between them and the command args.\n"
3653 "\n");
3654 return 0;
3655}
3656
3657
3658int main(int argc, char **argv)
3659{
3660 PKOBJCACHE pCache;
3661 PKOCENTRY pEntry;
3662
3663 const char *pszCacheDir = getenv("KOBJCACHE_DIR");
3664 const char *pszCacheName = NULL;
3665 const char *pszCacheFile = NULL;
3666 const char *pszEntryFile = NULL;
3667
3668 const char **papszArgvPreComp = NULL;
3669 unsigned cArgvPreComp = 0;
3670 const char *pszPreCompName = NULL;
3671 int fRedirPreCompStdOut = 0;
3672
3673 const char **papszArgvCompile = NULL;
3674 unsigned cArgvCompile = 0;
3675 const char *pszObjName = NULL;
3676 int fRedirCompileStdIn = 0;
3677
3678 const char *pszTarget = NULL;
3679
3680 enum { kOC_Options, kOC_CppArgv, kOC_CcArgv, kOC_BothArgv } enmMode = kOC_Options;
3681
3682 size_t cch;
3683 char *psz;
3684 int i;
3685
3686 SetErrorPrefix("kObjCache");
3687
3688 /*
3689 * Arguments passed in the environmnet?
3690 */
3691 psz = getenv("KOBJCACHE_OPTS");
3692 if (psz)
3693 AppendArgs(&argc, &argv, psz, "--kObjCache-options");
3694
3695 /*
3696 * Parse the arguments.
3697 */
3698 if (argc <= 1)
3699 return usage(stderr);
3700 for (i = 1; i < argc; i++)
3701 {
3702 if (!strcmp(argv[i], "--kObjCache-cpp"))
3703 {
3704 enmMode = kOC_CppArgv;
3705 if (!pszPreCompName)
3706 {
3707 if (++i >= argc)
3708 return SyntaxError("--kObjCache-cpp requires an object filename!\n");
3709 pszPreCompName = argv[i];
3710 }
3711 }
3712 else if (!strcmp(argv[i], "--kObjCache-cc"))
3713 {
3714 enmMode = kOC_CcArgv;
3715 if (!pszObjName)
3716 {
3717 if (++i >= argc)
3718 return SyntaxError("--kObjCache-cc requires an precompiler output filename!\n");
3719 pszObjName = argv[i];
3720 }
3721 }
3722 else if (!strcmp(argv[i], "--kObjCache-both"))
3723 enmMode = kOC_BothArgv;
3724 else if (!strcmp(argv[i], "--kObjCache-options"))
3725 enmMode = kOC_Options;
3726 else if (!strcmp(argv[i], "--help"))
3727 return usage(stderr);
3728 else if (enmMode != kOC_Options)
3729 {
3730 if (enmMode == kOC_CppArgv || enmMode == kOC_BothArgv)
3731 {
3732 if (!(cArgvPreComp % 16))
3733 papszArgvPreComp = xrealloc((void *)papszArgvPreComp, (cArgvPreComp + 17) * sizeof(papszArgvPreComp[0]));
3734 papszArgvPreComp[cArgvPreComp++] = argv[i];
3735 papszArgvPreComp[cArgvPreComp] = NULL;
3736 }
3737 if (enmMode == kOC_CcArgv || enmMode == kOC_BothArgv)
3738 {
3739 if (!(cArgvCompile % 16))
3740 papszArgvCompile = xrealloc((void *)papszArgvCompile, (cArgvCompile + 17) * sizeof(papszArgvCompile[0]));
3741 papszArgvCompile[cArgvCompile++] = argv[i];
3742 papszArgvCompile[cArgvCompile] = NULL;
3743 }
3744 }
3745 else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--entry-file"))
3746 {
3747 if (i + 1 >= argc)
3748 return SyntaxError("%s requires a cache entry filename!\n", argv[i]);
3749 pszEntryFile = argv[++i];
3750 }
3751 else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--cache-file"))
3752 {
3753 if (i + 1 >= argc)
3754 return SyntaxError("%s requires a cache filename!\n", argv[i]);
3755 pszCacheFile = argv[++i];
3756 }
3757 else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--name"))
3758 {
3759 if (i + 1 >= argc)
3760 return SyntaxError("%s requires a cache name!\n", argv[i]);
3761 pszCacheName = argv[++i];
3762 }
3763 else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--cache-dir"))
3764 {
3765 if (i + 1 >= argc)
3766 return SyntaxError("%s requires a cache directory!\n", argv[i]);
3767 pszCacheDir = argv[++i];
3768 }
3769 else if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--target"))
3770 {
3771 if (i + 1 >= argc)
3772 return SyntaxError("%s requires a target platform/arch name!\n", argv[i]);
3773 pszTarget = argv[++i];
3774 }
3775 else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--passthru"))
3776 fRedirPreCompStdOut = fRedirCompileStdIn = 1;
3777 else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--redir-stdout"))
3778 fRedirPreCompStdOut = 1;
3779 else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))
3780 g_cVerbosityLevel++;
3781 else if (!strcmp(argv[i], "-q") || !strcmp(argv[i], "--quiet"))
3782 g_cVerbosityLevel = 0;
3783 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-?")
3784 || !strcmp(argv[i], "/h") || !strcmp(argv[i], "/?") || !strcmp(argv[i], "/help"))
3785 {
3786 usage(stdout);
3787 return 0;
3788 }
3789 else if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version"))
3790 {
3791 printf("kObjCache - kBuild version %d.%d.%d ($Revision: 1184 $)\n"
3792 "Copyright (C) 2007 Knut St. Osmundsen\n",
3793 KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH);
3794 return 0;
3795 }
3796 else
3797 return SyntaxError("Doesn't grok '%s'!\n", argv[i]);
3798 }
3799 if (!pszEntryFile)
3800 return SyntaxError("No cache entry filename (-f)!\n");
3801 if (!pszTarget)
3802 return SyntaxError("No target name (-t)!\n");
3803 if (!cArgvCompile)
3804 return SyntaxError("No compiler arguments (--kObjCache-cc)!\n");
3805 if (!cArgvPreComp)
3806 return SyntaxError("No precompiler arguments (--kObjCache-cc)!\n");
3807
3808 /*
3809 * Calc the cache file name.
3810 * It's a bit messy since the extension has to be replaced.
3811 */
3812 if (!pszCacheFile)
3813 {
3814 if (!pszCacheDir)
3815 return SyntaxError("No cache dir (-d / KOBJCACHE_DIR) and no cache filename!\n");
3816 if (!pszCacheName)
3817 {
3818 psz = (char *)FindFilenameInPath(pszEntryFile);
3819 if (!*psz)
3820 return SyntaxError("The cache file (-f) specifies a directory / nothing!\n");
3821 cch = psz - pszEntryFile;
3822 pszCacheName = memcpy(xmalloc(cch + 5), psz, cch + 1);
3823 psz = strrchr(pszCacheName, '.');
3824 if (!psz || psz <= pszCacheName)
3825 psz = (char *)pszCacheName + cch;
3826 memcpy(psz, ".koc", sizeof(".koc") - 1);
3827 }
3828 pszCacheFile = MakePathFromDirAndFile(pszCacheName, pszCacheDir);
3829 }
3830
3831 /*
3832 * Create and initialize the two objects we'll be working on.
3833 *
3834 * We're supposed to be the only ones actually writing to the local file,
3835 * so it's perfectly fine to read it here before we lock it. This simplifies
3836 * the detection of object name and compiler argument changes.
3837 */
3838 SetErrorPrefix("kObjCache - %s", FindFilenameInPath(pszCacheFile));
3839 pCache = kObjCacheCreate(pszCacheFile);
3840
3841 pEntry = kOCEntryCreate(pszEntryFile);
3842 kOCEntryRead(pEntry);
3843 kOCEntrySetCompileObjName(pEntry, pszObjName);
3844 kOCEntrySetCompileArgv(pEntry, papszArgvCompile, cArgvCompile);
3845 kOCEntrySetTarget(pEntry, pszTarget);
3846 kOCEntrySetCppName(pEntry, pszPreCompName);
3847 kOCEntrySetPipedMode(pEntry, fRedirPreCompStdOut, fRedirCompileStdIn);
3848
3849 /*
3850 * Open (& lock) the two files and do validity checks and such.
3851 */
3852 kObjCacheLock(pCache);
3853 if ( kObjCacheIsNew(pCache)
3854 && kOCEntryNeedsCompiling(pEntry))
3855 {
3856 /*
3857 * Both files are missing/invalid.
3858 * Optimize this path as it is frequently used when making a clean build.
3859 */
3860 kObjCacheUnlock(pCache);
3861 InfoMsg(1, "doing full compile\n");
3862 kOCEntryPreCompileAndCompile(pEntry, papszArgvPreComp, cArgvPreComp);
3863 kObjCacheLock(pCache);
3864 }
3865 else
3866 {
3867 /*
3868 * Do the precompile (don't need to lock the cache file for this).
3869 */
3870 kObjCacheUnlock(pCache);
3871 kOCEntryPreCompile(pEntry, papszArgvPreComp, cArgvPreComp);
3872
3873 /*
3874 * Check if we need to recompile. If we do, try see if the is a cache entry first.
3875 */
3876 kOCEntryCalcRecompile(pEntry);
3877 if (kOCEntryNeedsCompiling(pEntry))
3878 {
3879 PKOCENTRY pUseEntry;
3880 kObjCacheLock(pCache);
3881 kObjCacheRemoveEntry(pCache, pEntry);
3882 pUseEntry = kObjCacheFindMatchingEntry(pCache, pEntry);
3883 if (pUseEntry)
3884 {
3885 InfoMsg(1, "using cache entry '%s'\n", kOCEntryAbsPath(pUseEntry));
3886 kOCEntryCopy(pEntry, pUseEntry);
3887 kOCEntryDestroy(pUseEntry);
3888 }
3889 else
3890 {
3891 kObjCacheUnlock(pCache);
3892 InfoMsg(1, "recompiling\n");
3893 kOCEntryCompileIt(pEntry);
3894 kObjCacheLock(pCache);
3895 }
3896 }
3897 else
3898 {
3899 InfoMsg(1, "no need to recompile\n");
3900 kObjCacheLock(pCache);
3901 }
3902 }
3903
3904 /*
3905 * Update the cache files.
3906 */
3907 kObjCacheRemoveEntry(pCache, pEntry);
3908 kObjCacheInsertEntry(pCache, pEntry);
3909 kOCEntryWrite(pEntry);
3910 kObjCacheUnlock(pCache);
3911 kObjCacheDestroy(pCache);
3912 return 0;
3913}
3914
3915
3916/** @page kObjCache Benchmarks.
3917 *
3918 * (2007-06-10)
3919 *
3920 * 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):
3921 * real 11m28.811s
3922 * user 13m59.291s
3923 * sys 3m24.590s
3924 *
3925 * 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):
3926 * real 1m26.895s
3927 * user 1m26.971s
3928 * sys 0m32.532s
3929 *
3930 * 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):
3931 * real 1m18.049s
3932 * user 1m20.462s
3933 * sys 0m27.887s
3934 *
3935 * 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):
3936 * real 13m27.751s
3937 * user 18m12.654s
3938 * sys 3m25.170s
3939 *
3940 * 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):
3941 * real 9m9.720s
3942 * user 8m53.005s
3943 * sys 2m13.110s
3944 *
3945 * 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):
3946 * real 10m18.129s
3947 * user 12m52.687s
3948 * sys 2m51.277s
3949 *
3950 * 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):
3951 * real 4m46.147s
3952 * user 5m27.087s
3953 * sys 1m11.775s
3954 *
3955 * 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):
3956 * real 4m17.572s
3957 * user 5m7.450s
3958 * sys 1m3.450s
3959 *
3960 * 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):
3961 * real 12m14.742s
3962 * user 17m11.794s
3963 * sys 2m51.454s
3964 *
3965 * 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):
3966 * real 12m33.821s
3967 * user 17m35.086s
3968 * sys 2m53.312s
3969 *
3970 * Note. The profile build can pick object files from the release build.
3971 * (all with KOBJCACHE_OPTS=-v; which means a bit more output and perhaps a second or two slower.)
3972 */
3973
Note: See TracBrowser for help on using the repository browser.

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